Blame examples_labltk/tetris.ml

Packit bd2e5d
(***********************************************************************)
Packit bd2e5d
(*                                                                     *)
Packit bd2e5d
(*                 MLTk, Tcl/Tk interface of OCaml                     *)
Packit bd2e5d
(*                                                                     *)
Packit bd2e5d
(*    Francois Rouaix, Francois Pessaux, Jun Furuse and Pierre Weis    *)
Packit bd2e5d
(*               projet Cristal, INRIA Rocquencourt                    *)
Packit bd2e5d
(*            Jacques Garrigue, Kyoto University RIMS                  *)
Packit bd2e5d
(*                                                                     *)
Packit bd2e5d
(*  Copyright 2002 Institut National de Recherche en Informatique et   *)
Packit bd2e5d
(*  en Automatique and Kyoto University.  All rights reserved.         *)
Packit bd2e5d
(*  This file is distributed under the terms of the GNU Library        *)
Packit bd2e5d
(*  General Public License, with the special exception on linking      *)
Packit bd2e5d
(*  described in file LICENSE found in the OCaml source tree.          *)
Packit bd2e5d
(*                                                                     *)
Packit bd2e5d
(***********************************************************************)
Packit bd2e5d
Packit bd2e5d
(* $Id$ *)
Packit bd2e5d
Packit bd2e5d
(* A Tetris game for LablTk *)
Packit bd2e5d
(* written by Jun P. Furuse *)
Packit bd2e5d
Packit bd2e5d
open StdLabels
Packit bd2e5d
open Tk
Packit bd2e5d
Packit bd2e5d
exception Done
Packit bd2e5d
Packit bd2e5d
type falling_block = {
Packit bd2e5d
    mutable pattern: int array list;
Packit bd2e5d
    mutable bcolor: int;
Packit bd2e5d
    mutable x: int;
Packit bd2e5d
    mutable y: int;
Packit bd2e5d
    mutable d: int;
Packit bd2e5d
    mutable alive: bool
Packit bd2e5d
  }
