File Coverage

blib/lib/Chess/Plisco/Engine/Tree.pm
Criterion Covered Total %
statement 293 321 91.2
branch 83 106 78.3
condition 22 34 64.7
subroutine 26 29 89.6
pod 0 10 0.0
total 424 500 84.8


line stmt bran cond sub pod time code
1             #! /bin/false
2              
3             # Copyright (C) 2021 Guido Flohr ,
4             # all rights reserved.
5              
6             # This program is free software. It comes without any warranty, to
7             # the extent permitted by applicable law. You can redistribute it
8             # and/or modify it under the terms of the Do What the Fuck You Want
9             # to Public License, Version 2, as published by Sam Hocevar. See
10             # http://www.wtfpl.net/ for more details.
11              
12             package Chess::Plisco::Engine::Tree;
13             $Chess::Plisco::Engine::Tree::VERSION = '0.4';
14 11     11   2116 use strict;
  11         22  
  11         368  
15 11     11   54 use integer;
  11         21  
  11         64  
16              
17 11     11   725 use Locale::TextDomain qw('Chess-Plisco');
  11         15832  
  11         99  
18              
19 11     11   27707 use Chess::Plisco qw(:all);
  11         25  
  11         4315  
20             # Macros from Chess::Plisco::Macro are already expanded here!
21 11     11   2063 use Chess::Plisco::Engine::Position;
  11         39  
  11         568  
22              
23 11     11   5841 use Time::HiRes qw(tv_interval);
  11         14693  
  11         47  
24              
25 11     11   1771 use constant DEBUG => $ENV{DEBUG_PLISCO_TREE};
  11         22  
  11         906  
26              
27 11     11   60 use constant MATE => -15000;
  11         20  
  11         440  
28 11     11   52 use constant INF => 16383;
  11         22  
  11         400  
29 11     11   53 use constant MAX_PLY => 512;
  11         22  
  11         388  
30 11     11   50 use constant DRAW => 0;
  11         20  
  11         432  
31              
32 11     11   4233 use Chess::Plisco::Engine::TranspositionTable;
  11         28  
  11         631  
33              
34             # These values get stored in the upper 32 bits of a moves so that they are
35             # searched first.
36 11     11   90 use constant MOVE_ORDERING_PV => 1 << 62;
  11         19  
  11         503  
37 11     11   54 use constant MOVE_ORDERING_TT => 1 << 61;
  11         19  
  11         1957  
