Browse Source

Plan 9 from Bell Labs 2005-09-06

David du Colombier 18 years ago
parent
commit
f886e82aa3

+ 7 - 6
dist/replica/_plan9.db

@@ -503,7 +503,7 @@
 386/bin/webcookies - 775 sys sys 1125346039 161625
 386/bin/webfs - 775 sys sys 1125346039 349737
 386/bin/webfsget - 775 sys sys 1115950144 39143
-386/bin/wikifs - 775 sys sys 1125346040 202135
+386/bin/wikifs - 775 sys sys 1125889831 202134
 386/bin/winwatch - 775 sys sys 1115950145 154555
 386/bin/xd - 775 sys sys 1125346040 64044
 386/bin/xmr - 775 sys sys 1115950145 40157
@@ -13840,14 +13840,15 @@ sys/src/games/music/playlistfs/playplumb.c - 664 sys sys 1103793924 280
 sys/src/games/music/playlistfs/volume.c - 664 sys sys 1103793924 2065
 sys/src/games/music/readcd - 775 sys sys 1103793915 996
 sys/src/games/sokoban - 20000000775 sys sys 1095792097 0
-sys/src/games/sokoban/README - 664 sys sys 1102439103 781
+sys/src/games/sokoban/README - 664 sys sys 1125919542 799
+sys/src/games/sokoban/animation.c - 664 sys sys 1125919541 973
 sys/src/games/sokoban/graphics.c - 664 sys sys 1095792097 1846
 sys/src/games/sokoban/level.c - 664 sys sys 1095792097 1654
 sys/src/games/sokoban/mkfile - 664 sys sys 1102439103 247
 sys/src/games/sokoban/move.c - 664 sys sys 1095792097 2671
-sys/src/games/sokoban/route.c - 664 sys sys 1102439103 3684
-sys/src/games/sokoban/sokoban.c - 664 sys sys 1102439104 5540
-sys/src/games/sokoban/sokoban.h - 664 sys sys 1102439105 1876
+sys/src/games/sokoban/route.c - 664 sys sys 1125919542 4486
+sys/src/games/sokoban/sokoban.c - 664 sys sys 1125919542 6358
+sys/src/games/sokoban/sokoban.h - 664 sys sys 1125919542 2062
 sys/src/games/sudoku - 20000000775 sys sys 1117225572 0
 sys/src/games/sudoku/game.c - 664 sys sys 1117226433 7788
 sys/src/games/sudoku/levels.c - 664 sys sys 1117226433 3186
@@ -14961,4 +14962,4 @@ usr/glenda/lib/profile - 664 glenda glenda 1105128663 890
 usr/glenda/readme.acme - 664 glenda glenda 1019860628 4753
 usr/glenda/readme.rio - 664 glenda glenda 1019860628 6370
 usr/glenda/tmp - 20000000775 glenda glenda 1018802620 0
-386/bin/wikifs - 775 sys sys 1125889831 202134
+sys/src/games/sokoban/mkfile - 664 sys sys 1125976294 261

+ 6 - 5
dist/replica/plan9.db

@@ -13840,14 +13840,15 @@ sys/src/games/music/playlistfs/playplumb.c - 664 sys sys 1103793924 280
 sys/src/games/music/playlistfs/volume.c - 664 sys sys 1103793924 2065
 sys/src/games/music/readcd - 775 sys sys 1103793915 996
 sys/src/games/sokoban - 20000000775 sys sys 1095792097 0
-sys/src/games/sokoban/README - 664 sys sys 1102439103 781
+sys/src/games/sokoban/README - 664 sys sys 1125919542 799
+sys/src/games/sokoban/animation.c - 664 sys sys 1125919541 973
 sys/src/games/sokoban/graphics.c - 664 sys sys 1095792097 1846
 sys/src/games/sokoban/level.c - 664 sys sys 1095792097 1654
-sys/src/games/sokoban/mkfile - 664 sys sys 1102439103 247
+sys/src/games/sokoban/mkfile - 664 sys sys 1125976294 261
 sys/src/games/sokoban/move.c - 664 sys sys 1095792097 2671
