9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
* [9fans] [patch] applied: sokoban-bfs-responsive-leak
@ 2005-09-05 12:09 patch-log
  0 siblings, 0 replies; only message in thread
From: patch-log @ 2005-09-05 12:09 UTC (permalink / raw)
  To: 9fans

d-rwxrwxr-x M 67778 rsc sys 0 Sep  5 13:25 /n/sources/patch/applied/sokoban-bfs-responsive-leak

README:
 - be gentle to glenda, by allowing her to walk
   the shortest distance in a multi-step walk
   (found by using breadth-first search
    instead of depth-first search)
 - while walking with animation enabled,
   remain responsive, by splitting the
   animation of a walk in single-step
   moves that are 'scheduled' using etimer(2).
   This allows e.g. redirecting a walk during
   the animation, while glenda is still walking,
   or disabling animation during a walk.
 - reduce the amount of leaks reported by leak(1)
   by building the levels menu contents 'by hand'
   instead of relying on menuhit to call genlevel
   (would there be a way to avoid leaking while
    letting menuhit call genlevel?)
   Note: I still get some leaks, after popping
   up the B2 menu:
	    acid: src(0x00009627); // 4
	    /sys/src/libc/fmt/vsmprint.c:38
   I don't quite see where they come from.

Axel.

------------------------------
DIFF:
/sys/src/games/sokoban/animation.c

/sys/src/games/sokoban/route.c
route.c.orig:4,11 - /n/sources/patch/applied/sokoban-bfs-responsive-leak/route.c:4,11
  
  #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)
route.c.orig:23,95 - /n/sources/patch/applied/sokoban-bfs-responsive-leak/route.c:23,45
  	return Pt(0,0);
  }
  
- Route*
- newroute(void)
+ int
+ validpush(Point g, Step *s, Point *t)
  {
- 	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 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++;
- }
+ 	m = dir2point(s->dir);
  
- 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);
- 
  	// 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:
route.c.orig:97,107 - /n/sources/patch/applied/sokoban-bfs-responsive-leak/route.c:47,57
  		}
  	}
  	// 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:
route.c.orig:114,230 - /n/sources/patch/applied/sokoban-bfs-responsive-leak/route.c:64,287
  }
  
  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)))
+ 	if (r == nil)
+ 		return 0;
+ 
+ 	m = s;
+ 	for (p=r->step; p < r->step +r->nstep; p++)
+ 		if (! isallowed(m, p, &m))
  			return 0;
- 		switch (level.board[g.x ][g.y]) {
- 		case Wall:
- 		case Cargo:
- 		case GoalCargo:
- 			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++;
  }
  
- static int
- dobfs(Point m, Point d, Route *r, Visited *v)
+ void
+ freeroute(Route *r)
  {
- 	if (eqpt(m, d))
- 		return 1;
+ 	if (r == nil)
+ 		return;
+ 	if (r->step != nil)
+ 		free(r->step);
+ 	free(r);
+ }
  
- 	v->board[m.x][m.y] = 1;
+ Route*
+ extend(Route *rr, int dir, int count, Point dest)
+ {
+ 	Route *r;
  
- 	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 = 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");
+ 
+ 		if (rr != nil)
+ 			memmove(r->step, rr->step, sizeof(Step)*rr->nstep);
+ 
+ 		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;
  }

/sys/src/games/sokoban/sokoban.c
sokoban.c.orig:18,24 - /n/sources/patch/applied/sokoban-bfs-responsive-leak/sokoban.c:18,23
  char		*GoalImage = "/sys/games/lib/sokoban/images/goal.bit";
  char		*WinImage = "/sys/games/lib/sokoban/images/win.bit";
  
