I am currently trying to build a game of life with OCaml but I encountered a problem when trying to apply the rules on the first generation, my board is always returned empty when I apply next_generation
and I can't find a way to apply the first generation by going through my board and then return the modified board.
When I apply next_generation
I expect my function to go through the lists that compose my board and to apply rules0
to each cell then return my board with the first generation applied but I always get an empty board at the end.
here is my code:
#use "topfind";;
#require "graphics";;
open Graphics ;;
open Random ;;
let put_list v I list =
if i < 0 then
invalid_arg "index must be a positive"
else
let rec put_list_rec v i list =
match list with
[] -> []
| e::t ->
if i = 0 then v::t
else e::put_list_rec v (i-1) t
in
put_list_rec v i list;;
let rec put_cell v (x,y) board =
match board with
[] -> board
| e::l ->
if x = 0 then (put_list v y e)::l
else e::put_cell v ((x-1),y) l;;
let board =
[[1;1;1;1;1;1;1;1;1;1];
[0;0;0;0;0;0;0;0;0;0];
[1;0;1;0;1;0;1;0;1;0];
[0;1;0;1;0;1;0;1;0;1];
[0;0;0;0;0;0;0;0;0;0];
[1;1;1;1;1;1;1;1;1;1];
[0;0;0;0;0;0;0;0;0;0];
[1;0;1;0;1;0;1;0;1;0];
[0;1;0;1;0;1;0;1;0;1];
[0;0;0;0;0;0;0;0;0;0]];;
let cell_color = function
| 0 -> white
| _ -> black;;
let grey = rgb 127 127 127;;
let draw_cell (x,y) size color =
if size <= 0 then
invalid_arg " size must be positive"
else
let color = set_color color in
let draw_cell_rec (x,y) size color =
fill_rect x y size size;
set_color grey;
draw_rect x y size size
in
draw_cell_rec (x,y) size color;;
let draw_board board cellsize =
if cellsize <= 0 then
invalid_arg " cellsize must be positive"
else
let rec draw_board_rec (x,y) board cellsize =
match board with
[] -> ()
| (e::l)::t when l <> [] ->
draw_cell (x,y) cellsize (cell_color e);
draw_board_rec (x,y+cellsize) (l::t) cellsize
| (e::l)::t when l = [] -> draw_board_rec (x+cellsize, 0) t cellsize
in
draw_board_rec (0,0) board cellsize;;
let rules0 cell near =
if cell < 0 || cell > 1 then
invalid_arg "cell must be 0 or 1"
else if near < 0 || near > 8 then
invalid_arg "near must be between 0 and 8"
else
match cell with
0 -> if near = 3 then 1 else 0
| _ -> if (near = 3) || (near = 2) then 1 else 0;;
let count_neighbours (x,y) board size =
if board = [] then
invalid_arg "board must not be empty"
else
let rec count_neighbours_rec (x,y) board =
match (x,y) with
(x,y) when y < 0 || y >= size || x < 0 || x >= size -> 0
| (_,_) -> get_cell(x,y) board
in
count_neighbours_rec (x-1,y-1) board +
count_neighbours_rec (x-1,y) board +
count_neighbours_rec (x-1,y+1) board +
count_neighbours_rec (x,y+1) board +
count_neighbours_rec (x,y-1) board +
count_neighbours_rec (x+1,y+1) board +
count_neighbours_rec (x+1,y) board +
count_neighbours_rec (x+1,y-1) board;;
let next_generation board size =
if size < 0 then
invalid_arg "size must be positive"
else
let bis = board in
let rec next_generation_rec bis (x,y) =
match bis with
[] -> bis
| []::t -> next_generation_rec t (x+1,0)
| (e::l)::t ->
put_cell (rules0 e (count_neighbours (x,y) board size)) (x,y) bis;
next_generation_rec (l::t) (x,y+1)
in
next_generation_rec bis (0,0);;
here is an exemple of what I get:
next_generation board 10;;
;;
- : int list list = []
I suspect your problem arises from thinking board
is mutable and that the put_cell
function will modify it.
But lists in OCaml are not mutable. You can work on a list to generate a new list and then pass that along to another function.
Ignoring the return value of put_cell
as you have in next_generation
is a sure sign that something is amiss, and OCaml should warn you about it. See the below very simple example where the value of the expression 1 + 2
is never used.
utop # let foo () = 1 + 2; print_endline "foo";;
Line 1, characters 13-18:
Warning 10 [non-unit-statement]: this expression should have type unit.
val foo : unit -> unit = <fun>
You either need to rework your code to account for this immutability, or switch to using a mutable data structure, like an array.
let board =
[| [| 1;1;1;1;1;1;1;1;1;1 |];
[| 0;0;0;0;0;0;0;0;0;0 |];
[| 1;0;1;0;1;0;1;0;1;0 |];
[| 0;1;0;1;0;1;0;1;0;1 |];
[| 0;0;0;0;0;0;0;0;0;0 |];
[| 1;1;1;1;1;1;1;1;1;1 |];
[| 0;0;0;0;0;0;0;0;0;0 |];
[| 1;0;1;0;1;0;1;0;1;0 |];
[| 0;1;0;1;0;1;0;1;0;1 |];
[| 0;0;0;0;0;0;0;0;0;0 |] |]
Where you could modify position (1, 2) in board
with:
board.(1).(2) <- 1