1 (***********************************************************************)
3 (* MLTk, Tcl/Tk interface of Objective Caml *)
5 (* Francois Rouaix, Francois Pessaux, Jun Furuse and Pierre Weis *)
6 (* projet Cristal, INRIA Rocquencourt *)
7 (* Jacques Garrigue, Kyoto University RIMS *)
9 (* Copyright 2002 Institut National de Recherche en Informatique et *)
10 (* en Automatique and Kyoto University. All rights reserved. *)
11 (* This file is distributed under the terms of the GNU Library *)
12 (* General Public License, with the special exception on linking *)
13 (* described in file LICENSE found in the Objective Caml source tree. *)
15 (***********************************************************************)
17 (* $Id: tetris.ml 6385 2004-06-12 03:20:00Z garrigue $ *)
19 (* A Tetris game for LablTk *)
20 (* written by Jun P. Furuse *)
27 type falling_block = {
28 mutable pattern: int array list;
54 (* Put here your favorite image files *)
206 let line_empty = int_of_string "0b1110000000000111"
207 let line_full = int_of_string "0b1111111111111111"
209 let decode_block dvec =
210 let btoi d = int_of_string ("0b"^d) in
211 Array.map ~f:btoi dvec
213 class cell t1 t2 t3 ~canvas ~x ~y = object
214 val mutable color = 0
216 method set ~color:col =
217 if color = col then () else
218 if color <> 0 && col = 0 then begin
219 Canvas.move canvas t1
220 ~x:(- block_size * (x + 1) -10 - cell_border * 2)
221 ~y:(- block_size * (y + 1) -10 - cell_border * 2);
222 Canvas.move canvas t2
223 ~x:(- block_size * (x + 1) -10 - cell_border * 2)
224 ~y:(- block_size * (y + 1) -10 - cell_border * 2);
225 Canvas.move canvas t3
226 ~x:(- block_size * (x + 1) -10 - cell_border * 2)
227 ~y:(- block_size * (y + 1) -10 - cell_border * 2)
229 Canvas.configure_rectangle canvas t2
230 ~fill: colors.(col - 1)
231 ~outline: colors.(col - 1);
232 Canvas.configure_rectangle canvas t1
235 Canvas.configure_rectangle canvas t3
236 ~fill: (`Color "light gray")
237 ~outline: (`Color "light gray");
238 if color = 0 && col <> 0 then begin
239 Canvas.move canvas t1
240 ~x: (block_size * (x+1)+10+ cell_border*2)
241 ~y: (block_size * (y+1)+10+ cell_border*2);
242 Canvas.move canvas t2
243 ~x: (block_size * (x+1)+10+ cell_border*2)
244 ~y: (block_size * (y+1)+10+ cell_border*2);
245 Canvas.move canvas t3
246 ~x: (block_size * (x+1)+10+ cell_border*2)
247 ~y: (block_size * (y+1)+10+ cell_border*2)
253 let cell_get (c, cf) x y = cf.(y).(x) #get
255 let cell_set (c, cf) ~x ~y ~color =
256 if x >= 0 && y >= 0 && Array.length cf > y && Array.length cf.(y) > x then
257 let cur = cf.(y).(x) in
258 if cur#get = color then () else cur#set ~color
260 let create_base_matrix ~cols ~rows =
261 let m = Array.create_matrix ~dimx:rows ~dimy:cols (0,0) in
262 for x = 0 to cols - 1 do for y = 0 to rows - 1 do
268 let scorev = Textvariable.create ()
269 and linev = Textvariable.create ()
270 and levv = Textvariable.create ()
271 and namev = Textvariable.create ()
273 let f = Frame.create fw ~borderwidth: 2 in
274 let c = Canvas.create f ~width: (block_size * 10)
275 ~height: (block_size * 20)
276 ~borderwidth: cell_border
279 and r = Frame.create f
280 and r' = Frame.create f in
282 let nl = Label.create r ~text: "Next" ~font: "variable" in
283 let nc = Canvas.create r ~width: (block_size * 4)
284 ~height: (block_size * 4)
285 ~borderwidth: cell_border
287 ~background: `Black in
288 let scl = Label.create r ~text: "Score" ~font: "variable" in
289 let sc = Label.create r ~textvariable: scorev ~font: "variable" in
290 let lnl = Label.create r ~text: "Lines" ~font: "variable" in
291 let ln = Label.create r ~textvariable: linev ~font: "variable" in
292 let levl = Label.create r ~text: "Level" ~font: "variable" in
293 let lev = Label.create r ~textvariable: levv ~font: "variable" in
294 let newg = Button.create r ~text: "New Game" ~font: "variable" in
297 pack [coe c; coe r; coe r'] ~side: `Left ~fill: `Y;
298 pack [coe nl; coe nc] ~side: `Top;
299 pack [coe scl; coe sc; coe lnl; coe ln; coe levl; coe lev; coe newg]
302 let cells_src = create_base_matrix ~cols:field_width ~rows:field_height in
304 Array.map cells_src ~f:
308 Canvas.create_rectangle c
309 ~x1:(-block_size - 8) ~y1:(-block_size - 8)
312 Canvas.create_rectangle c
313 ~x1:(-block_size - 10) ~y1:(-block_size - 10)
316 Canvas.create_rectangle c
317 ~x1:(-block_size - 12) ~y1:(-block_size - 12)
323 new cell ~canvas:c ~x ~y t1 t2 t3
326 let nexts_src = create_base_matrix ~cols:4 ~rows:4 in
328 Array.map nexts_src ~f:
332 Canvas.create_rectangle nc
333 ~x1:(-block_size - 8) ~y1:(-block_size - 8)
336 Canvas.create_rectangle nc
337 ~x1:(-block_size - 10) ~y1:(-block_size - 10)
340 Canvas.create_rectangle nc
341 ~x1:(-block_size - 12) ~y1:(-block_size - 12)
347 new cell ~canvas:nc ~x ~y t1 t2 t3
350 let game_over () = ()
353 [ coe f; coe c; coe r; coe nl; coe nc; coe scl; coe sc; coe levl; coe lev;
355 newg, (c, cells), (nc, nexts), scorev, linev, levv, game_over
358 let draw_block field ~color ~block ~x ~y =
361 let xd = block.(iy) in
363 if xd land !base <> 0 then
364 cell_set field ~x:(ix + x) ~y:(iy + y) ~color;
369 let timer_ref = (ref None : Timer.t option ref)
370 (* I know, this should be timer ref, but I'm not sure what should be
371 the initial value ... *)
373 let remove_timer () =
374 match !timer_ref with
376 | Some t -> Timer.remove t (* ; prerr_endline "removed!" *)
378 let do_after ~ms ~callback =
379 timer_ref := Some (Timer.add ~ms ~callback)
382 { pattern= !c.pattern;
390 let top = openTk () in
391 let lb = Label.create top
392 and fw = Frame.create top
394 let set_message s = Label.configure lb ~text:s in
395 pack [coe lb; coe fw] ~side: `Top;
399 let time = ref 1000 in
400 let blocks = List.map ~f:(List.map ~f:decode_block) blocks in
401 let field = Array.create 26 0 in
402 let widgets, button, cell_field, next_field, scorev, linev, levv, game_over
404 let canvas = fst cell_field in
408 field.(i) <- line_empty
410 field.(23) <- line_full;
413 cell_set cell_field ~x:j ~y:i ~color:0
418 cell_set next_field ~x:j ~y:i ~color:0
423 let draw_falling_block fb =
424 draw_block cell_field ~color: fb.bcolor
425 ~block: (List.nth fb.pattern fb.d)
429 and erase_falling_block fb =
430 draw_block cell_field ~color: 0
431 ~block: (List.nth fb.pattern fb.d)
438 let cur = field.(i + fb.y) in
440 cur lor ((List.nth fb.pattern fb.d).(i) lsl fb.x)
443 field.(i) <- line_empty
449 if i + fb.y >= 3 && i + fb.y <= 22 then
450 if field.(i + fb.y) = line_full then
453 field.(i + fb.y) <- line_empty;
455 cell_set cell_field ~x:j ~y:(i + fb.y - 3) ~color:0
462 let eye = ref 22 (* bottom *)
463 and cur = ref 22 (* bottom *)
467 while field.(!eye) = line_empty do
469 if !eye = 2 then raise Done
471 field.(!cur) <- field.(!eye);
473 cell_set cell_field ~x:j ~y:(!cur-3)
474 ~color:(cell_get cell_field j (!eye-3))
481 field.(i) <- line_empty;
483 cell_set cell_field ~x:j ~y:(i-3) ~color:0
488 let next = ref 42 (* THE ANSWER *)
490 ref { pattern= [[|0;0;0;0|]]; bcolor=0; x=0; y=0; d=0; alive= false}
494 draw_block next_field ~color: (!next+1)
495 ~block: (List.hd (List.nth blocks !next))
499 draw_block next_field ~color: 0
500 ~block: (List.hd (List.nth blocks !next))
504 let set_nextblock () =
506 { pattern= (List.nth blocks !next);
508 x=6; y= 1; d= 0; alive= true};
510 next := Random.int 7;
517 let cur = field.(i + fb.y) in
518 if cur land ((List.nth fb.pattern fb.d).(i) lsl fb.x) <> 0
527 if !current.alive then
529 if death_check m then false
532 erase_falling_block !current;
533 draw_falling_block m;
553 let i = Canvas.create_image canvas
554 ~x: (block_size * 5 + block_size / 2)
555 ~y: (block_size * 10 + block_size / 2)
557 Canvas.lower canvas i;
558 let img = Imagephoto.create () in
561 Imagephoto.configure img ~file: file;
562 Canvas.configure_image canvas i ~image: img
566 Printf.eprintf "%s : No such image...\n" file;
576 score := !score + l * l;
577 set_message (Printf.sprintf "%d pts" (1 lsl ((l - 1) * 2)))
579 Textvariable.set linev (string_of_int !line);
580 Textvariable.set scorev (string_of_int !score);
582 if !line /10 <> pline /10 then
583 (* update the background every 10 lines. *)
585 let num_image = List.length backgrounds - 1 in
587 let n = if n > num_image then num_image else n in
588 let file = List.nth backgrounds n in
591 Textvariable.set levv (string_of_int !level)
595 let rec newblock () =
596 set_message "TETRIS";
598 draw_falling_block !current;
599 if death_check !current then
601 !current.alive <- false;
602 set_message "GAME OVER";
607 time := 1100 - (!level / 4 * 300) - ((!level mod 4) * 200);
608 if !time < 60 - !level * 3 then time := 60 - !level * 3;
609 do_after ~ms:stop_a_bit ~callback:loop
613 let m = copy_block current in
615 if death_check m then
617 !current.alive <- false;
619 do_after ~ms:stop_a_bit ~callback:
621 let l = clear !current in
623 do_after ~ms:stop_a_bit ~callback:
627 do_after ~ms:stop_a_bit ~callback:newblock
635 erase_falling_block !current;
636 draw_falling_block m;
638 do_after ~ms:!time ~callback:loop
643 bind w ~events:[`KeyPress] ~fields:[`KeySymString] ~action:
645 match e.ev_KeySymString with
647 let m = copy_block current in
649 ignore (try_to_move m)
651 let m = copy_block current in
653 if m.d = List.length m.pattern then m.d <- 0;
654 ignore (try_to_move m)
656 let m = copy_block current in
658 if m.d < 0 then m.d <- List.length m.pattern - 1;
659 ignore (try_to_move m)
661 let m = copy_block current in
663 ignore (try_to_move m)
668 if !current.alive then
670 let m = copy_block current
671 and n = copy_block current in
674 if death_check m then false
675 else begin n.y <- m.y; true end
677 erase_falling_block !current;
678 draw_falling_block n;
688 (* Game Initialization *)
689 set_message "Initializing ...";
691 image_load (List.hd backgrounds);
698 next := Random.int 7;
699 set_message "Welcome to TETRIS";
701 draw_falling_block !current;
702 do_after ~ms:!time ~callback:loop
704 (* As an applet, it was required... *)
705 (* List.iter f: bind_game widgets; *)
707 Button.configure button ~command: game_init;
710 let _ = Printexc.print mainLoop ()