(* $Id$ *) (* The game logic *) type color = [`none|`white|`black] module type BoardSpec = sig type t val size : int val get : t -> x:int -> y:int -> color val set : t -> x:int -> y:int -> color:color -> unit end module Board (Spec : BoardSpec) = struct open Spec let size = size let on_board x y = x >= 0 && x < size && y >= 0 && y < size let rec string board ~x ~y ~dx ~dy ~color l = let x = x+dx and y = y+dy in if on_board x y then let col = get board ~x ~y in if col = (color : [`white|`black] :> color) then l else if col = `none then [] else string board ~x ~y ~dx ~dy ~color ((x,y)::l) else [] let find_swaps board ~x ~y ~color = if get board ~x ~y <> `none then [] else List.fold_left [-1,-1; -1,0; -1,1; 0,-1; 0,1; 1,-1; 1,0; 1,1] ~init:[] ~f:(fun acc (dx,dy) -> string board ~x ~y ~dx ~dy ~color [] @ acc) let action board ~x ~y ~color = let swaps = find_swaps board ~x ~y ~color in if swaps = [] then false else begin List.iter ((x,y)::swaps) ~f:(fun (x,y) -> set board ~x ~y ~color:(color :> color)); true end let check_impossible board ~color = try for x = 0 to size - 1 do for y = 0 to size - 1 do if find_swaps board ~x ~y ~color <> [] then raise Exit done done; true with Exit -> false let count_cells board = let w = ref 0 and b = ref 0 in for x = 0 to size - 1 do for y = 0 to size - 1 do match get board ~x ~y with `white -> incr w | `black -> incr b | `none -> () done done; (!w,!b) end (* GUI *) open GMain (* Toplevel window *) let window = GWindow.window ~title:"pousse" () (* Create pixmaps *) let pixdraw = GDraw.pixmap ~window ~width:40 ~height:40 ~mask:true () let pixdraw1 = GDraw.pixmap ~window ~width:40 ~height:40 ~mask:true () let pixdraw2 = GDraw.pixmap ~window ~width:40 ~height:40 ~mask:true () let _ = pixdraw1#set_foreground `BLACK; pixdraw1#arc ~x:3 ~y:3 ~width:34 ~height:34 ~filled:true (); pixdraw2#set_foreground `WHITE; pixdraw2#arc ~x:3 ~y:3 ~width:34 ~height:34 ~filled:true (); pixdraw2#set_foreground `BLACK; pixdraw2#arc ~x:3 ~y:3 ~width:34 ~height:34 () (* The cell class: a button with a pixmap on it *) class cell ?packing ?show () = let button = GButton.button ?packing ?show () in object (self) inherit GObj.widget button#as_widget method connect = button#connect val mutable color : color = `none val pm = GMisc.pixmap pixdraw ~packing:button#add () method color = color method set_color col = if col <> color then begin color <- col; pm#set_pixmap (match col with `none -> pixdraw | `black -> pixdraw1 | `white -> pixdraw2) end end module RealBoard = Board ( struct type t = cell array array let size = 8 let get (board : t) ~x ~y = board.(x).(y)#color let set (board : t) ~x ~y ~color = board.(x).(y)#set_color color end ) (* Conducting a game *) open RealBoard class game ~(frame : #GContainer.container) ~(label : #GMisc.label) ~(statusbar : #GMisc.statusbar) = let table = GPack.table ~columns:size ~rows:size ~packing:frame#add () in object (self) val cells = Array.init size ~f:(fun i -> Array.init size ~f:(fun j -> new cell ~packing:(table#attach ~top:i ~left:j) ())) val label = label val turn = statusbar#new_context ~name:"turn" val messages = statusbar#new_context ~name:"messages" val mutable current_color = `black method board = cells method table = table method player = current_color method swap_players () = current_color <- match current_color with `white -> turn#pop (); turn#push "Player is black"; `black | `black -> turn#pop (); turn#push "Player is white"; `white method finish () = turn#pop (); let w, b = count_cells cells in turn#push (if w > b then "White wins" else if w < b then "Black wins" else "Game is a draw"); () method update_label () = let w, b = count_cells cells in label#set_text (Printf.sprintf "White: %d Black: %d " w b) method play x y = if action cells ~x ~y ~color:current_color then begin self#update_label (); self#swap_players (); if check_impossible cells ~color:current_color then begin self#swap_players (); if check_impossible cells ~color:current_color then self#finish () end end else messages#flash "You cannot play there" initializer for i = 0 to size-1 do for j = 0 to size-1 do let cell = cells.(i).(j) in cell#connect#enter ~callback:cell#misc#grab_focus; cell#connect#clicked ~callback:(fun () -> self#play i j) done done; List.iter ~f:(fun (x,y,col) -> cells.(x).(y)#set_color col) [ 3,3,`black; 4,4,`black; 3,4,`white; 4,3,`white ]; self#update_label (); turn#push "Player is black"; () end (* Graphical elements *) let vbox = GPack.vbox ~packing:window#add () let frame = GBin.frame ~shadow_type:`IN ~packing:vbox#add () let hbox = GPack.hbox ~packing:vbox#pack () let bar = GMisc.statusbar ~packing:hbox#add () let frame2 = GBin.frame ~shadow_type:`IN ~packing:hbox#pack () let label = GMisc.label ~justify:`LEFT ~xpad:5 ~xalign:0.0 ~packing:frame2#add () let game = new game ~frame ~label ~statusbar:bar (* Start *) let _ = window#connect#destroy ~callback:Main.quit; window#show (); Main.main ()