--- /dev/null
+(* $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 ()