1
1
use std:: collections:: HashMap ;
2
- use std:: time:: Instant ;
2
+ use std:: time:: { Duration , Instant } ;
3
3
4
4
use chess:: { Action , Board , BoardStatus , ChessMove , Color , MoveGen } ;
5
5
6
6
use crate :: common:: constants:: modules:: * ;
7
- use crate :: common:: utils:: { self , Stats } ;
7
+ use crate :: common:: utils:: { self , module_enabled , Stats } ;
8
8
9
9
use super :: utils:: { Evaluation , TranspositionEntry } ;
10
10
11
11
pub ( crate ) struct Algorithm {
12
12
pub ( crate ) modules : u32 ,
13
13
transposition_table : HashMap < Board , TranspositionEntry > ,
14
+ pub ( crate ) time_per_move : Duration ,
14
15
}
15
16
16
17
impl Algorithm {
17
- pub ( crate ) fn new ( modules : u32 ) -> Self {
18
+ pub ( crate ) fn new ( modules : u32 , time_per_move : Duration ) -> Self {
18
19
Self {
19
20
modules,
20
21
transposition_table : HashMap :: new ( ) ,
22
+ time_per_move,
21
23
}
22
24
}
23
25
@@ -34,84 +36,100 @@ impl Algorithm {
34
36
) -> Evaluation {
35
37
if depth == 0 {
36
38
stats. leaves_visited += 1 ;
37
- return Evaluation :: new ( self . eval ( board) , None , None ) ;
39
+ let eval = self . eval ( board) ;
40
+ // if module_enabled(self.modules, TRANSPOSITION_TABLE) {
41
+ // let start = Instant::now();
42
+ // self.transposition_table
43
+ // .insert(*board, TranspositionEntry::new(depth, eval, None));
44
+ // stats.time_for_transposition_access += Instant::now() - start;
45
+ // stats.transposition_table_entries += 1
46
+ // }
47
+ return Evaluation :: new ( Some ( eval) , None , None ) ;
38
48
}
39
49
40
50
// Whether we should try to maximise the eval
41
51
let maximise: bool = board. side_to_move ( ) == Color :: White ;
42
- let mut best_evaluation =
43
- Evaluation :: new ( if maximise { f32:: MIN } else { f32:: MAX } , None , None ) ;
52
+ let mut best_evaluation = Evaluation :: new ( None , None , None ) ;
44
53
45
54
let legal_moves = MoveGen :: new_legal ( board) ;
46
55
let num_legal_moves = legal_moves. len ( ) ;
47
56
if num_legal_moves == 0 {
48
57
if board. checkers ( ) . popcnt ( ) == 0 {
49
58
// Is Stalemate, no checking pieces
50
- best_evaluation. eval = 0. ;
59
+ best_evaluation. eval = Some ( 0. ) ;
51
60
}
61
+
52
62
// If we arrive at here and it is checkmate, then we know that the side playing
53
- // has been checkmated, and therefore the current `best_eval` is correct. Because if we tried to
54
- // maximise, we failed, and if trying to minimise, we failed and therefore get the
55
- // lowest/highest eval
56
- println ! ( "The thing happened" ) ;
63
+ // has been checkmated.
64
+
65
+ best_evaluation. eval = Some ( if board. side_to_move ( ) == Color :: White {
66
+ f32:: MIN
67
+ } else {
68
+ f32:: MAX
69
+ } ) ;
57
70
return best_evaluation;
58
71
}
59
72
60
73
let mut boards = legal_moves
61
74
. map ( |chess_move| {
62
75
let board = board. make_move_new ( chess_move) ;
63
76
let mut transposition_entry = None ;
64
- if self . modules & TRANSPOSITION_TABLE != 0 {
65
- transposition_entry = self . transposition_table . get ( & board) ;
77
+ if module_enabled ( self . modules , TRANSPOSITION_TABLE ) {
78
+ let start = Instant :: now ( ) ;
79
+
80
+ transposition_entry = self . transposition_table . get ( & board) . copied ( ) ;
81
+
82
+ let time_for_transposition_access = Instant :: now ( ) - start;
83
+ stats. time_for_transposition_access += time_for_transposition_access;
66
84
}
67
85
( chess_move, board, transposition_entry)
68
86
} )
69
- . collect :: < Vec < ( ChessMove , Board , Option < & TranspositionEntry > ) > > ( ) ;
87
+ . collect :: < Vec < ( ChessMove , Board , Option < TranspositionEntry > ) > > ( ) ;
70
88
71
89
// Sort by eval
72
- boards. sort_by ( |board1, board2| {
73
- let ordering = board1
74
- . 2
75
- . map_or ( 0. , |entry| entry. eval )
76
- . partial_cmp ( & board2. 2 . map_or ( 0. , |entry| entry. eval ) )
77
- . expect ( "Eval is a valid value" ) ;
78
-
79
- if !maximise {
80
- return ordering. reverse ( ) ;
81
- }
82
- ordering
83
- } ) ;
90
+ // boards.sort_by(|board1, board2| {
91
+ // let eval1 = board1.2.map_or(self.eval(&board1.1), |entry| entry.eval);
92
+ // let eval2 = board2.2.map_or(self.eval(&board2.1), |entry| entry.eval);
93
+ // let ordering = eval1.partial_cmp(&eval2).expect("Eval is a valid value");
94
+
95
+ // if maximise {
96
+ // return ordering.reverse();
97
+ // }
98
+ // ordering
99
+ // });
84
100
85
101
for ( i, ( chess_move, new_board, transposition_entry) ) in boards. iter ( ) . enumerate ( ) {
86
- if deadline. map_or ( false , |deadline| {
87
- !Instant :: now ( ) . saturating_duration_since ( deadline) . is_zero ( )
88
- } ) {
102
+ if deadline. is_some_and ( utils:: passed_deadline) {
89
103
// The previous value of progress_on_next_layer comes from deeper layers returning.
90
104
// We want these contributions to be proportional to the contribution from a single
91
105
// node on our layer
92
106
stats. progress_on_next_layer *= 1. / num_legal_moves as f32 ;
93
- stats. progress_on_next_layer += i as f32 / num_legal_moves as f32 ;
107
+ stats. progress_on_next_layer +=
108
+ ( i. saturating_sub ( 1 ) ) as f32 / num_legal_moves as f32 ;
94
109
return best_evaluation;
95
110
} ;
96
111
97
- let evaluation = if transposition_entry. is_none ( )
98
- || transposition_entry. unwrap ( ) . depth < depth
112
+ let evaluation = if transposition_entry. is_some ( )
113
+ && transposition_entry. unwrap ( ) . depth >= depth
99
114
{
100
- self . node_eval_recursive ( new_board, depth - 1 , alpha, beta, false , deadline, stats)
101
- } else {
115
+ stats. transposition_table_accesses += 1 ;
102
116
Evaluation :: new (
103
- transposition_entry. unwrap ( ) . eval ,
117
+ Some ( transposition_entry. unwrap ( ) . eval ) ,
104
118
transposition_entry. unwrap ( ) . next_action ,
105
119
None ,
106
120
)
121
+ } else {
122
+ self . node_eval_recursive ( new_board, depth - 1 , alpha, beta, false , deadline, stats)
107
123
} ;
108
124
stats. nodes_visited += 1 ;
109
125
110
126
// Replace best_eval if ours is better
111
- if maximise && evaluation. eval > best_evaluation. eval
112
- || !maximise && evaluation. eval < best_evaluation. eval
127
+ if evaluation. eval . is_some ( )
128
+ && ( best_evaluation. eval . is_none ( )
129
+ || maximise && evaluation. eval . unwrap ( ) > best_evaluation. eval . unwrap ( )
130
+ || !maximise && evaluation. eval . unwrap ( ) < best_evaluation. eval . unwrap ( ) )
113
131
{
114
- if original && self . modules & ANALYZE != 0 {
132
+ if original && module_enabled ( self . modules , ANALYZE ) {
115
133
let mut vec = Vec :: new ( ) ;
116
134
let new_best_move = chess_move. to_string ( ) ;
117
135
let new_best_eval = evaluation. eval ;
@@ -135,11 +153,13 @@ impl Algorithm {
135
153
best_evaluation. next_action = Some ( Action :: MakeMove ( * chess_move) ) ;
136
154
}
137
155
138
- if self . modules & ALPHA_BETA != 0 {
139
- if maximise {
140
- alpha = alpha. max ( evaluation. eval ) ;
141
- } else {
142
- beta = beta. min ( evaluation. eval ) ;
156
+ if module_enabled ( self . modules , ALPHA_BETA ) {
157
+ if let Some ( eval) = evaluation. eval {
158
+ if maximise {
159
+ alpha = alpha. max ( eval) ;
160
+ } else {
161
+ beta = beta. min ( eval) ;
162
+ }
143
163
}
144
164
145
165
if alpha > beta {
@@ -149,10 +169,25 @@ impl Algorithm {
149
169
}
150
170
}
151
171
152
- self . transposition_table . insert (
153
- * board,
154
- TranspositionEntry :: new ( depth, best_evaluation. eval , best_evaluation. next_action ) ,
155
- ) ;
172
+ if module_enabled ( self . modules , TRANSPOSITION_TABLE ) && depth >= 3 {
173
+ if let Some ( best_eval) = best_evaluation. eval {
174
+ let start = Instant :: now ( ) ;
175
+ self . transposition_table . insert (
176
+ * board,
177
+ TranspositionEntry :: new ( depth, best_eval, best_evaluation. next_action ) ,
178
+ ) ;
179
+ stats. time_for_transposition_access += Instant :: now ( ) - start;
180
+ }
181
+ stats. transposition_table_entries += 1
182
+ }
183
+
184
+ if best_evaluation. debug_data . is_some ( ) {
185
+ let mut debug_data = best_evaluation. debug_data . take ( ) . unwrap ( ) ;
186
+ if let Some ( Action :: MakeMove ( next_move) ) = best_evaluation. next_action {
187
+ utils:: vector_push_debug!( debug_data, best_evaluation. eval, next_move. to_string( ) , ) ;
188
+ best_evaluation. debug_data = Some ( debug_data) ;
189
+ }
190
+ }
156
191
best_evaluation
157
192
}
158
193
@@ -161,23 +196,12 @@ impl Algorithm {
161
196
board : & Board ,
162
197
depth : u32 ,
163
198
deadline : Option < Instant > ,
164
- ) -> ( chess:: Action , Vec < String > , Stats ) {
199
+ ) -> ( Option < chess:: Action > , Vec < String > , Stats ) {
165
200
let mut stats = Stats :: default ( ) ;
166
201
let out =
167
202
self . node_eval_recursive ( board, depth, f32:: MIN , f32:: MAX , true , deadline, & mut stats) ;
168
- let action = if out. next_action . is_none ( ) {
169
- match board. status ( ) {
170
- BoardStatus :: Ongoing => {
171
- panic ! ( "No action returned by algorithm even though game is still ongoing" )
172
- }
173
- BoardStatus :: Stalemate => Action :: DeclareDraw ,
174
- BoardStatus :: Checkmate => Action :: Resign ( board. side_to_move ( ) ) ,
175
- }
176
- } else {
177
- out. next_action . unwrap ( )
178
- } ;
179
203
let analyzer_data = out. debug_data . unwrap_or_default ( ) ;
180
- ( action , analyzer_data, stats)
204
+ ( out . next_action , analyzer_data, stats)
181
205
}
182
206
183
207
pub ( crate ) fn next_action (
@@ -203,7 +227,24 @@ impl Algorithm {
203
227
}
204
228
}
205
229
deepest_complete_output. 2 . depth = deepest_complete_depth;
206
- deepest_complete_output
230
+ deepest_complete_output. 2 . tt_size = self . transposition_table . len ( ) as u32 ;
231
+
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
+ } ,
244
+ } ,
245
+ deepest_complete_output. 1 ,
246
+ deepest_complete_output. 2 ,
247
+ )
207
248
}
208
249
209
250
pub ( crate ) fn eval ( & self , board : & Board ) -> f32 {
@@ -224,4 +265,8 @@ impl Algorithm {
224
265
let diff = material_each_side. 0 as i32 - material_each_side. 1 as i32 ;
225
266
diff as f32
226
267
}
268
+
269
+ pub ( crate ) fn reset ( & mut self ) {
270
+ self . transposition_table = HashMap :: new ( ) ;
271
+ }
227
272
}
0 commit comments