38              
39             # For all combinations of promotion piece and captured piece, calculate a
40             # value suitable for sorting. We choose the raw material balance minus the
41             # piece that moves. That way, captures that the queen makes are less
42             # "attractive" than captures that the rook makes.
43             my @move_values = (0) x 369;
44              
45             sub new {
46 307     307 0 1130 my ($class, $position, $tt, $watcher, $info, $signatures) = @_;
47              
48             # Make sure that the reversible clock does not look beyond the know
49             # positions. This will simplify the detection of a draw by repetition.
50 307 100       1272 if ($position->[CP_POS_REVERSIBLE_CLOCK] >= @$signatures) {
51 2         5 $position->[CP_POS_REVERSIBLE_CLOCK] = @$signatures - 1;
52             }
53              
54             my $self = {
55             position => $position,
56             signatures => $signatures,
57             history_length => -1 + scalar @$signatures,
58             tt => $tt,
59             watcher => $watcher,
60       0     info => $info || sub {},
61 307   50     2860 };
62              
63 307         1181 bless $self, $class;
64             }
65              
66             sub checkTime {
67 5910     5910 0 14650 my ($self) = @_;
68              
69 5910         37954 $self->{watcher}->check;
70              
71 11     11   65 no integer;
  11         21  
  11         54  
72              
73 5910         45025 my $elapsed = 1000 * tv_interval($self->{start_time});
74              
75             # Taken from Stockfish: Start printing the current move after 0.5 s.
76             # Otherwise the output is getting messy in the beginning. Stockfish is
77             # using 3 s but we are slower.
78 5910 50       141323 if ($elapsed > 500) {
79 5910         15313 $self->{print_current_move} = 1;
80             }
81 5910         11413 my $allocated = $self->{allocated_time};
82 5910         12961 my $eta = $allocated - $elapsed;
83 5910 50 33     38430 if ($eta < 4 && !$self->{max_depth} && !$self->{max_nodes}) {
      33        
84 0         0 die "PLISCO_ABORTED\n";
85             }
86              
87 5910         10735 my $nodes = $self->{nodes};
88 5910 50       17987 my $nps = $elapsed ? (1000 * $nodes / $elapsed) : 10000;
89 5910         12680 my $max_nodes_to_tc = $nps >> 3;
90              
91 5910 50       27434 if ($self->{max_depth}) {
    0          
92 5910         14925 $self->{nodes_to_tc} = $nodes + $max_nodes_to_tc;
93             } elsif ($self->{max_nodes}) {
94             $self->{nodes_to_tc} =
95 0         0 cp_min($nodes + $max_nodes_to_tc, $self->{max_nodes});
96             } else {
97 0         0 my $nodes_to_tc = int(($eta * $nps) / 2000);
98              
99 0 0       0 $self->{nodes_to_tc} = $nodes +
100             (($nodes_to_tc < $max_nodes_to_tc) ? $nodes_to_tc : $max_nodes_to_tc);
101             }
102             }
103              
104             sub debug {
105 0     0 0 0 my ($self, $msg) = @_;
106              
107 0         0 chomp $msg;
108 0         0 print "DEBUG $msg\n";
109              
110 0         0 return 1;
111             }
112              
113             sub indent {
114 0     0 0 0 my ($self, $ply, $msg) = @_;
115              
116 0         0 chomp $msg;
117 0         0 my $indent = '..' x ($ply - 1);
118 0         0 $self->debug("[$ply/$self->{depth}] $indent$msg");
119             }
120              
121              
122             sub printPV {
123 2240     2240 0 4913 my ($self, $pline) = @_;
124              
125 11     11   3214 no integer;
  11         22  
  11         49  
126 2240         4188 my $position = $self->{position};
127 2240         4270 my $score = $self->{score};
128 2240         3287 my $mate_in;
129 2240 100       6806 if ($score >= -(MATE + MAX_PLY)) {
    100          
130 654         1731 $mate_in = (1 - (MATE + $score)) >> 1;
131             } elsif ($score <= (MATE + MAX_PLY)) {
132 11     11   645 use integer;
  11         24  
  11         40  
133 4         11 $mate_in = (MATE - $score) >> 1;
134             }
135              
136 2240         4446 my $nodes = $self->{nodes};
137 2240         11784 my $elapsed = tv_interval($self->{start_time});
138 2240 50       55693 my $nps = $elapsed ? (int(0.5 + $nodes / $elapsed)) : 0;
139 2240 100       7641 my $scorestr = $mate_in ? "mate $mate_in" : "cp $score";
140 2240         10922 my $pv = join ' ', $position->movesCoordinateNotation(@$pline);
141 2240         6188 my $time = int(0.5 + (1000 * $elapsed));
142 2240         22126 $self->{info}->("depth $self->{depth} seldepth $self->{seldepth}"
143             . " score $scorestr nodes $nodes nps $nps time $time pv $pv");
144 2240 50       12012 if ($self->{__debug}) {
145 0 0       0 $self->{info}->("tt_hits $self->{tt_hits}") if $self->{__debug};
146             }
147             }
148              
149             sub alphabeta {
150 706140     706140 0 1290096 my ($self, $ply, $depth, $alpha, $beta, $pline, $is_pv) = @_;
151              
152 706140         807501 my @line;
153              
154 706140 100       1279705 if ($self->{nodes} >= $self->{nodes_to_tc}) {
155 3271         13992 $self->checkTime;
156             }
157              
158 706140         957877 my $position = $self->{position};
159              
160 706140         827372 if (DEBUG) {
161             my $hex_signature = sprintf '%016x', $position->signature;
162             my $line = join ' ', @{$self->{line}};
163             $self->indent($ply, "alphabeta: alpha = $alpha, beta = $beta, line: $line,"
164             . " depth: $depth, sig: $hex_signature $position");
165             if ($is_pv) {
166             $self->indent($ply, "in PV");
167             }
168             }
169              
170 706140 50       1170309 if ($position->[CP_POS_HALF_MOVE_CLOCK] >= 100) {
171 0         0 if (DEBUG) {
172             $self->indent($ply, "draw detected");
173             }
174 0         0 return DRAW;
175             }
176              
177             # Check draw by repetition. FIXME! Try to find near repetitions with
178             # cuckoo tables.
179             #
180             # We know that the reversible clock is never pointing beyond the known
181             # positions/signatures because that gets adjusted in the constructor.
182 706140         959537 my $signatures = $self->{signatures};
183 706140         916679 my $signature = $position->[CP_POS_SIGNATURE];
184 706140 100       1167389 if ($ply > 1) {
185 705728         1473442 my $rc = $position->reversibleClock; # FIXME! Use this!!!
186 705728         968425 my $history_length = $self->{history_length};
187 705728         880449 my $signature_slot = $history_length + $ply;
188 705728         923839 my $max_back = $signature_slot - $rc - 1;
189 705728         790653 my $repetitions = 0;
190 705728         1425580 for (my $n = $signature_slot - 5; $n >= $max_back; $n -= 2) {
191 18967 100       53282 if ($signatures->[$n] == $signature) {
192 181         284 ++$repetitions;
193 181 100 100     1040 if ($repetitions >= 2 || $n >= $history_length) {
194 160         229 if (DEBUG) {
195             $self->indent($ply, "3-fold repetition");
196             }
197 160         416 return DRAW;
198             }
199             }
200             }
201             }
202              
203 705980         922620 my $tt = $self->{tt};
204 705980         811280 my $tt_move;
205 705980         741990 if (DEBUG) {
206             my $hex_sig = sprintf '%016x', $signature;
207             $self->indent($ply, "TT probe $hex_sig \@depth $depth, alpha = $alpha, beta = $beta");
208             }
209 705980         1758611 my $tt_value = $tt->probe($signature, $depth, $alpha, $beta, \$tt_move);
210              
211 705980         863131 if (DEBUG) {
212             if ($tt_move) {
213             my $cn = $position->moveCoordinateNotation($tt_move);
214             $self->indent($ply, "best move: $cn");
215             }
216             }
217 705980 100       1156586 if (defined $tt_value) {
218 96749         142863 ++$self->{tt_hits};
219 96749 50 66     207118 if ($tt_move && $ply == 1) {
220 0         0 @$pline = ($tt_move);
221 0         0 $self->{score} = $tt_value;
222             }
223              
224 96749         116575 if (DEBUG) {
225             my $hex_sig = sprintf '%016x', $signature;
226             my $cn = $position->moveCoordinateNotation($tt_move);
227             $self->indent($ply, "TT hit for $hex_sig, value $tt_value, best move $cn");
228             }
229 96749         161388 return $tt_value;
230             }
231              
232 609231 100       1024188 if ($depth <= 0) {
233 455921         928972 return $self->quiesce($ply, $alpha, $beta, $pline, $is_pv);
234             }
235              
236 153310         441300 my @moves = $position->pseudoLegalMoves;
237              
238             # Expand the moves with a score so that they can be sorted.
239 153310         436208 my ($pawns, $knights, $bishops, $rooks, $queens) =
240             @$position[CP_POS_PAWNS .. CP_POS_QUEENS];
241 153310         240836 my $pos_info = $position->[CP_POS_INFO];
242 153310         253481 my $her_pieces = $position->[CP_POS_WHITE_PIECES + (($pos_info & (1 << 4)) >> 4)];
243 153310         215291 my $ep_shift = (($pos_info & (0x3f << 5)) >> 5);
244 153310         199450 my $pv_move;
245 153310 100       318989 $pv_move = $pline->[$ply - 1] if @$pline >= $ply;
246 153310         225001 my $found = 0;
247 153310         277645 foreach my $move (@moves) {
248 4735730 100       8325806 if ((($move & 0x7fff) == ($pv_move & 0x7fff))) {
    100          
    100          
249 586         1033 $move |= MOVE_ORDERING_PV;
250 586         964 ++$found;
251             } elsif ((($move & 0x7fff) == ($tt_move & 0x7fff))) {
252 9783         16657 $move |= MOVE_ORDERING_TT;
253 9783         15486 ++$found;
254             } elsif ($depth > 1) {
255 276467         519733 $move |= $position->SEE($move) << 32;
256             } else {
257 4448894 100       6189078 last if $found >= 2;
258             }
259             }
260              
261             # Now sort the moves according to the material gain.
262 153310         589750 @moves = sort { $b <=> $a } @moves;
  10028782         10297132  
263              
264 153310         203192 my $legal = 0;
265 153310         198383 my $pv_found;
266 153310         193113 my $tt_type = Chess::Plisco::Engine::TranspositionTable::TT_SCORE_ALPHA();
267 153310         191098 my $best_move = 0;
268 153310   100     364238 my $print_current_move = $ply == 1 && $self->{print_current_move};
269 153310         265729 my $signature_slot = $self->{history_length} + $ply;
270 153310         229175 foreach my $move (@moves) {
271 2135611 100       3818070 my $state = $position->doMove($move) or next;
272 576886         977080 $signatures->[$signature_slot] = $position->[CP_POS_SIGNATURE];
273 576886         697143 ++$legal;
274 576886         830229 ++$self->{nodes};
275 576886 100       927966 $self->printCurrentMove($depth, $move, $legal) if $print_current_move;
276 576886         691766 my $val;
277 576886         665109 if (DEBUG) {
278             my $cn = $position->moveCoordinateNotation($move);
279             $self->indent($ply, "move $cn: start search");
280             push @{$self->{line}}, $cn;
281             }
282 576886 100       843987 if ($pv_found) {
283 31049         40083 if (DEBUG) {
284             $self->indent($ply, "null window search");
285             }
286 31049   66     130962 $val = -$self->alphabeta($ply + 1, $depth - 1,
287             -$alpha - 1, -$alpha, \@line, $is_pv && !$legal);
288 31049 100 100     108575 if (($val > $alpha) && ($val < $beta)) {
289 2885         4613 if (DEBUG) {
290             $self->indent($ply, "value $val outside null window, re-search");
291             }
292 2885   66     14356 $val = -$self->alphabeta($ply + 1, $depth - 1,
293             -$beta, -$alpha, \@line, $is_pv && !$legal);
294             }
295             } else {
296 545837         570171 if (DEBUG) {
297             $self->indent($ply, "recurse normal search");
298             }
299 545837   66     1745545 $val = -$self->alphabeta($ply + 1, $depth - 1,
300             -$beta, -$alpha, \@line, $is_pv && !$legal);
301             }
302 576886         814525 if (DEBUG) {
303             my $cn = $position->moveCoordinateNotation($move);
304             $self->indent($ply, "move $cn: value $val");
305             }
306 576886         1451707 $position->undoMove($state);
307 576886         652820 if (DEBUG) {
308             pop @{$self->{line}};
309             }
310 576886 100       1004049 if ($val >= $beta) {
311 110403         128899 if (DEBUG) {
312             my $hex_sig = sprintf '%016x', $signature;
313             my $cn = $position->moveCoordinateNotation($move);
314             $self->indent($ply, "$cn fail high ($val >= $beta), store $val(BETA) \@depth $depth for $hex_sig");
315             }
316 110403         313293 $tt->store($signature, $depth,
317             Chess::Plisco::Engine::TranspositionTable::TT_SCORE_BETA(),
318             $val, $move);
319 110403         620650 return $beta;
320             }
321 466483 100       1227015 if ($val > $alpha) {
322 5293         8558 $alpha = $val;
323 5293         8849 $pv_found = 1;
324 5293         12810 @$pline = ($move, @line);
325 5293         8942 $tt_type = Chess::Plisco::Engine::TranspositionTable::TT_SCORE_EXACT();
326 5293         8004 $best_move = $move;
327            
328 5293         6180 if (DEBUG) {
329             $self->indent($ply, "raise alpha to $alpha");
330             }
331 5293 100       14903 if ($is_pv) {
332 1933         4404 $self->{score} = $val;
333 1933         6647 $self->printPV($pline);
334             }
335             }
336             }
337              
338 42907 100       93644 if (!$legal) {
339             # Mate or stalemate.
340 16308 50       56008 if (!$position->inCheck) {
341 0         0 $alpha = DRAW;
342             } else {
343             #$alpha = MATE + $self->{depth} - $depth + 1;
344 16308         28009 $alpha = MATE + $ply;
345             }
346 16308         22025 if (DEBUG) {
347             $self->indent($ply, "mate/stalemate, score: $alpha");
348             }
349             }
350              
351 42907         52129 if (DEBUG) {
352             my $hex_sig = sprintf '%016x', $signature;
353             my $type;
354             if ($tt_type == TT_SCORE_ALPHA) {
355             $type = 'ALPHA';
356             } else {
357             $type = 'EXACT';
358             }
359             $self->indent($ply, "returning alpha $alpha, store ($type) \@depth $depth for $hex_sig");
360             }
361              
362 42907         156728 $tt->store($signature, $depth, $tt_type, $alpha, $best_move);
363              
364 42907         196629 return $alpha;
365             }
366              
367             sub quiesce {
368 902419     902419 0 1547898 my ($self, $ply, $alpha, $beta, $pline, $is_pv) = @_;
369              
370 902419 100       1709403 if ($self->{nodes} >= $self->{nodes_to_tc}) {
371 2639         11977 $self->checkTime;
372             }
373              
374 902419 100       1662676 $self->{seldepth} = ((($ply) > ($self->{seldepth})) ? ($ply) : ($self->{seldepth}));
375              
376 902419         1000730 my @line;
377 902419         1158763 my $position = $self->{position};
378              
379 902419         968482 if (DEBUG) {
380             my $hex_signature = sprintf '%016x', $position->signature;
381             my $line = join ' ', @{$self->{line}};
382             $self->indent($ply, "quiescence: alpha = $alpha, beta = $beta, line: $line,"
383             . " sig: $hex_signature $position");
384             if ($is_pv) {
385             $self->indent($ply, "in PV");
386             }
387             }
388              
389             # Expand the search, when in check.
390 902419 100       1463974 if ($position->[CP_POS_IN_CHECK]) {
391 125957         160062 if (DEBUG) {
392             $self->indent($ply, "quiescence check extension");
393             }
394 125957         376951 return $self->alphabeta($ply, 1, $alpha, $beta, $pline, 0);
395             }
396              
397 776462         975835 my $tt = $self->{tt};
398 776462         938962 my $signature = $position->[CP_POS_SIGNATURE];
399 776462         896167 my $tt_move;
400 776462         882628 if (DEBUG) {
401             my $hex_sig = sprintf '%016x', $signature;
402             $self->indent($ply, "quiescence TT probe $hex_sig \@depth 0, alpha = $alpha, beta = $beta");
403             }
404 776462         1804168 my $tt_value = $tt->probe($signature, 0, $alpha, $beta, \$tt_move);
405 776462         920685 if (DEBUG) {
406             if ($tt_move) {
407             my $cn = $position->moveCoordinateNotation($tt_move);
408             $self->indent($ply, "best move: $cn");
409             }
410             }
411              
412 776462 100       1280188 if (defined $tt_value) {
413 34187         40399 if (DEBUG) {
414             my $hex_sig = sprintf '%016x', $signature;
415             $self->indent($ply, "quiescence TT hit for $hex_sig, value $tt_value");
416             }
417 34187         52348 ++$self->{tt_hits};
418 34187         57869 return $tt_value;
419             }
420              
421 742275         1520671 my $val = $position->evaluate;
422 742275         957904 if (DEBUG) {
423             $self->indent($ply, "static evaluation: $val");
424             }
425 742275 100       1209451 if ($val >= $beta) {
426 552969         671004 if (DEBUG) {
427             my $hex_sig = sprintf '%016x', $signature;
428             $self->indent($ply, "quiescence standing pat ($val >= $beta), store $val(EXACT) \@depth 0 for $hex_sig");
429             }
430             # FIXME! Is that correct?
431 552969         1345522 $tt->store($signature, 0,
432             Chess::Plisco::Engine::TranspositionTable::TT_SCORE_EXACT(),
433             $val, 0
434             );
435 552969         1187714 return $beta;
436             }
437              
438 189306         260764 my $tt_type = Chess::Plisco::Engine::TranspositionTable::TT_SCORE_ALPHA();
439 189306 100       331907 if ($val > $alpha) {
440 6551         8741 $alpha = $val;
441             # FIXME! Correct?
442 6551         8942 $tt_type = Chess::Plisco::Engine::TranspositionTable::TT_SCORE_EXACT();
443             }
444              
445 189306         470887 my @pseudo_legal = $position->pseudoLegalAttacks;
446 189306         315813 my $pos_info = $position->[CP_POS_INFO];
447 189306         370615 my $her_pieces = $position->[CP_POS_WHITE_PIECES
448             + !((($position->[CP_POS_INFO] & (1 << 4)) >> 4))];
449 189306         228208 my (@moves);
450 189306         290149 my $signatures = $self->{signatures};
451 189306         301857 my $signature_slot = $self->{history_length} + $ply;
452 189306         358162 foreach my $move (@pseudo_legal) {
453 921804 100       2063829 my $state = $position->doMove($move) or next;
454 815479         1292432 $signatures->[$signature_slot] = $position->[CP_POS_SIGNATURE];
455 815479         2070302 $position->undoMove($state);
456 815479         1595730 my $see = $position->SEE($move);
457              
458             # A marginal difference can occur if bishops and knights have different
459             # values. But we want to ignore that.
460 815479 100       1613809 next if $see <= -CP_PAWN_VALUE;
461              
462             # FIXME! Do we have a PV move here?
463 590223 50       963922 if ($move == $tt_move) {
464 0         0 push @moves, MOVE_ORDERING_TT | $move;
465             } else {
466 590223         1588609 push @moves, ($see << 32) | $move;
467             }
468             }
469              
470 189306         279726 my $legal = 0;
471 189306         235698 my $tt_type = Chess::Plisco::Engine::TranspositionTable::TT_SCORE_ALPHA();
472 189306         241847 my $best_move = 0;
473 189306         648009 foreach my $move (sort { $b <=> $a } @moves) {
  747265         1034490  
474 446498         990112 my $state = $position->doMove($move);
475 446498         563114 if (DEBUG) {
476             my $cn = $position->moveCoordinateNotation($move);
477             push @{$self->{line}}, $cn;
478             $self->indent($ply, "move $cn: start quiescence search");
479             }
480 446498   33     777858 $is_pv = $is_pv && !$legal;
481 446498         652057 ++$self->{nodes};
482 446498         485301 if (DEBUG) {
483             $self->indent($ply, "recurse quiescence search");
484             }
485 446498         1140463 $val = -quiesce($self, $ply + 1, -$beta, -$alpha, $pline, $is_pv);
486 446498         577848 if (DEBUG) {
487             my $cn = $position->moveCoordinateNotation($move);
488             $self->indent($ply, "move $cn: value: $val");
489             pop @{$self->{line}};
490             }
491 446498         1075373 $position->undoMove($state);
492 446498 100       768678 if ($val >= $beta) {
493 55754         64553 if (DEBUG) {
494             my $hex_sig = sprintf '%016x', $signature;
495             my $cn = $position->moveCoordinateNotation($move);
496             $self->indent($ply, "$cn quiescence fail high ($val >= $beta), store $val(BETA) \@depth 0 for $hex_sig");
497             }
498 55754         159953 $tt->store($signature, 0,
499             Chess::Plisco::Engine::TranspositionTable::TT_SCORE_BETA(),
500             $val, $move);
501 55754         241428 return $beta;
502             }
503 390744 100       950087 if ($val > $alpha) {
504 6412         8475 if (DEBUG) {
505             $self->indent($ply, "raise quiescence alpha to $alpha");
506             }
507 6412         8317 $alpha = $val;
508 6412         11629 @$pline = ($move, @line);
509 6412         8449 $tt_type = Chess::Plisco::Engine::TranspositionTable::TT_SCORE_EXACT();
510 6412         8012 $best_move = $move;
511 6412 50       17452 if ($is_pv) {
512 0         0 $self->{score} = $val;
513 0         0 $self->printPV($pline);
514             }
515             }
516             }
517              
518 133552         170372 if (DEBUG) {
519             my $hex_sig = sprintf '%016x', $signature;
520             my $type;
521             if ($tt_type == TT_SCORE_ALPHA) {
522             $type = 'ALPHA';
523             } else {
524             $type = 'EXACT';
525             }
526             $self->indent($ply, "quiescence returning alpha $alpha, store ($type) \@depth 0 for $hex_sig");
527             }
528              
529 133552         383497 $tt->store($signature, 0, $tt_type, $val, $best_move);
530              
531 133552         494960 return $alpha;
532             }
533              
534             sub rootSearch {
535 307     307 0 845 my ($self, $pline) = @_;
536              
537 307         767 $self->{nodes} = 0;
538              
539 307         601 my $position = $self->{position};
540              
541 307   50     992 my $max_depth = $self->{max_depth} || (MAX_PLY - 1);
542 307         801 my $depth = $self->{depth} = 0;
543 307         655 $self->{seldepth} = 0;
544 307         708 my $score = $self->{score} = 0;
545              
546 307         672 my @line = @$pline;
547 307         746 eval {
548 307         1192 while (++$depth <= $max_depth) {
549 412         872 $self->{depth} = $depth;
550 412         593 if (DEBUG) {
551             $self->debug("Deepening to depth $depth");
552             $self->{line} = [];
553             }
554 412         1480 $score = -$self->alphabeta(1, $depth, -INF, +INF, \@line, 1);
555 412         791 if (DEBUG) {
556             $self->debug("Score at depth $depth: $score");
557             }
558 412 100       788 if ((do { my $mask = $score >> CP_INT_SIZE * CP_CHAR_BIT - 1; ($score + $mask) ^ $mask;}) > -(MATE + MAX_PLY)) {
  412         795  
  412         1664  
559 305         955 last;
560             }
561             }
562             };
563 307 50       955 if ($@) {
564 0 0       0 if ($@ ne "PLISCO_ABORTED\n") {
565 0         0 $self->{info}->(__"Error: exception raised: $@");
566             }
567             }
568 307         1144 @$pline = @line;
569             }
570              
571              
572             sub printCurrentMove {
573 2043     2043 0 5291 my ($self, $depth, $move, $moveno) = @_;
574              
575 11     11   25054 no integer;
  11         27  
  11         53  
576              
577 2043         4832 my $position = $self->{position};
578 2043         8217 my $cn = $position->moveCoordinateNotation($move);
579 2043         11199 my $elapsed = int(1000 * tv_interval($self->{start_time}));
580              
581 2043         52011 $self->{info}->("depth $depth currmove $cn currmovenumber $moveno"
582             . " time $elapsed");
583             }
584              
585             sub think {
586 307     307 0 2040 my ($self) = @_;
587              
588 307         630 my $position = $self->{position};
589 307         974 my @legal = $position->legalMoves;
590 307 50       1121 if (!@legal) {
591 0         0 $self->{info}->(__"Error: no legal moves");
592 0         0 return;
593             }
594              
595 307         574 my @line;
596              
597 307         1018 $self->{thinking} = 1;
598 307         710 $self->{tt_hits} = 0;
599              
600 307 50       954 if ($self->{debug}) {
601 0         0 $self->{info}->("allocated time: $self->{allocated_time}");
602             }
603              
604 307         1505 $self->rootSearch(\@line);
605              
606 307         987 delete $self->{thinking};
607              
608 307 50       979 if (@line) {
609 307         1299 $self->printPV(\@line);
610             } else {
611             # Search has returned no move.
612 0         0 $self->{info}->("Error: pick a random move because of search failure.");
613 0         0 $line[0] = $legal[int rand @legal];
614             }
615              
616 307         2744 return $line[0];
617             }
618              
619             # Fill the lookup table for the move values.
620             foreach my $mover (CP_PAWN .. CP_KING) {
621             my @piece_values = (
622             0,
623             CP_PAWN_VALUE,
624             CP_KNIGHT_VALUE,
625             CP_BISHOP_VALUE,
626             CP_ROOK_VALUE,
627             CP_QUEEN_VALUE,
628             );
629              
630             foreach my $victim (CP_NO_PIECE, CP_PAWN .. CP_QUEEN) {
631             my $index = ($victim << 6) | ($mover << 3);
632             my $value = $victim ? ($piece_values[$victim] - $mover) : 0;
633             $move_values[$index] = $value;
634             my $key = (CP_PIECE_CHARS->[0]->[$victim] || ' ') . CP_PIECE_CHARS->[0]->[$mover];
635             if ($mover == CP_PAWN) {
636             foreach my $promote (CP_KNIGHT .. CP_QUEEN) {
637             $move_values[$index | $promote]
638             = $value + $piece_values[$promote] - CP_PAWN_VALUE;
639             my $pc = CP_PIECE_CHARS->[0]->[$promote];
640             my $pvalue = $value + $piece_values[$promote] - CP_PAWN_VALUE;
641             }
642             }
643             }
644             }
645              
646             1;