aboutsummaryrefslogblamecommitdiff
path: root/judge/morpion_rec.ml
blob: 49e654486541be12d663b7886af1a834f295d455 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11


         
 
                 





                        
                                          





                          





                                                                 





























                                                              
                             




                                               
















                                                                        





                                                         
 




                                                                                


                                                                  

                                          
                                                                     
                         
                                        

                                    










                                     
                                                        



                                                     


                                
                     








                                                            
 
                             
                                



                        
                             



                                                                            
                                           




                              
                                                                          

                                                      


                     










                                                              

                                 
 

                               




                        

                             



                        
                                                                    


                      
                          
                               




                                                




                                                                            
                             
 
               
                            
                                                       



                                           
                                       






                                                     
 











                                                                       


   
open Core
open Main


module G = struct
  
  exception Invalid_pos

  type loc1 = int * int
  type loc = loc1 * loc1

  type c =      (* P1 <-> X || P2 <-> O *)
      | Empty
      | X
      | O
      | T

  type 'a r = 'a * 'a * 'a
  type 'a morpion = ('a r * 'a r * 'a r) * c

  (* On enregistre dans une grille de morpion le résultat
      pour cette grille : non attribué (Empty), X, O, ou nul (T)
     Pour y acceder, utiliser reduct : 'a morpion -> c *)
  let reduct (_, r) = r

  type game = game_status * c morpion morpion * loc1 option

  (* all_p1 : loc1 list *)
  let all_p1 = [ 1,1; 1,2; 1,3; 2,1; 2,2; 2,3; 3,1; 3,2; 3,3 ]
  (* all_w_s : loc1 list list *)
  let all_w_p1l = [
      [ 1,1; 1,2; 1,3 ];
      [ 2,1; 2,2; 2,3 ];
      [ 3,1; 3,2; 3,3 ];
      [ 1,1; 2,1; 3,1 ];
      [ 1,2; 2,2; 3,2 ];
      [ 1,3; 2,3; 3,3 ];
      [ 1,1; 2,2; 3,3 ];
      [ 1,3; 2,2; 3,1 ];
    ]

  (* encode : loc -> string *)
  let encode ((xg, yg), (xp, yp)) =
    Format.sprintf "%d %d %d %d" xg yg xp yp
  (* decode : string -> loc *)
  let decode s =
    Scanf.sscanf s "%d %d %d %d"
      (fun xg yg xp yp -> (xg, yg), (xp, yp))

  (* getp0 : ('a, 'a, 'a) -> int -> 'a *)
  let getp0 (a, b, c) x = match x with
    | 1 -> a | 2 -> b | 3 -> c
    | _ -> raise Invalid_pos
  (* getp1 : 'a morpion -> loc1 -> 'a *)
  let getp1 (m, _) (px, py) =
    getp0 (getp0 m px) py
  (* getp : 'a morpion morpion -> loc2 -> 'a *)
  let getp m (pg, pp) =
    getp1 (getp1 m pg) pp

  (* reduce_m : ('a -> c) -> 'a morpion -> c *)
  let reduce_m rf m =
    match
      all_w_p1l
      |> List.map (List.map (fun x -> rf (getp1 m x)))
      |> List.map (function
            | l when List.for_all ((=) X) l -> X
            | l when List.for_all ((=) O) l -> O
            | l when List.exists ((=) X) l && List.exists ((=) O) l -> T
            | l when List.exists ((=) T) l -> T
            | _ -> Empty)
    with
    | l when List.exists ((=) X) l -> X
    | l when List.exists ((=) O) l -> O
    | l when List.exists ((=) Empty) l -> Empty
    | _ -> T

  (* setp0 : ('a, 'a, 'a) -> int -> 'a -> ('a, 'a, 'a) *)
  let setp0 (a, b, c) x v = match x with
    | 1 -> (v, b, c)
    | 2 -> (a, v, c)
    | 3 -> (a, b, v)
    | _ -> raise Invalid_pos

  (* setp1 : 'a morpion -> loc1 -> 'a -> ('a -> 'c) -> 'a morpion *)
  let setp1 (m, r) (px, py) v rf =
    let k = setp0 m px (setp0 (getp0 m px) py v) in
    (k, if r = Empty then reduce_m rf (k, r) else r)
    (* pourquoi ce if ? parce que si quelqu'un a déjà gagné un petit morpion,
      alors même si l'adversaire aligne trois cases dedans APRES,
      le petit morpion reste attribué à la même personne. *)

  let setp1p (m, r) (px, py) v =
      setp1 (m, r) (px, py) v (fun x -> x)
  (* setp : 'a morpion morpion -> loc2 -> 'a -> 'a morpion morpion *)
  let setp m (pg, pp) v =
    let im = setp1p (getp1 m pg) pp v in
    let om = setp1 m pg im reduct in
    om

  (* r : 'a -> ('a, 'a, 'a) *)
  let r x = (x, x, x)

  (* *************************** *)
  (* Début du code intéressant ! *)

  let id = "morpion_rec"
  let name = "Morpion récursif!"

  let new_game = 
    TurnOf P1, (r (r (r (r Empty), Empty)), Empty), None

  let full_pm m =
    List.for_all (fun p -> getp1 m p <> Empty) all_p1

  let possibilities (s, m, lg) =
    let pg_poss = match lg with
      | None -> all_p1
      | Some x -> [x]
    in
    List.flatten
      (List.map (fun pg ->
          all_p1
          |> List.filter (fun pp -> getp m (pg, pp) = Empty)
          |> List.map (fun pp -> (pg, pp)))
        pg_poss)
    |> List.map encode


  let play (gs, m, pgo) act =
    let (pg, pp) = decode act in
    match gs with
    | TurnOf player when
        (match pgo with
          | None -> true
          | Some x -> pg = x)
        && getp m (pg, pp) = Empty 
      ->
        let op = other_player player in
        let new_m = setp m (pg, pp) (match player with P1 -> X | P2 -> O) in
        let new_s = match reduct new_m with
          | Empty -> TurnOf op
          | X -> Won P1
          | O -> Won P2
          | T -> Tie
        in
        (new_s, new_m, if full_pm (getp1 new_m pp) then None else Some pp)
    | TurnOf x -> (Eliminated x, m, pgo)
    | _ -> raise (Eliminated_ex "not someone's turn!")

  let s (s, _, _) = s


  (* ************************* *)
  (* Visualisation graphique ! *)

  open Graphics
  open Main
  open G_util

  let subpos (x1, y1, x2, y2) (l, c) =
    let dx, dy = (x2 - x1) / 3, (y2 - y1) / 3 in
    x1 + (l-1) * dx, y1 + (c-1) * dy, x1 + l * dx, y1 + c * dy
  let margin (x1, y1, x2, y2) m =
    (x1+m, y1+m, x2-m, y2-m)

  let disp_l lw pos =
    let x1, y1, x2, y2 = pos in
    function
    | X ->
      set_line_width lw;
      set_color p1c;
      draw_segments
        [| x1, y1, x2, y2;
           x1, y2, x2, y1 |];
      set_line_width 1
    | O ->
      set_line_width lw;
      set_color p2c;
      draw_circle ((x1+x2)/2) ((y1+y2)/2) (min (x2-x1) (y2-y1) / 2);
      set_line_width 1
    | _ -> ()

  let disp_r sdf box mor =
    let x1, y1, x2, y2 = box in
    let dx, dy = (x2 - x1) / 3, (y2 - y1) / 3 in
    let x12, x23 = x1 + dx, x1 + 2 * dx in
    let y12, y23 = y1 + dy, y1 + 2 * dy in
    set_color black;
    draw_segments
      [| x12, y1, x12, y2;
         x23, y1, x23, y2;
         x1, y12, x2, y12;
         x1, y23, x2, y23 |];
    List.iter (fun p -> sdf (margin (subpos box p) 6) (getp1 mor p)) all_p1;
    disp_l 2 box (reduct mor)

  let gbox () =
    let cx, cy = center() in
    let box = cx - 200, cy - 200, cx + 200, cy + 200 in
    box

  let display_game (s, mor, q) (pn1, pn2) =
    let box = gbox () in
    disp_r (disp_r (disp_l 1)) box mor;
    begin match q, s with
    | Some p, TurnOf player ->
      let x1, y1, x2, y2 = margin (subpos box p) 3 in
      set_color (pc player);
      draw_rect x1 y1 (x2-x1) (y2-y1)
    | _ -> ()
    end

  let in_box (x1, y1, x2, y2) (x, y) =
    (x >= x1 && x <= x2 && y >= y1 && y <= y2)

  let gui_act_at g xy =
    let box = gbox () in
    try
      let pg = List.find (fun p -> in_box (subpos box p) xy) all_p1 in
      let sbox = subpos box pg in
      let pp = List.find (fun p -> in_box (subpos sbox p) xy) all_p1 in
      encode (pg, pp)
    with Not_found -> ""


end