Skip to content

Commit 0263e9d

Browse files
author
Admiral Adama
committed
BUG - rewrote perft to be recursive, fixes out of memory error
1 parent 6f5ac1f commit 0263e9d

File tree

2 files changed

+150
-95
lines changed

2 files changed

+150
-95
lines changed

perft.php

Lines changed: 89 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -17,93 +17,108 @@
1717
}
1818

1919
$fen = $board->export_fen();
20-
21-
const PERFT_DEPTH = 3;
22-
const COUNT_CHECKS_AND_CHECKMATES = FALSE;
23-
const DEBUG = 0;
20+
$perft_depth = isset($_GET['perft_depth']) ? $_GET['perft_depth'] : 2;
21+
$detail_level = isset($_GET['detail_level']) ? $_GET['detail_level'] : 1;
22+
$debug = isset($_GET['debug']) ? $_GET['debug'] : 0;
2423
$data = array();
25-
$move_trees_generated = 0;
26-
$legal_moves[0][0] = new ChessMove(NULL, NULL, NULL, NULL, NULL, $board);
24+
$data['move_trees_generated'] = 0; // Used to get the average move time per tree.
25+
26+
function perft($current_depth, $max_depth, $color_to_move, $board, $data, $detail_level) {
27+
$data['move_trees_generated']++;
28+
$legal_moves_list = ChessRulebook::get_legal_moves_list($color_to_move, $board, TRUE, TRUE, $detail_level >= 3);
29+
30+
if ( ! isset($data[$current_depth]['nodes']) ) {
31+
$data[$current_depth]['depth'] = $current_depth;
32+
$data[$current_depth]['nodes'] = 0;
33+
$data[$current_depth]['captures'] = 0;
34+
$data[$current_depth]['en_passants'] = 0;
35+
$data[$current_depth]['castles'] = 0;
36+
$data[$current_depth]['promotions'] = 0;
37+
$data[$current_depth]['checks'] = 0;
38+
$data[$current_depth]['checkmates'] = 0;
39+
}
40+
41+
$data[$current_depth]['nodes'] += count($legal_moves_list);
42+
43+
if ( $detail_level >= 2 ) {
44+
foreach ( $legal_moves_list as $move ) {
45+
$data = get_details($move, $current_depth, $data, $detail_level);
46+
}
47+
}
48+
49+
if ( $current_depth != $max_depth ) {
50+
foreach ( $legal_moves_list as $move ) {
51+
// Doing this recursively instead of a list of moves for each depth prevents "out of memory" errors.
52+
$data = perft($current_depth + 1, $max_depth, $move->board->color_to_move, $move->board, $data, $detail_level);
53+
}
54+
}
55+
56+
return $data;
57+
}
58+
59+
function get_details($move, $depth, $data, $detail_level) {
60+
if ( $move->capture ) {
61+
$data[$depth]['captures']++;
62+
}
63+
64+
if ( $move->en_passant ) {
65+
$data[$depth]['en_passants']++;
66+
}
67+
68+
if ( $move->castling ) {
69+
$data[$depth]['castles']++;
70+
}
71+
72+
if ( $move->promotion_piece_type ) {
73+
$data[$depth]['promotions']++;
74+
}
75+
76+
if ( $detail_level >= 3 ) {
77+
if ( $move->check ) {
78+
$data[$depth]['checks']++;
79+
}
80+
81+
if ( $move->checkmate ) {
82+
$data[$depth]['checkmates']++;
83+
}
84+
}
85+
86+
return $data;
87+
}
2788

2889
$time = microtime();
2990
$time = explode(' ', $time);
3091
$time = $time[1] + $time[0];
3192
$start = $time;
3293

