implement Sweeper; # # michael@vitanuova.com # # Copyright © 2000 Vita Nuova Limited. All rights reserved. # Copyright © 2001 Vita Nuova Holdings Limited. All rights reserved. # include "sys.m"; sys: Sys; include "draw.m"; draw: Draw; Point, Rect, Image, Font, Context, Screen, Display: import draw; include "tk.m"; tk: Tk; Toplevel: import tk; include "tkclient.m"; tkclient: Tkclient; include "daytime.m"; daytime: Daytime; include "rand.m"; rand: Rand; stderr: ref Sys->FD; Sweeper: module { init: fn(ctxt: ref Draw->Context, argv: list of string); }; mainwin: ref Toplevel; score: int; mines: int; WIDTH: con 220; HEIGHT: con 220; EASY: con 20; SZB: con 10; SZI: con SZB+2; # internal board is 2 larger than visible board Cell: adt { mine, state: int; }; board: array of array of Cell; UNSELECTED, SELECTED, MARKED: con (1<Context, nil: list of string) { sys = load Sys Sys->PATH; draw = load Draw Draw->PATH; tk = load Tk Tk->PATH; tkclient = load Tkclient Tkclient->PATH; daytime = load Daytime Daytime->PATH; rand = load Rand Rand->PATH; stderr = sys->fildes(2); rand->init(daytime->now()); daytime = nil; tkclient->init(); if(ctxt == nil) ctxt = tkclient->makedrawcontext(); (win, wmcmd) := tkclient->toplevel(ctxt, "", "Mine Sweeper", Tkclient->Hide); mainwin = win; sys->pctl(Sys->NEWPGRP, nil); cmdch := chan of string; tk->namechan(win, cmdch, "cmd"); display_board(); pid := -1; finished := 0; init_board(); tkclient->onscreen(win, nil); tkclient->startinput(win, "kbd"::"ptr"::nil); for (;;) { alt { s := <-win.ctxt.kbd => tk->keyboard(win, s); s := <-win.ctxt.ptr => tk->pointer(win, *s); c := <-win.ctxt.ctl or c = <-win.wreq or c = <- wmcmd => # wm commands case c { "exit" => if(pid != -1) kill(pid); exit; * => tkclient->wmctl(win, c); } c := <- cmdch => # tk commands (nil, toks) := sys->tokenize(c, " "); case hd toks { "b" => x := int hd tl toks; y := int hd tl tl toks; i := board_check(x, y); case i { -1 => display_mines(); display_lost(); finished = 1; 0 to 8 => if (finished) break; score++; board[x][y].state = SELECTED; display_square(x, y, sys->sprint("%d", i), "olive"); if (i == 0) { # check all adjacent zeros display_zeros(x, y); } display_score(); if (score+mines == SZB*SZB) { display_mines(); display_win(); finished = 1; } * => ; } cmd(mainwin, "update"); "b3" => x := int hd tl toks; y := int hd tl tl toks; mark_square(x, y); cmd(mainwin, "update"); "restart" => init_board(); display_score(); reset_display(); finished = 0; * => sys->fprint(stderr, "%s\n", c); } } } } display_board() { i, j: int; pack: string; for(i = 0; i < len win_config; i++) cmd(mainwin, win_config[i]); for (i = 1; i <= SZB; i++) { cmd(mainwin, sys->sprint("frame .f%d", i)); pack = ""; for (j = 1; j <= SZB; j++) { pack += sys->sprint(" .f%d.b%dx%d", i, i, j); cmd(mainwin, sys->sprint("button .f%d.b%dx%d -text { } -width 14 -command {send cmd b %d %d}", i, i, j, i, j)); cmd(mainwin, sys->sprint("bind .f%d.b%dx%d {send cmd b3 %d %d}", i, i, j, i, j)); } cmd(mainwin, sys->sprint("pack %s -side left", pack)); cmd(mainwin, sys->sprint("pack .f%d -side top -fill x", i)); } for (i = 0; i < len win_config2; i++) cmd (mainwin, win_config2[i]); } reset_display() { for (i := 1; i <= SZB; i++) { for (j := 1; j <= SZB; j++) { s := sys->sprint(".f%d.b%dx%d configure -text { } -bg #dddddd -activebackground #eeeeee", i, i, j); cmd(mainwin, s); } } cmd(mainwin, "update"); } init_board() { i, j: int; score = 0; mines = 0; board = array[SZI] of array of Cell; for (i = 0; i < SZI; i++) board[i] = array[SZI] of Cell; # initialize board for (i = 0; i < SZI; i++) for (j =0; j < SZI; j++) { board[i][j].mine = 0; board[i][j].state = UNSELECTED; } # place mines for (i = 0; i < EASY; i++) { j = rand->rand(SZB*SZB); if (board[(j/SZB)+1][(j%SZB)+1].mine == 0) { # rand could yield same result twice board[(j/SZB)+1][(j%SZB)+1].mine = 1; mines++; } } cmd(mainwin, "update"); } display_score() { cmd(mainwin, ".f.l configure -text {Score: "+ sys->sprint("%d", score)+ "}"); } display_win() { cmd(mainwin, ".f.l configure -text {You have Won}"); } display_lost() { cmd(mainwin, ".f.l configure -text {You have Lost}"); } display_mines() { for (i := 1; i <= SZB; i++) for (j := 1; j <= SZB; j++) if (board[i][j].mine == 1) display_square(i, j, "M", "red"); } display_square(i, j: int, v: string, c: string) { cmd(mainwin, sys->sprint(".f%d.b%dx%d configure -text {%s} -bg %s -activebackground %s", i, i, j, v, c, c)); cmd(mainwin, "update"); } mark_square(i, j: int) { case board[i][j].state { UNSELECTED => board[i][j].state = MARKED; display_square(i, j, "?", "orange"); MARKED => board[i][j].state = UNSELECTED; display_square(i, j, " ", "#dddddd"); } } board_check(i, j: int) : int { if (board[i][j].mine == 1) return -1; if (board[i][j].state&(SELECTED|MARKED)) return -2; c := 0; for (x := i-1; x <= i+1; x++) for (y := j-1; y <= j+1; y++) if (board[x][y].mine == 1) c++; return c; } display_zeros(i, j: int) { for (x := i-1; x <= i+1; x++) { for (y := j-1; y <= j+1; y++) { if (x <1 || x>SZB || y<1 || y>SZB) continue; if (board_check(x, y) == 0) { score++; board[x][y].state = SELECTED; display_square(x, y, "0", "olive"); display_zeros(x, y); } } } } fatal(s: string) { sys->fprint(stderr, "%s\n", s); exit; } sleep(t: int) { sys->sleep(t); } kill(pid: int): int { fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE); if(fd == nil) return -1; if(sys->write(fd, array of byte "kill", 4) != 4) return -1; return 0; } cmd(top: ref Toplevel, s: string): string { e := tk->cmd(top, s); if (e != nil && e[0] == '!') sys->fprint(stderr, "sweeper: tk error on '%s': %s\n", s, e); return e; } win_config := array[] of { "frame .f -width 220 -height 220", "menubutton .f.sz -text Options -menu .f.sz.sm", "menu .f.sz.sm", ".f.sz.sm add command -label restart -command { send cmd restart }", "pack .f.sz -side left", "label .f.l -text {Score: }", "pack .f.l -side right", "frame .ft", "label .ft.l -text { }", "pack .ft.l -side left", "pack .f -side top -fill x", "pack .ft -side top -fill x", }; win_config2 := array[] of { "pack propagate . 0", "update", };