xref: /plan9-contrib/sys/src/games/sokoban/sokoban.c (revision 68cc0f8287607b9765032c3ad1aaf884a180bcfb)
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