implement RistiNolla;
include "sys.m";
sys: Sys;
Dir: import sys;
Connection : import Sys;
include "draw.m";
draw: Draw;
Screen, Display, Image, Context, Point, Rect: import draw;
include "tk.m";
tk: Tk;
Toplevel: import tk;
include "tkclient.m";
tkclient: Tkclient;
Hide: import tkclient;
include "dialog.m";
dialog : Dialog;
RistiNolla : module
{
init: fn(ctxt: ref Draw->Context, argv: list of string);
};
PORT : con "2000";
WINBUT : con Hide;
BSZ : con 20;
BSZI : con BSZ + 2;
EMPTY, PLAYER1, PLAYER2 : con iota;
# board[frame][button]
board := array[BSZI] of { * => array[BSZI] of {* => EMPTY} };
ctxt: ref Draw->Context;
mainwin : ref Tk->Toplevel;
workerpid : int;
cmd : chan of string;
gamecmd : chan of string;
localcmd : chan of string;
remotecmd : chan of string;
init(xctxt: ref Draw->Context, nil: list of string)
{
sys = load Sys Sys->PATH;
if (xctxt == nil) {
sys->fprint(sys->fildes(2), "testi: no window context\n");
raise "fail:bad context";
}
ctxt = xctxt;
draw = load Draw Draw->PATH;
tk = load Tk Tk->PATH;
tkclient = load Tkclient Tkclient->PATH;
dialog = load Dialog Dialog->PATH;
sys->pctl(Sys->NEWPGRP, nil);
tkclient->init();
dialog->init();
wmctl : chan of string;
(mainwin, wmctl) = tkclient->toplevel(ctxt, nil, "RistiNolla", WINBUT);
if(mainwin == nil)
{
sys->fprint(sys->fildes(2), "RistiNolla: creation of toplevel window failed\n");
raise "fail:creation of toplevel window failed";
}
gamecmd = chan of string;
cmd = chan of string;
tk->namechan(mainwin, cmd, "cmd");
localcmd = chan of string;
tk->namechan(mainwin, localcmd, "pcmd");
remotecmd = chan of string;
display_board();
center(mainwin);
tkclient->onscreen(mainwin, "exact");
tkclient->startinput(mainwin, "kbd"::"ptr"::nil);
for(;;) alt {
s := <- mainwin.ctxt.kbd =>
tk->keyboard(mainwin, s);
s := <- mainwin.ctxt.ptr =>
tk->pointer(mainwin, *s);
s := <- mainwin.ctxt.ctl or
s = <- mainwin.wreq or
s = <- wmctl =>
case s {
"exit" =>
fd := sys->open("#p/" + string workerpid +"/ctl", sys->OWRITE);
if(fd != nil)
sys->fprint(fd, "kill");
tkclient->wmctl(mainwin, "exit");
* =>
tkclient->wmctl(mainwin, s);
}
menucmd := <- cmd =>
case menucmd {
"host" =>
reset_board();
(n, conn) := sys->announce("tcp!*!" + PORT);
if (n < 0)
{
tk->cmd(mainwin,".ft.ls configure -text {Pelin isännöinti epäonnistui}");
tk->cmd(mainwin, "update");
}
else
{
spawn listenthread(conn);
tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state disabled");
tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state disabled");
tk->cmd(mainwin,".ft.ls configure -text {Odotetaan toista pelaajaa}");
tk->cmd(mainwin, "update");
}
"join" =>
reset_board();
address := "tcp!" + dialog->getstring(ctxt,mainwin.image, "Anna isännän osoite") + "!" + PORT;
(ok, conn) := sys->dial(address, "");
if (ok < 0)
{
tk->cmd(mainwin,".ft.ls configure -text {Yhteyden muodostus epäonnistui}");
tk->cmd(mainwin, "update");
}
else
{
tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state disabled");
tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state disabled");
tk->cmd(mainwin,".ft.ls configure -text {Vastustajan vuoro}");
tk->cmd(mainwin, "update");
spawn workerthread(conn);
spawn runclient(conn);
}
}
}
}
runserver(conn : Connection)
{
absorb(localcmd);
localch := localcmd;
dummych := chan of string;
for(;;) alt {
game := <- gamecmd =>
case game {
"close" =>
tk->cmd(mainwin,".ft.ls configure -text {Toinen pelaaja sulki yhteyden}");
tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state normal");
tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state normal");
tk->cmd(mainwin, "update");
exit;
}
# täällä käsitellään tiedot pelilaudan painalluksista
local := <-localch =>
localch = dummych;
(n, tokens) := sys->tokenize(local, " ");
if(n >= 3)
case hd tokens {
"b" =>
row := int hd tl tokens;
column := int hd tl tl tokens;
if(board[row][column] == EMPTY)
{
message := local + "\r\n";
wdfd := sys->open(conn.dir + "/data", Sys->OWRITE);
sys->write(wdfd, array of byte message, len array of byte message);
board[row][column] = PLAYER1;
tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text {X}", row, column));
if(testrows(row, column, 5, PLAYER1) == 1)
{
tk->cmd(mainwin,".ft.ls configure -text {Voitit!}");
tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state normal");
tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state normal");
tk->cmd(mainwin, "update");
fd := sys->open("#p/" + string workerpid +"/ctl", sys->OWRITE);
if(fd != nil)
sys->fprint(fd, "kill");
exit;
}
tk->cmd(mainwin,".ft.ls configure -text {Vastustajan vuoro}");
tk->cmd(mainwin, "update");
}
else
localch = localcmd;
* =>
localch = localcmd;
}
else
localch = localcmd;
remote := <-remotecmd =>
(n, tokens) := sys->tokenize(remote, " ");
if(n >= 3)
case hd tokens {
"b" =>
row := int hd tl tokens;
column := int hd tl tl tokens;
board[row][column] = PLAYER2;
tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text {0}", row, column));
if(testrows(row, column, 5, PLAYER2) == 1)
{
tk->cmd(mainwin,".ft.ls configure -text {Vastustaja voitti!}");
tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state normal");
tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state normal");
tk->cmd(mainwin, "update");
fd := sys->open("#p/" + string workerpid +"/ctl", sys->OWRITE);
if(fd != nil)
sys->fprint(fd, "kill");
exit;
}
tk->cmd(mainwin,".ft.ls configure -text {Oma vuoro}");
tk->cmd(mainwin, "update");
absorb(localcmd);
localch = localcmd;
}
}
}
runclient(conn : Connection)
{
dummych := chan of string;
localch := dummych;
for(;;) alt {
game := <- gamecmd =>
case game {
"close" =>
tk->cmd(mainwin,".ft.ls configure -text {Toinen pelaaja sulki yhteyden}");
tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state normal");
tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state normal");
tk->cmd(mainwin, "update");
exit;
}
# täällä käsitellään tiedot pelilaudan painalluksista
local := <-localch =>
localch = dummych;
(n, tokens) := sys->tokenize(local, " ");
if(n >= 3)
case hd tokens {
"b" =>
row := int hd tl tokens;
column := int hd tl tl tokens;
if(board[row][column] == EMPTY)
{
message := local + "\r\n";
sys->write(conn.dfd, array of byte message, len array of byte message);
board[row][column] = PLAYER2;
tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text {0}", row, column));
if(testrows(row, column, 5, PLAYER2) == 1)
{
tk->cmd(mainwin,".ft.ls configure -text {Voitit!}");
tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state normal");
tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state normal");
tk->cmd(mainwin, "update");
fd := sys->open("#p/" + string workerpid +"/ctl", sys->OWRITE);
if(fd != nil)
sys->fprint(fd, "kill");
exit;
}
tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text {0}", row, column));
tk->cmd(mainwin,".ft.ls configure -text {Vastustajan vuoro}");
tk->cmd(mainwin, "update");
}
else
localch = localcmd;
* =>
localch = localcmd;
}
else
localch = localcmd;
remote := <-remotecmd =>
(n, tokens) := sys->tokenize(remote, " ");
if(n >= 3)
case hd tokens {
"b" =>
row := int hd tl tokens;
column := int hd tl tl tokens;
board[row][column] = PLAYER1;
tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text {X}", row, column));
if(testrows(row, column, 5, PLAYER1) == 1)
{
tk->cmd(mainwin,".ft.ls configure -text {Vastustaja voitti!}");
tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state normal");
tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state normal");
tk->cmd(mainwin, "update");
fd := sys->open("#p/" + string workerpid +"/ctl", sys->OWRITE);
if(fd != nil)
sys->fprint(fd, "kill");
exit;
}
tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text {X}", row, column));
tk->cmd(mainwin,".ft.ls configure -text {Oma vuoro}");
tk->cmd(mainwin, "update");
absorb(localcmd);
localch = localcmd;
}
}
}
center(t: ref Tk->Toplevel)
{
org: Point;
ir := tk->rect(t, ".", Tk->Border|Tk->Required);
org.x = t.screenr.dx() / 2 - ir.dx() / 2;
org.y = t.screenr.dy() / 2 - ir.dy() / 2;
if (org.y < 0)
{
org.y = 0;
}
tk->cmd(t, ". configure -x " + string org.x + " -y " + string org.y);
}
display_board()
{
i, j: int;
pack: string;
tk->cmd(mainwin, "frame .f");
tk->cmd(mainwin, "pack .f -fill x");
tk->cmd(mainwin, "menubutton .f.menu -text Peli -menu .f.menu.gm");
tk->cmd(mainwin, "menu .f.menu.gm");
tk->cmd(mainwin, ".f.menu.gm add command -label {isännöi} -command {send cmd host}");
tk->cmd(mainwin, ".f.menu.gm add command -label {liity} -command {send cmd join}");
tk->cmd(mainwin, "pack .f.menu -side left");
for (i = 1; i <= BSZ; i++)
{
tk->cmd(mainwin, sys->sprint("frame .f%d", i));
pack = "";
for (j = 1; j <= BSZ; j++)
{
pack += sys->sprint(" .f%d.b%d", i, j);
tk->cmd(mainwin, sys->sprint("button .f%d.b%d -text { } -width 14 -command {send pcmd b %d %d}", i, j, i, j));
}
tk->cmd(mainwin, sys->sprint("pack %s -side left", pack));
tk->cmd(mainwin, sys->sprint("pack .f%d -side top -fill x", i));
}
tk->cmd(mainwin, "frame .ft");
tk->cmd(mainwin, "label .ft.li -text {Tila: }");
tk->cmd(mainwin, "label .ft.ls -text {Ei yhteydessä}");
tk->cmd(mainwin, "pack .ft.li .ft.ls -side left -fill x");
tk->cmd(mainwin, "pack .ft -side bottom -fill x");
tk->cmd(mainwin, "update");
}
listenthread(conn : Connection)
{
(ok, c) := sys->listen(conn);
if (ok < 0)
{
sys->fprint(sys->fildes(2), "Server: listen failed\n");
raise "fail:listen failed";
}
tk->cmd(mainwin,".ft.ls configure -text {Oma vuoro}");
tk->cmd(mainwin, "update");
spawn runserver(c);
spawn workerthread(c);
}
workerthread(conn : Connection)
{
workerpid = sys->pctl(0, nil);
buf := array [1] of byte;
rdfd := sys->open(conn.dir + "/data", Sys->OREAD);
output := "";
while( (n := sys->read(rdfd, buf, len buf ) ) > 0 )
{
output[len output] = int buf[0];
if(len output >= 2)
{
if(output[len output - 2:] == "\r\n")
{
remotecmd <- = output[:len output - 2];
output = "";
}
}
}
gamecmd <- = "close";
}
absorb(ch : chan of string)
{
for(;;)
{
alt
{
<- ch =>
;
* =>
return;
}
}
}
length(row, column, drow, dcolumn, item: int): int
{
l := 0;
while(board[row][column] == item)
{
row += drow;
column += dcolumn;
l++;
}
return l;
}
testrows(row, column, items, item: int) : int
{
vaaka := (length(row, column, 0, -1, item) + length(row, column, 0, 1, item) - 1);
if(vaaka >= items)
{
mark_board(row, column, 0, -1, item);
mark_board(row, column, 0, 1, item);
return 1;
}
pysty := (length(row, column, -1, 0, item) + length(row, column, 1, 0, item) - 1);
if(pysty >= items)
{
mark_board(row, column, -1, 0, item);
mark_board(row, column, 1, 0, item);
return 1;
}
vino1 := (length(row, column, -1, -1, item) + length(row, column, 1, 1, item) - 1);
if(vino1 >= items)
{
mark_board(row, column, -1, -1, item);
mark_board(row, column, 1, 1, item);
return 1;
}
vino2 := (length(row, column, -1, 1, item) + length(row, column, 1, -1, item) - 1);
if(vino2 >= items)
{
mark_board(row, column, -1, 1, item);
mark_board(row, column, 1, -1, item);
return 1;
}
return 0;
}
mark_board(row, column, drow, dcolumn, item : int)
{
while(board[row][column] == item)
{
tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -bg olive -activebackground olive", row, column));
row += drow;
column += dcolumn;
}
}
reset_board()
{
# nollaa board
for (i := 0; i < BSZI; i++)
for (j := 0; j < BSZI; j++)
board[i][j] = EMPTY;
# tyhjennä board näytöllä
for (i = 1; i <= BSZ; i++)
for (j = 1; j <= BSZ; j++)
tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text { } -bg #dddddd -activebackground #eeeeee", i, j));
tk->cmd(mainwin, "update");
}
|