ref: d540dcf6834b4bec8a2d21f2fe95eccf49f97b03
dir: /appl/spree/engines/othello.b/
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;
}