shithub: battleship

Download patch

ref: 05825e751d69cde554c21ddfd00e646049425e31
parent: 2293ed78636e5f66b5e2884bebd193803cb6939f
author: rodri <rgl@antares-labs.eu>
date: Tue Sep 26 14:39:02 EDT 2023

initial work towards spectator mode.

--- a/bts.c
+++ b/bts.c
@@ -23,6 +23,7 @@
 	CMmatchesb,	/* list header */
 	CMmatch,	/* list entry */
 	CMmatchese,	/* list tail */
+	CMwatching,
 	CMwin,
 	CMlose,
 };
@@ -40,6 +41,7 @@
 	CMmatchesb,	"matches",	1,
 	CMmatch,	"m",		4,
 	CMmatchese,	"end",		1,
+	CMwatching,	"watching",	6,
 	CMwin,		"win",		1,
 	CMlose,		"lose",		1,
 };
@@ -116,6 +118,7 @@
 int layoutdone;
 Point2 lastshot;
 Menulist *matches;
+MatchInfo match; /* of which we are an spectator */
 
 struct {
 	int state;
@@ -310,6 +313,10 @@
 
 	s = "";
 	switch(game.state){
+	case Watching:
+		snprint(aux, sizeof aux, "watching %s vs. %s", match.pl[0], match.pl[1]);
+		s = aux;
+		break;
 	case Ready: s = "looking for players"; break;
 	case Outlaying: s = "place the fleet"; break;
 	case Waiting: s = "wait for your turn"; break;
@@ -326,9 +333,9 @@
 	vstring(dst, p, display->white, ZP, font, s);
 
 	p = Pt(alienboard.bbox.max.x+2, alienboard.bbox.min.y);
-	vstring(dst, p, display->white, ZP, font, oid);
+	vstring(dst, p, display->white, ZP, font, game.state == Watching? match.pl[1]: oid);
 	p = subpt(localboard.bbox.min, Pt(font->width+2,0));
-	vstring(dst, p, display->white, ZP, font, uid);
+	vstring(dst, p, display->white, ZP, font, game.state == Watching? match.pl[0]: uid);
 
 	if(game.state == Outlaying){
 		if(c == nil)
@@ -656,8 +663,10 @@
 	mc->xy = subpt(mc->xy, screen->r.min);
 
 	if(game.state == Waiting0)
-		if((selmatch = matches->update(matches, mc, drawchan)) >= 0)
-			if(debug) fprint(2, "selected match id %d title %s\n", selmatch, matches->entries[selmatch].title);
+		if((selmatch = matches->update(matches, mc, drawchan)) >= 0){
+			if(debug) fprint(2, "selected match id %d title %s\n", matches->entries[selmatch].id, matches->entries[selmatch].title);
+			chanprint(egress, "watch %d\n", matches->entries[selmatch].id);
+		}
 
 	if(game.state == Outlaying && curship != nil){
 		newbbox = mkshipbbox(toboard(&localboard, mc->xy), curship->orient, curship->ncells);
@@ -752,6 +761,7 @@
 	Cmdbuf *cb;
 	Cmdtab *ct;
 	Point2 cell;
+	uchar buf[BY2MAP];
 	int i;
 
 	if(debug)
@@ -783,6 +793,18 @@
 			matches->add(matches, strtoul(cb->f[1], nil, 10), smprint("%s vs %s", cb->f[2], cb->f[3]));
 		else if(matches->filling && ct->index == CMmatchese)
 			matches->filling = 0;
+		else if(ct->index == CMwatching){
+			match.id = strtoul(cb->f[1], nil, 10);
+			match.pl[0] = estrdup(cb->f[2]);
+			match.pl[1] = estrdup(cb->f[3]);
+			match.bl[0] = &localboard;
+			match.bl[1] = &alienboard;
+			dec64(buf, sizeof buf, cb->f[4], strlen(cb->f[4]));
+			bitunpackmap(match.bl[0], buf, sizeof buf);
+			dec64(buf, sizeof buf, cb->f[5], strlen(cb->f[5]));
+			bitunpackmap(match.bl[1], buf, sizeof buf);
+			game.state = Watching;
+		}
 		break;
 	case Ready:
 		if(ct->index == CMlayout){
--- a/btsd.c
+++ b/btsd.c
@@ -317,6 +317,7 @@
 	Cmdtab *ct;
 	Player *p, *op;
 	Stands stands;
+	uchar buf1[BY2MAP], buf2[BY2MAP];
 	uint n0;
 
 	Point2 cell;
@@ -432,8 +433,8 @@
 				}else{
 					op = p == m->pl[0]? m->pl[1]: m->pl[0];
 					chanprint(op->io.out, "win\n");
-					op->state = Waiting0;
 					op->battle = nil;
+					op->state = Waiting0;
 					freeplayer(p);
 					freemsg(msg);
 					goto Finish;
@@ -442,6 +443,11 @@
 				takeseat(&stands, p);
 				p->battle = m;
 				p->state = Watching;
+				bitpackmap(buf1, sizeof buf1, m->pl[0]);
+				bitpackmap(buf2, sizeof buf2, m->pl[1]);
+				chanprint(p->io.out, "watching %d %s %s %.*[ %.*[\n",
+					m->id, m->pl[0]->name, m->pl[1]->name,
+					sizeof buf1, buf1, sizeof buf2, buf2);
 			}else if(strcmp(msg->body, "leave seat") == 0){
 				leaveseat(&stands, p);
 				p->battle = nil;
@@ -642,6 +648,7 @@
 	char *addr;
 
 	GEOMfmtinstall();
+	fmtinstall('[', encodefmt);
 	addr = "tcp!*!3047";
 	ARGBEGIN{
 	case 'd':
--- a/dat.h
+++ b/dat.h
@@ -5,6 +5,9 @@
 	Tmiss,
 	NTILES,
 
+	TBITS = 2, /* ceil(log(NTILES)/log(2)) */
+	TMASK = (1<<TBITS) - 1,
+
 	Scarrier = 0,
 	Sbattleship,
 	Scruiser,
@@ -31,6 +34,7 @@
 	SCRH = Boardmargin+MAPH*TH+TH+MAPH*TH+Boardmargin,
 
 	KB = 1024,
+	BY2MAP = TBITS*MAPW*MAPH/8+1,
 };
 
 typedef struct Ship Ship;
@@ -41,6 +45,7 @@
 typedef struct Match Match;
 typedef struct Msg Msg;
 typedef struct Stands Stands;
+typedef struct MatchInfo MatchInfo;
 
 struct Ship
 {
@@ -104,6 +109,13 @@
 	Player **seats;
 	ulong nused;
 	ulong cap;
+};
+
+struct MatchInfo
+{
+	int id;
+	char *pl[2];
+	Board *bl[2];
 };
 
 typedef struct Mentry Mentry;
--- a/fns.h
+++ b/fns.h
@@ -23,6 +23,8 @@
 char *statename(int);
 int max(int, int);
 int min(int, int);
+int bitpackmap(uchar*, ulong, Map*);
+int bitunpackmap(Map*, uchar*, ulong);
 
 /*
  * menulist
--- a/util.c
+++ b/util.c
@@ -100,6 +100,9 @@
 			switch(m->map[j][i]){
 			case Twater: fprint(fd, "W"); break;
 			case Tship: fprint(fd, "S"); break;
+			case Thit: fprint(fd, "X"); break;
+			case Tmiss: fprint(fd, "O"); break;
+			default: fprint(fd, "?"); break;
 			}
 		fprint(fd, "\n");
 	}
@@ -152,4 +155,45 @@
 min(int a, int b)
 {
 	return a < b? a: b;
+}
+
+int
+bitpackmap(uchar *buf, ulong len, Map *m)
+{
+	int i, j, off, n;
+
+	assert(len >= BY2MAP);
+
+	off = n = 0;
+	*buf = 0;
+	for(i = 0; i < MAPW; i++)
+		for(j = 0; j < MAPH; j++){
+			if(off >= 8){
+				buf[++n] = 0;
+				off = 0;
+			}
+			buf[n] |= (m->map[i][j] & TMASK) << off;
+			off += TBITS;
+		}
+	return n+1;
+}
+
+int
+bitunpackmap(Map *m, uchar *buf, ulong len)
+{
+	int i, j, off, n;
+
+	assert(len >= BY2MAP);
+
+	off = n = 0;
+	for(i = 0; i < MAPW; i++)
+		for(j = 0; j < MAPH; j++){
+			if(off >= 8){
+				n++;
+				off = 0;
+			}
+			m->map[i][j] = buf[n] >> off & TMASK;
+			off += TBITS;
+		}
+	return n+1;
 }