1 (** {2 Finite set iterators} *)
12 function cost param elt : int
14 function min param (fset elt) : int
16 axiom min_is_a_lower_bound:
17 forall p: param, s: fset elt.
18 forall x. mem x s -> cost p x >= min p s
20 axiom min_appears_in_set:
21 forall p: param, s: fset elt. not (is_empty s) ->
22 exists x. mem x s /\ cost p x = min p s
32 (** type describing a position in the game. It must include every
33 needed info, in particular in general it will include whose
37 (** type describing a move *)
40 (** the initial_position of the game *)
41 constant initial_position : position
43 (** gives the list of legal moves in the given position *)
44 val function legal_moves position : list move
46 (** [do_move p m] returns the position obtained by doing the move [m]
49 val function do_move position move : position
51 (** [position_value p] returns an evaluation of position [p], an
52 integer [v] between [-infinity] and [+infinity] which is supposed
53 to be as higher as the position is good FOR THE PLAYER WHO HAS
54 THE TURN. [v] must be 0 for a position where nobody can win
55 anymore (draw game). For a position where the player who has the
56 turn wins, [v] must be between [winning_value] and [infinity],
57 and if the other player wins, [v] must be between [-infinity] and
60 constant winning_value : int
61 val constant infinity : int
62 val function position_value position : int
64 axiom position_value_bound :
65 forall p:position. - infinity < position_value p < infinity
69 [minmax p d] returns the min-max evaluation of position [p] at depth [d].
70 As for [position_value], the value is for the player who has the turn.
73 val function minmax position int : int
75 axiom minmax_depth_0 :
76 forall p:position. minmax p 0 = position_value p
78 type param = (position,int)
79 function cost (p:param) (m:move) : int =
80 match p with (p,n) -> minmax (do_move p m) n
83 clone import Min as MinMaxRec with
91 axiom minmax_depth_non_zero:
92 forall p:position, n:int. n > 0 ->
94 let moves = Elements.elements (legal_moves p) in
95 if Fset.is_empty moves then position_value p else
96 - MinMaxRec.min (p,n-1) moves
101 forall p:position, m:move.
102 let moves = legal_moves p in
103 Mem.mem m moves -> - (position_value (do_move p m)) <= minmax p 1
107 forall p:position, d:int.
108 d >= 0 -> - infinity < minmax p d < infinity
110 lemma minmax_nomove :
111 forall p:position, d:int.
112 d >= 0 /\ legal_moves p = Nil ->
113 minmax p d = position_value p
128 use TwoPlayerGame as G
133 let rec move_value_alpha_beta alpha beta pos depth move =
134 requires { depth >= 1 }
136 let pos' = G.do_move pos move in
137 let m = G.minmax pos' (depth-1) in
138 if - beta < m < - alpha then result = - m
139 else if m <= - beta then result >= beta
140 else result <= alpha }
142 let pos' = G.do_move pos move in
143 let s = negabeta (-beta) (-alpha) pos' (depth-1)
146 with negabeta alpha beta pos depth =
147 requires { depth >= 0 }
149 if alpha < G.minmax pos depth < beta then result = G.minmax pos depth else
150 if G.minmax pos depth <= alpha then result <= alpha else
153 if depth = 0 then G.position_value pos else
154 let l = G.legal_moves pos in
156 | Nil -> G.position_value pos
159 move_value_alpha_beta alpha beta pos depth m
161 if best >= beta then best else
162 negabeta_rec (max best alpha) beta pos depth best l
165 with negabeta_rec alpha beta pos depth best l =
166 requires { depth >= 1 }
168 let moves = Elements.elements l in
169 if Fset.is_empty moves then result = best else
170 let m = G.MinMaxRec.min (pos,depth) moves in
171 if alpha < m < beta then result = m
172 else if m <= alpha then result <= alpha else
174 variant { depth, 1, l }
179 move_value_alpha_beta alpha beta pos depth c
181 let new_best = max s best in
182 if new_best >= beta then new_best else
183 negabeta_rec (max new_best alpha) beta pos depth new_best l
186 (* alpha-beta at a given depth *)
188 let alpha_beta pos depth =
189 requires { depth >= 0 }
190 ensures { result = G.minmax pos depth }
191 negabeta (-G.infinity) G.infinity pos depth
193 (* iterative deepening *)
196 let best_move_alpha_beta pos =
200 (move_value_alpha_beta (-G.infinity) G.infinity pos 2 0 c,c))
203 if List.length l < 2 then l else
204 let current_best_moves =
206 (List.sort (fun (x,_) (y,_) -> compare y x) l)
209 for max_depth = 3 to 1000 do
211 match !current_best_moves with
212 | (v1,_)::(v2,_)::_ ->
213 if v1 >= G.winning_value or v2 <= - G.winning_value
220 (move_value_alpha_beta (-G.infinity) G.infinity
221 pos max_depth 0 c,c))
224 current_best_moves :=
225 (List.sort (fun (x,_) (y,_) -> compare y x) l)
229 | Timeout -> !current_best_moves