open Graphics

let col = 20
let lig = 10
let pas = 30

let () =
  open_graph (Printf.sprintf " %dx%d" (pas * col) (pas * lig));
  set_color black;
  for i = 0 to col - 1 do
    let x = i * pas in
    moveto x 0; lineto x (pas * lig);
  done;
  for j = 0 to lig - 1 do
    let y = j * pas in
    moveto 0 y; lineto (pas * col) y
  done

let () = at_exit close_graph

type cell = 
  | Cache of bool * bool (* bombe / drapeau *)
  | Decouvert

let cells = Array.create_matrix col lig (Cache (false,false))

let cell i j = cells.(i).(j)

let nbb = 20

let bombe i j = match cell i j with
  | Cache (b,_) -> b
  | Decouvert -> false

let () = 
  Random.self_init ();
  let k = ref 0 in
  while !k < nbb do
    let i = Random.int col in
    let j = Random.int lig in
    if not (bombe i j) then begin
      cells.(i).(j) <- Cache (true, false);
      incr k
    end
  done

let colorie c i j =
  set_color c;
  let x = i * pas + 2 in
  let y = j * pas + 2 in
  fill_rect x y (pas - 4) (pas - 4)
    
let gris = rgb 200 200 200

let () =
  for i = 0 to col - 1 do
    for j = 0 to lig - 1 do
      colorie gris i j
    done
  done

let nb_drapeaux = ref 0
let nb_decouvert = ref 0

exception Perdu
exception Gagne

let autour i j f =
  let f i' j' = if i' >= 0 && i' < col && j' >= 0 && j' < lig then f i' j' in
  f (i-1) j;
  f (i-1) (j+1);
  f i (j+1);
  f (i+1) (j+1);
  f (i+1) j;
  f (i+1) (j-1);
  f i (j-1);
  f (i-1) (j-1)

let rec decouvre i j = 
  incr nb_decouvert;
  colorie white i j;
  let n = ref 0 in
  autour i j (fun i' j' -> if bombe i' j' then incr n);
  cells.(i).(j) <- Decouvert;
  if !n > 0 then begin 
    set_color black;
    moveto (i * pas + pas/3) (j * pas + pas/3);
    draw_char (Char.chr (Char.code '0' + !n))
  end else 
    autour i j decouvre_si_cache

and decouvre_si_cache i j = match cell i j with
  | Cache _ -> decouvre i j 
  | Decouvert -> ()

let click (x,y) =
  ignore (wait_next_event [Button_up]);
  let i = x / pas in
  let j = y / pas in
  match cell i j with
    | Cache (true, _) -> raise Perdu
    | Cache (false, _) -> decouvre i j
    | Decouvert -> ()

let dessine_drapeau i j =
  let x = i * pas in
  let y = j * pas in
  set_color red;
  fill_poly [| x+2, y+2; x+pas-4,y+pas/2; x+2,y+pas-4 |]

let pose_drapeau (x,y) =
  let i = x / pas in
  let j = y / pas in
  match cell i j with
    | Cache (b, false) -> 
	dessine_drapeau i j; incr nb_drapeaux; cells.(i).(j) <- Cache (b, true)
    | Cache (b, true) -> 
	colorie gris i j; decr nb_drapeaux; cells.(i).(j) <- Cache (b, false)
    | Decouvert -> ()

let dessine_bombe i j =
  let x = i * pas in
  let y = j * pas in
  set_color blue;
  fill_circle (x+pas/2) (y+pas/2) (pas/2-5)

let revele () =
  for i = 0 to col - 1 do
    for j = 0 to lig - 1 do
      if bombe i j then dessine_bombe i j
    done
  done;
  ignore (wait_next_event [Key_pressed])

let () = 
  try
    while true do
      let st = wait_next_event [Button_down; Key_pressed] in
      if st.button then click (mouse_pos ());
      if st.keypressed then begin
	let c = st.key in
	if c = 'q' then raise Exit else pose_drapeau (mouse_pos ())
      end;
      if !nb_drapeaux = nbb && !nb_drapeaux + !nb_decouvert = col * lig then 
	raise Gagne
    done
  with 
    | Perdu -> revele (); print_endline "perdu"; exit 0
    | Gagne -> revele (); print_endline "gagné"; exit 0
    | Exit -> exit 0