xref: /plan9-contrib/sys/src/games/sokoban/route.c (revision 0a5ecf54db771ed558d00bf1584611954c2e5e4e)
197787252SDavid du Colombier #include <u.h>
297787252SDavid du Colombier #include <libc.h>
397787252SDavid du Colombier #include <draw.h>
497787252SDavid du Colombier 
597787252SDavid du Colombier #include "sokoban.h"
697787252SDavid du Colombier 
701e2ed2dSDavid du Colombier static int dirlist[] = { Up, Down, Left, Right, Up, Down, Left, Right, };
801e2ed2dSDavid du Colombier static int ndir = 4;
997787252SDavid du Colombier 
1097787252SDavid du Colombier static Point
dir2point(int dir)1197787252SDavid du Colombier dir2point(int dir)
1297787252SDavid du Colombier {
1397787252SDavid du Colombier 	switch(dir) {
1497787252SDavid du Colombier 	case Up:
1597787252SDavid du Colombier 		return Pt(0, -1);
1697787252SDavid du Colombier 	case Down:
1797787252SDavid du Colombier 		return Pt(0, 1);
1897787252SDavid du Colombier 	case Left:
1997787252SDavid du Colombier 		return Pt(-1, 0);
2097787252SDavid du Colombier 	case Right:
2197787252SDavid du Colombier 		return Pt(1, 0);
2297787252SDavid du Colombier 	}
2397787252SDavid du Colombier 	return Pt(0,0);
2497787252SDavid du Colombier }
2597787252SDavid du Colombier 
2697787252SDavid du Colombier int
validpush(Point g,Step * s,Point * t)2701e2ed2dSDavid du Colombier validpush(Point g, Step *s, Point *t)
2897787252SDavid du Colombier {
2997787252SDavid du Colombier 	int i;
3001e2ed2dSDavid du Colombier 	Point m;
3101e2ed2dSDavid du Colombier 
3201e2ed2dSDavid du Colombier 	if (s == nil)
3301e2ed2dSDavid du Colombier 		return 0;
3401e2ed2dSDavid du Colombier 
3501e2ed2dSDavid du Colombier 	m = dir2point(s->dir);
3697787252SDavid du Colombier 
3797787252SDavid du Colombier 	// first test for  Cargo next to us (in direction dir)
3801e2ed2dSDavid du Colombier 	if (s->count > 0) {
3997787252SDavid du Colombier 		g = addpt(g, m);
4097787252SDavid du Colombier 		if (!ptinrect(g, Rpt(Pt(0,0), level.max)))
4197787252SDavid du Colombier 			return 0;
4297787252SDavid du Colombier 		switch (level.board[g.x][g.y]) {
4397787252SDavid du Colombier 		case Wall:
4497787252SDavid du Colombier 		case Empty:
4597787252SDavid du Colombier 		case Goal:
4697787252SDavid du Colombier 			return 0;
4797787252SDavid du Colombier 		}
4897787252SDavid du Colombier 	}
4997787252SDavid du Colombier 	// then test for  enough free space for us _and_ Cargo
5001e2ed2dSDavid du Colombier 	for (i=0; i < s->count; i++) {
5197787252SDavid du Colombier 		g = addpt(g, m);
5297787252SDavid du Colombier 		if (!ptinrect(g, Rpt(Pt(0,0), level.max)))
5397787252SDavid du Colombier 			return 0;
5497787252SDavid du Colombier 		switch (level.board[g.x][g.y]) {
5597787252SDavid du Colombier 		case Wall:
5697787252SDavid du Colombier 		case Cargo:
5797787252SDavid du Colombier 		case GoalCargo:
5897787252SDavid du Colombier 			return 0;
5997787252SDavid du Colombier 		}
6097787252SDavid du Colombier 	}
6197787252SDavid du Colombier 	if (t != nil)
6297787252SDavid du Colombier 		*t = g;
6397787252SDavid du Colombier 	return 1;
6497787252SDavid du Colombier }
6597787252SDavid du Colombier 
6697787252SDavid du Colombier int
isvalid(Point s,Route * r,int (* isallowed)(Point,Step *,Point *))6701e2ed2dSDavid du Colombier isvalid(Point s, Route* r, int (*isallowed)(Point, Step*, Point*))
6897787252SDavid du Colombier {
6901e2ed2dSDavid du Colombier 	Point m;
7001e2ed2dSDavid du Colombier 	Step *p;
7197787252SDavid du Colombier 
7201e2ed2dSDavid du Colombier 	if (r == nil)
7397787252SDavid du Colombier 		return 0;
7497787252SDavid du Colombier 
7501e2ed2dSDavid du Colombier 	m = s;
7601e2ed2dSDavid du Colombier 	for (p = r->step; p < r->step + r->nstep; p++)
7701e2ed2dSDavid du Colombier 		if (!isallowed(m, p, &m))
7897787252SDavid du Colombier 			return 0;
7997787252SDavid du Colombier 	return 1;
8097787252SDavid du Colombier }
8197787252SDavid du Colombier 
8201e2ed2dSDavid du Colombier static Walk*
newwalk(void)8301e2ed2dSDavid du Colombier newwalk(void)
8497787252SDavid du Colombier {
8501e2ed2dSDavid du Colombier 	Walk *w;
8601e2ed2dSDavid du Colombier 
87*0a5ecf54SDavid du Colombier 	w = mallocz(sizeof *w, 1);
88*0a5ecf54SDavid du Colombier 	if (w == nil)
8901e2ed2dSDavid du Colombier 		sysfatal("cannot allocate walk");
9001e2ed2dSDavid du Colombier 	return w;
9101e2ed2dSDavid du Colombier }
9201e2ed2dSDavid du Colombier 
9301e2ed2dSDavid du Colombier static void
resetwalk(Walk * w)9401e2ed2dSDavid du Colombier resetwalk(Walk *w)
9501e2ed2dSDavid du Colombier {
9601e2ed2dSDavid du Colombier 	Route **p;
9701e2ed2dSDavid du Colombier 
9801e2ed2dSDavid du Colombier 	if (w == nil || w->route == nil)
9901e2ed2dSDavid du Colombier 		return;
10001e2ed2dSDavid du Colombier 
10101e2ed2dSDavid du Colombier 	for (p=w->route; p < w->route + w->nroute; p++)
10201e2ed2dSDavid du Colombier 		freeroute(*p);
10301e2ed2dSDavid du Colombier 	w->nroute = 0;
10401e2ed2dSDavid du Colombier }
10501e2ed2dSDavid du Colombier 
10601e2ed2dSDavid du Colombier static void
freewalk(Walk * w)10701e2ed2dSDavid du Colombier freewalk(Walk *w)
10801e2ed2dSDavid du Colombier {
10901e2ed2dSDavid du Colombier 	if (w == nil)
11001e2ed2dSDavid du Colombier 		return;
11101e2ed2dSDavid du Colombier 
11201e2ed2dSDavid du Colombier 	resetwalk(w);
11301e2ed2dSDavid du Colombier 	if(w->route)
11401e2ed2dSDavid du Colombier 		free(w->route);
11501e2ed2dSDavid du Colombier 	free(w);
11601e2ed2dSDavid du Colombier }
11701e2ed2dSDavid du Colombier 
11801e2ed2dSDavid du Colombier static void
addroute(Walk * w,Route * r)11901e2ed2dSDavid du Colombier addroute(Walk *w, Route *r)
12001e2ed2dSDavid du Colombier {
12101e2ed2dSDavid du Colombier 	if (w == nil || r == nil)
12201e2ed2dSDavid du Colombier 		return;
12301e2ed2dSDavid du Colombier 
12401e2ed2dSDavid du Colombier 	if (w->beyond < w->nroute+1) {
12501e2ed2dSDavid du Colombier 		w->beyond = w->nroute+1;
12601e2ed2dSDavid du Colombier 		w->route = realloc(w->route, sizeof(Route*)*w->beyond);
12701e2ed2dSDavid du Colombier 	}
12801e2ed2dSDavid du Colombier 	if (w->route == nil)
12901e2ed2dSDavid du Colombier 		sysfatal("cannot allocate route in addroute");
13001e2ed2dSDavid du Colombier 	w->route[w->nroute] = r;
13101e2ed2dSDavid du Colombier 	w->nroute++;
13201e2ed2dSDavid du Colombier }
13301e2ed2dSDavid du Colombier 
13401e2ed2dSDavid du Colombier void
freeroute(Route * r)13501e2ed2dSDavid du Colombier freeroute(Route *r)
13601e2ed2dSDavid du Colombier {
13701e2ed2dSDavid du Colombier 	if (r == nil)
13801e2ed2dSDavid du Colombier 		return;
13901e2ed2dSDavid du Colombier 	free(r->step);
14001e2ed2dSDavid du Colombier 	free(r);
14101e2ed2dSDavid du Colombier }
14201e2ed2dSDavid du Colombier 
14301e2ed2dSDavid du Colombier Route*
extend(Route * rr,int dir,int count,Point dest)14401e2ed2dSDavid du Colombier extend(Route *rr, int dir, int count, Point dest)
14501e2ed2dSDavid du Colombier {
14601e2ed2dSDavid du Colombier 	Route *r;
14701e2ed2dSDavid du Colombier 
148*0a5ecf54SDavid du Colombier 	r = mallocz(sizeof *r, 1);
14901e2ed2dSDavid du Colombier 	if (r == nil)
15001e2ed2dSDavid du Colombier 		sysfatal("cannot allocate route in extend");
15101e2ed2dSDavid du Colombier 	r->dest = dest;
15201e2ed2dSDavid du Colombier 
15301e2ed2dSDavid du Colombier 	if (count > 0) {
15401e2ed2dSDavid du Colombier 		r->nstep = 1;
15501e2ed2dSDavid du Colombier 		if (rr != nil)
15601e2ed2dSDavid du Colombier 			r->nstep += rr->nstep;
15701e2ed2dSDavid du Colombier 
15801e2ed2dSDavid du Colombier 		r->step = malloc(sizeof(Step)*r->nstep);
15901e2ed2dSDavid du Colombier 		if (r->step == nil)
16001e2ed2dSDavid du Colombier 			sysfatal("cannot allocate step in extend");
16101e2ed2dSDavid du Colombier 
16201e2ed2dSDavid du Colombier 		if (rr != nil)
16301e2ed2dSDavid du Colombier 			memmove(r->step, rr->step, sizeof(Step)*rr->nstep);
16401e2ed2dSDavid du Colombier 
16501e2ed2dSDavid du Colombier 		r->step[r->nstep-1].dir = dir;
16601e2ed2dSDavid du Colombier 		r->step[r->nstep-1].count = count;
16701e2ed2dSDavid du Colombier 	}
16801e2ed2dSDavid du Colombier 	return r;
16901e2ed2dSDavid du Colombier }
17001e2ed2dSDavid du Colombier 
17101e2ed2dSDavid du Colombier static Step*
laststep(Route * r)17201e2ed2dSDavid du Colombier laststep(Route*r)
17301e2ed2dSDavid du Colombier {
174*0a5ecf54SDavid du Colombier 	if (r != nil && r->nstep > 0)
17501e2ed2dSDavid du Colombier 		return &r->step[r->nstep-1];
17601e2ed2dSDavid du Colombier 	return nil;
17701e2ed2dSDavid du Colombier }
17801e2ed2dSDavid du Colombier 
17901e2ed2dSDavid du Colombier static int*
startwithdirfromroute(Route * r,int * dl,int n)18001e2ed2dSDavid du Colombier startwithdirfromroute(Route *r, int* dl, int n)
18101e2ed2dSDavid du Colombier {
18201e2ed2dSDavid du Colombier 	Step *s;
18301e2ed2dSDavid du Colombier 	int *p;
18401e2ed2dSDavid du Colombier 
18501e2ed2dSDavid du Colombier 	if (r == nil || dl == nil)
18601e2ed2dSDavid du Colombier 		return dl;
18701e2ed2dSDavid du Colombier 
18801e2ed2dSDavid du Colombier 	s =  laststep(r);
18901e2ed2dSDavid du Colombier 	if (s == nil || s->count == 0)
19001e2ed2dSDavid du Colombier 		return dl;
19101e2ed2dSDavid du Colombier 
19201e2ed2dSDavid du Colombier 	for (p=dl; p < dl + n; p++)
19301e2ed2dSDavid du Colombier 		if (*p == s->dir)
19401e2ed2dSDavid du Colombier 			break;
19501e2ed2dSDavid du Colombier 	return p;
19601e2ed2dSDavid du Colombier }
19701e2ed2dSDavid du Colombier 
19801e2ed2dSDavid du Colombier static Route*
bfstrydir(Route * r,int dir,Visited * v)19901e2ed2dSDavid du Colombier bfstrydir(Route *r, int dir, Visited *v)
20001e2ed2dSDavid du Colombier {
20101e2ed2dSDavid du Colombier 	Point m, p, n;
20201e2ed2dSDavid du Colombier 
20301e2ed2dSDavid du Colombier 	if (r == nil)
20401e2ed2dSDavid du Colombier 		return nil;
20501e2ed2dSDavid du Colombier 
20601e2ed2dSDavid du Colombier 	m = r->dest;
20701e2ed2dSDavid du Colombier 	p = dir2point(dir);
20801e2ed2dSDavid du Colombier 	n = addpt(m, p);
20997787252SDavid du Colombier 
210*0a5ecf54SDavid du Colombier 	if (ptinrect(n, Rpt(Pt(0,0), level.max)) && v->board[n.x][n.y] == 0) {
21197787252SDavid du Colombier 		v->board[n.x][n.y] = 1;
21297787252SDavid du Colombier 		switch (level.board[n.x][n.y]) {
21397787252SDavid du Colombier 		case Empty:
21497787252SDavid du Colombier 		case Goal:
21501e2ed2dSDavid du Colombier 			return extend(r, dir, 1, n);
21697787252SDavid du Colombier 		}
21797787252SDavid du Colombier 	}
21801e2ed2dSDavid du Colombier 	return nil;
21997787252SDavid du Colombier }
22097787252SDavid du Colombier 
22101e2ed2dSDavid du Colombier static Route*
bfs(Point src,Point dst,Visited * v)22201e2ed2dSDavid du Colombier bfs(Point src, Point dst, Visited *v)
22397787252SDavid du Colombier {
22401e2ed2dSDavid du Colombier 	Walk *cur, *new, *tmp;
22501e2ed2dSDavid du Colombier 	Route *r, **p;
22601e2ed2dSDavid du Colombier 	int progress, *dirs, *dirp;
22701e2ed2dSDavid du Colombier 	Point n;
22897787252SDavid du Colombier 
22901e2ed2dSDavid du Colombier 	if (v == nil)
23001e2ed2dSDavid du Colombier 		return nil;
23197787252SDavid du Colombier 
23201e2ed2dSDavid du Colombier 	cur = newwalk();
23301e2ed2dSDavid du Colombier 	new = newwalk();
23401e2ed2dSDavid du Colombier 	v->board[src.x][src.y] = 1;
23501e2ed2dSDavid du Colombier 	r = extend(nil, 0, 0, src);
23601e2ed2dSDavid du Colombier 	if (eqpt(src, dst)) {
23701e2ed2dSDavid du Colombier 		freewalk(cur);
23801e2ed2dSDavid du Colombier 		freewalk(new);
23901e2ed2dSDavid du Colombier 		return r;
24001e2ed2dSDavid du Colombier 	}
24101e2ed2dSDavid du Colombier 	addroute(cur, r);
24201e2ed2dSDavid du Colombier 	progress = 1;
24301e2ed2dSDavid du Colombier 	while (progress) {
24401e2ed2dSDavid du Colombier 		progress = 0;
24501e2ed2dSDavid du Colombier 		for (p=cur->route; p < cur->route + cur->nroute; p++) {
24601e2ed2dSDavid du Colombier 			dirs = startwithdirfromroute(*p, dirlist, ndir);
24701e2ed2dSDavid du Colombier 			for (dirp=dirs; dirp < dirs + ndir; dirp++) {
24801e2ed2dSDavid du Colombier 				r = bfstrydir(*p, *dirp, v);
24901e2ed2dSDavid du Colombier 				if (r != nil) {
25001e2ed2dSDavid du Colombier 					progress = 1;
25101e2ed2dSDavid du Colombier 					n = r->dest;
25201e2ed2dSDavid du Colombier 					if (eqpt(n, dst)) {
25301e2ed2dSDavid du Colombier 						freewalk(cur);
25401e2ed2dSDavid du Colombier 						freewalk(new);
25501e2ed2dSDavid du Colombier 						return(r);
25601e2ed2dSDavid du Colombier 					}
25701e2ed2dSDavid du Colombier 					addroute(new, r);
25801e2ed2dSDavid du Colombier 				}
25901e2ed2dSDavid du Colombier 			}
26001e2ed2dSDavid du Colombier 		}
26101e2ed2dSDavid du Colombier 		resetwalk(cur);
26201e2ed2dSDavid du Colombier 		tmp = cur;
26301e2ed2dSDavid du Colombier 		cur = new;
26401e2ed2dSDavid du Colombier 		new = tmp;
26501e2ed2dSDavid du Colombier 	}
26601e2ed2dSDavid du Colombier 	freewalk(cur);
26701e2ed2dSDavid du Colombier 	freewalk(new);
26801e2ed2dSDavid du Colombier 	return nil;
26997787252SDavid du Colombier }
27097787252SDavid du Colombier 
27101e2ed2dSDavid du Colombier Route*
findroute(Point src,Point dst)27201e2ed2dSDavid du Colombier findroute(Point src, Point dst)
27397787252SDavid du Colombier {
27401e2ed2dSDavid du Colombier 	Visited v;
27501e2ed2dSDavid du Colombier 	Route* r;
27697787252SDavid du Colombier 
27701e2ed2dSDavid du Colombier 	memset(&v, 0, sizeof(Visited));
27801e2ed2dSDavid du Colombier 	r = bfs(src, dst, &v);
27901e2ed2dSDavid du Colombier 	return r;
28097787252SDavid du Colombier }
281