33-
for ( $depth = 1; $depth <= PERFT_DEPTH; $depth++ ) {
34-
$data[$depth]['depth'] = $depth;
35-
$data[$depth]['nodes'] = 0;
36-
$data[$depth]['captures'] = 0;
37-
$data[$depth]['en_passants'] = 0;
38-
$data[$depth]['castles'] = 0;
39-
$data[$depth]['promotions'] = 0;
40-
if ( COUNT_CHECKS_AND_CHECKMATES ) {
41-
$data[$depth]['checks'] = 0;
42-
$data[$depth]['checkmates'] = 0;
43-
} else {
44-
$data[$depth]['checks'] = '';
45-
$data[$depth]['checkmates'] = '';
46-
}
94+
$data = perft(1, $perft_depth, $board->color_to_move, $board, $data, $detail_level);
95+
96+
$move_trees_generated = $data['move_trees_generated'];
97+
unset($data['move_trees_generated']);
98+
99+
if ( $debug ) {
100+
$legal_moves_list1 = ChessRulebook::get_legal_moves_list($board->color_to_move, $board, TRUE, TRUE, FALSE);
47101

48-
$legal_moves[$depth] = array();
49-
foreach ( $legal_moves[$depth - 1] as $move ) {
50-
$legal_moves_list = ChessRulebook::get_legal_moves_list($move->board->color_to_move, $move->board, TRUE, TRUE, COUNT_CHECKS_AND_CHECKMATES);
51-
$move_trees_generated++;
102+
foreach ( $legal_moves_list1 as $move1 ) {
103+
$key1 = $move1->starting_square->get_alphanumeric() . '-' . $move1->ending_square->get_alphanumeric();
52104

53-
foreach ( $legal_moves_list as $move2 ) {
54-
if ( DEBUG ) {
55-
if ( $depth == 1 ) {
56-
$key3 = $move2->starting_square->get_alphanumeric() . '-' . $move2->ending_square->get_alphanumeric();
57-
58-
$debug[$key3] = array(
59-
'count' => 0,
60-
'fen' => $move2->board->export_fen(),
61-
);
62-
}
63-
64-
if ( $depth == 2 ) {
65-
$key3 = $move->starting_square->get_alphanumeric() . '-' . $move->ending_square->get_alphanumeric();
66-
67-
$debug[$key3]['count']++;
68-
}
69-
}
70-
71-
$legal_moves[$depth][] = $move2;
72-
73-
$data[$depth]['nodes']++;
74-
75-
if ( $move2->capture ) {
76-
$data[$depth]['captures']++;
77-
}
78-
79-
if ( $move2->en_passant ) {
80-
$data[$depth]['en_passants']++;
81-
}
82-
83-
if ( $move2->castling ) {
84-
$data[$depth]['castles']++;
85-
}
86-
87-
if ( $move2->promotion_piece_type ) {
88-
$data[$depth]['promotions']++;
89-
}
105+
$debug_data[$key1] = array(
106+
'count' => 0,
107+
'fen' => $move1->board->export_fen(),
108+
);
109+
110+
$legal_moves_list2 = ChessRulebook::get_legal_moves_list($move1->board->color_to_move, $move1->board, TRUE, TRUE, FALSE);
111+
112+
foreach ( $legal_moves_list2 as $move2 ) {
113+
$key2 = $move2->starting_square->get_alphanumeric() . '-' . $move2->ending_square->get_alphanumeric();
90114

91-
if ( COUNT_CHECKS_AND_CHECKMATES ) {
92-
if ( $move2->check ) {
93-
$data[$depth]['checks']++;
94-
}
95-
96-
if ( $move2->checkmate ) {
97-
$data[$depth]['checkmates']++;
98-
}
99-
}
115+
$debug_data[$key1]['count']++;
100116
}
101117
}
102-
unset($legal_moves[$depth - 1]);
103118
}
104-
105-
if ( DEBUG ) {
106-
ksort($debug);
119+
120+
if ( $debug ) {
121+
ksort($debug_data);
107122
}
108123

109124
require_once('views/perft.html');

views/perft.html

Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,36 +22,74 @@ <h1>
2222
<p>
2323
FEN:<br />
2424
<input type="text" name="fen" value="<?php echo $fen; ?>" /><br />
25-
<input type="submit" value="Import FEN" />
25+
</p>
26+
27+
<p>
28+
Depth:<br />
29+
<input type="number" name="perft_depth" value="<?php echo $perft_depth; ?>" /><br />
30+
</p>
31+
32+
<p>
33+
Detail Level:<br />
34+
<input type="radio" name="detail_level" value="1" <?php if ( $detail_level == 1 ) {echo "checked";} ?> /> Nodes Only
35+
<input type="radio" name="detail_level" value="2" <?php if ( $detail_level == 2 ) {echo "checked";} ?> /> Details
36+
<input type="radio" name="detail_level" value="3" <?php if ( $detail_level == 3 ) {echo "checked";} ?> /> Checks & Checkmates
37+
</p>
38+
39+
<p>
40+
Debug:<br />
41+
<input type="radio" name="debug" value="0" <?php if ( $debug == 0 ) {echo "checked";} ?> /> Normal
42+
<input type="radio" name="debug" value="1" <?php if ( $debug == 1 ) {echo "checked";} ?> /> Debug
43+
</p>
44+
45+
<p>
46+
<input type="submit" value="Perft" />
2647
</p>
2748
</form>
2849

2950
<table>
3051
<thead>
3152
<tr>
32-
<th>Depth</th>
33-
<th>Nodes</th>
34-
<th>Captures</th>
35-
<th>En Passants</th>
36-
<th>Castles</th>
37-
<th>Promotions</th>
38-
<th>Checks</th>
39-
<th>Checkmates</th>
53+
<th>Depth</th>
54+
<th>Nodes</th>
55+
<?php if ( $detail_level >= 2 ): ?>
56+
57+
<th>Captures</th>
58+
<th>En Passants</th>
59+
<th>Castles</th>
60+
<th>Promotions</th>
61+
62+
<?php if ( $detail_level >= 3 ): ?>
63+
64+
<th>Checks</th>
65+
<th>Checkmates</th>
66+
<?php endif; ?>
67+
68+
<?php endif; ?>
69+
4070
</tr>
4171
</thead>
4272

4373
<tbody>
44-
<?php foreach ( $data as $key => $value ): ?>
74+
<?php foreach ( $data as $value ): ?>
4575

4676
<tr>
47-
<td><?php echo $value['depth']; ?></td>
48-
<td><?php echo $value['nodes']; ?></td>
49-
<td><?php echo $value['captures']; ?></td>
50-
<td><?php echo $value['en_passants']; ?></td>
51-
<td><?php echo $value['castles']; ?></td>
52-
<td><?php echo $value['promotions']; ?></td>
53-
<td><?php echo $value['checks']; ?></td>
54-
<td><?php echo $value['checkmates']; ?></td>
77+
<td><?php echo $value['depth']; ?></td>
78+
<td><?php echo $value['nodes']; ?></td>
79+
<?php if ( $detail_level >= 2 ): ?>
80+
81+
<td><?php echo $value['captures']; ?></td>
82+
<td><?php echo $value['en_passants']; ?></td>
83+
<td><?php echo $value['castles']; ?></td>
84+
<td><?php echo $value['promotions']; ?></td>
85+
86+
<?php if ( $detail_level >= 3 ): ?>
87+
<td><?php echo $value['checks']; ?></td>
88+
<td><?php echo $value['checkmates']; ?></td>
89+
<?php endif; ?>
90+
91+
<?php endif; ?>
92+
5593
</tr>
5694
<?php endforeach; ?>
5795

@@ -82,13 +120,15 @@ <h1>
82120

83121
</p>
84122

85-
<?php if ( DEBUG ): ?>
123+
<?php if ( $debug ): ?>
86124

87125
<p>
88126
<b><i><u>DEBUG</u></i></b><br />
89-
<?php foreach ( $debug as $key => $value ): ?>
127+
<?php $count = 0; ?>
128+
<?php foreach ( $debug_data as $key => $value ): ?>
90129

91-
<?php echo $key; ?> - <?php echo $value['count']; ?> - <?php echo $value['fen']; ?><br />
130+
<?php $count++; ?>
131+
<?php echo $count; ?> - <?php echo $key; ?> - <?php echo $value['count']; ?> - <?php echo $value['fen']; ?><br />
92132
<?php endforeach; ?>
93133

94134
</p>

0 commit comments

Comments
 (0)