@@ -12,14 +12,17 @@ pub(crate) struct Algorithm {
12
12
pub ( crate ) modules : u32 ,
13
13
transposition_table : HashMap < Board , TranspositionEntry > ,
14
14
pub ( crate ) time_per_move : Duration ,
15
+ /// Number of times that a given board has been played
16
+ pub ( crate ) board_played_times : HashMap < Board , u32 > ,
15
17
}
16
18
17
19
impl Algorithm {
18
20
pub ( crate ) fn new ( modules : u32 , time_per_move : Duration ) -> Self {
19
21
Self {
20
22
modules,
21
- transposition_table : HashMap :: new ( ) ,
23
+ transposition_table : HashMap :: with_capacity ( 45 ) ,
22
24
time_per_move,
25
+ board_played_times : HashMap :: new ( ) ,
23
26
}
24
27
}
25
28
@@ -33,6 +36,7 @@ impl Algorithm {
33
36
original : bool ,
34
37
deadline : Option < Instant > ,
35
38
stats : & mut Stats ,
39
+ num_extensions : u32 ,
36
40
) -> Evaluation {
37
41
if depth == 0 {
38
42
stats. leaves_visited += 1 ;
@@ -70,7 +74,7 @@ impl Algorithm {
70
74
return best_evaluation;
71
75
}
72
76
73
- let mut boards = legal_moves
77
+ let boards = legal_moves
74
78
. map ( |chess_move| {
75
79
let board = board. make_move_new ( chess_move) ;
76
80
let mut transposition_entry = None ;
@@ -109,18 +113,35 @@ impl Algorithm {
109
113
return best_evaluation;
110
114
} ;
111
115
112
- let evaluation = if transposition_entry. is_some ( )
113
- && transposition_entry. unwrap ( ) . depth >= depth
114
- {
115
- stats. transposition_table_accesses += 1 ;
116
- Evaluation :: new (
117
- Some ( transposition_entry. unwrap ( ) . eval ) ,
118
- transposition_entry. unwrap ( ) . next_action ,
119
- None ,
120
- )
121
- } else {
122
- self . node_eval_recursive ( new_board, depth - 1 , alpha, beta, false , deadline, stats)
123
- } ;
116
+ let extend_by =
117
+ if !module_enabled ( self . modules , SEARCH_EXTENSIONS ) || num_extensions > 3 {
118
+ 0
119
+ } else if num_legal_moves <= 3 || new_board. checkers ( ) . popcnt ( ) != 0 {
120
+ 1
121
+ } else {
122
+ 0
123
+ } ;
124
+
125
+ let evaluation =
126
+ if transposition_entry. is_some ( ) && transposition_entry. unwrap ( ) . depth >= depth {
127
+ stats. transposition_table_accesses += 1 ;
128
+ Evaluation :: new (
129
+ Some ( transposition_entry. unwrap ( ) . eval ) ,
130
+ transposition_entry. unwrap ( ) . next_action ,
131
+ None ,
132
+ )
133
+ } else {
134
+ self . node_eval_recursive (
135
+ new_board,
136
+ depth - 1 + extend_by,
137
+ alpha,
138
+ beta,
139
+ false ,
140
+ deadline,
141
+ stats,
142
+ num_extensions + extend_by,
143
+ )
144
+ } ;
124
145
stats. nodes_visited += 1 ;
125
146
126
147
// Replace best_eval if ours is better
@@ -191,31 +212,44 @@ impl Algorithm {
191
212
best_evaluation
192
213
}
193
214
194
- fn next_action_internal (
215
+ fn next_action (
195
216
& mut self ,
196
217
board : & Board ,
197
218
depth : u32 ,
198
219
deadline : Option < Instant > ,
199
220
) -> ( Option < chess:: Action > , Vec < String > , Stats ) {
200
221
let mut stats = Stats :: default ( ) ;
201
- let out =
202
- self . node_eval_recursive ( board, depth, f32:: MIN , f32:: MAX , true , deadline, & mut stats) ;
222
+ let out = self . node_eval_recursive (
223
+ board,
224
+ depth,
225
+ f32:: MIN ,
226
+ f32:: MAX ,
227
+ true ,
228
+ deadline,
229
+ & mut stats,
230
+ 0 ,
231
+ ) ;
203
232
let analyzer_data = out. debug_data . unwrap_or_default ( ) ;
204
233
( out. next_action , analyzer_data, stats)
205
234
}
206
235
207
- pub ( crate ) fn next_action (
236
+ pub ( crate ) fn next_action_iterative_deepening (
208
237
& mut self ,
209
238
board : & Board ,
210
239
deadline : Instant ,
211
240
) -> ( chess:: Action , Vec < String > , Stats ) {
241
+ self . board_played_times . insert (
242
+ * board,
243
+ * self . board_played_times . get ( board) . unwrap_or ( & 0 ) + 1 ,
244
+ ) ;
245
+
212
246
// Guarantee that at least the first layer gets done.
213
247
const START_DEPTH : u32 = 1 ;
214
- let mut deepest_complete_output = self . next_action_internal ( board, START_DEPTH , None ) ;
248
+ let mut deepest_complete_output = self . next_action ( board, START_DEPTH , None ) ;
215
249
let mut deepest_complete_depth = START_DEPTH ;
216
250
217
251
for depth in ( deepest_complete_depth + 1 ) ..=10 {
218
- let latest_output = self . next_action_internal ( board, depth, Some ( deadline) ) ;
252
+ let latest_output = self . next_action ( board, depth, Some ( deadline) ) ;
219
253
if utils:: passed_deadline ( deadline) {
220
254
// The cancelled layer is the one with this data
221
255
deepest_complete_output. 2 . progress_on_next_layer =
@@ -229,22 +263,31 @@ impl Algorithm {
229
263
deepest_complete_output. 2 . depth = deepest_complete_depth;
230
264
deepest_complete_output. 2 . tt_size = self . transposition_table . len ( ) as u32 ;
231
265
232
- (
233
- match deepest_complete_output. 0 {
234
- Some ( action) => action,
235
- None => match board. status ( ) {
236
- BoardStatus :: Ongoing => {
237
- println ! ( "{}" , board) ;
238
- println ! ( "{:#?}" , deepest_complete_output. 1 ) ;
239
- panic ! ( "No action returned by algorithm even though game is still ongoing" )
240
- }
241
- BoardStatus :: Stalemate => Action :: DeclareDraw ,
242
- BoardStatus :: Checkmate => Action :: Resign ( board. side_to_move ( ) ) ,
243
- } ,
266
+ let mut action = match deepest_complete_output. 0 {
267
+ Some ( action) => action,
268
+ None => match board. status ( ) {
269
+ BoardStatus :: Ongoing => {
270
+ println ! ( "{}" , board) ;
271
+ println ! ( "{:#?}" , deepest_complete_output. 1 ) ;
272
+ panic ! ( "No action returned by algorithm even though game is still ongoing" )
273
+ }
274
+ BoardStatus :: Stalemate => Action :: DeclareDraw ,
275
+ BoardStatus :: Checkmate => Action :: Resign ( board. side_to_move ( ) ) ,
244
276
} ,
245
- deepest_complete_output. 1 ,
246
- deepest_complete_output. 2 ,
247
- )
277
+ } ;
278
+
279
+ if let Action :: MakeMove ( chess_move) = action {
280
+ let new_board = board. make_move_new ( chess_move) ;
281
+ let old_value = * self . board_played_times . get ( & new_board) . unwrap_or ( & 0 ) ;
282
+ if old_value >= 3 {
283
+ // Oh no! We should declare draw by three-fold repetition. This is not checked
284
+ // unless we do this.
285
+ action = Action :: DeclareDraw ;
286
+ }
287
+ self . board_played_times . insert ( new_board, old_value + 1 ) ;
288
+ }
289
+
290
+ ( action, deepest_complete_output. 1 , deepest_complete_output. 2 )
248
291
}
249
292
250
293
pub ( crate ) fn eval ( & self , board : & Board ) -> f32 {
@@ -259,6 +302,12 @@ impl Algorithm {
259
302
f32:: MAX
260
303
} ;
261
304
}
305
+ if let Some ( & board_played_times) = self . board_played_times . get ( board) {
306
+ if board_played_times >= 2 {
307
+ // This is third time this is played. Draw by three-fold repetition
308
+ return 0. ;
309
+ }
310
+ }
262
311
let material_each_side = utils:: material_each_side ( board) ;
263
312
264
313
// Negative when black has advantage
@@ -268,5 +317,6 @@ impl Algorithm {
268
317
269
318
pub ( crate ) fn reset ( & mut self ) {
270
319
self . transposition_table = HashMap :: new ( ) ;
320
+ self . board_played_times = HashMap :: new ( ) ;
271
321
}
272
322
}
0 commit comments