Getting Started With Incr_dom - Part 3
Note: Incr dom is superceded by a new, much easier to use library called Bonsai
In this installment of the tutorial we’ll get the basics of the Tetris game working. We’ll create a 10x24 space board and use the standard pieces. There will be a preview box, score, and line clear animation.
Adding A Board
So we don’t clutter the main tetris.ml
, let’s create a new file lib/board.ml
to store the board model. This will encapsulate both the shapes and the board operations. To start create a type for the cells:
module Cell = struct
type t = S | Z | T | L | I | O | J | Empty
[@@deriving sexp, compare]
end
Fairly straight forward, we put it in a module to keep the helper functions namespaced. Now we need to create our board representation. We’ll use an immutable map because OCaml arrays are mutable. We’d have to copy the array each time we made and update, but the map will automatically share as much as possible.
module IntPair = struct
module T = struct
type t = int * int
[@@deriving compare, sexp]
end
include T
include Comparable.Make(T)
end
type t = Cell.t IntPair.Map.t
[@@deriving sexp, compare]
We wrap up the int * int
type in a module so we can derive the Map
module without having to deal with all the type variables. Now that we have our board, we need an initial empty board. Let’s create a variable to store that:
let empty_board =
let board = ref IntPair.Map.empty in
for x=0 to 9 do
for y=0 to 23 do
board := IntPair.Map.set !board ~key:(x, y) ~data:Cell.Empty
done
done;
!board
;;
Pretty simple, take an empty map and fill it with an entry for every coordinate in our board mapping to the empty cell. Let’s now add this to the overall model and try to show it on the New Game
screen. In lib/tetris.ml
a couple updates are needed. First the Model.t
type needs to have a board
field. The Model.init
function needs to set the board field to Board.empty_board
. Lastly, when we ChangePage
to the NewGame
page we need to reset the board state.
module Model = struct
(* ... *)
type t = {
current_page : page;
board : Board.t;
}
[@@deriving sexp, compare]
let init = {
current_page = MainMenu;
board = Board.empty_board;
}
(* ... *)
end
(* ... *)
let apply_action (model : Model.t) (* ... *) =
match action with
| ChangePage NewGame ->
Model.{ board = Board.empty_board; current_page = NewGame }
| ChangePage new_page ->
Model.{ model with current_page = new_page }
;;
But, the board still needs to be drawn on screen. To do that we need to update the new_game_page
function to take a board and render the appropriate HTML.
Under construction…