ref: fbea30a51227ccf96382519966f09791b02d0f8e
parent: d9752f9750402f01386dceb3606b6cba7a675155
author: rodri <rgl@antares-labs.eu>
date: Tue Aug 29 06:19:42 EDT 2023
major development stride: - implemented per-line msg framing - wrote most of the game logic - fixed an issue where the ship would go off-board when rotated
--- a/bts.c
+++ b/bts.c
@@ -22,6 +22,7 @@
Board localboard;
Ship armada[NSHIPS];
Ship *curship;
+Point2 lastshot;
struct {
int state;
@@ -96,16 +97,6 @@
}
void
-settile(Board *b, Point2 cell, int type)
-{
- Point p;
-
- p.x = cell.x;
- p.y = cell.y;
- b->map[p.x][p.y] = type;
-}
-
-void
drawtile(Image *dst, Board *b, Point2 cell, int type)
{
Point p;
@@ -287,14 +278,7 @@
for(i = 0; i < nelem(armada); i++){
s = &armada[i];
- switch(i){
- case Scarrier: s->ncells = 5; break;
- case Sbattleship: s->ncells = 4; break;
- case Scruiser: /* fallthrough */
- case Ssubmarine: s->ncells = 3; break;
- case Sdestroyer: s->ncells = 2; break;
- default: sysfatal("initarmada: unknown ship: %d", i);
- }
+ s->ncells = shiplen(i);
s->orient = OV;
s->hit = emalloc(s->ncells*sizeof(int));
memset(s->hit, 0, s->ncells*sizeof(int));
@@ -354,11 +338,15 @@
cell = toboard(b, mc->xy);
switch(game.state){
case Outlaying:
- if(curship != nil && ++curship-armada >= nelem(armada))
- curship = nil;
+ if(b == &localboard)
+ if(curship != nil && ++curship-armada >= nelem(armada))
+ curship = nil;
break;
case Playing:
- chanprint(egress, "shoot %s", cell2coords(cell));
+ if(b == &alienboard){
+ chanprint(egress, "shoot %s\n", cell2coords(cell));
+ lastshot = cell;
+ }
break;
}
send(drawchan, nil);
@@ -382,8 +370,24 @@
mc->xy = addpt(mc->xy, screen->r.min);
switch(menuhit(2, mc, &menu, _screen)){
case ROTATE:
- if(curship != nil)
+ if(curship != nil){
curship->orient = curship->orient == OH? OV: OH;
+ curship->bbox = mkshipbbox(curship->p, curship->orient, curship->ncells);
+
+ /* steer it, captain! don't let it go off-board! */
+ if(!rectinrect(curship->bbox, localboard.bbox))
+ switch(curship->orient){
+ case OH:
+ curship->bbox.min.x -= curship->bbox.max.x-localboard.bbox.max.x;
+ curship->bbox.max.x = localboard.bbox.max.x;
+ break;
+ case OV:
+ curship->bbox.min.y -= curship->bbox.max.y-localboard.bbox.max.y;
+ curship->bbox.max.y = localboard.bbox.max.y;
+ break;
+ }
+ curship->p = toboard(&localboard, curship->bbox.min);
+ }
break;
}
send(drawchan, nil);
@@ -428,7 +432,7 @@
n += snprint(buf+n, sizeof buf - n, "%s%c",
cell2coords(armada[i].p), armada[i].orient == OH? 'h': 'v');
}
- chanprint(egress, "layout %s", buf);
+ chanprint(egress, "layout %s\n", buf);
break;
}
send(drawchan, nil);
@@ -513,55 +517,81 @@
}
void
+processcmd(char *cmd)
+{
+ char *coords[2];
+
+ if(debug)
+ fprint(2, "rcvd '%s'\n", cmd);
+
+ if(strcmp(cmd, "win") == 0){
+// celebrate();
+ game.state = Waiting0;
+ }else if(strcmp(cmd, "lose") == 0){
+// keelhaul();
+ game.state = Waiting0;
+ }
+
+ switch(game.state){
+ case Waiting0:
+ if(strcmp(cmd, "layout") == 0){
+ game.state = Outlaying;
+ curship = &armada[0];
+ }
+ break;
+ case Outlaying:
+ if(strcmp(cmd, "wait") == 0)
+ game.state = Waiting;
+ else if(strcmp(cmd, "play") == 0)
+ game.state = Playing;
+ break;
+ case Playing:
+ if(strcmp(cmd, "wait") == 0)
+ game.state = Waiting;
+ else if(strcmp(cmd, "hit") == 0)
+ settile(&alienboard, lastshot, Thit);
+ else if(strcmp(cmd, "miss") == 0)
+ settile(&alienboard, lastshot, Tmiss);
+ break;
+ case Waiting:
+ if(strcmp(cmd, "play") == 0)
+ game.state = Playing;
+ else if(strncmp(cmd, "hit", 3) == 0){
+ if(gettokens(cmd+4, coords, nelem(coords), "-") == nelem(coords))
+ settile(&localboard, Pt2(strtoul(coords[0], nil, 10), strtoul(coords[1], nil, 10), 1), Thit);
+ }else if(strncmp(cmd, "miss", 4) == 0){
+ if(gettokens(cmd+5, coords, nelem(coords), "-") == nelem(coords))
+ settile(&localboard, Pt2(strtoul(coords[0], nil, 10), strtoul(coords[1], nil, 10), 1), Tmiss);
+ }
+ break;
+ }
+ send(drawchan, nil);
+}
+
+void
netrecvthread(void *arg)
{
Ioproc *io;
- char buf[256], *coords[2];
- int n, fd;
+ char buf[256], *s, *e;
+ int n, tot, fd;
fd = *(int*)arg;
io = ioproc();
- while((n = ioread(io, fd, buf, sizeof(buf)-1)) > 0){
- buf[n] = 0;
-
- if(debug)
- fprint(2, "rcvd '%s'\n", buf);
-
- if(strcmp(buf, "win") == 0)
- game.state = Waiting0;
- else if(strcmp(buf, "lose") == 0)
- game.state = Waiting0;
-
- switch(game.state){
- case Waiting0:
- if(strcmp(buf, "layout") == 0){
- game.state = Outlaying;
- curship = &armada[0];
- }
- break;
- case Outlaying:
- if(strcmp(buf, "wait") == 0)
- game.state = Waiting;
- else if(strcmp(buf, "play") == 0)
- game.state = Playing;
- break;
- case Playing:
- if(strcmp(buf, "wait") == 0)
- game.state = Waiting;
- break;
- case Waiting:
- if(strcmp(buf, "play") == 0)
- game.state = Playing;
- else if(strncmp(buf, "hit", 3) == 0){
- if(gettokens(buf+4, coords, nelem(coords), "-") == nelem(coords))
- settile(&localboard, Pt2(strtoul(coords[0], nil, 10), strtoul(coords[1], nil, 10), 1), Thit);
- }else if(strncmp(buf, "miss", 4) == 0){
- if(gettokens(buf+5, coords, nelem(coords), "-") == nelem(coords))
- settile(&localboard, Pt2(strtoul(coords[0], nil, 10), strtoul(coords[1], nil, 10), 1), Tmiss);
- }
- break;
+ tot = 0;
+ while((n = ioread(io, fd, buf+tot, sizeof(buf)-1-tot)) > 0){
+ tot += n;
+ buf[tot] = 0;
+ s = buf;
+ while((e = strchr(s, '\n')) != nil){
+ *e++ = 0;
+ processcmd(s);
+ tot -= e-s;
+ memmove(buf, e, tot);
+ s = e;
}
+ if(tot >= sizeof(buf)-1)
+ tot = 0;
}
closeioproc(io);
threadexitsall("connection lost");
@@ -643,8 +673,8 @@
game.state = Waiting0;
drawchan = chancreate(sizeof(void*), 0);
- ingress = chancreate(sizeof(char*), 16);
- egress = chancreate(sizeof(char*), 16);
+ ingress = chancreate(sizeof(char*), 1);
+ egress = chancreate(sizeof(char*), 1);
threadcreate(bobross, nil, mainstacksize);
threadcreate(inputthread, &in, mainstacksize);
threadcreate(netrecvthread, &fd, mainstacksize);
--- a/btsd.c
+++ b/btsd.c
@@ -55,15 +55,26 @@
{
Chanpipe *cp;
Ioproc *io;
- char buf[256];
- int n;
+ char buf[256], *s, *e;
+ int n, tot;
cp = arg;
io = ioproc();
- while((n = ioread(io, cp->fd, buf, sizeof(buf)-1)) > 0){
- buf[n] = 0;
- chanprint(cp->c, "%s", buf);
+ tot = 0;
+ while((n = ioread(io, cp->fd, buf+tot, sizeof(buf)-1-tot)) > 0){
+ tot += n;
+ buf[tot] = 0;
+ s = buf;
+ while((e = strchr(s, '\n')) != nil){
+ *e++ = 0;
+ chanprint(cp->c, "%s", s);
+ tot -= e-s;
+ memmove(buf, e, tot);
+ s = e;
+ }
+ if(tot >= sizeof(buf)-1)
+ tot = 0;
}
if(debug)
fprint(2, "[%d] lost connection\n", getpid());
@@ -76,17 +87,23 @@
serveproc(void *arg)
{
NetConnInfo *nci[2];
- Player **m;
+ Match *m;
+ Player *p, *op;
Chanpipe cp[2];
Alt a[3];
- int i, n0, tid[2];
+ int i;
+ uint n0;
char *s;
+ Point2 cell;
+ char *coords[5];
+ int j, orient;
+
m = arg;
s = nil;
- nci[0] = getnetconninfo(nil, m[0]->fd);
- nci[1] = getnetconninfo(nil, m[1]->fd);
+ nci[0] = getnetconninfo(nil, m->pl[0]->fd);
+ nci[1] = getnetconninfo(nil, m->pl[1]->fd);
if(nci[0] == nil || nci[1] == nil)
sysfatal("getnetconninfo: %r");
threadsetname("serveproc %s ↔ %s", nci[0]->raddr, nci[1]->raddr);
@@ -94,9 +111,9 @@
freenetconninfo(nci[1]);
cp[0].c = chancreate(sizeof(char*), 1);
- cp[0].fd = m[0]->fd;
+ cp[0].fd = m->pl[0]->fd;
cp[1].c = chancreate(sizeof(char*), 1);
- cp[1].fd = m[1]->fd;
+ cp[1].fd = m->pl[1]->fd;
a[0].c = cp[0].c; a[0].v = &s; a[0].op = CHANRCV;
a[1].c = cp[1].c; a[1].v = &s; a[1].op = CHANRCV;
@@ -103,34 +120,73 @@
a[2].op = CHANEND;
threadsetgrp(truerand());
- tid[0] = threadcreate(netrecvthread, &cp[0], mainstacksize);
- tid[1] = threadcreate(netrecvthread, &cp[1], mainstacksize);
+ threadcreate(netrecvthread, &cp[0], mainstacksize);
+ threadcreate(netrecvthread, &cp[1], mainstacksize);
- assert(m[0]->state == Waiting0 && m[1]->state == Waiting0);
- write(m[0]->fd, "layout", 6);
- write(m[1]->fd, "layout", 6);
- m[0]->state = Outlaying;
- m[1]->state = Outlaying;
+ write(m->pl[0]->fd, "layout\n", 7);
+ write(m->pl[1]->fd, "layout\n", 7);
+ m->pl[0]->state = Outlaying;
+ m->pl[1]->state = Outlaying;
while((i = alt(a)) >= 0){
+ p = m->pl[i];
+ op = m->pl[i^1];
+
if(a[i].err != nil){
if(debug)
fprint(2, "[%d] alt: %s\n", getpid(), a[i].err);
- write(m[i^1]->fd, "win", 3);
- m[i^1]->state = Waiting0;
- pushplayer(m[i^1]);
- freeplayer(m[i]);
+ write(op->fd, "win\n", 4);
+ pushplayer(op);
+ freeplayer(p);
break;
}
if(debug)
fprint(2, "[%d] said '%s'\n", i, s);
- if(write(m[i^1]->fd, s, strlen(s)) != strlen(s)){
- write(m[i]->fd, "win", 3);
- m[i]->state = Waiting0;
- pushplayer(m[i]);
- freeplayer(m[i^1]);
- free(s);
+
+ switch(p->state){
+ case Outlaying:
+ if(strncmp(s, "layout", 6) == 0)
+ if(gettokens(s+7, coords, nelem(coords), ",") == nelem(coords)){
+ if(debug)
+ fprint(2, "rcvd layout from %d\n", i);
+ for(j = 0; j < nelem(coords); j++){
+ cell = coords2cell(coords[j]);
+ orient = coords[j][strlen(coords[j])-2] == 'h'? OH: OV;
+ settiles(p, cell, orient, shiplen(j), Tship);
+ }
+ p->state = Waiting;
+ if(debug)
+ fprint(2, "curstates [%d] %d / [%d] %d\n", i, p->state, i^1, op->state);
+ if(op->state == Waiting){
+ n0 = truerand();
+ if(debug)
+ fprint(2, "let the game begin: %d plays, %d waits\n", n0%2, (n0+1)%2);
+ write(m->pl[n0%2]->fd, "play\n", 5);
+ m->pl[n0%2]->state = Playing;
+ write(m->pl[(n0+1)%2]->fd, "wait\n", 5);
+ }
+ }
break;
+ case Playing:
+ if(strncmp(s, "shoot", 5) == 0){
+ cell = coords2cell(s+6);
+ if(gettile(op, cell) == Tship){
+ settile(op, cell, Thit);
+ write(p->fd, "hit\n", 4);
+ fprint(op->fd, "hit %s\n", cell2coords(cell));
+ }else{
+ settile(op, cell, Tmiss);
+ write(p->fd, "miss\n", 5);
+ fprint(op->fd, "miss %s\n", cell2coords(cell));
+ }
+ write(p->fd, "wait\n", 5);
+ write(op->fd, "play\n", 5);
+ p->state = Waiting;
+ op->state = Playing;
+ if(debug)
+ fprint(2, "%d waits, %d plays\n", i, i^1);
+ }
+ break;
}
free(s);
}
@@ -175,7 +231,7 @@
void
matchmaker(void *)
{
- Player **match;
+ Match *m;
threadsetname("matchmaker");
@@ -185,11 +241,15 @@
continue;
}
- match = emalloc(2*sizeof(Player*));
- match[0] = popplayer();
- match[1] = popplayer();
+ m = emalloc(sizeof *m);
+ m->pl[0] = popplayer();
+ m->pl[1] = popplayer();
+ m->pl[0]->state = Waiting0;
+ m->pl[1]->state = Waiting0;
+ memset(m->pl[0]->map, Twater, MAPW*MAPH);
+ memset(m->pl[1]->map, Twater, MAPW*MAPH);
- proccreate(serveproc, match, mainstacksize);
+ proccreate(serveproc, m, mainstacksize);
}
}
@@ -243,6 +303,7 @@
{
char *addr;
+ GEOMfmtinstall();
addr = "tcp!*!3047";
ARGBEGIN{
case 'd':
--- a/dat.h
+++ b/dat.h
@@ -33,9 +33,11 @@
typedef struct Input Input;
typedef struct Ship Ship;
+typedef struct Map Map;
typedef struct Board Board;
typedef struct Player Player;
typedef struct Playerq Playerq;
+typedef struct Match Match;
typedef struct Chanpipe Chanpipe;
struct Input
@@ -54,15 +56,21 @@
int sunk;
};
+struct Map
+{
+ char map[MAPW][MAPH];
+};
+
struct Board
{
RFrame;
- char map[17][17];
+ Map;
Rectangle bbox;
};
struct Player
{
+ Map;
int fd;
int sfd;
int state;
@@ -74,6 +82,11 @@
Player **players;
ulong cap;
ulong nplayers;
+};
+
+struct Match
+{
+ Player *pl[2];
};
struct Chanpipe
--- a/fns.h
+++ b/fns.h
@@ -12,3 +12,7 @@
*/
char *cell2coords(Point2);
Point2 coords2cell(char*);
+int gettile(Map*, Point2);
+void settile(Map*, Point2, int);
+void settiles(Map*, Point2, int, int, int);
+int shiplen(int);
--- a/util.c
+++ b/util.c
@@ -9,6 +9,13 @@
#include "fns.h"
static char rowtab[] = "abcdefghijklmnopq";
+static int shiplentab[] = {
+ [Scarrier] 5,
+ [Sbattleship] 4,
+ [Scruiser] 3,
+ [Ssubmarine] 3,
+ [Sdestroyer] 2,
+};
char *
@@ -16,8 +23,8 @@
{
static char s[3+1];
- assert(cell.x < 17 && cell.x >= 0
- && cell.y < 17 && cell.y >= 0);
+ assert(cell.x >= 0 && cell.x < MAPW
+ && cell.y >= 0 && cell.y < MAPH);
snprint(s, sizeof s, "%c%d", rowtab[(int)cell.y], (int)cell.x);
return s;
@@ -36,7 +43,43 @@
cell.y = p-rowtab;
cell.x = strtol(s+1, nil, 10);
- assert(cell.x < 17 && cell.x >= 0);
+ assert(cell.x >= 0 && cell.x < MAPW);
return cell;
+}
+
+int
+gettile(Map *m, Point2 cell)
+{
+ return m->map[(int)cell.x][(int)cell.y];
+}
+
+void
+settile(Map *m, Point2 cell, int type)
+{
+ m->map[(int)cell.x][(int)cell.y] = type;
+}
+
+void
+settiles(Map *m, Point2 cell, int o, int ncells, int type)
+{
+ Point2 sv;
+
+ switch(o){
+ case OH: sv = Vec2(1,0); break;
+ case OV: sv = Vec2(0,1); break;
+ default: sysfatal("settiles: wrong ship orientation");
+ }
+
+ while(ncells-- > 0){
+ settile(m, cell, type);
+ cell = addpt2(cell, sv);
+ }
+}
+
+int
+shiplen(int stype)
+{
+ assert(stype >= 0 && stype < NSHIPS);
+ return shiplentab[stype];
}