shithub: asif

Download patch

ref: dd04e30867a84320194bf9f9f7724c55060e016f
parent: 366e6a5a214357bee19268314eed9d590f663246
author: qwx <qwx@sciops.net>
date: Thu Jul 7 03:53:24 EDT 2022

fix up dijkstra search code and add to path

department of redundancy department, but at least it works

--- a/app/path/drw.c
+++ b/app/path/drw.c
@@ -166,7 +166,7 @@
 	viewr = Rpt(ZP, Pt(gridwidth*Nodesz+1, gridheight*Nodesz+1));
 	viewΔ = divpt(addpt(subpt(ZP, subpt(screen->r.max, screen->r.min)), viewr.max), 2);
 	hudr.min = addpt(screen->r.min, subpt(Pt(2, viewr.max.y+2), viewΔ));
-	hudr.max = addpt(hudr.min, Pt(viewr.max.x-2, font->height*3));
+	hudr.max = addpt(hudr.min, Pt(screen->r.max.x, font->height*3));
 	freeimage(view);
 	view = eallocimage(viewr, 0, DNofill);
 	draw(screen, screen->r, col[Cbg], nil, ZP);
--- a/app/path/map.c
+++ b/app/path/map.c
@@ -30,7 +30,7 @@
 	switch(alg){
 	case Pa∗: pathfn = a∗findpath; break;
 	case Pbfs: pathfn = bfsfindpath; break;
-	//case Pdijkstra: pathfn = dijkstrafindpath; break;
+	case Pdijkstra: pathfn = dijkstrafindpath; break;
 	default: sysfatal("setparm: unknown algo type %d", alg);
 	}
 	switch(dist){
--- a/app/path/mkfile
+++ b/app/path/mkfile
@@ -2,10 +2,10 @@
 BIN=$home/bin/$objtype/asif
 TARG=path
 HFILES=../../asif.h ../../path/path.h dat.h fns.h
-#	../../path/dijkstra.$O\
 OFILES=\
 	../../path/a∗.$O\
 	../../path/bfs.$O\
+	../../path/dijkstra.$O\
 	../../path/grid.$O\
 	../../dprint.$O\
 	../../emalloc.$O\
--- a/app/path/path.c
+++ b/app/path/path.c
@@ -66,7 +66,7 @@
 static void
 usage(void)
 {
-	fprint(2, "usage: %s [-D4] [-s width[,height]]\n", argv0);
+	fprint(2, "usage: %s [-D4] [-a algo] [-d dist] [-s width[,height]]\n", argv0);
 	threadexits("usage");
 }
 
@@ -73,13 +73,14 @@
 void
 threadmain(int argc, char **argv)
 {
-	int w, h, d, m;
+	int w, h, a, d, m;
 	char *s;
 
 	w = 64;
 	h = 64;
-	d = Move8;
-	m = Doctile;
+	a = -1;
+	d = -1;
+	m = Move8;
 	ARGBEGIN{
 	case 'D':
 		if(++debuglevel >= Logparanoid)
@@ -86,9 +87,35 @@
 			mainmem->flags |= POOL_NOREUSE | POOL_PARANOIA | POOL_LOGGING;
 		break;
 	case '4':
-		d = Move4;
-		m = Dmanhattan;
+		m = Move4;
+		d = Dmanhattan;
 		break;
+	case 'a':
+		s = EARGF(usage());
+		if(strcmp(s, "a∗") == 0)
+			a = Pa∗;
+		else if(strcmp(s, "dijkstra") == 0)
+			a = Pdijkstra;
+		else if(strcmp(s, "bfs") == 0)
+			a = Pbfs;
+		else{
+			fprint(2, "unsupported algorithm\n");
+			usage();
+		}
+		break;
+	case 'd':
+		s = EARGF(usage());
+		if(strcmp(s, "octile") == 0)
+			d = Doctile;
+		else if(strcmp(s, "manhattan") == 0)
+			d = Dmanhattan;
+		else if(strcmp(s, "euclid") == 0)
+			d = Deuclid;
+		else{
+			fprint(2, "unsupported distance function\n");
+			usage();
+		}
+		break;
 	case 's':
 		w = strtol(EARGF(usage()), &s, 0);
 		if(w <= 0)
@@ -108,7 +135,11 @@
 		sysfatal("invalid map size, must be in ]0,512]");
 	keyfn = grkey;
 	mousefn = grmouse;
-	setparm(d, Pa∗, m);
+	if(d < 0)
+		d = m == Move8 ? Doctile : Dmanhattan;
+	if(a < 0)
+		a = Pa∗;
+	setparm(m, a, d);
 	init(w, h);
 	evloop();
 }
--- a/path/a∗.c
+++ b/path/a∗.c
@@ -4,8 +4,6 @@
 #include "asif.h"
 #include "path.h"
 
-/* FIXME: assumes grid */
-
 typedef Vertex Point;
 
 typedef struct PNode PNode;
@@ -44,9 +42,7 @@
  * https://www.redbloblgames.com/pathfinding/a-star/implementation.html
  * one addition: make cost function to increase at a slower rate to
  * resolve tie-breakers in favor of closer nodes, otherwise we will
- * explore all nodes in the rectangle between the two points; does
- * NOT work with 4-way movement and manhattan distance for some reason */
- 	// FIXME: ^- reverify that it does not work
+ * explore all nodes in the rectangle between the two points */
 static double
 movecost(int Δx, int Δy)
 {
--- a/path/bfs.c
+++ b/path/bfs.c
@@ -13,8 +13,12 @@
 static PNode**	(*successorfn)(Node*);
 
 static void
-cleanup(void)
+backtrack(Node *a, Node *b)
 {
+	Node *n;
+
+	for(n=b; n!=a; n=n->from)
+		n->from->to = n;
 }
 
 static double
@@ -110,6 +114,9 @@
 		}
 	}
 	vecfree(front);
+	if(u != b)
+		return -1;
+	backtrack(a, b);
 	return 0;
 }
 
