Advertisement
jalih

Network TicTacToe game for two player in Inferno's Limbo

Oct 26th, 2012
162
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.97 KB | None | 0 0
  1.  
  2. implement TicTacToe;
  3.  
  4. include "sys.m";
  5. sys: Sys;
  6. Dir: import sys;
  7. Connection : import Sys;
  8.  
  9. include "draw.m";
  10. draw: Draw;
  11. Screen, Display, Image, Context, Point, Rect: import draw;
  12.  
  13. include "tk.m";
  14. tk: Tk;
  15. Toplevel: import tk;
  16.  
  17. include "tkclient.m";
  18. tkclient: Tkclient;
  19. Hide: import tkclient;
  20.  
  21. include "dialog.m";
  22. dialog : Dialog;
  23.  
  24.  
  25. TicTacToe : module
  26. {
  27. init: fn(ctxt: ref Draw->Context, argv: list of string);
  28. };
  29.  
  30. PORT : con "2000";
  31. WINBUT : con Hide;
  32. BSZ : con 20;
  33. BSZI : con BSZ + 2;
  34. EMPTY, PLAYER1, PLAYER2 : con iota;
  35.  
  36. # board[frame][button]
  37. board := array[BSZI] of { * => array[BSZI] of {* => EMPTY} };
  38.  
  39. ctxt: ref Draw->Context;
  40. mainwin : ref Tk->Toplevel;
  41.  
  42. workerpid : int;
  43.  
  44. cmd : chan of string;
  45. gamecmd : chan of string;
  46. localcmd : chan of string;
  47. remotecmd : chan of string;
  48.  
  49.  
  50. init(xctxt: ref Draw->Context, nil: list of string)
  51. {
  52. sys = load Sys Sys->PATH;
  53.  
  54. if (xctxt == nil) {
  55. sys->fprint(sys->fildes(2), "Tic-Tac-Toe: no window context\n");
  56. raise "fail:bad context";
  57. }
  58.  
  59. ctxt = xctxt;
  60.  
  61. draw = load Draw Draw->PATH;
  62. tk = load Tk Tk->PATH;
  63. tkclient = load Tkclient Tkclient->PATH;
  64. dialog = load Dialog Dialog->PATH;
  65.  
  66. sys->pctl(Sys->NEWPGRP, nil);
  67.  
  68. tkclient->init();
  69. dialog->init();
  70.  
  71. wmctl : chan of string;
  72. (mainwin, wmctl) = tkclient->toplevel(ctxt, nil, "Tic-Tac-Toe", WINBUT);
  73. if(mainwin == nil)
  74. {
  75. sys->fprint(sys->fildes(2), "Tic_Tac-Toe: creation of toplevel window failed\n");
  76. raise "fail:creation of toplevel window failed";
  77. }
  78. gamecmd = chan of string;
  79.  
  80. cmd = chan of string;
  81. tk->namechan(mainwin, cmd, "cmd");
  82.  
  83. localcmd = chan of string;
  84. tk->namechan(mainwin, localcmd, "pcmd");
  85.  
  86. remotecmd = chan of string;
  87.  
  88. display_board();
  89.  
  90. center(mainwin);
  91. tkclient->onscreen(mainwin, "exact");
  92. tkclient->startinput(mainwin, "kbd"::"ptr"::nil);
  93.  
  94. for(;;) alt {
  95. s := <- mainwin.ctxt.kbd =>
  96. tk->keyboard(mainwin, s);
  97.  
  98. s := <- mainwin.ctxt.ptr =>
  99. tk->pointer(mainwin, *s);
  100.  
  101. s := <- mainwin.ctxt.ctl or
  102. s = <- mainwin.wreq or
  103. s = <- wmctl =>
  104. case s {
  105. "exit" =>
  106. fd := sys->open("#p/" + string workerpid +"/ctl", sys->OWRITE);
  107. if(fd != nil)
  108. sys->fprint(fd, "kill");
  109.  
  110. tkclient->wmctl(mainwin, "exit");
  111.  
  112. * =>
  113. tkclient->wmctl(mainwin, s);
  114. }
  115. menucmd := <- cmd =>
  116. case menucmd {
  117. "host" =>
  118. reset_board();
  119. (n, conn) := sys->announce("tcp!*!" + PORT);
  120. if (n < 0)
  121. {
  122. tk->cmd(mainwin,".ft.ls configure -text {Hosting of a game failed}");
  123. tk->cmd(mainwin, "update");
  124. }
  125. else
  126. {
  127. spawn listenthread(conn);
  128.  
  129. tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state disabled");
  130. tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state disabled");
  131. tk->cmd(mainwin,".ft.ls configure -text {Waiting for opponent}");
  132. tk->cmd(mainwin, "update");
  133. }
  134.  
  135. "join" =>
  136. reset_board();
  137. address := "tcp!" + dialog->getstring(ctxt,mainwin.image, "Enter host's address") + "!" + PORT;
  138. (ok, conn) := sys->dial(address, "");
  139. if (ok < 0)
  140. {
  141. tk->cmd(mainwin,".ft.ls configure -text {Connection failed}");
  142. tk->cmd(mainwin, "update");
  143. }
  144. else
  145. {
  146. tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state disabled");
  147. tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state disabled");
  148. tk->cmd(mainwin,".ft.ls configure -text {Opponent's turn}");
  149. tk->cmd(mainwin, "update");
  150.  
  151. spawn workerthread(conn);
  152. spawn runclient(conn);
  153. }
  154. }
  155.  
  156. }
  157.  
  158. }
  159.  
  160.  
  161. runserver(conn : Connection)
  162. {
  163.  
  164. absorb(localcmd);
  165. localch := localcmd;
  166. dummych := chan of string;
  167.  
  168. moves := 0;
  169.  
  170. for(;;)
  171. {
  172. alt
  173. {
  174. game := <- gamecmd =>
  175. case game {
  176. "close" =>
  177. tk->cmd(mainwin,".ft.ls configure -text {Opponent closed the connection}");
  178. tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state normal");
  179. tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state normal");
  180. tk->cmd(mainwin, "update");
  181. exit;
  182. }
  183. local := <-localch =>
  184. localch = dummych;
  185. (n, tokens) := sys->tokenize(local, " ");
  186. if(n >= 3)
  187. case hd tokens {
  188. "b" =>
  189. row := int hd tl tokens;
  190. column := int hd tl tl tokens;
  191.  
  192. if(board[row][column] == EMPTY)
  193. {
  194. message := local + "\r\n";
  195. wdfd := sys->open(conn.dir + "/data", Sys->OWRITE);
  196. sys->write(wdfd, array of byte message, len array of byte message);
  197.  
  198. board[row][column] = PLAYER1;
  199. moves ++;
  200.  
  201. tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text {X}", row, column));
  202.  
  203. if(testrows(row, column, 5, PLAYER1) == 1)
  204. {
  205. tk->cmd(mainwin,".ft.ls configure -text {You won!}");
  206. tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state normal");
  207. tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state normal");
  208. tk->cmd(mainwin, "update");
  209.  
  210. fd := sys->open("#p/" + string workerpid +"/ctl", sys->OWRITE);
  211. if(fd != nil)
  212. sys->fprint(fd, "kill");
  213. exit;
  214. }
  215. tk->cmd(mainwin,".ft.ls configure -text {Opponent's turn}");
  216. tk->cmd(mainwin, "update");
  217. }
  218. else
  219. localch = localcmd;
  220. * =>
  221. localch = localcmd;
  222. }
  223. else
  224. localch = localcmd;
  225. remote := <-remotecmd =>
  226. (n, tokens) := sys->tokenize(remote, " ");
  227. if(n >= 3)
  228. case hd tokens {
  229. "b" =>
  230. row := int hd tl tokens;
  231. column := int hd tl tl tokens;
  232.  
  233. board[row][column] = PLAYER2;
  234. moves ++;
  235.  
  236. tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text {0}", row, column));
  237.  
  238. if (testrows(row, column, 5, PLAYER2) == 1)
  239. {
  240. tk->cmd(mainwin,".ft.ls configure -text {Opponent won!}");
  241. tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state normal");
  242. tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state normal");
  243. tk->cmd(mainwin, "update");
  244.  
  245. fd := sys->open("#p/" + string workerpid +"/ctl", sys->OWRITE);
  246. if(fd != nil)
  247. sys->fprint(fd, "kill");
  248.  
  249. exit;
  250. }
  251.  
  252. tk->cmd(mainwin,".ft.ls configure -text {Your turn}");
  253. tk->cmd(mainwin, "update");
  254.  
  255. absorb(localcmd);
  256. localch = localcmd;
  257.  
  258. }
  259. }
  260.  
  261. if (moves >= BSZ * BSZ)
  262. {
  263. tk->cmd(mainwin,".ft.ls configure -text {Draw!}");
  264. tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state normal");
  265. tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state normal");
  266. tk->cmd(mainwin, "update");
  267.  
  268. fd := sys->open("#p/" + string workerpid +"/ctl", sys->OWRITE);
  269. if(fd != nil)
  270. sys->fprint(fd, "kill");
  271.  
  272. exit;
  273. }
  274.  
  275. }
  276.  
  277. }
  278.  
  279.  
  280. runclient(conn : Connection)
  281. {
  282.  
  283. dummych := chan of string;
  284. localch := dummych;
  285.  
  286. moves := 0;
  287.  
  288. for(;;)
  289. {
  290. alt
  291. {
  292. game := <- gamecmd =>
  293. case game {
  294. "close" =>
  295. tk->cmd(mainwin,".ft.ls configure -text {Opponent closed the connection}");
  296. tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state normal");
  297. tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state normal");
  298. tk->cmd(mainwin, "update");
  299. exit;
  300. }
  301. local := <-localch =>
  302. localch = dummych;
  303. (n, tokens) := sys->tokenize(local, " ");
  304. if(n >= 3)
  305. case hd tokens {
  306. "b" =>
  307. row := int hd tl tokens;
  308. column := int hd tl tl tokens;
  309.  
  310. if(board[row][column] == EMPTY)
  311. {
  312. message := local + "\r\n";
  313. sys->write(conn.dfd, array of byte message, len array of byte message);
  314.  
  315. board[row][column] = PLAYER2;
  316. moves ++;
  317.  
  318. tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text {0}", row, column));
  319.  
  320. if(testrows(row, column, 5, PLAYER2) == 1)
  321. {
  322. tk->cmd(mainwin,".ft.ls configure -text {You won!}");
  323. tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state normal");
  324. tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state normal");
  325. tk->cmd(mainwin, "update");
  326.  
  327. fd := sys->open("#p/" + string workerpid +"/ctl", sys->OWRITE);
  328. if(fd != nil)
  329. sys->fprint(fd, "kill");
  330.  
  331. exit;
  332. }
  333. tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text {0}", row, column));
  334. tk->cmd(mainwin,".ft.ls configure -text {Opponent's turn}");
  335. tk->cmd(mainwin, "update");
  336. }
  337. else
  338. localch = localcmd;
  339. * =>
  340. localch = localcmd;
  341. }
  342. else
  343. localch = localcmd;
  344. remote := <-remotecmd =>
  345. (n, tokens) := sys->tokenize(remote, " ");
  346. if(n >= 3)
  347. case hd tokens {
  348. "b" =>
  349. row := int hd tl tokens;
  350. column := int hd tl tl tokens;
  351.  
  352. board[row][column] = PLAYER1;
  353. moves ++;
  354.  
  355. tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text {X}", row, column));
  356.  
  357. if(testrows(row, column, 5, PLAYER1) == 1)
  358. {
  359. tk->cmd(mainwin,".ft.ls configure -text {Opponent won!}");
  360. tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state normal");
  361. tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state normal");
  362. tk->cmd(mainwin, "update");
  363.  
  364. fd := sys->open("#p/" + string workerpid +"/ctl", sys->OWRITE);
  365. if(fd != nil)
  366. sys->fprint(fd, "kill");
  367.  
  368. exit;
  369. }
  370.  
  371. tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text {X}", row, column));
  372. tk->cmd(mainwin,".ft.ls configure -text {Your turn}");
  373. tk->cmd(mainwin, "update");
  374.  
  375. absorb(localcmd);
  376. localch = localcmd;
  377.  
  378. }
  379. }
  380. if (moves >= BSZ * BSZ)
  381. {
  382. tk->cmd(mainwin,".ft.ls configure -text {Draw!}");
  383. tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state normal");
  384. tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state normal");
  385. tk->cmd(mainwin, "update");
  386.  
  387. fd := sys->open("#p/" + string workerpid +"/ctl", sys->OWRITE);
  388. if(fd != nil)
  389. sys->fprint(fd, "kill");
  390.  
  391. exit;
  392. }
  393. }
  394.  
  395. }
  396.  
  397.  
  398. center(t: ref Tk->Toplevel)
  399. {
  400. org: Point;
  401. ir := tk->rect(t, ".", Tk->Border|Tk->Required);
  402. org.x = t.screenr.dx() / 2 - ir.dx() / 2;
  403. org.y = t.screenr.dy() / 2 - ir.dy() / 2;
  404.  
  405. if (org.y < 0)
  406. {
  407. org.y = 0;
  408. }
  409.  
  410. tk->cmd(t, ". configure -x " + string org.x + " -y " + string org.y);
  411. }
  412.  
  413.  
  414. display_board()
  415. {
  416. i, j: int;
  417. pack: string;
  418.  
  419. tk->cmd(mainwin, "frame .f");
  420. tk->cmd(mainwin, "pack .f -fill x");
  421. tk->cmd(mainwin, "menubutton .f.menu -text Game -menu .f.menu.gm");
  422. tk->cmd(mainwin, "menu .f.menu.gm");
  423. tk->cmd(mainwin, ".f.menu.gm add command -label {host game} -command {send cmd host}");
  424. tk->cmd(mainwin, ".f.menu.gm add command -label {join game} -command {send cmd join}");
  425. tk->cmd(mainwin, "pack .f.menu -side left");
  426.  
  427. for (i = 1; i <= BSZ; i++)
  428. {
  429. tk->cmd(mainwin, sys->sprint("frame .f%d", i));
  430. pack = "";
  431.  
  432. for (j = 1; j <= BSZ; j++)
  433. {
  434. pack += sys->sprint(" .f%d.b%d", i, j);
  435. tk->cmd(mainwin, sys->sprint("button .f%d.b%d -text { } -width 14 -command {send pcmd b %d %d}", i, j, i, j));
  436. }
  437.  
  438. tk->cmd(mainwin, sys->sprint("pack %s -side left", pack));
  439. tk->cmd(mainwin, sys->sprint("pack .f%d -side top -fill x", i));
  440.  
  441. }
  442.  
  443. tk->cmd(mainwin, "frame .ft");
  444. tk->cmd(mainwin, "label .ft.li -text {Status: }");
  445. tk->cmd(mainwin, "label .ft.ls -text {Not connected}");
  446. tk->cmd(mainwin, "pack .ft.li .ft.ls -side left -fill x");
  447. tk->cmd(mainwin, "pack .ft -side bottom -fill x");
  448. tk->cmd(mainwin, "update");
  449.  
  450. }
  451.  
  452.  
  453. listenthread(conn : Connection)
  454. {
  455.  
  456. (ok, c) := sys->listen(conn);
  457. if (ok < 0)
  458. {
  459. sys->fprint(sys->fildes(2), "Server: listen failed\n");
  460. raise "fail:listen failed";
  461. }
  462.  
  463.  
  464. tk->cmd(mainwin,".ft.ls configure -text {Your turn}");
  465. tk->cmd(mainwin, "update");
  466.  
  467. spawn runserver(c);
  468. spawn workerthread(c);
  469.  
  470. }
  471.  
  472.  
  473. workerthread(conn : Connection)
  474. {
  475. workerpid = sys->pctl(0, nil);
  476.  
  477. buf := array [1] of byte;
  478. rdfd := sys->open(conn.dir + "/data", Sys->OREAD);
  479. output := "";
  480.  
  481. while( (n := sys->read(rdfd, buf, len buf ) ) > 0 )
  482. {
  483. output[len output] = int buf[0];
  484.  
  485. if(len output >= 2)
  486. {
  487. if(output[len output - 2:] == "\r\n")
  488. {
  489. remotecmd <- = output[:len output - 2];
  490. output = "";
  491. }
  492.  
  493. }
  494.  
  495. }
  496.  
  497. gamecmd <- = "close";
  498.  
  499.  
  500. }
  501.  
  502.  
  503. absorb(ch : chan of string)
  504. {
  505. for(;;)
  506. {
  507. alt
  508. {
  509. <- ch =>
  510. ;
  511. * =>
  512. return;
  513. }
  514. }
  515. }
  516.  
  517.  
  518. length(row, column, drow, dcolumn, item: int): int
  519. {
  520. l := 0;
  521.  
  522. while(board[row][column] == item)
  523. {
  524. row += drow;
  525. column += dcolumn;
  526.  
  527. l++;
  528. }
  529.  
  530. return l;
  531.  
  532. }
  533.  
  534.  
  535. testrows(row, column, items, item: int) : int
  536. {
  537. horizontal := (length(row, column, 0, -1, item) + length(row, column, 0, 1, item) - 1);
  538. if(horizontal >= items)
  539. {
  540. mark_board(row, column, 0, -1, item);
  541. mark_board(row, column, 0, 1, item);
  542. return 1;
  543. }
  544.  
  545. vertical := (length(row, column, -1, 0, item) + length(row, column, 1, 0, item) - 1);
  546. if(vertical >= items)
  547. {
  548. mark_board(row, column, -1, 0, item);
  549. mark_board(row, column, 1, 0, item);
  550. return 1;
  551. }
  552.  
  553. dir1 := (length(row, column, -1, -1, item) + length(row, column, 1, 1, item) - 1);
  554. if(dir1 >= items)
  555. {
  556. mark_board(row, column, -1, -1, item);
  557. mark_board(row, column, 1, 1, item);
  558. return 1;
  559. }
  560.  
  561. dir2 := (length(row, column, -1, 1, item) + length(row, column, 1, -1, item) - 1);
  562. if(dir2 >= items)
  563. {
  564. mark_board(row, column, -1, 1, item);
  565. mark_board(row, column, 1, -1, item);
  566. return 1;
  567. }
  568.  
  569. return 0;
  570.  
  571. }
  572.  
  573.  
  574. mark_board(row, column, drow, dcolumn, item : int)
  575. {
  576. while(board[row][column] == item)
  577. {
  578. tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -bg olive -activebackground olive", row, column));
  579.  
  580. row += drow;
  581. column += dcolumn;
  582. }
  583.  
  584. }
  585.  
  586.  
  587. reset_board()
  588. {
  589. for (i := 0; i < BSZI; i++)
  590. for (j := 0; j < BSZI; j++)
  591. board[i][j] = EMPTY;
  592.  
  593. for (i = 1; i <= BSZ; i++)
  594. for (j = 1; j <= BSZ; j++)
  595. tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text { } -bg #dddddd -activebackground #eeeeee", i, j));
  596.  
  597. tk->cmd(mainwin, "update");
  598.  
  599. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement