(* Correction (version avancée) - TP07 : ricochet robots - partie 1 *)

(* description du plateau de jeu *)

let obs_l = [| [|0; 4; 10; 16|]; [|0; 14; 16|]; [|0; 6; 16|];
   [|0; 9; 16|]; [|0; 3; 15; 16|]; [|0; 7; 16|]; [|0; 1; 12; 16|]; [|0; 7; 9; 16|];
   [|0; 7; 9; 16|]; [|0; 4; 13; 16|]; [|0; 6; 16|]; [|0; 10; 16|]; [|0; 8; 16|];
   [|0; 2; 15; 16|]; [|0; 4; 10; 16|]; [|0; 5; 12; 16|] |];;

let obs_c = [| [|0; 5; 11; 16|]; [|0; 6; 13; 16|]; [|0; 4; 16|];
   [|0; 15; 16|]; [|0; 10; 16|]; [|0; 3; 16|]; [|0; 10; 16|]; [|0; 6; 7; 9; 12; 16|];
   [|0; 7; 9; 16|]; [|0; 3; 12; 16|]; [|0; 14; 16|]; [|0; 16|]; [|0; 7; 16|];
   [|0; 2; 10; 16|]; [|0; 4; 13; 16|]; [|0; 2; 12; 16|] |];;

(* affichage de la grille *)

let gray = Graphics.rgb 100 100 100;;

let affiche_grille () =
   Graphics.set_color gray;
   for j = 0 to 16 do
      Graphics.moveto 0 (j * 50);
      Graphics.lineto 800 (j * 50);
   done;
   for i = 0 to 16 do
      Graphics.moveto (i * 50) 0;
      Graphics.lineto (i * 50) 800;
   done;;;;

(* positionnement des murs *)
let mur (x0,y0) (x1,y1) =
   Graphics.set_line_width 3;
   Graphics.moveto x0 y0;
   Graphics.lineto x1 y1;
;;

let place_mur_V i sj =
   mur (50 * sj, 800 - 50 * i) (50 * sj, 800 - 50 * (i+1))
;;
let place_murs_ligne ()=
Graphics.set_color Graphics.red;
for i = Array.length obs_l - 1 downto 0 do
   let ligne = obs_l.(i) in
   for j = 0 to Array.length ligne - 1 do
     place_mur_V i ligne.(j)
   done
done;;

let place_mur_H si j =
   mur (50 * j, 800 - 50 * si) (50 * (j+1), 800 - 50 * si)
;;
let place_murs_colonne ()=
Graphics.set_color Graphics.red;
for j = 0 to Array.length obs_c - 1 do
   let colonne = obs_c.(j) in
   for i = 0 to Array.length colonne - 1 do
      place_mur_H colonne.(i) j
   done
done;;
(* position et couleurs des robots *)

let robots = [|(5,12); (0,0); (2,1); (4,7);|];;
let colors = [|Graphics.red; Graphics.green; Graphics.blue; Graphics.yellow;|];;

let traceRobots () = 
   for i = 0 to Array.length robots - 1 do
      let (lig,col) = robots.(i) in
      Graphics.set_color colors.(i);
      Graphics.fill_circle (col*50 + 25) ((16-lig)*50 - 25)  20
   done;;

(* l'arrivée *)

let lig_arriv, col_arriv = (14,3);;

let traceArrivee () = 
   Graphics.set_color Graphics.black;
   Graphics.fill_rect (col_arriv*50 + 5) ((16-lig_arriv)*50 - 45) 40 40;;

(* déplacement *)

let teleporteRobot i (lig,col) = 
   let lig0, col0 = robots.(i) in
   Graphics.set_color Graphics.white;
   Graphics.fill_circle (col0*50 + 25) ((16-lig0)*50 - 25)  20;
   Graphics.set_color colors.(i);
   Graphics.fill_circle (col*50 + 25) ((16-lig)*50 - 25)  20;
   robots.(i) <- (lig, col)
;;

let casesup i tab = (* cherche la plus petite valeur supérieure à i dans tab croissant *)
   let v = ref 16 in
   for j = Array.length tab - 1 downto 0 do
      if tab.(j) > i then
         v := tab.(j)
   done; !v - 1
;;

let caseinf i tab = (* cherche la plus grande valeur inférieure à i dans tab croissant *)
   let v = ref 0 in
   for j = 0 to Array.length tab - 1 do
      if tab.(j) <= i then
         v := tab.(j)
   done; !v
;;

(* Déplacement de robot gêné par un autre robot ? *)
let filter (l_orig,c_orig) (l_dest,c_dest) direction (l_ri, c_ri) =
   match direction with
   | '2' -> if c_ri = c_dest && l_orig < l_ri && l_ri <= l_dest then (l_ri - 1, c_dest)
   else (l_dest,c_dest)
   | '8' -> if c_ri = c_dest && l_orig > l_ri && l_ri >= l_dest then (l_ri + 1, c_dest)
   else (l_dest,c_dest)
   | '6' -> if l_ri = l_dest && c_orig < c_ri && c_ri <= c_dest then (l_dest, c_ri - 1)
   else (l_dest,c_dest)
   | '4' -> if l_ri = l_dest && c_orig > c_ri && c_ri >= c_dest then (l_dest, c_ri + 1)
   else (l_dest,c_dest)
   | _ -> failwith "impossible"

let deplaceRobot i direction = 
   let lig, col = robots.(i) in
   let lig2, col2 = (* la destination sans tenir compte des autres robots *)
   match direction with
   | '2' -> let obs = obs_c.(col) in 
   let ldest = casesup lig obs in (ldest,col)
   | '6' -> let obs = obs_l.(lig) in 
   let cdest = casesup col obs in (lig,cdest)
   | '8' -> let obs = obs_c.(col) in 
   let ldest = caseinf lig obs in (ldest,col)
   | '4' -> let obs = obs_l.(lig) in 
   let cdest = caseinf col obs in (lig,cdest)
   | _ -> Printf.printf "mouvement invalide (2-4-6-8)\n"; (lig,col)
  in
  let dest = ref (lig2,col2) in
  (* on tient compte des autres robots *)
  for j = 0 to Array.length robots - 1 do
      dest := filter (lig,col) !dest direction robots.(j)
  done;
  !dest
;;

(* Démarrage du jeu *)
(* NB : jeu fonctionnel
0 : robot rouge
1 : robot vert
2 : robot bleu
3 : robot jaune 
Déplacements au pavé numérique : 2(bas) 4(gauche) 6(droite) 8(haut)  *)
let jeu () =
   Graphics.open_graph " 801x801";
   Graphics.set_window_title "Ricochet Robots";
   affiche_grille ();
   place_murs_ligne ();
   place_murs_colonne ();
   traceRobots ();
   traceArrivee ();
   let vict = ref false in
   while (not !vict) do
      let irob = int_of_char (Graphics.read_key ()) - 48 in
      if (irob >= 0 && irob <= 3) then begin
         let (lig, col) = deplaceRobot irob (Graphics.read_key ())
         in
         Printf.printf "robot %d vers (%d %d)\n" irob lig col;
         teleporteRobot irob (lig, col);
         if (lig = lig_arriv && col = col_arriv && irob = 0) then vict := true
      end else
         Printf.printf "numéro de robot invalide\n";
   done;
   Graphics.set_color Graphics.black;
   Graphics.moveto 353 400;
   Graphics.draw_string "!!! VICTOIRE !!!";
   Printf.printf "Victoire !\n";
   Graphics.read_key ();;

jeu ();;