-sys/src/games/sokoban/route.c - 664 sys sys 1102439103 3684
-sys/src/games/sokoban/sokoban.c - 664 sys sys 1102439104 5540
-sys/src/games/sokoban/sokoban.h - 664 sys sys 1102439105 1876
+sys/src/games/sokoban/route.c - 664 sys sys 1125919542 4486
+sys/src/games/sokoban/sokoban.c - 664 sys sys 1125919542 6358
+sys/src/games/sokoban/sokoban.h - 664 sys sys 1125919542 2062
 sys/src/games/sudoku - 20000000775 sys sys 1117225572 0
 sys/src/games/sudoku/game.c - 664 sys sys 1117226433 7788
 sys/src/games/sudoku/levels.c - 664 sys sys 1117226433 3186

+ 6 - 0
dist/replica/plan9.log

@@ -21231,3 +21231,9 @@
 1125837066 4 c rc/bin/termrc - 775 sys sys 1125835735 2653
 1125837066 5 c sys/src/cmd/wikifs/fs.c - 664 sys sys 1125835789 15730
 1125891078 0 c 386/bin/wikifs - 775 sys sys 1125889831 202134
+1125919884 0 c sys/src/games/sokoban/README - 664 sys sys 1125919542 799
+1125919884 1 a sys/src/games/sokoban/animation.c - 664 sys sys 1125919541 973
+1125919884 2 c sys/src/games/sokoban/route.c - 664 sys sys 1125919542 4486
+1125919884 3 c sys/src/games/sokoban/sokoban.c - 664 sys sys 1125919542 6358
+1125919884 4 c sys/src/games/sokoban/sokoban.h - 664 sys sys 1125919542 2062
+1125977498 0 c sys/src/games/sokoban/mkfile - 664 sys sys 1125976294 261

+ 1 - 1
sys/src/games/sokoban/README

@@ -17,6 +17,6 @@ without touching anything, it will do so.
 
 Otherwise, nothing will happen.
 
-The search algorithm is pretty simplistic;
+The breadth-first search algorithm should find a fast route.
 it can be seen in action by toggling the 'animate'
 entry in the button 3 menu.

+ 60 - 0
sys/src/games/sokoban/animation.c

@@ -0,0 +1,60 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+#include "sokoban.h"
+
+void
+initanimation(Animation *a)
+{
+	if (a == nil)
+		return;
+
+	memset(a, 0, sizeof(Animation));
+}
+
+void
+setupanimation(Animation *a, Route *r)
+{
+	if (a == nil || r == nil || r->step == nil)
+		return;
+
+	a->route = r;
+	a->step = r->step;
+	if (a->step < a->route->step + a->route->nstep)
+		a->count = a->step->count;
+	else
+		stopanimation(a);
+}
+
+int
+onestep(Animation *a)
+{
+	if (a == nil)
+		return 0;
+
+	if (a->count > 0 && a->step != nil && a->route != nil) {
+		move(a->step->dir);
+		a->count--;
+		if (a->count == 0) {
+			a->step++;
+			if (a->step < a->route->step + a->route->nstep)
+				a->count = a->step->count;
+			else
+				stopanimation(a);
+		}
+	} else if (a->count > 0 && (a->step == nil || a->route == nil))
+		stopanimation(a);
+	return (a->count > 0);
+}
+
+void
+stopanimation(Animation *a)
+{
+	if (a == nil)
+		return;
+
+	if (a->route != nil)
+		freeroute(a->route);
+	memset(a, 0, sizeof(Animation));
+}

+ 3 - 3
sys/src/games/sokoban/mkfile

@@ -3,13 +3,13 @@ BIN=/$objtype/bin/games
 
 TARG=sokoban
 OFILES=\
-	sokoban.$O\
-	move.$O\
+	animation.$O\
 	graphics.$O\
 	level.$O\
+	move.$O\
+	sokoban.$O\
 	route.$O\
 
-
 HFILES=sokoban.h\
 
 UPDATE=\

+ 199 - 142
sys/src/games/sokoban/route.c