--- a/path/dijkstra.c
+++ b/path/dijkstra.c
@@ -4,65 +4,184 @@
 #include <draw.h>
 #include <mouse.h>
 #include <keyboard.h>
-#include "../asif.h"
-#include "dat.h"
-#include "fns.h"
+#include "asif.h"
+#include "path.h"
 
 /* currently uniform cost search and no reprioritizing (decrease-key operation) */
 
-/* explore in all directions at the same time */
+typedef Vertex Point;
+
+typedef struct PNode PNode;
+struct PNode{
+	Node *n;
+	double g;
+	double Δg;
+	Pairheap *pq;
+};
+
+static PNode**	(*successorfn)(Node*);
+static Zpool *zpool;
+
+static void
+cleanup(Pairheap **queue)
+{
+	Pairheap *p;
+
+	while((p = popqueue(queue)) != nil){
+		zfree(p->aux);
+		free(p);
+	}
+}
+
+static void
+backtrack(Node *a, Node *b)
+{
+	Node *n;
+
+	for(n=b; n!=a; n=n->from)
+		n->from->to = n;
+}
+
+/* slightly penalize diagonal movement for nicer-looking paths; cf.:
+ * https://www.redbloblgames.com/pathfinding/a-star/implementation.html
+ * one addition: make cost function to increase at a slower rate to
+ * resolve tie-breakers in favor of closer nodes, otherwise we will
+ * explore all nodes in the rectangle between the two points */
 static double
 movecost(int Δx, int Δy)
 {
-	return Δx != 0 && Δy != 0 ? SQRT2 : 1.0;
+	return Δx != 0 && Δy != 0 ? 1.001 : 1.0;
 }
 