- 
  char *buttons[] = 
  {
  	"restart",
sokoban.c.orig:29,46 - /n/sources/patch/applied/sokoban-bfs-responsive-leak/sokoban.c:28,63
  	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)
  {
sokoban.c.orig:119,125 - /n/sources/patch/applied/sokoban-bfs-responsive-leak/sokoban.c:136,142
  mouse2route(Mouse m)
  {
  	Point p, q;
- 	Route *r, *rr;
+ 	Route *r;
  
  	p = subpt(m.xy, screen->r.min);
  	p.x /= BoardX;
sokoban.c.orig:129,163 - /n/sources/patch/applied/sokoban-bfs-responsive-leak/sokoban.c:146,171
  	// fprint(2, "x=%d y=%d\n", q.x, q.y);
  
  	if (q.x == 0 && q.y ==  0)
- 		return newroute();
+ 		return nil;
  
- 	r = newroute();
- 	if (q.x < 0)
- 		pushstep(r, Left, -q.x);
- 	if (q.x > 0)
- 		pushstep(r, Right, q.x);
+ 	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 (q.y < 0)
- 		pushstep(r, Up, -q.y);
- 	if (q.y > 0)
- 		pushstep(r, Down, q.y);
+ 		if (r != nil && isvalid(level.glenda, r, validpush))
+ 			return r;
+ 		freeroute(r);
+ 	}
  
- 	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 findroute(level.glenda, p);
  }
  
  char *
sokoban.c.orig:203,211 - /n/sources/patch/applied/sokoban-bfs-responsive-leak/sokoban.c:211,224
  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];
  	else
sokoban.c.orig:215,220 - /n/sources/patch/applied/sokoban-bfs-responsive-leak/sokoban.c:228,234
  		fprint(2, "usage: %s [levelfile]\n", argv[0]);
  		exits("usage");
  	}
+ 	buildmenu();
  
  	animate = 0;
  	buttons[3] = animate ? "noanimate" : "animate";
sokoban.c.orig:223,241 - /n/sources/patch/applied/sokoban-bfs-responsive-leak/sokoban.c:237,264
  		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;
sokoban.c.orig:243,248 - /n/sources/patch/applied/sokoban-bfs-responsive-leak/sokoban.c:266,272
  				lmenu.lasthit = level.index;
  				l=emenuhit(2, &m, &lmenu);
  				if(l>=0){
+ 					stopanimation(&a);
  					level = levels[l];
  					drawlevel();
  					drawscreen();
sokoban.c.orig:251,267 - /n/sources/patch/applied/sokoban-bfs-responsive-leak/sokoban.c:275,296
  			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;
sokoban.c.orig:278,284 - /n/sources/patch/applied/sokoban-bfs-responsive-leak/sokoban.c:307,315
  			if(level.done)
  				break;
  
- 			switch(e.kbdc) {
+ 			stopanimation(&a);
+ 
+ 			switch(ev.kbdc) {
  			case 127:
  			case 'q':
  			case 'Q':
sokoban.c.orig:310,321 - /n/sources/patch/applied/sokoban-bfs-responsive-leak/sokoban.c:341,363
  			case 61457:
  			case 61458:
  			case ' ':
- 				move(key2move(e.kbdc));
+ 				move(key2move(ev.kbdc));
  				drawscreen();
  				break;
  			default:
  				// fprint(2, "key: %d]\n", e.kbdc);
  				break;
+ 			}
+ 			break;
+ 
+ 		default:
+ 			if (e == timer) {
+ 				if (animate)
+ 					onestep(&a);
+ 				else
+ 					while(onestep(&a))
+ 						;
+ 				drawscreen();
  			}
  			break;
  		}

/sys/src/games/sokoban/sokoban.h
sokoban.h.orig:32,52 - /n/sources/patch/applied/sokoban-bfs-responsive-leak/sokoban.h:32,64
  	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 */
sokoban.h.orig:58,64 - /n/sources/patch/applied/sokoban-bfs-responsive-leak/sokoban.h:70,75
  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 */
sokoban.h.orig:90,105 - /n/sources/patch/applied/sokoban-bfs-responsive-leak/sokoban.h:101,118
  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);

/sys/src/games/sokoban/README
README.orig:17,22 - /n/sources/patch/applied/sokoban-bfs-responsive-leak/README:17,22
  
  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.



^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2005-09-05 12:09 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-09-05 12:09 [9fans] [patch] applied: sokoban-bfs-responsive-leak patch-log

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).