@@ -4,8 +4,8 @@
 
 #include "sokoban.h"
 
-static int trydir(int, Point, Point, Route*, Visited*);
-static int dofind(Point, Point, Route*, Visited*);
+static int dirlist[] = { Up, Down, Left, Right, Up, Down, Left, Right, };
+static int ndir = 4;
 
 static Point
 dir2point(int dir)
@@ -23,73 +23,23 @@ dir2point(int dir)
 	return Pt(0,0);
 }
 
-Route*
-newroute(void)
-{
-	Route *r = malloc(sizeof(Route));
-	memset(r, 0, sizeof(Route));
-	return r;
-}
-
-void
-freeroute(Route *r)
-{
-	if (r->step != nil) {
-		free(r->step);
-		memset(r, 0, sizeof(Route));
-	}
-	free(r);
-}
-
-void
-reverseroute(Route *r)
+int
+validpush(Point g, Step *s, Point *t)
 {
 	int i;
-	Step tmp;
+	Point m;
 
-	for (i=1; i< r->nstep; i++) {
-		tmp = r->step[i];
-		r->step[i] = r->step[i-1];
-		r->step[i-1] = tmp;
-	}
-}
+	if (s == nil)
+		return 0;
 
-void
-pushstep(Route *r, int dir, int count)
-{
-	if (r->beyond < r->nstep+1) {
-		r->beyond = r->nstep+1;
-		r->step = realloc(r->step, sizeof(Step)*r->beyond);
-	}
-	if (r->step == nil)
-		exits("pushstep out of memory");
-	r->step[r->nstep].dir = dir;
-	r->step[r->nstep].count = count;
-	r->nstep++;
-}
-
-void
-popstep(Route*r)
-{
-	if (r->nstep > 0) {
-		r->nstep--;
-		r->step[r->nstep].dir = 0;
-		r->step[r->nstep].count = 0;
-	}
-}
-
-int
-validpush(Point g, Step s, Point *t)
-{
-	int i;
-	Point m = dir2point(s.dir);
+	m = dir2point(s->dir);
 
 	// first test for  Cargo next to us (in direction dir)
-	if (s.count > 0) {
+	if (s->count > 0) {
 		g = addpt(g, m);
 		if (!ptinrect(g, Rpt(Pt(0,0), level.max)))
 			return 0;
-		switch (level.board[g.x ][g.y]) {
+		switch (level.board[g.x][g.y]) {
 		case Wall:
 		case Empty:
 		case Goal:
@@ -97,11 +47,11 @@ validpush(Point g, Step s, Point *t)
 		}
 	}
 	// then test for  enough free space for us _and_ Cargo
-	for (i=0; i < s.count; i++) {
+	for (i=0; i < s->count; i++) {
 		g = addpt(g, m);
 		if (!ptinrect(g, Rpt(Pt(0,0), level.max)))
 			return 0;
-		switch (level.board[g.x ][g.y]) {
+		switch (level.board[g.x][g.y]) {
 		case Wall:
 		case Cargo:
 		case GoalCargo:
@@ -114,117 +64,224 @@ validpush(Point g, Step s, Point *t)
 }
 
 int
-validwalk(Point g, Step s, Point *t)
+isvalid(Point s, Route* r, int (*isallowed)(Point, Step*, Point*))
 {
-	int i;
-	Point m = dir2point(s.dir);
+	Point m;
+	Step *p;
 
-	for (i=0; i < s.count; i++) {
-		g = addpt(g, m);
-		if (!ptinrect(g, Rpt(Pt(0,0), level.max)))
-			return 0;
-		switch (level.board[g.x ][g.y]) {
-		case Wall:
-		case Cargo:
-		case GoalCargo:
+	if (r == nil)
+		return 0;
+
+	m = s;
+	for (p=r->step; p < r->step +r->nstep; p++)
+		if (! isallowed(m, p, &m))
 			return 0;
-		}
-	}
-	if (t != nil)
-		*t = g;
 	return 1;
 }
 
-int
-isvalid(Point s, Route* r, int (*isallowed)(Point, Step, Point*))
+static Walk*
+newwalk(void)
 {
-	int i;
-	Point m = s;
+	Walk *w;
 
-	for (i=0; i< r->nstep; i++)
-		if (! isallowed(m, r->step[i], &m))
-			return 0;
-	return 1;
+	w = malloc(sizeof(Walk));
+	if (w->route == nil)
+		sysfatal("cannot allocate walk");
+	memset(w, 0, sizeof(Walk));
+	return w;
 }
 
-static int
-trydir(int dir, Point m, Point d, Route *r, Visited *v)
+static void
+resetwalk(Walk *w)
 {
-	Point p = dir2point(dir);
-	Point n = addpt(m, p);
+	Route **p;
 
-	if (ptinrect(n, Rpt(Pt(0,0), level.max)) &&
-	    v->board[n.x][n.y] == 0) {
-		v->board[n.x][n.y] = 1;
-		switch (level.board[n.x ][n.y]) {
-		case Empty:
-		case Goal:
-			pushstep(r, dir, 1);
-			if (dofind(n, d, r, v))
-				return 1;
-			else
-				popstep(r);
-		}
-	}
-	return 0;
+	if (w == nil || w->route == nil)
+		return;
+
+	for (p=w->route; p < w->route + w->nroute; p++)
+		freeroute(*p);
+	w->nroute = 0;
 }
 
-static int
-dofind(Point m, Point d, Route *r, Visited *v)
+static void
+freewalk(Walk *w)
 {
-	if (eqpt(m, d))
-		return 1;
+	if (w == nil)
+		return;
 
-	v->board[m.x][m.y] = 1;
+	resetwalk(w);
+	if(w->route)
+		free(w->route);
+	free(w);
+}
 
-	return trydir(Left, m, d, r, v) ||
-	            trydir(Up, m, d, r, v) ||
-	            trydir(Right, m, d, r, v) ||
-	            trydir(Down, m, d, r, v);
+static void
+addroute(Walk *w, Route *r)
+{
+	if (w == nil || r == nil)
+		return;
+
+	if (w->beyond < w->nroute+1) {
+		w->beyond = w->nroute+1;
+		w->route = realloc(w->route, sizeof(Route*)*w->beyond);
+	}
+	if (w->route == nil)
+		sysfatal("cannot allocate route in addroute");
+	w->route[w->nroute] = r;
+	w->nroute++;
+}
+
+void
+freeroute(Route *r)
+{
+	if (r == nil)
+		return;
+	if (r->step != nil)
+		free(r->step);
+	free(r);
 }
 
-static int
-dobfs(Point m, Point d, Route *r, Visited *v)
+Route*
+extend(Route *rr, int dir, int count, Point dest)
 {
-	if (eqpt(m, d))
-		return 1;
+	Route *r;
+
+	r = malloc(sizeof(Route));
+	if (r == nil)
+		sysfatal("cannot allocate route in extend");
+
+	memset(r, 0, sizeof(Route));
+
+	r->dest = dest;
+
+	if (count > 0) {
+		r->nstep = 1;
+		if (rr != nil)
+			r->nstep += rr->nstep;
+
+		r->step = malloc(sizeof(Step)*r->nstep);
+		if (r->step == nil)
+			sysfatal("cannot allocate step in extend");
 
-	v->board[m.x][m.y] = 1;
+		if (rr != nil)
+			memmove(r->step, rr->step, sizeof(Step)*rr->nstep);
 
-	return trydir(Left, m, d, r, v) ||
-	            trydir(Up, m, d, r, v) ||
-	            trydir(Right, m, d, r, v) ||
-	            trydir(Down, m, d, r, v);
+		r->step[r->nstep-1].dir = dir;
+		r->step[r->nstep-1].count = count;
+	}
+	return r;
 }
 
-int
-findwalk(Point src, Point dst, Route *r)
+static Step*
+laststep(Route*r)
 {
-	Visited* v;
-	int found;
+	if (r != nil && r->nstep > 0) {
+		return &r->step[r->nstep-1];
+	}
+	return nil;
+}
 
-	v = malloc(sizeof(Visited));
-	memset(v, 0, sizeof(Visited));
-	found = dofind(src, dst, r, v);
-	free(v);
+static int*
+startwithdirfromroute(Route *r, int* dl, int n)
+{
+	Step *s;
+	int *p;
 	
-	return found;
+	if (r == nil || dl == nil)
+		return dl;
+
+	s =  laststep(r);
+	if (s == nil || s->count == 0)
+		return dl;
+
+	for (p=dl; p < dl + n; p++)
+		if (*p == s->dir)
+			break;
+	return p;
 }
 
-void
-applyroute(Route *r)
+static Route*
+bfstrydir(Route *r, int dir, Visited *v)
 {
-	int j, i;
-	
-	for (i=0; i< r->nstep; i++) {
-		j = r->step[i].count;
-		while (j > 0) {
-			move(r->step[i].dir);
-			if (animate) {
-				drawscreen();
-				sleep(200);
+	Point m, p, n;
+
+	if (r == nil)
+		return nil;
+
+	m = r->dest;
+	p = dir2point(dir);
+	n = addpt(m, p);
+
+	if (ptinrect(n, Rpt(Pt(0,0), level.max)) &&
+	    v->board[n.x][n.y] == 0) {
+		v->board[n.x][n.y] = 1;
+		switch (level.board[n.x][n.y]) {
+		case Empty:
+		case Goal:
+			return extend(r, dir, 1, n);
+		}
+	}
+	return nil;
+}
+
+static Route*
+bfs(Point src, Point dst, Visited *v)
+{
+	Walk *cur, *new, *tmp;
+	Route *r, **p;
+	int progress, *dirs, *dirp;
+	Point n;
+
+	if (v == nil)
+		return nil;
+
+	cur = newwalk();
+	new = newwalk();
+	v->board[src.x][src.y] = 1;
+	r = extend(nil, 0, 0, src);
+	if (eqpt(src, dst)) {
+		freewalk(cur);
+		freewalk(new);
+		return r;
+	}
+	addroute(cur, r);
+	progress = 1;
+	while (progress) {
+		progress = 0;
+		for (p=cur->route; p < cur->route + cur->nroute; p++) {
+			dirs = startwithdirfromroute(*p, dirlist, ndir);
+			for (dirp=dirs; dirp < dirs + ndir; dirp++) {
+				r = bfstrydir(*p, *dirp, v);
+				if (r != nil) {
+					progress = 1;
+					n = r->dest;
+					if (eqpt(n, dst)) {
+						freewalk(cur);
+						freewalk(new);
+						return(r);
+					}
+					addroute(new, r);
+				}
 			}
-			j--;
 		}
+		resetwalk(cur);
+		tmp = cur;
+		cur = new;
+		new = tmp;
 	}
+	freewalk(cur);
+	freewalk(new);
+	return nil;
+}
+
+Route*
+findroute(Point src, Point dst)
+{
+	Visited v;
+	Route* r;
+
+	memset(&v, 0, sizeof(Visited));
+	r = bfs(src, dst, &v);
+	return r;
 }

+ 85 - 43
sys/src/games/sokoban/sokoban.c

@@ -18,7 +18,6 @@ char		*GoalCargoImage = "/sys/games/lib/sokoban/images/goalcargo.bit";
 char		*GoalImage = "/sys/games/lib/sokoban/images/goal.bit";
 char		*WinImage = "/sys/games/lib/sokoban/images/win.bit";
 
-
 char *buttons[] = 
 {
 	"restart",
@@ -29,18 +28,36 @@ char *buttons[] =
 	0
 };
 
+char **levelnames;
+
 Menu menu = 
 {
-	buttons
+	buttons,
 };
 
 Menu lmenu =
 {
-	nil,
-	genlevels,
-	0,
+	levelnames,
 };
 
+void
+buildmenu(void)
+{
+	int i;
+
+	if (levelnames != nil) {
+		for(i=0; levelnames[i] != 0; i++)
+			free(levelnames[i]);
+	}
+	levelnames = realloc(levelnames, sizeof(char*)*(numlevels+1));
+	if (levelnames == nil)
+		sysfatal("cannot allocate levelnames");
+	for(i=0; i < numlevels; i++)
+		levelnames[i] = genlevels(i);
+	levelnames[numlevels] = 0;
+	lmenu.item = levelnames;
+}
+
 Image *
 eallocimage(Rectangle r, int repl, uint color)
 {
@@ -119,7 +136,7 @@ static Route*
 mouse2route(Mouse m)
 {
 	Point p, q;
-	Route *r, *rr;
+	Route *r;
 
 	p = subpt(m.xy, screen->r.min);
 	p.x /= BoardX;
@@ -129,35 +146,26 @@ mouse2route(Mouse m)
 	// fprint(2, "x=%d y=%d\n", q.x, q.y);
 
 	if (q.x == 0 && q.y ==  0)
-		return newroute();
-
-	r = newroute();
-	if (q.x < 0)
-		pushstep(r, Left, -q.x);
-	if (q.x > 0)
-		pushstep(r, Right, q.x);
-
-	if (q.y < 0)
-		pushstep(r, Up, -q.y);
-	if (q.y > 0)
-		pushstep(r, Down, q.y);
-
-	if ((q.x == 0 || q.y ==  0) && isvalid(level.glenda, r, validpush))
-		return r;
-
-	if (isvalid(level.glenda, r, validwalk))
-		return r;
-	reverseroute(r);
-	if (isvalid(level.glenda, r, validwalk))
-		return r;
-	freeroute(r);
-
-	rr = newroute();
-	if (findwalk(level.glenda, p, rr))
-		return rr;
-	freeroute(rr);
-
-	return newroute();
+		return nil;
+
+	if (q.x == 0 || q.y ==  0) {
+		if (q.x < 0)
+			r = extend(nil, Left, -q.x, Pt(level.glenda.x, p.y));
+		else if (q.x > 0)
+			r = extend(nil, Right, q.x, Pt(level.glenda.x, p.y));
+		else if (q.y < 0)
+			r = extend(nil, Up, -q.y, level.glenda);
+		else if (q.y > 0)
+			r = extend(nil, Down, q.y, level.glenda);
+		else
+			r = nil;
+
+		if (r != nil && isvalid(level.glenda, r, validpush))
+			return r;
+		freeroute(r);
+	}
+
+	return findroute(level.glenda, p);
 }
 
 char *
@@ -203,8 +211,13 @@ void
 main(int argc, char **argv)
 {
 	Mouse m;
-	Event e;
+	Event ev;
+	int e;
 	Route *r;
+	int timer;
+	Animation a;
+	int animate;
+
 
 	if(argc == 2) 
 		levelfile = argv[1];
@@ -215,6 +228,7 @@ main(int argc, char **argv)
 		fprint(2, "usage: %s [levelfile]\n", argv[0]);
 		exits("usage");
 	}
+	buildmenu();
 
 	animate = 0;
 	buttons[3] = animate ? "noanimate" : "animate";
@@ -223,19 +237,28 @@ main(int argc, char **argv)
 		sysfatal("initdraw failed: %r");
 	einit(Emouse|Ekeyboard);
 
+	timer = etimer(0, 200);
+	initanimation(&a);
+
 	allocimages();
 	glenda = gright;
 	eresized(0);
 
 	for(;;) {
-		switch(event(&e)) {
+		e = event(&ev);
+		switch(e) {
 		case Emouse:
-			m = e.mouse;
+			m = ev.mouse;
 			if(m.buttons&1) {
+				stopanimation(&a);
 				r = mouse2route(m);
-				applyroute(r);
-				freeroute(r);
-				drawscreen();
+				if (r)
+					setupanimation(&a, r);
+				if (! animate) {
+					while(onestep(&a))
+						;
+					drawscreen();
+				}
 			}
 			if(m.buttons&2) {
 				int l;
@@ -243,6 +266,7 @@ main(int argc, char **argv)
 				lmenu.lasthit = level.index;
 				l=emenuhit(2, &m, &lmenu);
 				if(l>=0){
+					stopanimation(&a);
 					level = levels[l];
 					drawlevel();
 					drawscreen();
@@ -251,17 +275,22 @@ main(int argc, char **argv)
 			if(m.buttons&4)
 				switch(emenuhit(3, &m, &menu)) {
 				case 0:
+					stopanimation(&a);
 					level = levels[level.index];
 					drawlevel();
 					drawscreen();
 					break;
 				case 1:
+					stopanimation(&a);
 					loadlevels(LEasy);
+					buildmenu();
 					drawlevel();
 					drawscreen();
 					break;
 				case 2:
+					stopanimation(&a);
 					loadlevels(LHard);
+					buildmenu();
 					drawlevel();
 					drawscreen();
 					break;
@@ -278,7 +307,9 @@ main(int argc, char **argv)
 			if(level.done)
 				break;
 
-			switch(e.kbdc) {
+			stopanimation(&a);
+
+			switch(ev.kbdc) {
 			case 127:
 			case 'q':
 			case 'Q':
@@ -310,7 +341,7 @@ main(int argc, char **argv)
 			case 61457:
 			case 61458:
 			case ' ':
-				move(key2move(e.kbdc));
+				move(key2move(ev.kbdc));
 				drawscreen();
 				break;
 			default:
@@ -318,6 +349,17 @@ main(int argc, char **argv)
 				break;
 			}
 			break;
+
+		default:
+			if (e == timer) {
+				if (animate)
+					onestep(&a);
+				else
+					while(onestep(&a))
+						;
+				drawscreen();
+			}
+			break;
 		}
 
 		if(finished()) {

+ 28 - 15
sys/src/games/sokoban/sokoban.h

@@ -32,21 +32,33 @@ enum {
 	Maxlevels = 200,
 };
 
-typedef struct {
+typedef struct Step {
 	uint dir;		/* direction */
 	uint count;	/* number of single-step moves */
 } Step;
 
-typedef struct {
-	uint nstep;	/* number of valid steps */
+typedef struct Route {
+	uint nstep;	/* number of valid Step */
 	Step *step;
-	uint beyond;	/* number of allocated Step */
+	Point dest;	/* result of step */
 } Route;
 
-typedef struct {
+typedef struct Walk {
+	uint nroute;	/* number of valid Route* */
+	Route **route;
+	uint beyond;	/* number of allocated Route* */
+} Walk;
+
+typedef struct Visited {
 	uint 	board[MazeX][MazeY];
 } Visited;
 
+typedef struct Animation {
+	Route* route;
+	Step *step;
+	int count;
+} Animation;
+
 typedef struct {
 	Point 	glenda;
 	Point 	max;		/* that's how much the board spans */
@@ -58,7 +70,6 @@ typedef struct {
 Level level;		/* the current level */
 Level levels[Maxlevels];	/* all levels from this file */
 int numlevels;		/* how many levels do we have */
-int animate;		/* boolean: animate during multi-step move? */
 
 Image *img;		/* buffer */
 Image *text;		/* for text messages */
@@ -90,16 +101,18 @@ int loadlevels(char *);
 void move(int);
 
 /* route.c */
-Route* newroute(void);
+int validpush(Point, Step*, Point*);
+int isvalid(Point, Route*, int (*)(Point, Step*, Point*));
 void freeroute(Route*);
-void reverseroute(Route*);
-void pushstep(Route*, int, int);
-void popstep(Route*);
-int validwalk(Point, Step, Point*);
-int validpush(Point, Step, Point*);
-int isvalid(Point, Route*, int (*)(Point, Step, Point*));
-int findwalk(Point, Point, Route*);
-void applyroute(Route*);
+Route* extend(Route*, int, int, Point);
+Route* findroute(Point, Point);
+
+/* animation.c */
+void initanimation(Animation*);
+void setupanimation(Animation*, Route*);
+int onestep(Animation*);
+void stopanimation(Animation*);
+
 
 /* sokoban.c */
 char *genlevels(int);