Packit bd2e5d
Packit bd2e5d
let stop_a_bit = 300
Packit bd2e5d
Packit bd2e5d
let field_width = 10
Packit bd2e5d
let field_height = 20
Packit bd2e5d
Packit bd2e5d
let colors = [|
Packit bd2e5d
  `Color "red";
Packit bd2e5d
  `Color "yellow";
Packit bd2e5d
Packit bd2e5d
  `Color "blue";
Packit bd2e5d
  `Color "orange";
Packit bd2e5d
Packit bd2e5d
  `Color "magenta";
Packit bd2e5d
  `Color "green";
Packit bd2e5d
Packit bd2e5d
  `Color "cyan"
Packit bd2e5d
|]
Packit bd2e5d
Packit bd2e5d
(* Put here your favorite image files *)
Packit bd2e5d
let backgrounds = [
Packit bd2e5d
  "Lambda2.back.gif"
Packit bd2e5d
]
Packit bd2e5d
Packit bd2e5d
(* blocks *)
Packit bd2e5d
let block_size = 16
Packit bd2e5d
let cell_border = 2
Packit bd2e5d
Packit bd2e5d
let blocks = [
Packit bd2e5d
  [ [|"0000";
Packit bd2e5d
      "0000";
Packit bd2e5d
      "1111";
Packit bd2e5d
      "0000" |];
Packit bd2e5d
Packit bd2e5d
    [|"0010";
Packit bd2e5d
      "0010";
Packit bd2e5d
      "0010";
Packit bd2e5d
      "0010" |];
Packit bd2e5d
Packit bd2e5d
    [|"0000";
Packit bd2e5d
      "0000";
Packit bd2e5d
      "1111";
Packit bd2e5d
      "0000" |];
Packit bd2e5d
Packit bd2e5d
    [|"0010";
Packit bd2e5d
      "0010";
Packit bd2e5d
      "0010";
Packit bd2e5d
      "0010" |] ];
Packit bd2e5d
Packit bd2e5d
  [ [|"0000";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0000" |];
Packit bd2e5d
Packit bd2e5d
    [|"0000";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0000" |];
Packit bd2e5d
Packit bd2e5d
    [|"0000";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0000" |];
Packit bd2e5d
Packit bd2e5d
    [|"0000";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0000" |] ];
Packit bd2e5d
Packit bd2e5d
  [ [|"0000";
Packit bd2e5d
      "0111";
Packit bd2e5d
      "0100";
Packit bd2e5d
      "0000" |];
Packit bd2e5d
Packit bd2e5d
    [|"0000";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0010";
Packit bd2e5d
      "0010" |];
Packit bd2e5d
Packit bd2e5d
    [|"0000";
Packit bd2e5d
      "0010";
Packit bd2e5d
      "1110";
Packit bd2e5d
      "0000" |];
Packit bd2e5d
Packit bd2e5d
    [|"0100";
Packit bd2e5d
      "0100";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0000" |] ];
Packit bd2e5d
Packit bd2e5d
  [ [|"0000";
Packit bd2e5d
      "0100";
Packit bd2e5d
      "0111";
Packit bd2e5d
      "0000" |];
Packit bd2e5d
Packit bd2e5d
    [|"0000";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0100";
Packit bd2e5d
      "0100" |];
Packit bd2e5d
Packit bd2e5d
    [|"0000";
Packit bd2e5d
      "1110";
Packit bd2e5d
      "0010";
Packit bd2e5d
      "0000" |];
Packit bd2e5d
Packit bd2e5d
    [|"0010";
Packit bd2e5d
      "0010";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0000" |] ];
Packit bd2e5d
Packit bd2e5d
  [ [|"0000";
Packit bd2e5d
      "1100";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0000" |];
Packit bd2e5d
Packit bd2e5d
    [|"0010";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0100";
Packit bd2e5d
      "0000" |];
Packit bd2e5d
Packit bd2e5d
    [|"0000";
Packit bd2e5d
      "1100";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0000" |];
Packit bd2e5d
Packit bd2e5d
    [|"0010";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0100";
Packit bd2e5d
      "0000" |] ];
Packit bd2e5d
Packit bd2e5d
  [ [|"0000";
Packit bd2e5d
      "0011";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0000" |];
Packit bd2e5d
Packit bd2e5d
    [|"0100";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0010";
Packit bd2e5d
      "0000" |];
Packit bd2e5d
Packit bd2e5d
    [|"0000";
Packit bd2e5d
      "0011";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0000" |];
Packit bd2e5d
Packit bd2e5d
    [|"0000";
Packit bd2e5d
      "0100";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0010" |] ];
Packit bd2e5d
Packit bd2e5d
  [ [|"0000";
Packit bd2e5d
      "0000";
Packit bd2e5d
      "1110";
Packit bd2e5d
      "0100" |];
Packit bd2e5d
Packit bd2e5d
    [|"0000";
Packit bd2e5d
      "0100";
Packit bd2e5d
      "1100";
Packit bd2e5d
      "0100" |];
Packit bd2e5d
Packit bd2e5d
    [|"0000";
Packit bd2e5d
      "0100";
Packit bd2e5d
      "1110";
Packit bd2e5d
      "0000" |];
Packit bd2e5d
Packit bd2e5d
    [|"0000";
Packit bd2e5d
      "0100";
Packit bd2e5d
      "0110";
Packit bd2e5d
      "0100" |] ]
Packit bd2e5d
Packit bd2e5d
]
Packit bd2e5d
Packit bd2e5d
let line_empty = int_of_string "0b1110000000000111"
Packit bd2e5d
let line_full = int_of_string  "0b1111111111111111"
Packit bd2e5d
Packit bd2e5d
let decode_block dvec =
Packit bd2e5d
  let btoi d = int_of_string ("0b"^d) in
Packit bd2e5d
  Array.map ~f:btoi dvec
Packit bd2e5d
Packit bd2e5d
class cell t1 t2 t3 ~canvas ~x ~y = object
Packit bd2e5d
  val mutable color = 0
Packit bd2e5d
  method get = color
Packit bd2e5d
  method set ~color:col =
Packit bd2e5d
    if color = col then () else
Packit bd2e5d
    if color <> 0 && col = 0 then begin
Packit bd2e5d
      Canvas.move canvas t1
Packit bd2e5d
        ~x:(- block_size * (x + 1) -10 - cell_border * 2)
Packit bd2e5d
        ~y:(- block_size * (y + 1) -10 - cell_border * 2);
Packit bd2e5d
      Canvas.move canvas t2
Packit bd2e5d
        ~x:(- block_size * (x + 1) -10 - cell_border * 2)
Packit bd2e5d
        ~y:(- block_size * (y + 1) -10 - cell_border * 2);
Packit bd2e5d
      Canvas.move canvas t3
Packit bd2e5d
        ~x:(- block_size * (x + 1) -10 - cell_border * 2)
Packit bd2e5d
        ~y:(- block_size * (y + 1) -10 - cell_border * 2)
Packit bd2e5d
    end else begin
Packit bd2e5d
      Canvas.configure_rectangle canvas t2
Packit bd2e5d
        ~fill: colors.(col - 1)
Packit bd2e5d
        ~outline: colors.(col - 1);
Packit bd2e5d
      Canvas.configure_rectangle canvas t1
Packit bd2e5d
        ~fill: `Black
Packit bd2e5d
        ~outline: `Black;
Packit bd2e5d
      Canvas.configure_rectangle canvas t3
Packit bd2e5d
        ~fill: (`Color "light gray")
