|
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 ()
|