shithub: asif

Download patch

ref: be641a495dbf3c12757c1cceaec81a83406aa44c
parent: c43fe6ddcfe9b0c93572f0042267573e05361bef
author: qwx <[email protected]>
date: Tue Oct 25 22:56:37 EDT 2022

path(1): store existing results per scenario, output paths, and misc improvements

- don't run all scenarios by default; properly segment the
different code paths, remove redundancies
- don't start graphics for non-interactive profiling mode
- add start/goal/block node mouse modes to avoid conflicts with
panning and selection; mb1 now only selects, mb2 pans, mb3 inserts
a node given mouse mode
- on-hud error display
- rename opened→expanded, touched→opened, more correct terms
- better default parameters handling
- reduce stack size until it's actually warranted to go that
high, probably still is too high

--- a/app/path/client.c
+++ b/app/path/client.c
@@ -11,9 +11,10 @@
 #include "dat.h"
 #include "fns.h"
 
-int	(*mousefn)(Mouse, Point);
-int	(*keyfn)(Rune);
+typedef Vertex Point;
 
+static int	(*mousefn)(Mouse, Point);
+static int	(*keyfn)(Rune);
 static Keyboardctl *kc;
 static Mousectl *mc;
 
@@ -64,18 +65,24 @@
 }
 
 void
-init(char *scen, char *res, Vertex v, int m, int a, int d)
+initgraphics(int (*kfn)(Rune), int (*mfn)(Mouse, Point))
 {
-	fmtinstall('P', Pfmt);
-	fmtinstall('R', Rfmt);
-	fmtinstall('V', Vfmt);
-	fmtinstall('N', Nfmt);
-	initfs();
-	if(initmap(scen, res, v, m, a, d) < 0)
-		sysfatal("init: %r");
+	keyfn = kfn;
+	mousefn = mfn;
 	initdrw();
 	if((kc = initkeyboard(nil)) == nil)
 		sysfatal("initkeyboard: %r");
 	if((mc = initmouse(nil, screen)) == nil)
 		sysfatal("initmouse: %r");
+}
+
+void
+init(char *map, Vertex dim, int m, int a, int d)
+{
+	fmtinstall('P', Pfmt);
+	fmtinstall('R', Rfmt);
+	fmtinstall('V', Vfmt);
+	fmtinstall('N', Nfmt);
+	initfs();
+	initmap(map, dim, m, a, d);
 }
--- a/app/path/dat.h
+++ b/app/path/dat.h
@@ -1,14 +1,31 @@
+typedef struct Sim Sim;
+
 extern Node *selected;
 extern Node *start, *goal;
 
 enum{
+	Mmodegoal,
+	Mmodestart,
+	Mmodeblock,
+	Mmodes,
+};
+extern int mousemode;
+
+enum{
 	Pbfs,
 	Pdijkstra,
 	Pa∗,
 };
-extern int	(*pathfn)(Node*, Node*);
 
-extern int nscen, scenid;
+struct Sim{
+	Prof;
+	VArray *path;
+	Vertex start;
+	Vertex goal;
+};
+extern int nscen, curscen;
+extern char *mapfile;
+extern VArray *sims;
 
 extern int nodesz;
 extern int showgrid;
--- a/app/path/drw.c
+++ b/app/path/drw.c
@@ -50,6 +50,21 @@
 	pan.y -= p.y;
 }
 
+void
+errmsg(char *fmt, ...)
+{
+	char s[256];
+	va_list arg;
+	Point p;
+
+	va_start(arg, fmt);
+	vseprint(s, s+sizeof s, fmt, arg);
+	va_end(arg);
+	p = addpt(screen->r.min, subpt(Pt(2, viewr.max.y+2), viewΔ));
+	p.y += font->height * 2;
+	string(screen, p, col[Cgoal], ZP, font, s);
+}
+
 Node *
 scrselect(Point p)
 {
@@ -98,6 +113,10 @@
 		else if(n->open)
 			sp = strecpy(sp, s+sizeof s, "O");
 	}
