1a7b22450SDavid du Colombier #include <u.h>
2a7b22450SDavid du Colombier #include <libc.h>
3a7b22450SDavid du Colombier #include <draw.h>
4a7b22450SDavid du Colombier #include <event.h>
5a7b22450SDavid du Colombier
6a7b22450SDavid du Colombier #include "sokoban.h"
7a7b22450SDavid du Colombier
80a5ecf54SDavid du Colombier #define SOKOTREE "/sys/games/lib/sokoban/"
90a5ecf54SDavid du Colombier
100a5ecf54SDavid du Colombier char *LEasy = SOKOTREE "levels/easy.slc";
110a5ecf54SDavid du Colombier char *LHard = SOKOTREE "levels/hard.slc";
12a7b22450SDavid du Colombier char *levelfile;
13a7b22450SDavid du Colombier
140a5ecf54SDavid du Colombier #define SOKOIMG SOKOTREE "images/"
150a5ecf54SDavid du Colombier
160a5ecf54SDavid du Colombier char *GRImage = SOKOIMG "right.bit";
170a5ecf54SDavid du Colombier char *GLImage = SOKOIMG "left.bit";
180a5ecf54SDavid du Colombier char *WallImage = SOKOIMG "wall.bit";
190a5ecf54SDavid du Colombier char *EmptyImage = SOKOIMG "empty.bit";
200a5ecf54SDavid du Colombier char *CargoImage = SOKOIMG "cargo.bit";
210a5ecf54SDavid du Colombier char *GoalCargoImage= SOKOIMG "goalcargo.bit";
220a5ecf54SDavid du Colombier char *GoalImage = SOKOIMG "goal.bit";
230a5ecf54SDavid du Colombier char *WinImage = SOKOIMG "win.bit";
24a7b22450SDavid du Colombier
25a7b22450SDavid du Colombier char *buttons[] =
26a7b22450SDavid du Colombier {
27a7b22450SDavid du Colombier "restart",
28a7b22450SDavid du Colombier "easy",
29a7b22450SDavid du Colombier "hard",
3097787252SDavid du Colombier "noanimate", /* this menu string initialized in main */
31a7b22450SDavid du Colombier "exit",
32a7b22450SDavid du Colombier 0
33a7b22450SDavid du Colombier };
34a7b22450SDavid du Colombier
3501e2ed2dSDavid du Colombier char **levelnames;
3601e2ed2dSDavid du Colombier
37a7b22450SDavid du Colombier Menu menu =
38a7b22450SDavid du Colombier {
3901e2ed2dSDavid du Colombier buttons,
40a7b22450SDavid du Colombier };
41a7b22450SDavid du Colombier
42*68cc0f82SDavid du Colombier Menu lmenu;
43a7b22450SDavid du Colombier
4401e2ed2dSDavid du Colombier void
buildmenu(void)4501e2ed2dSDavid du Colombier buildmenu(void)
4601e2ed2dSDavid du Colombier {
4701e2ed2dSDavid du Colombier int i;
4801e2ed2dSDavid du Colombier
4901e2ed2dSDavid du Colombier if (levelnames != nil) {
5001e2ed2dSDavid du Colombier for(i=0; levelnames[i] != 0; i++)
5101e2ed2dSDavid du Colombier free(levelnames[i]);
5201e2ed2dSDavid du Colombier }
5301e2ed2dSDavid du Colombier levelnames = realloc(levelnames, sizeof(char*)*(numlevels+1));
5401e2ed2dSDavid du Colombier if (levelnames == nil)
5501e2ed2dSDavid du Colombier sysfatal("cannot allocate levelnames");
5601e2ed2dSDavid du Colombier for(i=0; i < numlevels; i++)
5701e2ed2dSDavid du Colombier levelnames[i] = genlevels(i);
5801e2ed2dSDavid du Colombier levelnames[numlevels] = 0;
5901e2ed2dSDavid du Colombier lmenu.item = levelnames;
6001e2ed2dSDavid du Colombier }
6101e2ed2dSDavid du Colombier
62a7b22450SDavid du Colombier Image *
eallocimage(Rectangle r,int repl,uint color)63a7b22450SDavid du Colombier eallocimage(Rectangle r, int repl, uint color)
64a7b22450SDavid du Colombier {
65a7b22450SDavid du Colombier Image *tmp;
66a7b22450SDavid du Colombier
67a7b22450SDavid du Colombier tmp = allocimage(display, r, screen->chan, repl, color);
68a7b22450SDavid du Colombier if(tmp == nil)
69a7b22450SDavid du Colombier sysfatal("cannot allocate buffer image: %r");
70a7b22450SDavid du Colombier
71a7b22450SDavid du Colombier return tmp;
72a7b22450SDavid du Colombier }
73a7b22450SDavid du Colombier
74a7b22450SDavid du Colombier Image *
eloadfile(char * path)75a7b22450SDavid du Colombier eloadfile(char *path)
76a7b22450SDavid du Colombier {
77a7b22450SDavid du Colombier Image *img;
78a7b22450SDavid du Colombier int fd;
79a7b22450SDavid du Colombier
80a7b22450SDavid du Colombier fd = open(path, OREAD);
81a7b22450SDavid du Colombier if(fd < 0) {
82a7b22450SDavid du Colombier fprint(2, "cannot open image file %s: %r\n", path);
83a7b22450SDavid du Colombier exits("image");
84a7b22450SDavid du Colombier }
85a7b22450SDavid du Colombier img = readimage(display, fd, 0);
86a7b22450SDavid du Colombier if(img == nil)
87a7b22450SDavid du Colombier sysfatal("cannot load image: %r");
88a7b22450SDavid du Colombier close(fd);
89a7b22450SDavid du Colombier
90a7b22450SDavid du Colombier return img;
91a7b22450SDavid du Colombier }
92a7b22450SDavid du Colombier
93a7b22450SDavid du Colombier
94a7b22450SDavid du Colombier void
allocimages(void)95a7b22450SDavid du Colombier allocimages(void)
96a7b22450SDavid du Colombier {
97a7b22450SDavid du Colombier Rectangle one = Rect(0, 0, 1, 1);
98a7b22450SDavid du Colombier
99a7b22450SDavid du Colombier bg = eallocimage(one, 1, DDarkyellow);
100a7b22450SDavid du Colombier text = eallocimage(one, 1, DBluegreen);
101a7b22450SDavid du Colombier
102a7b22450SDavid du Colombier gright = eloadfile(GRImage);
103a7b22450SDavid du Colombier gleft = eloadfile(GLImage);
104a7b22450SDavid du Colombier wall = eloadfile(WallImage);
105a7b22450SDavid du Colombier empty = eloadfile(EmptyImage);
106a7b22450SDavid du Colombier empty->repl = 1;
107a7b22450SDavid du Colombier goalcargo = eloadfile(GoalCargoImage);
108a7b22450SDavid du Colombier cargo = eloadfile(CargoImage);
109a7b22450SDavid du Colombier goal = eloadfile(GoalImage);
110a7b22450SDavid du Colombier win = eloadfile(WinImage);
111a7b22450SDavid du Colombier }
112a7b22450SDavid du Colombier
113a7b22450SDavid du Colombier int
key2move(int key)114a7b22450SDavid du Colombier key2move(int key)
115a7b22450SDavid du Colombier {
116a7b22450SDavid du Colombier int k = 0;
117a7b22450SDavid du Colombier
118a7b22450SDavid du Colombier switch(key) {
119a7b22450SDavid du Colombier case 61454:
120a7b22450SDavid du Colombier k = Up;
121a7b22450SDavid du Colombier break;
122a7b22450SDavid du Colombier case 63488:
123a7b22450SDavid du Colombier k = Down;
124a7b22450SDavid du Colombier break;
125a7b22450SDavid du Colombier case 61457:
126a7b22450SDavid du Colombier k = Left;
127a7b22450SDavid du Colombier break;
128a7b22450SDavid du Colombier case 61458:
129a7b22450SDavid du Colombier k = Right;
130a7b22450SDavid du Colombier break;
131a7b22450SDavid du Colombier }
132a7b22450SDavid du Colombier
133a7b22450SDavid du Colombier return k;
134a7b22450SDavid du Colombier }
13597787252SDavid du Colombier
13697787252SDavid du Colombier static Route*
mouse2route(Mouse m)13797787252SDavid du Colombier mouse2route(Mouse m)
138a7b22450SDavid du Colombier {
13997787252SDavid du Colombier Point p, q;
14001e2ed2dSDavid du Colombier Route *r;
141a7b22450SDavid du Colombier
142a7b22450SDavid du Colombier p = subpt(m.xy, screen->r.min);
143a7b22450SDavid du Colombier p.x /= BoardX;
144a7b22450SDavid du Colombier p.y /= BoardY;
145a7b22450SDavid du Colombier
14697787252SDavid du Colombier q = subpt(p, level.glenda);
14797787252SDavid du Colombier // fprint(2, "x=%d y=%d\n", q.x, q.y);
14897787252SDavid du Colombier
14997787252SDavid du Colombier if (q.x == 0 && q.y == 0)
15001e2ed2dSDavid du Colombier return nil;
15197787252SDavid du Colombier
15201e2ed2dSDavid du Colombier if (q.x == 0 || q.y == 0) {
15397787252SDavid du Colombier if (q.x < 0)
15401e2ed2dSDavid du Colombier r = extend(nil, Left, -q.x, Pt(level.glenda.x, p.y));
15501e2ed2dSDavid du Colombier else if (q.x > 0)
15601e2ed2dSDavid du Colombier r = extend(nil, Right, q.x, Pt(level.glenda.x, p.y));
15701e2ed2dSDavid du Colombier else if (q.y < 0)
15801e2ed2dSDavid du Colombier r = extend(nil, Up, -q.y, level.glenda);
15901e2ed2dSDavid du Colombier else if (q.y > 0)
16001e2ed2dSDavid du Colombier r = extend(nil, Down, q.y, level.glenda);
16101e2ed2dSDavid du Colombier else
16201e2ed2dSDavid du Colombier r = nil;
16397787252SDavid du Colombier
16401e2ed2dSDavid du Colombier if (r != nil && isvalid(level.glenda, r, validpush))
16597787252SDavid du Colombier return r;
16697787252SDavid du Colombier freeroute(r);
16701e2ed2dSDavid du Colombier }
16897787252SDavid du Colombier
16901e2ed2dSDavid du Colombier return findroute(level.glenda, p);
170a7b22450SDavid du Colombier }
171a7b22450SDavid du Colombier
172a7b22450SDavid du Colombier char *
genlevels(int i)173a7b22450SDavid du Colombier genlevels(int i)
174a7b22450SDavid du Colombier {
175a7b22450SDavid du Colombier
176a7b22450SDavid du Colombier if(i >= numlevels)
177a7b22450SDavid du Colombier return 0;
178a7b22450SDavid du Colombier
179a7b22450SDavid du Colombier return smprint("level %d", i+1);
180a7b22450SDavid du Colombier }
181a7b22450SDavid du Colombier
182a7b22450SDavid du Colombier
183a7b22450SDavid du Colombier int
finished(void)184a7b22450SDavid du Colombier finished(void)
185a7b22450SDavid du Colombier {
186a7b22450SDavid du Colombier int x, y;
187a7b22450SDavid du Colombier for(x = 0; x < MazeX; x++)
188a7b22450SDavid du Colombier for(y = 0; y < MazeY; y++)
189a7b22450SDavid du Colombier if(level.board[x][y] == Goal)
190a7b22450SDavid du Colombier return 0;
191a7b22450SDavid du Colombier
192a7b22450SDavid du Colombier return 1;
193a7b22450SDavid du Colombier }
194a7b22450SDavid du Colombier
195a7b22450SDavid du Colombier void
eresized(int new)196a7b22450SDavid du Colombier eresized(int new)
197a7b22450SDavid du Colombier {
198a7b22450SDavid du Colombier Point p;
199a7b22450SDavid du Colombier
200a7b22450SDavid du Colombier if(new && getwindow(display, Refnone) < 0)
201a7b22450SDavid du Colombier sysfatal("can't reattach to window");
202a7b22450SDavid du Colombier
203a7b22450SDavid du Colombier p = Pt(Dx(screen->r), Dy(screen->r));
204a7b22450SDavid du Colombier
205a7b22450SDavid du Colombier if(!new || !eqpt(p, boardsize(level.max))) {
206a7b22450SDavid du Colombier drawlevel();
207a7b22450SDavid du Colombier }
208a7b22450SDavid du Colombier drawscreen();
209a7b22450SDavid du Colombier }
210a7b22450SDavid du Colombier
211a7b22450SDavid du Colombier void
main(int argc,char ** argv)212a7b22450SDavid du Colombier main(int argc, char **argv)
213a7b22450SDavid du Colombier {
214a7b22450SDavid du Colombier Mouse m;
21501e2ed2dSDavid du Colombier Event ev;
21601e2ed2dSDavid du Colombier int e;
21797787252SDavid du Colombier Route *r;
21801e2ed2dSDavid du Colombier int timer;
21901e2ed2dSDavid du Colombier Animation a;
22001e2ed2dSDavid du Colombier int animate;
22101e2ed2dSDavid du Colombier
222a7b22450SDavid du Colombier
223a7b22450SDavid du Colombier if(argc == 2)
224a7b22450SDavid du Colombier levelfile = argv[1];
225a7b22450SDavid du Colombier else
226a7b22450SDavid du Colombier levelfile = LEasy;
227a7b22450SDavid du Colombier
228a7b22450SDavid du Colombier if(! loadlevels(levelfile)) {
229a7b22450SDavid du Colombier fprint(2, "usage: %s [levelfile]\n", argv[0]);
230a7b22450SDavid du Colombier exits("usage");
231a7b22450SDavid du Colombier }
23201e2ed2dSDavid du Colombier buildmenu();
233a7b22450SDavid du Colombier
23497787252SDavid du Colombier animate = 0;
23597787252SDavid du Colombier buttons[3] = animate ? "noanimate" : "animate";
23697787252SDavid du Colombier
237a7b22450SDavid du Colombier if(initdraw(nil, nil, "sokoban") < 0)
238a7b22450SDavid du Colombier sysfatal("initdraw failed: %r");
239a7b22450SDavid du Colombier einit(Emouse|Ekeyboard);
240a7b22450SDavid du Colombier
24101e2ed2dSDavid du Colombier timer = etimer(0, 200);
24201e2ed2dSDavid du Colombier initanimation(&a);
24301e2ed2dSDavid du Colombier
244a7b22450SDavid du Colombier allocimages();
245a7b22450SDavid du Colombier glenda = gright;
246a7b22450SDavid du Colombier eresized(0);
247a7b22450SDavid du Colombier
248a7b22450SDavid du Colombier for(;;) {
24901e2ed2dSDavid du Colombier e = event(&ev);
25001e2ed2dSDavid du Colombier switch(e) {
251a7b22450SDavid du Colombier case Emouse:
25201e2ed2dSDavid du Colombier m = ev.mouse;
253a7b22450SDavid du Colombier if(m.buttons&1) {
25401e2ed2dSDavid du Colombier stopanimation(&a);
25597787252SDavid du Colombier r = mouse2route(m);
25601e2ed2dSDavid du Colombier if (r)
25701e2ed2dSDavid du Colombier setupanimation(&a, r);
25801e2ed2dSDavid du Colombier if (! animate) {
25901e2ed2dSDavid du Colombier while(onestep(&a))
26001e2ed2dSDavid du Colombier ;
261a7b22450SDavid du Colombier drawscreen();
262a7b22450SDavid du Colombier }
26301e2ed2dSDavid du Colombier }
264a7b22450SDavid du Colombier if(m.buttons&2) {
265a7b22450SDavid du Colombier int l;
266a7b22450SDavid du Colombier /* levels start from 1 */
267a7b22450SDavid du Colombier lmenu.lasthit = level.index;
268a7b22450SDavid du Colombier l=emenuhit(2, &m, &lmenu);
269a7b22450SDavid du Colombier if(l>=0){
27001e2ed2dSDavid du Colombier stopanimation(&a);
271a7b22450SDavid du Colombier level = levels[l];
272a7b22450SDavid du Colombier drawlevel();
273a7b22450SDavid du Colombier drawscreen();
274a7b22450SDavid du Colombier }
275a7b22450SDavid du Colombier }
276a7b22450SDavid du Colombier if(m.buttons&4)
277a7b22450SDavid du Colombier switch(emenuhit(3, &m, &menu)) {
278a7b22450SDavid du Colombier case 0:
27901e2ed2dSDavid du Colombier stopanimation(&a);
280a7b22450SDavid du Colombier level = levels[level.index];
281a7b22450SDavid du Colombier drawlevel();
282a7b22450SDavid du Colombier drawscreen();
283a7b22450SDavid du Colombier break;
284a7b22450SDavid du Colombier case 1:
28501e2ed2dSDavid du Colombier stopanimation(&a);
286a7b22450SDavid du Colombier loadlevels(LEasy);
28701e2ed2dSDavid du Colombier buildmenu();
288a7b22450SDavid du Colombier drawlevel();
289a7b22450SDavid du Colombier drawscreen();
290a7b22450SDavid du Colombier break;
291a7b22450SDavid du Colombier case 2:
29201e2ed2dSDavid du Colombier stopanimation(&a);
293a7b22450SDavid du Colombier loadlevels(LHard);
29401e2ed2dSDavid du Colombier buildmenu();
295a7b22450SDavid du Colombier drawlevel();
296a7b22450SDavid du Colombier drawscreen();
297a7b22450SDavid du Colombier break;
298a7b22450SDavid du Colombier case 3:
29997787252SDavid du Colombier animate = !animate;
30097787252SDavid du Colombier buttons[3] = animate ? "noanimate" : "animate";
30197787252SDavid du Colombier break;
30297787252SDavid du Colombier case 4:
303a7b22450SDavid du Colombier exits(nil);
304a7b22450SDavid du Colombier }
305a7b22450SDavid du Colombier break;
306a7b22450SDavid du Colombier
307a7b22450SDavid du Colombier case Ekeyboard:
308a7b22450SDavid du Colombier if(level.done)
309a7b22450SDavid du Colombier break;
310a7b22450SDavid du Colombier
31101e2ed2dSDavid du Colombier stopanimation(&a);
31201e2ed2dSDavid du Colombier
31301e2ed2dSDavid du Colombier switch(ev.kbdc) {
314a7b22450SDavid du Colombier case 127:
315a7b22450SDavid du Colombier case 'q':
316a7b22450SDavid du Colombier case 'Q':
317a7b22450SDavid du Colombier exits(nil);
318a7b22450SDavid du Colombier case 'n':
319a7b22450SDavid du Colombier case 'N':
320a7b22450SDavid du Colombier if(level.index < numlevels - 1) {
321a7b22450SDavid du Colombier level = levels[++level.index];
322a7b22450SDavid du Colombier drawlevel();
323a7b22450SDavid du Colombier drawscreen();
324a7b22450SDavid du Colombier }
325a7b22450SDavid du Colombier break;
326a7b22450SDavid du Colombier case 'p':
327a7b22450SDavid du Colombier case 'P':
328a7b22450SDavid du Colombier if(level.index > 0) {
329a7b22450SDavid du Colombier level = levels[--level.index];
330a7b22450SDavid du Colombier drawlevel();
331a7b22450SDavid du Colombier drawscreen();
332a7b22450SDavid du Colombier }
333a7b22450SDavid du Colombier break;
334a7b22450SDavid du Colombier case 'r':
335a7b22450SDavid du Colombier case 'R':
336a7b22450SDavid du Colombier level = levels[level.index];
337a7b22450SDavid du Colombier drawlevel();
338a7b22450SDavid du Colombier drawscreen();
339a7b22450SDavid du Colombier break;
340a7b22450SDavid du Colombier case 61454:
341a7b22450SDavid du Colombier case 63488:
342a7b22450SDavid du Colombier case 61457:
343a7b22450SDavid du Colombier case 61458:
344a7b22450SDavid du Colombier case ' ':
34501e2ed2dSDavid du Colombier move(key2move(ev.kbdc));
346a7b22450SDavid du Colombier drawscreen();
347a7b22450SDavid du Colombier break;
34897787252SDavid du Colombier default:
34997787252SDavid du Colombier // fprint(2, "key: %d]\n", e.kbdc);
35097787252SDavid du Colombier break;
351a7b22450SDavid du Colombier }
352a7b22450SDavid du Colombier break;
35301e2ed2dSDavid du Colombier
35401e2ed2dSDavid du Colombier default:
35501e2ed2dSDavid du Colombier if (e == timer) {
35601e2ed2dSDavid du Colombier if (animate)
35701e2ed2dSDavid du Colombier onestep(&a);
35801e2ed2dSDavid du Colombier else
35901e2ed2dSDavid du Colombier while(onestep(&a))
36001e2ed2dSDavid du Colombier ;
36101e2ed2dSDavid du Colombier drawscreen();
36201e2ed2dSDavid du Colombier }
36301e2ed2dSDavid du Colombier break;
364a7b22450SDavid du Colombier }
365a7b22450SDavid du Colombier
366a7b22450SDavid du Colombier if(finished()) {
367a7b22450SDavid du Colombier level.done = 1;
368a7b22450SDavid du Colombier drawwin();
369a7b22450SDavid du Colombier drawscreen();
370a7b22450SDavid du Colombier sleep(3000);
371a7b22450SDavid du Colombier if(level.index < numlevels - 1) {
372a7b22450SDavid du Colombier level = levels[++level.index];
373a7b22450SDavid du Colombier drawlevel();
374a7b22450SDavid du Colombier drawscreen();
375a7b22450SDavid du Colombier }
376a7b22450SDavid du Colombier }
377a7b22450SDavid du Colombier }
378a7b22450SDavid du Colombier }
379