summaryrefslogtreecommitdiff
path: root/appl/spree/engines/othello.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/spree/engines/othello.b')
-rw-r--r--appl/spree/engines/othello.b242
1 files changed, 242 insertions, 0 deletions
diff --git a/appl/spree/engines/othello.b b/appl/spree/engines/othello.b
new file mode 100644
index 00000000..2f36c47b
--- /dev/null
+++ b/appl/spree/engines/othello.b
@@ -0,0 +1,242 @@
+implement Gatherengine;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sets.m";
+ All, None: import Sets;
+include "../spree.m";
+ spree: Spree;
+ Attributes, Range, Object, Clique, Member: import spree;
+include "objstore.m";
+ objstore: Objstore;
+include "../gather.m";
+
+clique: ref Clique;
+
+Black, White, Nocolour: con iota; # first two must be 0 and 1.
+N: con 8;
+
+boardobj: ref Object;
+board: array of array of int;
+pieces: array of int;
+turn := Nocolour;
+members := array[2] of ref Member; # member ids of those playing
+
+Point: adt {
+ x, y: int;
+ add: fn(p: self Point, p1: Point): Point;
+ inboard: fn(p: self Point): int;
+};
+
+clienttype(): string
+{
+ return "othello";
+}
+
+init(srvmod: Spree, g: ref Clique, nil: list of string, nil: int): string
+{
+ sys = load Sys Sys->PATH;
+ clique = g;
+ spree = srvmod;
+
+ objstore = load Objstore Objstore->PATH;
+ if (objstore == nil) {
+ sys->print("othello: cannot load %s: %r", Objstore->PATH);
+ return "bad module";
+ }
+ objstore->init(srvmod, g);
+
+ return nil;
+}
+
+maxmembers(): int
+{
+ return 2;
+}
+
+readfile(nil: int, nil: big, nil: int): array of byte
+{
+ return nil;
+}
+
+propose(members: array of string): string
+{
+ if (len members != 2)
+ return "need exactly two members";
+ return nil;
+}
+
+archive()
+{
+ objstore->setname(boardobj, "board");
+}
+
+start(pl: array of ref Member, archived: int)
+{
+ members = pl;
+ board = array[N] of {* => array[N] of {* => Nocolour}};
+ pieces = array[2] of {* => 0};
+ if (archived) {
+ objstore->unarchive();
+ boardobj = objstore->get("board");
+ for (i := 0; i < N; i++) {
+ for (j := 0; j < N; j++) {
+ a := boardobj.getattr(pt2attr((j, i)));
+ if (a != nil) {
+ piece := int a;
+ board[j][i] = piece;
+ if (piece != Nocolour)
+ pieces[piece]++;
+ }
+ }
+ }
+ turn = int boardobj.getattr("turn");
+ } else {
+ boardobj = clique.newobject(nil, All, nil);
+ boardobj.setattr("members", string members[Black].name + " " + string members[White].name, All);
+ for (ps := (Black, (3, 3)) :: (Black, (4, 4)) :: (White, (3, 4)) :: (White, Point(4, 3)) :: nil;
+ ps != nil;
+ ps = tl ps) {
+ (colour, p) := hd ps;
+ setpiece(colour, p);
+ }
+ turn = Black;
+ boardobj.setattr("turn", string Black, All);
+ }
+}
+
+cliqueover()
+{
+ turn = Nocolour;
+ boardobj.setattr("winner", string winner(), All);
+ boardobj.setattr("turn", string turn, All);
+}
+
+command(member: ref Member, cmd: string): string
+{
+ {
+ (n, toks) := sys->tokenize(cmd, " \n");
+ assert(n > 0, "unknown command");
+
+ case hd toks {
+ "move" =>
+ assert(n == 3, "bad command usage");
+ assert(turn != Nocolour, "clique has finished");
+ assert(member == members[White] || member == members[Black], "you are not playing");
+ assert(member == members[turn], "it is not your turn");
+ p := Point(int hd tl toks, int hd tl tl toks);
+ assert(p.x >= 0 && p.x < N && p.y >= 0 && p.y < N, "invalid move position");
+ assert(board[p.x][p.y] == Nocolour, "position is already occupied");
+ assert(newmove(turn, p, 1), "cannot move there");
+
+ turn = reverse(turn);
+ if (!canplay()) {
+ turn = reverse(turn);
+ if (!canplay())
+ cliqueover();
+ }
+ boardobj.setattr("turn", string turn, All);
+ return nil;
+ }
+ sys->print("othello: unknown client command '%s'\n", hd toks);
+ return "who knows";
+ } exception e {
+ "parse:*" =>
+ return e[6:];
+ }
+}
+
+Directions := array[] of {Point(0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1), (-1, 0), (-1, 1)};
+
+setpiece(colour: int, p: Point)
+{
+ v := board[p.x][p.y];
+ if (v != Nocolour)
+ pieces[v]--;
+ board[p.x][p.y] = colour;
+ pieces[colour]++;
+ boardobj.setattr(pt2attr(p), string colour, All);
+}
+
+pt2attr(pt: Point): string
+{
+ s := " ";
+ s[0] = pt.x + 'a';
+ s[1] = pt.y + 'a';
+ return s;
+}
+
+# member colour has tried to place a piece at mp.
+# return -1 if it's an illegal move, 0 otherwise.
+# (in which case appropriate updates are sent out all round).
+# if update is 0, just check for the move's validity
+# (no change to the board, no updates sent)
+newmove(colour: int, mp: Point, update: int): int
+{
+ totchanged := 0;
+ for (i := 0; i < len Directions; i++) {
+ d := Directions[i];
+ n := 0;
+ for (p := mp.add(d); p.inboard(); p = p.add(d)) {
+ n++;
+ if (board[p.x][p.y] == colour || board[p.x][p.y] == Nocolour)
+ break;
+ }
+ if (p.inboard() && board[p.x][p.y] == colour && n > 1) {
+ if (!update)
+ return 1;
+ totchanged += n - 1;
+ for (p = mp.add(d); --n > 0; p = p.add(d))
+ setpiece(reverse(board[p.x][p.y]), p);
+ }
+ }
+ if (totchanged > 0) {
+ setpiece(colour, mp);
+ return 1;
+ }
+ return 0;
+}
+
+# who has most pieces?
+winner(): int
+{
+ if (pieces[White] > pieces[Black])
+ return White;
+ else if (pieces[Black] > pieces[White])
+ return Black;
+ return Nocolour;
+}
+
+# is there any possible legal move?
+canplay(): int
+{
+ for (y := 0; y < N; y++)
+ for (x := 0; x < N; x++)
+ if (board[x][y] == Nocolour && newmove(turn, (x, y), 0))
+ return 1;
+ return 0;
+}
+
+reverse(colour: int): int
+{
+ if (colour == Nocolour)
+ return Nocolour;
+ return !colour;
+}
+
+Point.add(p: self Point, p1: Point): Point
+{
+ return (p.x + p1.x, p.y + p1.y);
+}
+
+Point.inboard(p: self Point): int
+{
+ return p.x >= 0 && p.x < N && p.y >= 0 && p.y < N;
+}
+
+assert(b: int, err: string)
+{
+ if (b == 0)
+ raise "parse:" + err;
+}