+	sp = seprint(sp, s+sizeof s, "; RMB sets %s",
+		mousemode == Mmodegoal ? "goal" :
+		mousemode == Mmodestart ? "start" :
+		"block");
 	USED(sp);
 	p = addpt(screen->r.min, subpt(Pt(2, viewr.max.y+2), viewΔ));
 	string(screen, p, col[Cfree], ZP, font, s);
@@ -104,7 +123,7 @@
 	if(start != nil && goal != nil){
 		seprint(s, s+sizeof s,
 			"path len=%d Δ=%.2f $=%.2f opened=%d expanded=%d updated=%d closed=%d",
-			stats.steps, stats.dist, stats.cost, stats.touched, stats.opened,
+			stats.steps, stats.dist, stats.cost, stats.opened, stats.expanded,
 			stats.updated, stats.closed);
 		p.y += font->height;
 		string(screen, p, col[Cfree], ZP, font, s);
@@ -206,7 +225,7 @@
 	if(-viewΔ.y < font->height * 2)
 		viewΔ.y = 0;
 	hudr.min = addpt(screen->r.min, subpt(Pt(2, viewr.max.y+2), viewΔ));
-	hudr.max = addpt(hudr.min, Pt(screen->r.max.x, font->height*2));
+	hudr.max = addpt(hudr.min, Pt(screen->r.max.x, font->height*3));
 	freeimage(view);
 	view = eallocimage(viewr, 0, DNofill);
 	updatedrw(1);
--- a/app/path/fns.h
+++ b/app/path/fns.h
@@ -1,18 +1,25 @@
-void	init(char*, char*, Vertex, int, int, int);
+void	initgraphics(int (*)(Rune), int (*)(Mouse, Point));
+void	init(char*, Vertex, int, int, int);
 Node*	scrselect(Point);
+void	errmsg(char*, ...);
 void	updatedrw(int);
 int	menter(char*, char*, int);
 void	evloop(void);
-void	showscen(int);
-void	reloadscen(void);
-void	runscens(void);
-int	readscen(char*, char*, Vertex*);
+void	writeresults(void);
+int	readresults(char*);
+int	readscens(char*);
+int	readmaphdr(char*, Vertex*);
+int	readmap(char*);
 void	initfs(void);
 int	Vfmt(Fmt*);
 int	Nfmt(Fmt*);
-int	initmap(char*, char*, Vertex, int, int, int);
+int	trypath(Node*, Node*);
+int	showscen(int);
+int	reloadscen(void);
+void	runallscens(void);
+int	initmap(char*, Vertex, int, int, int);
 void	initdrw(void);
 void	resetdrw(void);
 Vertex	n2s(Node*);
-void	clearmap(void);
-int	setparm(int, int, int);
+
+#pragma	varargck	argpos	errmsg	1
--- a/app/path/fs.c
+++ b/app/path/fs.c
@@ -7,41 +7,50 @@
 #include "dat.h"
 #include "fns.h"
 
+char *mapfile;
 int nscen, scenid;
 
 /* https://bitbucket.org/dharabor/pathfinding/src/gppc/gppc-2014/scenarios/ */
-typedef struct Sim Sim;
-struct Sim{
-	Prof;
-	Vertex start;
-	Vertex goal;
+enum{
+	Nscen = 4100,
 };
-static VArray *sims;
-static char *scenmap;
+VArray *sims;
 
-void
-showscen(int id)
+static VArray *
+parsepath(char *s, Sim *sim)
 {
-	Sim *sp;
+	int n;
+	u64int x;
+	char *p, *t;
+	VArray *v;
+	Vertex px;
 
-	assert(id >= 0 && id < nscen);
-	sp = sims->p;
-	sp += id;
-	start = p2n(sp->start);
-	goal = p2n(sp->goal);
-	if(pathfn(start, goal) < 0)
-		fprint(2, "showscen: findpath from %N to %N: %r\n",
-			start, goal);
+	n = 0;
+	v = valloc(sim->steps, sizeof(Vertex));
+	while((p = strchr(s, ',')) != nil){
+		*p = 0;
+		x = strtoull(s, &t, 10);
+		if(t == s)
+			sysfatal("parsepath: invalid node number");
+		px = V(x % gridwidth, x / gridwidth);
+		vinsert(v, (char*)&px);
+		n++;
+		s = p + 1;
+	}
+	if(n != sim->steps)
+		sysfatal("parsepath -- phase error");
+	return v;
 }
 
-static void
+int
 readresults(char *path)
 {
 	int n;
-	char *s, *arg[32];
+	char *s, *arg[9];
 	Biobuf *bf;
 	Sim *sp, *se;
 
+	assert(sims != nil);
 	if((bf = Bopen(path, OREAD)) == nil)
 		sysfatal("readresults: %r");
 	sp = sims->p;
@@ -49,77 +58,113 @@
 	while(sp < se){
 		if((s = Brdstr(bf, '\n', 1)) == nil)
 			sysfatal("readresults: %r");
-		if((n = getfields(s, arg, nelem(arg), 1, " \t")) != 8)
-			sysfatal("invalid record length %d not 8", n);
+		if((n = getfields(s, arg, nelem(arg), 1, " \t")) != nelem(arg)){
+			werrstr("invalid record length %d not %d", n, nelem(arg));
+			return -1;
+		}
 		sp->cost = strtod(arg[7], nil);
 		sp->steps = atoi(arg[2]);
-		sp->touched = atoi(arg[3]);
-		sp->opened = atoi(arg[4]);
+		sp->opened = atoi(arg[3]);
+		sp->expanded = atoi(arg[4]);
 		sp->updated = atoi(arg[5]);
 		sp->closed = atoi(arg[6]);
+		if(sp->steps <= 0)
+			dprint(Lognone, "path::readresults: invalid entry line %zd\n",
+				sp - (Sim *)sims->p);
+		else
+			sp->path = parsepath(arg[8], sp);
 		free(s);
 		sp++;
 	}
 	Bterm(bf);
+	if(sp - (Sim *)sims->p != nscen){
+		werrstr("results file -- phase error");
+		return -1;
+	}
+	return 0;
 }
 
 void
-runscens(void)
+writeresults(void)
 {
+	int i;
 	Sim *sp, *se;
+	Vertex *p, *e;
 
-	sp = sims->p;
-	se = sp + sims->n;
-	fprint(2, "id\tsteps\ttouched\texpanded\tupdated\topened\tcost\tdist\n");
-	while(sp < se){
-		start = p2n(sp->start);
-		goal = p2n(sp->goal);
-		if(pathfn(start, goal) < 0)
-			fprint(2, "runscens: findpath from %N to %N: %r\n",
-				start, goal);
-		sp->Prof = stats;
-		fprint(2, "%zd\t%d\t%d\t%d\t%d\t%d\t%.3f\t%.3f\n",
-			sp - (Sim*)sims->p,
-			stats.steps, stats.touched, stats.opened,
-			stats.updated, stats.closed, stats.cost, stats.dist);
-		sp++;
+	print("id\tsteps\topened\texpanded\tupdated\texpanded\tcost\tdist\n");
+	for(i=0, sp=sims->p, se=sp+sims->n; sp<se; sp++, i++){
+		print("%d\t%d\t%d\t%d\t%d\t%d\t%.3f\t%.3f\t",
+			i, sp->steps, sp->opened, sp->expanded,
+			sp->updated, sp->closed, sp->cost, sp->dist);
+		for(p=sp->path->p, e=p+sp->path->n; p<e; p++)
+			print("%s%d", p == sp->path->p ? "" : ",",
+				p->y * gridwidth + p->x);
+		print("\n");
 	}
 }
 
-static int
-readscenmaphdr(Biobuf *bf, Vertex *v)
+int
+readmaphdr(char *path, Vertex *dim)
 {
-	int done;
+	int r, done;
 	char *s, *t;
+	Biobuf *bf;
 
+	if((bf = Bopen(path, OREAD)) == nil)
+		sysfatal("readmaphdr: %r");
 	done = 0;
+	r = -1;
 	while((s = Brdstr(bf, '\n', 1)) != nil){
 		t = strtok(s, " ");
 		if(strcmp(t, "type") == 0 || strcmp(t, "version") == 0){
 			;
 		}else if(strcmp(t, "height") == 0){
-			if((t = strtok(nil, " ")) == nil)
-				return -1;
-			v->y = atoi(t);
+			if((t = strtok(nil, " ")) == nil){
+				werrstr("invalid height field");
+				goto end;
+			}
+			dim->y = atoi(t);
 		}else if(strcmp(t, "width") == 0){
-			if((t = strtok(nil, " ")) == nil)
-				return -1;
-			v->x = atoi(t);
+			if((t = strtok(nil, " ")) == nil){
+				werrstr("invalid width field");
+				goto end;
+			}
+			dim->x = atoi(t);
 		}else if(strcmp(t, "map") == 0)
 			done = 1;
 		else{
 			werrstr("unknown verb %s", t);
-			return -1;
+			goto end;
 		}
 		free(s);
+		if(done){
+			r = 0;
+			break;
+		}
+	}
+end:
+	Bterm(bf);
+	return r;
+}
+
+static int
+skiphdr(Biobuf *bf)
+{
+	int done;
+	char *s;
+
+	while((s = Brdstr(bf, '\n', 1)) != nil){
+		done = strcmp(s, "map") == 0;
+		free(s);
 		if(done)
 			return 0;
 	}
+	werrstr("skipheader: skipped past eof");
 	return -1;
 }
 
-static int
-readscenmap(char *path, Vertex *v)
+int
+readmap(char *path)
 {
 	char c, *s, *t;
 	Vertex u;
@@ -126,12 +171,8 @@
 	Biobuf *bf;
 
 	if((bf = Bopen(path, OREAD)) == nil)
-		sysfatal("readscenmap: %r");
-	if(readscenmaphdr(bf, v) < 0)
-		return -1;
-	if(gridwidth == 0)
-		initgrid(v->x, v->y);
-	cleargrid();
+		sysfatal("readmap: %r");
+	skiphdr(bf);
 	for(u.y=0; u.y<gridheight; u.y++){
 		if((s = Brdstr(bf, '\n', 1)) == nil)
 			return -1;
@@ -156,74 +197,22 @@
 	return 0;
 }
 
-void
-reloadscen(void)
-{
-	Vertex v;
-
-	if(readscenmap(scenmap, &v) < 0)
-		sysfatal("reloadscen: %r");
-}
-
-static int
-readscenhdr(Biobuf *bf, Vertex *v)
-{
-	int done;
-	char *s, *t;
-
-	done = 0;
-	while((s = Brdstr(bf, '\n', 1)) != nil){
-		t = strtok(s, " ");
-		if(strcmp(t, "type") == 0){
-			;
-		}else if(strcmp(t, "height") == 0){
-			if((t = strtok(nil, " ")) == nil)
-				return -1;
-			v->y = atoi(t);
-		}else if(strcmp(t, "width") == 0){
-			if((t = strtok(nil, " ")) == nil)
-				return -1;
-			v->x = atoi(t);
-		}else if(strcmp(t, "map") == 0)
-			done = 1;
-		else{
-			werrstr("unknown verb %s", t);
-			return -1;
-		}
-		free(s);
-		if(done)
-			return 0;
-	}
-	return -1;
-}
-
 int
-readscen(char *path, char *respath, Vertex *v)
+readscens(char *path)
 {
 	int n;
-	char *s, *arg[32];
+	char *s, *arg[9];
 	Biobuf *bf;
 	Sim sim;
 
-	if(path == nil)
-		return 0;
-	doprof = 1;
-	if((s = strrchr(path, '.')) == nil){
-		werrstr("invalid path name");
-		return -1;
-	}
-	*s = 0;
-	scenmap = estrdup(path);
-	if(readscenmap(path, v) < 0)
-		return -1;
-	*s = '.';
 	if((bf = Bopen(path, OREAD)) == nil)
 		sysfatal("readscen: %r");
-	sims = valloc(4100, sizeof(Sim));
+	memset(&sim, 0, sizeof sim);
+	sims = valloc(Nscen, sizeof(Sim));
 	free(Brdstr(bf, '\n', 1));	/* "version 1\n" */
 	while((s = Brdstr(bf, '\n', 1)) != nil){
-		if((n = getfields(s, arg, nelem(arg), 1, " \t")) != 9){
-			werrstr("invalid record length %d not 9", n);
+		if((n = getfields(s, arg, nelem(arg), 1, " \t")) < nelem(arg)){
+			werrstr("invalid record length %d not %d", n, nelem(arg));
 			return -1;
 		}
 		sim.start.x = atoi(arg[4]);
@@ -236,12 +225,8 @@
 		free(s);
 	}
 	Bterm(bf);
-	if(nscen != 4100)
-		sysfatal("scen file -- phase error");
-	if(respath != nil){
-		readresults(respath);
-		showscen(0);
-	}
+	if(nscen != Nscen)
+		sysfatal("scenario file -- phase error");
 	return 0;
 }
 
--- a/app/path/map.c
+++ b/app/path/map.c
@@ -7,8 +7,10 @@
 #include "fns.h"
 
 int movemode;
-int	(*pathfn)(Node*, Node*);
+int curscen;
 
+static int	(*pathfn)(Node*, Node*);
+
 Vertex
 n2s(Node *n)
 {
@@ -21,17 +23,102 @@
 }
 
 int
-setparm(int mmode, int alg, int dist)
+trypath(Node *a, Node *b)
 {
-	switch(mmode){
+	if(a == nil || b == nil)
+		return -1;
+	dprint(Logdebug, "path::trypath: from %N to %N: ", start, goal);
+	if(pathfn(a, b) < 0){
+		dprint(Logdebug, "failed: %r\n");
+		return -1;
+	}
+	dprint(Logdebug, "success\n");
+	return 0;
+}
+
+int
+showscen(int id)
+{
+	Sim *sp;
+
+	assert(id >= 0 && id < sims->n);
+	curscen = id;
+	sp = sims->p;
+	sp += id;
+	start = p2n(sp->start);
+	goal = p2n(sp->goal);
+	if(trypath(start, goal) < 0){
+		errmsg("failed: %r");
+		return -1;
+	}
+	return 0;
+}
+
+int
+reloadscen(void)
+{
+	cleargrid();
+	if(mapfile == nil)
+		return 0;
+	if(readmap(mapfile) < 0)
+		return -1;
+	return showscen(curscen);
+}
+
+void
+savepath(Sim *sp)
+{
+	Node *n;
+	Vertex v;
+
+	if(sp->path == nil)
+		sp->path = valloc(sp->steps, sizeof(Vertex));
+	else
+		vnuke(sp->path);
+	for(n=start; n!=nil; n=n->to){
+		v = n->Vertex;
+		vinsert(sp->path, (char*)&v);
+		if(n == goal)
+			return;
+	}
+	/* FIXME: dumpcore function? */
+	fprint(2, "savepath: malformed path\n");
+	abort();
+}
+
+void
+runallscens(void)
+{
+	Sim *sp, *se;
+
+	for(sp=sims->p, se=sp+sims->n; sp<se; sp++){
+		if(showscen(sp - (Sim *)sims->p) < 0)
+			continue;
+		sp->Prof = stats;
+		savepath(sp);
+	}
+}
+
+static int
+setparm(Vertex *dim, int move, int alg, int dist)
+{
+	switch(move){
 	case Move8: /* wet floor */
-	case Move4: movemode = mmode; break;
-	default: sysfatal("setparm: unknown move mode %d", mmode);
+	case Move4: movemode = move; break;
+	case -1:
+		movemode = Move8;
+		dprint(Logdebug, "set by default: 8-direction movement\n");
+		break;
+	default: sysfatal("setparm: unknown move mode %d", move);
 	}
 	switch(alg){
 	case Pa∗: pathfn = a∗findpath; break;
 	case Pbfs: pathfn = bfsfindpath; break;
 	case Pdijkstra: pathfn = dijkstrafindpath; break;
+	case -1:
+		pathfn = a∗findpath;
+		dprint(Logdebug, "set by default: unmodified A∗ algorithm\n");
+		break;
 	default: sysfatal("setparm: unknown algo type %d", alg);
 	}
 	switch(dist){
@@ -38,28 +125,30 @@
 	case Deuclid: distfn = eucdist; break;
 	case Dmanhattan: distfn = manhdist; break;
 	case Doctile: distfn = octdist; break;
+	case -1:
+		distfn = octdist;
+		dprint(Logdebug, "set by default: octile distance\n");
+		break;
 	default: sysfatal("setparm: unknown distance function %d", dist);
 	}
-	clearmap();
+	if(dim->x == -1)
+		dim->x = 64;
+	if(dim->y == -1)
+		dim->y = 64;
+	if(dim->x <= 0 || dim->x >= 512 || dim->y <= 0 || dim->y >= 512)
+		sysfatal("setparm: invalid map size, must be in ]0,512]");
 	return 0;
 }
 
-void
-clearmap(void)
-{
-	start = goal = nil;
-	if(grid == nil || doprof)
-		return;
-	cleargrid();
-}
-
 int
-initmap(char *scen, char *res, Vertex v, int m, int a, int d)
+initmap(char *map, Vertex dim, int move, int alg, int dist)
 {
-	setparm(m, a, d);
-	if(scen == nil)
-		initgrid(v.x, v.y);
-	else if(readscen(scen, res, &v) < 0)
-		sysfatal("readscen: %r");
+	mapfile = map;
+	setparm(&dim, move, alg, dist);
+	if(map != nil && readmaphdr(map, &dim) < 0)
+		sysfatal("initmap: %r");
+	initgrid(dim.x, dim.y);
+	if(map != nil && readmap(map) < 0)
+		sysfatal("initmap: %r");
 	return 0;
 }
--- a/app/path/path.c
+++ b/app/path/path.c
@@ -11,17 +11,14 @@
 #include "dat.h"
 #include "fns.h"
 
-extern int	(*mousefn)(Mouse, Point);
 extern void	dopan(Point);
 extern Point pan;
-extern int	(*keyfn)(Rune);
 
-mainstacksize = 512*1024;
+mainstacksize = 128*1024;
 
 Node *start, *goal;
+int mousemode;
 
-static setgoalmode;
-
 static int
 grmouse(Mouse m, Point Δ)
 {
@@ -28,9 +25,10 @@
 	static Node *old;
 	Node *n;
 
-	if(m.buttons == 0)
+	if(m.buttons == 0){
+		old = nil;
 		return 0;
-	else if((m.buttons & 7) == 2){
+	}else if((m.buttons & 7) == 2){
 		dopan(Δ);
 		updatedrw(1);
 		return 1;
@@ -38,26 +36,20 @@
 	if((n = scrselect(m.xy)) == nil || old == n)
 		return 0;
 	switch(m.buttons & 7){
-	case 1:
-		if(old == nil || isblocked(n) ^ isblocked(old))
-			toggleblocked(n);
-		break;
+	case 1: break; /* just selecting the node */
 	case 4:
-		if(setgoalmode){
-			if(start != n && !isblocked(n))
-				goal = n;
-		}else{
-			if(goal != n && !isblocked(n))
-				start = n;
+		switch(mousemode){
+		case Mmodegoal: if(n != start && !isblocked(n)) goal = n; break;
+		case Mmodestart: if(n != goal && !isblocked(n)) start = n; break;
+		case Mmodeblock:
+			if(n != start && n != goal
+			&& (old == nil || isblocked(n) ^ isblocked(old)))
+				toggleblocked(n);
+			break;
 		}
-		break;
 	}
 	old = n;
-	if(start != nil && goal != nil)
-		if(pathfn(start, goal) < 0){
-			dprint(Logdebug, "grid::findpath: findpath from [%#p,%P] to [%#p,%P]: %r\n",
-				start, n2p(start), goal, n2p(goal));
-		}
+	trypath(start, goal);
 	return 1;
 }
 
@@ -67,7 +59,7 @@
 	int n;
 	char buf[128];
 
-	snprint(buf, sizeof buf, "%d", scenid);
+	snprint(buf, sizeof buf, "%d", curscen);
 	if(menter("Scenario id?", buf, sizeof buf) < 0){
 		fprint(2, "getscen: %r\n");
 		return -1;
@@ -76,7 +68,6 @@
 		fprint(2, "getscen: invalid id %s\n", buf);
 		return -1;
 	}
-	scenid = n;
 	showscen(n);
 	return 0;
 }
@@ -89,16 +80,13 @@
 	case 'q':
 		threadexitsall(nil);
 	case 'r':
-		if(doprof){
-			reloadscen();
-			showscen(scenid);
-		}else
-			cleargrid();
+		reloadscen();
 		updatedrw(0);
 		break;
 	case ' ':
 	case '\n':
-		setgoalmode ^= 1;
+		mousemode = (mousemode + 1) % Mmodes;
+		updatedrw(0);
 		break;
 	case 'g':
 		showgrid ^= 1;
@@ -106,8 +94,6 @@
 		break;
 	case '0': case '1': case '2': case '3': case '4':
 	case '5': case '6': case '7': case '8': case '9':
-		if(!doprof)
-			break;
 		if(setscen() >= 0)
 			updatedrw(0);
 		break;
@@ -135,7 +121,7 @@
 static void
 usage(void)
 {
-	fprint(2, "usage: %s [-D4] [-a algo] [-d dist] [-s width[,height]] [-m scen]\n", argv0);
+	fprint(2, "usage: %s [-D4p] [-a algo] [-d dist] [-s width[,height]] [-m map] [-S scen] [-r res]\n", argv0);
 	threadexits("usage");
 }
 
@@ -143,15 +129,17 @@
 threadmain(int argc, char **argv)
 {
 	int w, h, a, d, m;
-	char *s, *scenres, *scenmap;
+	char *s, *map, *scen, *res;
 
-	w = 64;
-	h = 64;
+	w = -1;
+	h = -1;
 	a = -1;
 	d = -1;
-	m = Move8;
-	scenmap = nil;
-	scenres = nil;
+	m = -1;
+	doprof = 0;
+	map = nil;
+	scen = nil;
+	res = nil;
 	ARGBEGIN{
 	case 'D':
 		if(++debuglevel >= Logparanoid)
@@ -188,11 +176,17 @@
 		}
 		break;
 	case 'm':
-		scenmap = EARGF(usage());
+		map = EARGF(usage());
 		break;
+	case 'p':
+		doprof = 1;
+		break;
 	case 'r':
-		scenres = EARGF(usage());
+		res = EARGF(usage());
 		break;
+	case 'S':
+		scen = EARGF(usage());
+		break;
 	case 's':
 		w = strtol(EARGF(usage()), &s, 0);
 		if(w <= 0)
@@ -207,18 +201,24 @@
 		break;
 	default: usage();
 	}ARGEND
-	if(w <= 0 || w > 512
-	|| h <= 0 || h > 512)
-		sysfatal("invalid map size, must be in ]0,512]");
-	if(d < 0)
-		d = m == Move8 ? Doctile : Dmanhattan;
-	if(a < 0)
-		a = Pa∗;
-	keyfn = grkey;
-	mousefn = grmouse;
-	init(scenmap, scenres, (Vertex){w,h}, m, a, d);
-	if(doprof && scenres == nil)
-		runscens();
+	init(map, (Vertex){w,h}, m, a, d);
+	if(scen != nil){
+		if(map == nil)
+			sysfatal("mat not run scenarios without a map");
+		readscens(scen);
+		if(doprof){
+			runallscens();
+			writeresults();
+			threadexitsall(nil);
+		}
+		if(res != nil)
+			readresults(res);
+	}
+	initgraphics(grkey, grmouse);
+	if(scen != nil){
+		showscen(0);
+		updatedrw(0);
+	}
 	evloop();
 	threadexitsall(nil);
 }
--- a/path/a∗.c
+++ b/path/a∗.c
@@ -76,7 +76,7 @@
 			sysfatal("a∗: %r");
 		for(v=*vl++; v!=nil; v=*vl++){
 			pv = v->aux;
-			stats.touched++;
+			stats.opened++;
 			if(v->closed)
 				continue;
 			g = pu->g + unitmovecost(u, v);
@@ -84,10 +84,10 @@
 			if(!v->open){
 				v->from = u;
 				v->open = 1;
-				stats.opened++;
+				stats.expanded++;
 				pv->h = distfn(v, b);
 				pv->g = g;
-				dprint(Logtrace, "a∗: opened [%#p,%P] h %.4f g %.4f f %.4f\n",
+				dprint(Logtrace, "a∗: expanded [%#p,%P] h %.4f g %.4f f %.4f\n",
 					v, n2p(v), pv->h, pv->g, pv->h + pv->g);
 				pv->pq = pushqueue(pv->g + pv->h, pv, &queue);
 			}else if(Δg > 0){
--- a/path/bfs.c
+++ b/path/bfs.c
@@ -42,10 +42,10 @@
 		if((vl = expand(u)) == nil)
 			sysfatal("bfs: %r");
 		for(v=*vl++; v!=nil; v=*vl++){
-			stats.touched++;
+			stats.opened++;
 			if(v->open)
 				continue;
-			stats.opened++;
+			stats.expanded++;
 			v->from = u;
 			vecpush(front, &v);
 			v->open = 1;
--- a/path/dijkstra.c
+++ b/path/dijkstra.c
@@ -79,7 +79,7 @@
 			sysfatal("a∗: %r");
 		for(v=*vl++; v!=nil; v=*vl++){
 			pv = v->aux;
-			stats.touched++;
+			stats.opened++;
 			if(v->closed)
 				continue;
 			g = pu->g + unitmovecost(u, v);
@@ -88,9 +88,9 @@
 			if(!v->open){
 				v->from = u;
 				v->open = 1;
-				stats.opened++;
+				stats.expanded++;
 				pv->g = g;
-				dprint(Logtrace, "dijkstra: opened [%#p,%P] g %.4f\n",
+				dprint(Logtrace, "dijkstra: expanded [%#p,%P] g %.4f\n",
 					v, n2p(v), pv->g);
 				pv->pq = pushqueue(pv->g, pv, &queue);
 			}
--- a/path/path.h
+++ b/path/path.h
@@ -70,8 +70,8 @@
 	double dist;
 	double cost;
 	int steps;
-	int touched;
 	int opened;
+	int expanded;
 	int updated;
 	int closed;
 };