ref: 895af3a6c0ece7ed089ee2a5356cb1933a30cca3
author: sirjofri <sirjofri@sirjofri.de>
date: Sat Dec 13 17:38:42 EST 2025
test filesystem, stone placement, filesystem wrapper script the filesystem wrapper script is mostly to experiment with descriptive filesystem generation.
--- /dev/null
+++ b/dat.h
@@ -1,0 +1,31 @@
+typedef struct Gogame Gogame;
+typestr struct _vec Vec;
+
+/* WHITE and BLACK should be in sync in both enums */
+typedef enum {+ GWAIT,
+ GBLACK,
+ GWHITE,
+ GFINISH,
+} Gostate;
+
+typedef enum {+ EMPTY,
+ BLACK,
+ WHITE,
+ WASBLACK,
+ WASWHITE,
+} Gostone;
+
+struct _vec {+ int x;
+ int y;
+};
+
+struct Gogame {+ Gostate state;
+ Vec size;
+ Gostone *board;
+ int whitecaptures;
+ int blackcaptures;
+};
--- /dev/null
+++ b/fns.h
@@ -1,0 +1,16 @@
+Gogame *newgame(Vec size);
+void freegame(Gogame*);
+Gostone getcurrent(Gogame*, Vec pos);
+int setstone(Gogame*, Vec pos, Gostone);
+int passround(Gogame*, Gostone);
+
+Srv *getgenfs(void);
+
+#define V(a, b) (Vec){(a), (b)}+Vec _vec_pos_(Vec a);
+Vec _vec_neg_(Vec a);
+Vec _vec_add_(Vec a, Vec b);
+Vec _vec_sub_(Vec a, Vec b);
+int _vec_eq_(Vec a, Vec b);
+Vec _vec_asadd_(Vec *a, Vec b);
+Vec _vec_assub_(Vec *a, Vec b);
--- /dev/null
+++ b/fs.fs
@@ -1,0 +1,113 @@
+%{+#include "dat.h"
+#include "fns.h"
+
+extern Gogame *game1;
+
+%}
+
+/game1
+r{+ // just print the full board and captures
+ // board: bw<space>, on char per field
+
+ char *str, *sptr;
+ Gostone stone;
+
+ str = mallocz((game1->size.x + 1) * game1->size.y + 1, 1);
+ sptr = str;
+
+ for (int y = 0; y < game1->size.y; y++) {+ for (int x = 0; x < game1->size.x; x++) {+ stone = getcurrent(game1, V(x, y));
+ switch (stone) {+ case WHITE:
+ *sptr = 'w';
+ break;
+ case BLACK:
+ *sptr = 'b';
+ break;
+ default:
+ *sptr = '.';
+ }
+ sptr++;
+ }
+ *sptr = '\n';
+ sptr++;
+ }
+
+ readstr(r, str);
+ free(str);
+
+ print("state: %d\n", game1->state);+
+ respond(r, nil);
+r}
+w{+ // b = black, w = white
+ // <c> <x> <y>
+ // <c> <cmd>
+ char *fields[3];
+ int x, y, n;
+ Gostone stone;
+
+ char *input = mallocz(r->ifcall.count + 1, 1);
+ memcpy(input, r->ifcall.data, r->ifcall.count);
+ if (input[r->ifcall.count-1] == '\n')
+ input[r->ifcall.count-1] = 0;
+
+ n = getfields(input, fields, 3, 1, " \t");
+ if (n < 2 || n > 3) {+ free(input);
+ respond(r, "wrong input");
+ return;
+ }
+
+ switch (fields[0][0]) {+ case 'w':
+ stone = WHITE;
+ break;
+ case 'b':
+ stone = BLACK;
+ break;
+ default:
+ free(input);
+ respond(r, "bad input");
+ return;
+ }
+
+ if (n == 2) {+ switch (fields[1][0]) {+ case 'p':
+ if (!passround(game1, stone)) {+ responderror(r);
+ } else {+ respond(r, nil);
+ }
+ break;
+ }
+ free(input);
+ return;
+ }
+
+ x = atoi(fields[1]) - 1;
+ y = atoi(fields[2]) - 1;
+
+ if (!setstone(game1, V(x, y), stone)) {+ free(input);
+ responderror(r);
+ return;
+ }
+
+ free(input);
+ r->ofcall.count = r->ifcall.count;
+ respond(r, nil);
+w}
+
+/game2
+r{+ respond(r, "nothing in data r");
+r}
+w{+ respond(r, "nothing in data w");
+w}
\ No newline at end of file
--- /dev/null
+++ b/game.c
@@ -1,0 +1,193 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <fcall.h>
+#include <9p.h>
+#include "dat.h"
+#include "fns.h"
+
+static void validateboard(Gogame *game);
+
+static int
+getarrpos(Gogame *game, Vec pos)
+{+ if (pos.x < 0 || pos.x >= game->size.x)
+ goto Err;
+ if (pos.y < 0 || pos.y >= game->size.y)
+ goto Err;
+ return pos.y * game->size.x + pos.x;
+Err:
+ werrstr("invalid position (%d, %d)", pos.x, pos.y);+ return -1;
+}
+
+Gogame*
+newgame(Vec size)
+{+ Gogame *game;
+
+ game = mallocz(sizeof(Gogame), 1);
+ assert(game);
+
+ game->board = mallocz(sizeof(Gostone) * size.x * size.y, 1);
+ assert(game->board);
+ game->size = size;
+ game->state = GBLACK;
+ return game;
+}
+
+void
+freegame(Gogame *game)
+{+ assert(game);
+ if (game->board)
+ free(game->board);
+ free(game);
+}
+
+Gostone
+getcurrent(Gogame *game, Vec pos)
+{+ int apos;
+ assert(game && game->board);
+
+ apos = getarrpos(game, pos);
+ if (apos < 0)
+ return -1;
+ return game->board[apos];
+}
+
+static int
+isfree(Gogame *game, Vec pos, Gostone stone, Gostone opposite)
+{+ Gostone s;
+ int nfreedoms = 4;
+ Vec v;
+
+ for (int i = 0; i < 4; i++) {+ switch (i) {+ case 0:
+ v = pos + V(1, 0);
+ break;
+ case 1:
+ v = pos + V(0, 1);
+ break;
+ case 2:
+ v = pos - V(1, 0);
+ break;
+ case 3:
+ v = pos - V(0, 1);
+ break;
+ }
+ s = getcurrent(game, v);
+ if (s == opposite) {+ nfreedoms--;
+ continue;
+ }
+ if (!isfree(game, v, stone, opposite))
+ return 0;
+ }
+
+ return !!nfreedoms;
+}
+
+static int
+validmove(Gogame *game, Vec pos, Gostone stone)
+{+ Gostone opposite = EMPTY;
+
+ if (stone == WHITE)
+ opposite = BLACK;
+ if (stone == BLACK)
+ opposite = WHITE;
+
+ if (opposite == EMPTY) {+ werrstr("no valid stone");+ return 0;
+ }
+
+ switch (getcurrent(game, pos)) {+ case BLACK:
+ case WHITE:
+ werrstr("there's already a stone");+ return 0;
+ case WASBLACK:
+ if (stone == WHITE)
+ break;
+ werrstr("white on ex-white");+ return 0;
+ case WASWHITE:
+ if (stone == BLACK)
+ break;
+ werrstr("black on ex-black");+ return 0;
+ default:
+ return 1;
+ }
+
+ return isfree(game, pos, stone, opposite);
+}
+
+int
+setstone(Gogame *game, Vec pos, Gostone stone)
+{+ int apos;
+ assert(game && game->board);
+
+ if (game->state != stone) {+ werrstr("Can't set: It's not your turn");+ return 0;
+ }
+
+ apos = getarrpos(game, pos);
+ if (apos < 0)
+ return 0;
+
+ if (!validmove(game, pos, stone))
+ return 0;
+
+ game->board[apos] = stone;
+ validateboard(game);
+
+ switch (stone) {+ case WHITE:
+ game->state = GBLACK;
+ break;
+ case BLACK:
+ game->state = GWHITE;
+ break;
+ }
+
+ return 1;
+}
+
+int
+passround(Gogame *game, Gostone stone)
+{+ assert(game && game->board);
+
+ print("state: %d, stone: %d\n", game->state, stone);+
+ if (game->state != stone) {+ werrstr("Can't pass: It's not your turn");+ return 0;
+ }
+
+ switch (stone) {+ case WHITE:
+ game->state = GBLACK;
+ break;
+ case BLACK:
+ game->state = GWHITE;
+ break;
+ default:
+ werrstr("bad move");+ return 0;
+ }
+ return 1;
+}
+
+static void
+validateboard(Gogame *game)
+{+}
--- /dev/null
+++ b/main.c
@@ -1,0 +1,40 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "dat.h"
+#include "fns.h"
+
+Gogame *game1 = nil;
+
+void
+usage(void)
+{+ fprint(2, "usage: %s [-s srvname]\n", argv0);
+ exits("usage");+}
+
+char *srvname = "gofs";
+char *mtpt = "/mnt/gofs";
+
+void
+main(int argc, char **argv)
+{+ Srv *fs;
+
+ ARGBEGIN{+ case 's':
+ srvname = EARGF(usage());
+ break;
+ case 'm':
+ mtpt = EARGF(usage());
+ break;
+ }ARGEND;
+
+ game1 = newgame(V(19, 19));
+
+ fs = getgenfs();
+ postmountsrv(fs, srvname, mtpt, MREPL);
+ exits(nil);
+}
--- /dev/null
+++ b/misc.c
@@ -1,0 +1,63 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <fcall.h>
+#include <9p.h>
+#include "dat.h"
+#include "fns.h"
+
+Vec
+_vec_pos_(Vec a)
+{+ Vec r;
+ r.x = a.x;
+ r.y = a.y;
+ return r;
+}
+
+Vec
+_vec_neg_(Vec a)
+{+ Vec r;
+ r.x = -a.x;
+ r.y = -a.y;
+ return r;
+}
+
+Vec
+_vec_add_(Vec a, Vec b)
+{+ a.x += b.x;
+ a.y += b.y;
+ return a;
+}
+
+Vec
+_vec_sub_(Vec a, Vec b)
+{+ a.x -= b.x;
+ a.y -= b.y;
+ return a;
+}
+
+int
+_vec_eq_(Vec a, Vec b)
+{+ return a.x == b.x && a.y == b.y;
+}
+
+Vec
+_vec_asadd_(Vec *a, Vec b)
+{+ a->x += b.x;
+ a->y += b.y;
+ return *a;
+}
+
+Vec
+_vec_assub_(Vec *a, Vec b)
+{+ a->x -= b.x;
+ a->y -= b.y;
+ return *a;
+}
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,15 @@
+</$objtype/mkfile
+
+TARG=gofs
+OFILES=\
+ main.$O\
+ game.$O\
+ misc.$O\
+ fs.$O\
+
+HFILES=dat.h fns.h
+
+</sys/src/cmd/mkone
+
+fs.c: fs.fs mkfs.awk
+ mkfs.awk fs.fs > $target
--- /dev/null
+++ b/mkfs.awk
@@ -1,0 +1,143 @@
+#!/bin/awk -f
+
+function cleanpath(path) {+ _name = path;
+ gsub(/\//, "_", _name);
+ gsub(/ /, "_", _name);
+ return _name;
+}
+
+function getfilename(path) {+ _name = path;
+ gsub(/\//, "", _name);
+ return _name;
+}
+
+function startread() {+ _name = cleanpath(currentpath);
+ printf("static void\nfsread_%s(Req *r)\n{\n", _name);+}
+
+function startwrite() {+ _name = cleanpath(currentpath);
+ printf("static void\nfswrite_%s(Req *r)\n{\n", _name);+}
+
+function stopfunc() {+ printf("}\n\n");+}
+
+(state == 0 || state == 1) && /^$/ {+ next;
+}
+
+state == 0 && /^\%\{/ {+ state = 3; # passthrough
+ printf("#line %d \"%s\"\n", NR, FILENAME);+ next;
+}
+
+state == 3 && /^\%\}/ {+ state = 0;
+ next;
+}
+
+state == 3 {+ print;
+}
+
+state == 0 || state == 1 && /^\// {+ currentpath = $0;
+ paths[npaths++] = currentpath;
+ state = 1; # in file
+}
+
+state == 1 && /^[rw]\{/ {+ state = 2; # passthrough
+ printf("#line %d \"%s\"\n", NR, FILENAME);+ if ($1 == "r{")+ startread();
+ else
+ startwrite();
+ next;
+}
+
+state == 2 && /^[rw]\}/ {+ state = 1;
+ stopfunc();
+}
+
+state == 2 {+ print;
+}
+
+BEGIN{+ print("#include <u.h>");+ print("#include <libc.h>");+ print("#include <fcall.h>");+ print("#include <thread.h>");+ print("#include <9p.h>");+ print("");+ print("extern char *files[];");+ print("extern int nfiles;");+ print("");+}
+
+END{+ printf("#line %d \"%s\"\n", NR, FILENAME);+ for (i = 0; i < npaths; i++) {+ path = cleanpath(paths[i]);
+ printf("char *F_%s = \"%s\";\n", path, path);+ }
+ print("");+
+ print("static void");+ print("fsread(Req *r)");+ print("{");+ print(" void *a = r->fid->file->aux;");+ for (i = 0; i < npaths; i++) {+ path = cleanpath(paths[i]);
+ printf(" if (a == F_%s) fsread_%s(r); else\n", path, path);+ }
+ print(" respond(r, \"niy\");");+ print("}");+ print("");+ print("static void");+ print("fswrite(Req *r)");+ print("{");+ print(" void *a = r->fid->file->aux;");+ for (i = 0; i < npaths; i++) {+ path = cleanpath(paths[i]);
+ printf(" if (a == F_%s) fswrite_%s(r); else\n", path, path);+ }
+ print(" respond(r, \"niy\");");+ print("}");+ print("");+ print("Srv fs = {");+ print(" .read = fsread,");+ print(" .write = fswrite,");+ print("};");+ print("");+ print("static void");+ print("populatetree(void)");+ print("{");+
+ print(" File *f;");+ for (i = 0; i < npaths; i++) {+ path = paths[i];
+ name = getfilename(path);
+ path = cleanpath(path);
+ printf(" f = createfile(fs.tree->root, \"%s\", \"nil\", 0666, F_%s);\n", name, path);+ print(" USED(f);");+ }
+
+ print("}");+ print("");+ print("Srv*");+ print("getgenfs()");+ print("{");+ print(" fs.tree = alloctree(nil, nil, DMDIR|0777, nil);");+ print(" populatetree();");+ print(" return &fs;");+ print("}");+}
--
⑨