-static Node *
+static PNode **
+successors8(Node *nu)
+{
+	static PNode *suc[8+1];
+	static dtab[2*(nelem(suc)-1)]={
+		1,0, 0,-1, -1,0, 0,1,
+		-1,-1, -1,1, 1,-1, 1,1,
+	};
+	int i;
+	Node *nv;
+	PNode *v, **vp;
+	Point p;
+	Rectangle r;
+
+	memset(suc, 0, sizeof suc);
+	p = n2p(nu);
+	r = Rect(0, 0, gridwidth, gridheight);
+	for(i=0, vp=suc; i<nelem(dtab); i+=2){
+		if(!ptinrect(addpt(p, Pt(dtab[i], dtab[i+1])), r))
+			continue;
+		nv = nu + dtab[i+1] * gridwidth + dtab[i];
+		assert(nv >= grid && nv < grid + gridwidth * gridheight);
+		if(isblocked(nv))
+			continue;
+		v = zalloc(zpool);
+		v->n = nv;
+		v->Δg = movecost(dtab[i], dtab[i+1]);
+		*vp++ = v;
+	}
+	return suc;
+}
+
+static PNode **
+successors4(Node *nu)
+{
+	static PNode *suc[4+1];
+	static int dtab[2*(nelem(suc)-1)]={
+		1,0, -1,0, 0,-1, 0,1,
+	}, rdtab[nelem(dtab)]={
+		0,1, 0,-1, -1,0, 1,0,
+	};
+	int i, *t;
+	Node *nv;
+	PNode *v, **vp;
+	Point p;
+	Rectangle r;
+
+	memset(suc, 0, sizeof suc);
+	p = n2p(nu);
+	r = Rect(0, 0, gridwidth, gridheight);
+	/* path straightening; cf.:
+	 * https://www.redbloblgames.com/pathfinding/a-star/implementation.html */
+	t = (p.x + p.y) % 2 == 0 ? rdtab : dtab;
+	for(i=0, vp=suc; i<nelem(dtab); i+=2){
+		if(!ptinrect(addpt(p, Pt(t[i], t[i+1])), r))
+			continue;
+		nv = nu + t[i+1] * gridwidth + t[i];
+		assert(nv >= grid && nv < grid + gridwidth * gridheight);
+		if(isblocked(nv))
+			continue;
+		v = zalloc(zpool);
+		v->n = nv;
+		v->Δg = movecost(t[i], t[i+1]);
+		*vp++ = v;
+	}
+	return suc;
+}
+
+static int
 dijkstra(Node *a, Node *b)
 {
 	double g, Δg;
-	Node *x, *s, **sl;
+	PNode *u, *v, **vl;
+	Node *nu, *nv;
 	Pairheap *queue, *pn;
 
-	assert(a != nil && b != nil);
-	assert(a != b);
 	queue = nil;
-	a->pq = pushqueue(0, a, &queue);
-	x = a;
+	nu = a;
+	u = zalloc(zpool);
+	u->n = a;
+	u->pq = pushqueue(distfn(a, b), u, &queue);
 	while((pn = popqueue(&queue)) != nil){
-		x = pn->aux;
+		u = pn->aux;
+		nu = u->n;
 		free(pn);
-		if(x == b)
+		if(nu == b)
 			break;
-		x->closed = 1;
-		dprint(Logtrace, "dijkstrdijkstra: closed [%#p,%P] h %.4f g %.4f\n",
-			x, n2p(x), x->h, x->g);
-		if((sl = successorfn(x)) == nil)
-			sysfatal("dijkstra: %r");
-		for(s=*sl++; s!=nil; s=*sl++){
-			if(s->closed)
+		nu->closed = 1;
+		dprint(Logtrace, "dijkstra: closed [%#p,%P] g %.4f\n",
+			u, n2p(nu), u->g);
+		if((vl = successorfn(nu)) == nil)
+			sysfatal("a∗: %r");
+		for(v=*vl++; v!=nil; v=*vl++){
+			nv = v->n;
+			if(nv->closed)
 				continue;
-			assert(!isblocked(s));
-			g = x->g + s->Δg;
-			Δg = s->g - g;
-			if(!s->open){
-				s->from = x;
-				s->open = 1;
-				s->g = g;
-				dprint(Logtrace, "dijkstra: opened [%#p,%P] h %.4f g %.4f f %.4f\n",
-					s, n2p(s), s->h, s->g, s->h + s->g);
-				s->pq = pushqueue(s->g, s, &queue);
+			g = u->g + v->Δg;
+			Δg = v->g - g;
+			if(!nv->open){
+				nv->from = nu;
+				nv->open = 1;
+				v->g = g;
+				dprint(Logtrace, "dijkstra: opened [%#p,%P] g %.4f\n",
+					v, n2p(nv), v->g);
+				v->pq = pushqueue(v->g, v, &queue);
 			}
+			assert(Δg <= 0);
 		}
 	}
-	nukequeue(&queue);
-	return x;
+	cleanup(&queue);
+	if(nu != b)
+		return -1;
+	backtrack(a, b);
+	return 0;
 }
 
-void
-threadmain(int argc, char **argv)
+int
+dijkstrafindpath(Node *a, Node *b)
 {
-	init(argc, argv);
-	initgrid(dijkstra, movecost);
-	evloop();
+	assert(a != nil && b != nil && a != b);
+	clearpath();
+	if(zpool == nil)
+		zpool = znew(sizeof(PNode));
+	successorfn = movemode == Move8 ? successors8 : successors4;
+	dprint(Logdebug, "grid::dijkstrafindpath: dijkstra from [%#p,%P] to [%#p,%P]\n",
+		a, n2p(a), b, n2p(b));
+	if(dijkstra(a, b) < 0){
+		dprint(Logdebug, "grid::dijkstrafindpath: failed to find a path\n");
+		return -1;
+	}
+	return 0;
 }