diff options
author | rodri <rgl@antares-labs.eu> | 2023-08-29 10:19:42 +0000 |
---|---|---|
committer | rodri <rgl@antares-labs.eu> | 2023-08-29 10:19:42 +0000 |
commit | fbea30a51227ccf96382519966f09791b02d0f8e (patch) | |
tree | e7e4a37ef81fffafd42219ebae31b31c34b48f2e | |
parent | d9752f9750402f01386dceb3606b6cba7a675155 (diff) | |
download | battleship-fbea30a51227ccf96382519966f09791b02d0f8e.tar.gz battleship-fbea30a51227ccf96382519966f09791b02d0f8e.tar.bz2 battleship-fbea30a51227ccf96382519966f09791b02d0f8e.zip |
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
-rw-r--r-- | bts.c | 162 | ||||
-rw-r--r-- | btsd.c | 127 | ||||
-rw-r--r-- | dat.h | 15 | ||||
-rw-r--r-- | fns.h | 4 | ||||
-rw-r--r-- | util.c | 49 |
5 files changed, 254 insertions, 103 deletions
@@ -22,6 +22,7 @@ Board alienboard; Board localboard; Ship armada[NSHIPS]; Ship *curship; +Point2 lastshot; struct { int state; @@ -96,16 +97,6 @@ gettileimage(int type) } 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 @@ initarmada(void) 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 @@ lmb(Mousectl *mc) 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 @@ mmb(Mousectl *mc) 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 @@ rmb(Mousectl *mc) 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 @@ inputthread(void *arg) } 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 @@ threadmain(int argc, char *argv[]) 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); @@ -55,15 +55,26 @@ netrecvthread(void *arg) { 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 @@ void 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,42 +111,81 @@ serveproc(void *arg) 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; 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 @@ reaper(void *) void matchmaker(void *) { - Player **match; + Match *m; threadsetname("matchmaker"); @@ -185,11 +241,15 @@ matchmaker(void *) 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 @@ threadmain(int argc, char *argv[]) { char *addr; + GEOMfmtinstall(); addr = "tcp!*!3047"; ARGBEGIN{ case 'd': @@ -33,9 +33,11 @@ enum { 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 @@ struct Ship 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; @@ -76,6 +84,11 @@ struct Playerq ulong nplayers; }; +struct Match +{ + Player *pl[2]; +}; + struct Chanpipe { Channel *c; @@ -12,3 +12,7 @@ Image *eallocimage(Display*, Rectangle, ulong, int, ulong); */ 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); @@ -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 @@ cell2coords(Point2 cell) { 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 @@ coords2cell(char *s) 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]; +} |