Packit bd2e5d
        ~outline: (`Color "light gray");
Packit bd2e5d
      if color = 0 && col <> 0 then begin
Packit bd2e5d
        Canvas.move canvas t1
Packit bd2e5d
          ~x: (block_size * (x+1)+10+ cell_border*2)
Packit bd2e5d
          ~y: (block_size * (y+1)+10+ cell_border*2);
Packit bd2e5d
        Canvas.move canvas t2
Packit bd2e5d
          ~x: (block_size * (x+1)+10+ cell_border*2)
Packit bd2e5d
          ~y: (block_size * (y+1)+10+ cell_border*2);
Packit bd2e5d
        Canvas.move canvas t3
Packit bd2e5d
          ~x: (block_size * (x+1)+10+ cell_border*2)
Packit bd2e5d
          ~y: (block_size * (y+1)+10+ cell_border*2)
Packit bd2e5d
      end
Packit bd2e5d
    end;
Packit bd2e5d
    color <- col
Packit bd2e5d
end
Packit bd2e5d
Packit bd2e5d
let cell_get (c, cf) x y = cf.(y).(x) #get
Packit bd2e5d
Packit bd2e5d
let cell_set (c, cf) ~x ~y ~color =
Packit bd2e5d
  if x >= 0 && y >= 0 && Array.length cf > y && Array.length cf.(y) > x then
Packit bd2e5d
    let cur = cf.(y).(x) in
Packit bd2e5d
    if cur#get = color then () else cur#set ~color
Packit bd2e5d
Packit bd2e5d
let create_base_matrix ~cols ~rows =
Packit bd2e5d
  let m = Array.create_matrix ~dimx:rows ~dimy:cols (0,0) in
Packit bd2e5d
  for x = 0 to cols - 1 do for y = 0 to rows - 1 do
Packit bd2e5d
    m.(y).(x) <- (x,y)
Packit bd2e5d
  done done;
Packit bd2e5d
  m
Packit bd2e5d
Packit bd2e5d
let init fw =
Packit bd2e5d
  let scorev = Textvariable.create ()
Packit bd2e5d
  and linev = Textvariable.create ()
Packit bd2e5d
  and levv = Textvariable.create ()
Packit bd2e5d
  in
Packit bd2e5d
  let f = Frame.create fw ~borderwidth: 2 in
Packit bd2e5d
  let c = Canvas.create f ~width: (block_size * 10)
Packit bd2e5d
                          ~height: (block_size * 20)
Packit bd2e5d
                          ~borderwidth: cell_border
Packit bd2e5d
                          ~relief: `Sunken
Packit bd2e5d
                          ~background: `Black
Packit bd2e5d
  and r = Frame.create f
Packit bd2e5d
  and r' = Frame.create f in
Packit bd2e5d
Packit bd2e5d
  let nl = Label.create r ~text: "Next"  ~font: "variable" in
Packit bd2e5d
  let nc = Canvas.create r ~width: (block_size * 4)
Packit bd2e5d
                           ~height: (block_size * 4)
Packit bd2e5d
                           ~borderwidth: cell_border
Packit bd2e5d
                           ~relief: `Sunken
Packit bd2e5d
                           ~background: `Black in
Packit bd2e5d
  let scl = Label.create r ~text: "Score" ~font: "variable" in
Packit bd2e5d
  let sc = Label.create r ~textvariable: scorev ~font: "variable" in
Packit bd2e5d
  let lnl = Label.create r ~text: "Lines" ~font: "variable" in
Packit bd2e5d
  let ln = Label.create r ~textvariable: linev ~font: "variable" in
Packit bd2e5d
  let levl = Label.create r ~text: "Level" ~font: "variable" in
Packit bd2e5d
  let lev = Label.create r ~textvariable: levv ~font: "variable" in
Packit bd2e5d
  let newg = Button.create r ~text: "New Game" ~font: "variable" in
Packit bd2e5d
Packit bd2e5d
  pack [f];
Packit bd2e5d
  pack [coe c; coe r; coe r'] ~side: `Left ~fill: `Y;
Packit bd2e5d
  pack [coe nl; coe nc] ~side: `Top;
Packit bd2e5d
  pack [coe scl; coe sc; coe lnl; coe ln; coe levl; coe lev; coe newg]
Packit bd2e5d
    ~side: `Top;
Packit bd2e5d
Packit bd2e5d
  let cells_src = create_base_matrix ~cols:field_width ~rows:field_height in
Packit bd2e5d
  let cells =
Packit bd2e5d
    Array.map cells_src ~f:
Packit bd2e5d
      (Array.map ~f:
Packit bd2e5d
         begin fun (x,y) ->
Packit bd2e5d
           let t1 =
Packit bd2e5d
             Canvas.create_rectangle c
Packit bd2e5d
               ~x1:(-block_size - 8) ~y1:(-block_size - 8)
Packit bd2e5d
               ~x2:(-9)              ~y2:(-9)
Packit bd2e5d
           and t2 =
Packit bd2e5d
             Canvas.create_rectangle c
Packit bd2e5d
               ~x1:(-block_size - 10) ~y1:(-block_size - 10)
Packit bd2e5d
               ~x2:(-11)              ~y2:(-11)
Packit bd2e5d
           and t3 =
Packit bd2e5d
             Canvas.create_rectangle c
Packit bd2e5d
               ~x1:(-block_size - 12) ~y1:(-block_size - 12)
Packit bd2e5d
               ~x2:(-13)              ~y2:(-13)
Packit bd2e5d
           in
Packit bd2e5d
           Canvas.raise c t1;
Packit bd2e5d
           Canvas.raise c t2;
Packit bd2e5d
           Canvas.lower c t3;
Packit bd2e5d
           new cell ~canvas:c ~x ~y t1 t2 t3
Packit bd2e5d
         end)
Packit bd2e5d
  in
Packit bd2e5d
  let nexts_src = create_base_matrix ~cols:4 ~rows:4 in
Packit bd2e5d
  let nexts =
Packit bd2e5d
    Array.map nexts_src ~f:
Packit bd2e5d
      (Array.map ~f:
Packit bd2e5d
         begin fun (x,y) ->
Packit bd2e5d
           let t1 =
Packit bd2e5d
             Canvas.create_rectangle nc
Packit bd2e5d
               ~x1:(-block_size - 8) ~y1:(-block_size - 8)
Packit bd2e5d
               ~x2:(-9)              ~y2:(-9)
Packit bd2e5d
           and t2 =
Packit bd2e5d
             Canvas.create_rectangle nc
Packit bd2e5d
               ~x1:(-block_size - 10) ~y1:(-block_size - 10)
Packit bd2e5d
               ~x2:(-11)              ~y2:(-11)
Packit bd2e5d
           and t3 =
Packit bd2e5d
             Canvas.create_rectangle nc
Packit bd2e5d
               ~x1:(-block_size - 12) ~y1:(-block_size - 12)
Packit bd2e5d
               ~x2:(-13)              ~y2:(-13)
Packit bd2e5d
           in
Packit bd2e5d
           Canvas.raise nc t1;
Packit bd2e5d
           Canvas.raise nc t2;
Packit bd2e5d
           Canvas.lower nc t3;
Packit bd2e5d
           new cell ~canvas:nc ~x ~y t1 t2 t3
Packit bd2e5d
         end)
Packit bd2e5d
  in
Packit bd2e5d
  let game_over () = ()
Packit bd2e5d
  in
Packit bd2e5d
    (* What a mess ! *)
Packit bd2e5d
  [ coe f; coe c; coe r; coe nl; coe nc; coe scl; coe sc; coe levl; coe lev;
Packit bd2e5d
    coe lnl; coe ln ],
Packit bd2e5d
  newg, (c, cells), (nc, nexts), scorev, linev, levv, game_over
Packit bd2e5d
Packit bd2e5d
Packit bd2e5d
let draw_block field ~color ~block ~x ~y =
Packit bd2e5d
  for iy = 0 to 3 do
Packit bd2e5d
    let base = ref 1 in
Packit bd2e5d
    let xd = block.(iy) in
Packit bd2e5d
    for ix = 0 to 3 do
Packit bd2e5d
      if xd land !base <> 0 then
Packit bd2e5d
        cell_set field ~x:(ix + x) ~y:(iy + y) ~color;
Packit bd2e5d
      base := !base lsl 1
Packit bd2e5d
    done
Packit bd2e5d
  done
Packit bd2e5d
Packit bd2e5d
let timer_ref = (ref None : Timer.t option ref)
Packit bd2e5d
(* I know, this should be timer ref, but I'm not sure what should be
Packit bd2e5d
   the initial value ... *)
Packit bd2e5d
Packit bd2e5d
let remove_timer () =
Packit bd2e5d
  match !timer_ref with
Packit bd2e5d
    None -> ()
Packit bd2e5d
  | Some t -> Timer.remove t (* ; prerr_endline "removed!" *)
Packit bd2e5d
Packit bd2e5d
let do_after ~ms ~callback =
Packit bd2e5d
  timer_ref := Some (Timer.add ~ms ~callback)
Packit bd2e5d
Packit bd2e5d
let copy_block c =
Packit bd2e5d
  { pattern= !c.pattern;
Packit bd2e5d
    bcolor= !c.bcolor;
Packit bd2e5d
    x= !c.x;
Packit bd2e5d
    y= !c.y;
Packit bd2e5d
    d= !c.d;
Packit bd2e5d
    alive= !c.alive }
Packit bd2e5d
Packit bd2e5d
let _ =
Packit bd2e5d
  let top = openTk () in
Packit bd2e5d
  let lb = Label.create top
Packit bd2e5d
  and fw = Frame.create top
Packit bd2e5d
  in
Packit bd2e5d
  let set_message s = Label.configure lb ~text:s in
Packit bd2e5d
  pack [coe lb; coe fw] ~side: `Top;
Packit bd2e5d
  let score = ref 0 in
Packit bd2e5d
  let line = ref 0 in
Packit bd2e5d
  let level = ref 0 in
Packit bd2e5d
  let time = ref 1000 in
Packit bd2e5d
  let blocks = List.map ~f:(List.map ~f:decode_block) blocks in
Packit bd2e5d
  let field = Array.create 26 0 in
Packit bd2e5d
  let widgets, button, cell_field, next_field, scorev, linev, levv, game_over
Packit bd2e5d
      = init fw in
Packit bd2e5d
  let canvas = fst cell_field in
Packit bd2e5d
Packit bd2e5d
  let init_field () =
Packit bd2e5d
    for i = 0 to 25 do
Packit bd2e5d
      field.(i) <- line_empty
Packit bd2e5d
    done;
Packit bd2e5d
    field.(23) <- line_full;
Packit bd2e5d
    for i = 0 to 19 do
Packit bd2e5d
      for j = 0 to 9 do
Packit bd2e5d
        cell_set cell_field ~x:j ~y:i ~color:0
Packit bd2e5d
      done
Packit bd2e5d
    done;
Packit bd2e5d
    for i = 0 to 3 do
Packit bd2e5d
      for j = 0 to 3 do
Packit bd2e5d
        cell_set next_field ~x:j ~y:i ~color:0
Packit bd2e5d
      done
Packit bd2e5d
    done
Packit bd2e5d
  in
Packit bd2e5d
Packit bd2e5d
  let draw_falling_block fb =
Packit bd2e5d
    draw_block cell_field ~color: fb.bcolor
Packit bd2e5d
      ~block: (List.nth fb.pattern fb.d)
Packit bd2e5d
      ~x:     (fb.x - 3)
Packit bd2e5d
      ~y:     (fb.y - 3)
Packit bd2e5d
Packit bd2e5d
  and erase_falling_block fb =
Packit bd2e5d
    draw_block cell_field ~color: 0
Packit bd2e5d
      ~block: (List.nth fb.pattern fb.d)
Packit bd2e5d
      ~x:     (fb.x - 3)
Packit bd2e5d
      ~y:     (fb.y - 3)
Packit bd2e5d
  in
Packit bd2e5d
Packit bd2e5d
  let stone fb =
Packit bd2e5d
    for i=0 to 3 do
Packit bd2e5d
      let cur = field.(i + fb.y) in
Packit bd2e5d
      field.(i + fb.y) <-
Packit bd2e5d
         cur lor ((List.nth fb.pattern fb.d).(i) lsl fb.x)
Packit bd2e5d
    done;
Packit bd2e5d
    for i=0 to 2 do
Packit bd2e5d
      field.(i) <- line_empty
Packit bd2e5d
    done
Packit bd2e5d
Packit bd2e5d
  and clear fb =
Packit bd2e5d
    let l = ref 0 in
Packit bd2e5d
    for i = 0 to 3 do
Packit bd2e5d
      if i + fb.y >= 3 && i + fb.y <= 22 then
Packit bd2e5d
        if field.(i + fb.y) = line_full then
Packit bd2e5d
          begin
Packit bd2e5d
            incr l;
Packit bd2e5d
            field.(i + fb.y) <- line_empty;
Packit bd2e5d
            for j = 0 to 9 do
Packit bd2e5d
              cell_set cell_field ~x:j ~y:(i + fb.y - 3) ~color:0
Packit bd2e5d
            done
Packit bd2e5d
          end
Packit bd2e5d
    done;
Packit bd2e5d
    !l
Packit bd2e5d
Packit bd2e5d
  and fall_lines () =
Packit bd2e5d
    let eye = ref 22 (* bottom *)
Packit bd2e5d
    and cur = ref 22 (* bottom *)
Packit bd2e5d
    in
Packit bd2e5d
    try
Packit bd2e5d
      while !eye >= 3 do
Packit bd2e5d
        while field.(!eye) = line_empty do
Packit bd2e5d
          decr eye;
Packit bd2e5d
          if !eye = 2 then raise Done
Packit bd2e5d
        done;
Packit bd2e5d
        field.(!cur) <- field.(!eye);
Packit bd2e5d
        for j = 0 to 9 do
Packit bd2e5d
          cell_set cell_field ~x:j ~y:(!cur-3)
Packit bd2e5d
            ~color:(cell_get cell_field j (!eye-3))
Packit bd2e5d
        done;
Packit bd2e5d
        decr eye;
Packit bd2e5d
        decr cur
Packit bd2e5d
      done
Packit bd2e5d
    with Done -> ();
Packit bd2e5d
      for i = 3 to !cur do
Packit bd2e5d
        field.(i) <- line_empty;
Packit bd2e5d
        for j = 0 to 9 do
Packit bd2e5d
          cell_set cell_field ~x:j ~y:(i-3) ~color:0
Packit bd2e5d
        done
Packit bd2e5d
      done
Packit bd2e5d
  in
Packit bd2e5d
Packit bd2e5d
  let next = ref 42 (* THE ANSWER *)
Packit bd2e5d
  and current =
Packit bd2e5d
    ref { pattern= [[|0;0;0;0|]]; bcolor=0; x=0; y=0; d=0; alive= false}
Packit bd2e5d
  in
Packit bd2e5d
Packit bd2e5d
  let draw_next () =
Packit bd2e5d
    draw_block next_field ~color: (!next+1)
Packit bd2e5d
      ~block: (List.hd (List.nth blocks !next))
Packit bd2e5d
      ~x: 0 ~y: 0
Packit bd2e5d
Packit bd2e5d
  and erase_next () =
Packit bd2e5d
    draw_block next_field ~color: 0
Packit bd2e5d
      ~block: (List.hd (List.nth blocks !next))
Packit bd2e5d
      ~x: 0 ~y: 0
Packit bd2e5d
  in
Packit bd2e5d
Packit bd2e5d
  let set_nextblock () =
Packit bd2e5d
    current :=
Packit bd2e5d
       { pattern= (List.nth blocks !next);
Packit bd2e5d
         bcolor= !next+1;
Packit bd2e5d
         x=6; y= 1; d= 0; alive= true};
Packit bd2e5d
    erase_next ();
Packit bd2e5d
    next := Random.int 7;
Packit bd2e5d
    draw_next ()
Packit bd2e5d
  in
Packit bd2e5d
Packit bd2e5d
  let death_check fb =
Packit bd2e5d
    try
Packit bd2e5d
      for i=0 to 3 do
Packit bd2e5d
        let cur = field.(i + fb.y) in
Packit bd2e5d
        if cur land ((List.nth fb.pattern fb.d).(i) lsl fb.x) <> 0
Packit bd2e5d
        then raise Done
Packit bd2e5d
      done;
Packit bd2e5d
      false
Packit bd2e5d
    with
Packit bd2e5d
      Done -> true
Packit bd2e5d
  in
Packit bd2e5d
Packit bd2e5d
  let try_to_move m =
Packit bd2e5d
    if !current.alive then
Packit bd2e5d
      let sub m =
Packit bd2e5d
        if death_check m then false
Packit bd2e5d
        else
Packit bd2e5d
          begin
Packit bd2e5d
            erase_falling_block !current;
Packit bd2e5d
            draw_falling_block m;
Packit bd2e5d
            current := m;
Packit bd2e5d
            true
Packit bd2e5d
          end
Packit bd2e5d
      in
Packit bd2e5d
      if sub m then true
Packit bd2e5d
      else
Packit bd2e5d
        begin
Packit bd2e5d
          m.x <- m.x + 1;
Packit bd2e5d
          if sub m then true
Packit bd2e5d
          else
Packit bd2e5d
            begin
Packit bd2e5d
              m.x <- m.x - 2;
Packit bd2e5d
              sub m
Packit bd2e5d
            end
Packit bd2e5d
        end
Packit bd2e5d
    else false
Packit bd2e5d
  in
Packit bd2e5d
Packit bd2e5d
  let image_load =
Packit bd2e5d
    let i = Canvas.create_image canvas
Packit bd2e5d
        ~x: (block_size * 5 + block_size / 2)
Packit bd2e5d
        ~y: (block_size * 10 + block_size / 2)
Packit bd2e5d
        ~anchor: `Center in
Packit bd2e5d
    Canvas.lower canvas i;
Packit bd2e5d
    let img = Imagephoto.create () in
Packit bd2e5d
    fun file ->
Packit bd2e5d
      try
Packit bd2e5d
        Imagephoto.configure img ~file: file;
Packit bd2e5d
        Canvas.configure_image canvas i ~image: img
Packit bd2e5d
      with
Packit bd2e5d
        _ ->
Packit bd2e5d
          begin
Packit bd2e5d
            Printf.eprintf "%s : No such image...\n" file;
Packit bd2e5d
            flush stderr
Packit bd2e5d
          end
Packit bd2e5d
  in
Packit bd2e5d
Packit bd2e5d
  let add_score l =
Packit bd2e5d
    let pline = !line in
Packit bd2e5d
    if l <> 0 then
Packit bd2e5d
      begin
Packit bd2e5d
        line := !line + l;
Packit bd2e5d
        score := !score + l * l;
Packit bd2e5d
        set_message (Printf.sprintf "%d pts" (1 lsl ((l - 1) * 2)))
Packit bd2e5d
      end;
Packit bd2e5d
    Textvariable.set linev (string_of_int !line);
Packit bd2e5d
    Textvariable.set scorev (string_of_int !score);
Packit bd2e5d
Packit bd2e5d
    if !line /10 <> pline /10 then
Packit bd2e5d
      (* update the background every 10 lines. *)
Packit bd2e5d
      begin
Packit bd2e5d
        let num_image = List.length backgrounds - 1 in
Packit bd2e5d
        let n = !line/10 in
Packit bd2e5d
        let n = if n > num_image then num_image else n in
Packit bd2e5d
        let file = List.nth backgrounds n in
Packit bd2e5d
        image_load file;
Packit bd2e5d
        incr level;
Packit bd2e5d
        Textvariable.set levv (string_of_int !level)
Packit bd2e5d
      end
Packit bd2e5d
  in
Packit bd2e5d
Packit bd2e5d
  let rec newblock () =
Packit bd2e5d
    set_message "TETRIS";
Packit bd2e5d
    set_nextblock ();
Packit bd2e5d
    draw_falling_block !current;
Packit bd2e5d
    if death_check !current then
Packit bd2e5d
      begin
Packit bd2e5d
        !current.alive <- false;
Packit bd2e5d
        set_message "GAME OVER";
Packit bd2e5d
        game_over ()
Packit bd2e5d
      end
Packit bd2e5d
    else
Packit bd2e5d
      begin
Packit bd2e5d
        time := 1100 - (!level / 4 * 300) - ((!level mod 4) * 200);
Packit bd2e5d
        if !time < 60 - !level * 3 then time := 60 - !level * 3;
Packit bd2e5d
        do_after ~ms:stop_a_bit ~callback:loop
Packit bd2e5d
      end
Packit bd2e5d
Packit bd2e5d
  and loop () =
Packit bd2e5d
    let m = copy_block current in
Packit bd2e5d
    m.y <- m.y + 1;
Packit bd2e5d
    if death_check m then
Packit bd2e5d
      begin
Packit bd2e5d
        !current.alive <- false;
Packit bd2e5d
        stone !current;
Packit bd2e5d
        do_after ~ms:stop_a_bit ~callback:
Packit bd2e5d
          begin fun () ->
Packit bd2e5d
            let l = clear !current in
Packit bd2e5d
            if l > 0 then
Packit bd2e5d
              do_after ~ms:stop_a_bit ~callback:
Packit bd2e5d
                begin fun () ->
Packit bd2e5d
                  fall_lines ();
Packit bd2e5d
                  add_score l;
Packit bd2e5d
                  do_after ~ms:stop_a_bit ~callback:newblock
Packit bd2e5d
                end
Packit bd2e5d
            else
Packit bd2e5d
              newblock ()
Packit bd2e5d
          end
Packit bd2e5d
      end
Packit bd2e5d
    else
Packit bd2e5d
      begin
Packit bd2e5d
        erase_falling_block !current;
Packit bd2e5d
        draw_falling_block m;
Packit bd2e5d
        current := m;
Packit bd2e5d
        do_after ~ms:!time ~callback:loop
Packit bd2e5d
      end
Packit bd2e5d
  in
Packit bd2e5d
Packit bd2e5d
  let bind_game w =
Packit bd2e5d
    bind w ~events:[`KeyPress] ~fields:[`KeySymString] ~action:
Packit bd2e5d
      begin fun e ->
Packit bd2e5d
        match e.ev_KeySymString with
Packit bd2e5d
        | "h"|"Left" ->
Packit bd2e5d
            let m = copy_block current in
Packit bd2e5d
            m.x <- m.x - 1;
Packit bd2e5d
            ignore (try_to_move m)
Packit bd2e5d
        | "j"|"Up" ->
Packit bd2e5d
            let m = copy_block current in
Packit bd2e5d
            m.d <- m.d + 1;
Packit bd2e5d
            if m.d = List.length m.pattern then m.d <- 0;
Packit bd2e5d
            ignore (try_to_move m)
Packit bd2e5d
        | "k"|"Down" ->
Packit bd2e5d
            let m = copy_block current in
Packit bd2e5d
            m.d <- m.d - 1;
Packit bd2e5d
            if m.d < 0 then m.d <- List.length m.pattern - 1;
Packit bd2e5d
            ignore (try_to_move m)
Packit bd2e5d
        | "l"|"Right" ->
Packit bd2e5d
            let m = copy_block current in
Packit bd2e5d
            m.x <- m.x + 1;
Packit bd2e5d
            ignore (try_to_move m)
Packit bd2e5d
        | "m" ->
Packit bd2e5d
            remove_timer ();
Packit bd2e5d
            loop ()
Packit bd2e5d
        | "space" ->
Packit bd2e5d
            if !current.alive then
Packit bd2e5d
              begin
Packit bd2e5d
                let m = copy_block current
Packit bd2e5d
                and n = copy_block current in
Packit bd2e5d
                while
Packit bd2e5d
                  m.y <- m.y + 1;
Packit bd2e5d
                  if death_check m then false
Packit bd2e5d
                  else begin n.y <- m.y; true end
Packit bd2e5d
                do () done;
Packit bd2e5d
                erase_falling_block !current;
Packit bd2e5d
                draw_falling_block n;
Packit bd2e5d
                current := n;
Packit bd2e5d
                remove_timer ();
Packit bd2e5d
                loop ()
Packit bd2e5d
              end
Packit bd2e5d
        | _ -> ()
Packit bd2e5d
      end
Packit bd2e5d
  in
Packit bd2e5d
Packit bd2e5d
  let game_init () =
Packit bd2e5d
    (* Game Initialization *)
Packit bd2e5d
    set_message "Initializing ...";
Packit bd2e5d
    remove_timer ();
Packit bd2e5d
    image_load (List.hd backgrounds);
Packit bd2e5d
    time := 1000;
Packit bd2e5d
    score := 0;
Packit bd2e5d
    line := 0;
Packit bd2e5d
    level := 1;
Packit bd2e5d
    add_score 0;
Packit bd2e5d
    init_field ();
Packit bd2e5d
    next := Random.int 7;
Packit bd2e5d
    set_message "Welcome to TETRIS";
Packit bd2e5d
    set_nextblock ();
Packit bd2e5d
    draw_falling_block !current;
Packit bd2e5d
    do_after ~ms:!time ~callback:loop
Packit bd2e5d
  in
Packit bd2e5d
    (* As an applet, it was required... *)
Packit bd2e5d
    (* List.iter f: bind_game widgets; *)
Packit bd2e5d
  bind_game top;
Packit bd2e5d
  Button.configure button ~command: game_init;
Packit bd2e5d
  game_init ()
Packit bd2e5d
Packit bd2e5d
let _ = Printexc.print mainLoop ()