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::Position; |
13
|
|
|
|
|
|
|
$Chess::Plisco::Engine::Position::VERSION = '0.4'; |
14
|
11
|
|
|
11
|
|
342158
|
use strict; |
|
11
|
|
|
|
|
112
|
|
|
11
|
|
|
|
|
350
|
|
15
|
11
|
|
|
11
|
|
1541
|
use integer; |
|
11
|
|
|
|
|
59
|
|
|
11
|
|
|
|
|
57
|
|
16
|
|
|
|
|
|
|
|
17
|
11
|
|
|
11
|
|
4268
|
use Chess::Plisco qw(:all); |
|
11
|
|
|
|
|
43
|
|
|
11
|
|
|
|
|
5169
|
|
18
|
|
|
|
|
|
|
# Macros from Chess::Plisco::Macro are already expanded here! |
19
|
11
|
|
|
11
|
|
3889
|
use Chess::Plisco::Engine::Tree; |
|
11
|
|
|
|
|
35
|
|
|
11
|
|
|
|
|
335
|
|
20
|
|
|
|
|
|
|
|
21
|
11
|
|
|
11
|
|
66
|
use base qw(Chess::Plisco); |
|
11
|
|
|
|
|
20
|
|
|
11
|
|
|
|
|
1275
|
|
22
|
|
|
|
|
|
|
|
23
|
11
|
|
|
11
|
|
71
|
use constant CP_POS_GAME_PHASE => 14; |
|
11
|
|
|
|
|
21
|
|
|
11
|
|
|
|
|
657
|
|
24
|
11
|
|
|
11
|
|
55
|
use constant CP_POS_OPENING_SCORE => 15; |
|
11
|
|
|
|
|
21
|
|
|
11
|
|
|
|
|
414
|
|
25
|
11
|
|
|
11
|
|
53
|
use constant CP_POS_ENDGAME_SCORE => 16; |
|
11
|
|
|
|
|
32
|
|
|
11
|
|
|
|
|
445
|
|
26
|
|
|
|
|
|
|
|
27
|
11
|
|
|
11
|
|
54
|
use constant PAWN_PHASE => 0; |
|
11
|
|
|
|
|
29
|
|
|
11
|
|
|
|
|
474
|
|
28
|
11
|
|
|
11
|
|
57
|
use constant KNIGHT_PHASE => 1; |
|
11
|
|
|
|
|
28
|
|
|
11
|
|
|
|
|
385
|
|
29
|
11
|
|
|
11
|
|
51
|
use constant BISHOP_PHASE => 1; |
|
11
|
|
|
|
|
20
|
|
|
11
|
|
|
|
|
427
|
|
30
|
11
|
|
|
11
|
|
53
|
use constant ROOK_PHASE => 2; |
|
11
|
|
|
|
|
20
|
|
|
11
|
|
|
|
|
375
|
|
31
|
11
|
|
|
11
|
|
50
|
use constant QUEEN_PHASE => 4; |
|
11
|
|
|
|
|
20
|
|
|
11
|
|
|
|
|
611
|
|
32
|
11
|
|
|
|
|
615
|
use constant TOTAL_PHASE => PAWN_PHASE * 16 |
33
|
|
|
|
|
|
|
+ KNIGHT_PHASE * 4 + BISHOP_PHASE * 4 |
34
|
11
|
|
|
11
|
|
55
|
+ ROOK_PHASE * 4 + QUEEN_PHASE * 2; |
|
11
|
|
|
|
|
22
|
|
35
|
11
|
|
|
|
|
24275
|
use constant PHASE_INC => [ |
36
|
|
|
|
|
|
|
0, |
37
|
|
|
|
|
|
|
PAWN_PHASE, |
38
|
|
|
|
|
|
|
KNIGHT_PHASE, |
39
|
|
|
|
|
|
|
BISHOP_PHASE, |
40
|
|
|
|
|
|
|
ROOK_PHASE, |
41
|
|
|
|
|
|
|
QUEEN_PHASE, |
42
|
|
|
|
|
|
|
0, |
43
|
11
|
|
|
11
|
|
61
|
]; |
|
11
|
|
|
|
|
21
|
|
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
my @op_value = (0, 82, 337, 365, 477, 1025, 0); |
47
|
|
|
|
|
|
|
my @eg_value = (0, 94, 281, 297, 512, 936, 0); |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# piece/sq tables |
50
|
|
|
|
|
|
|
# values from Rofchade: http://www.talkchess.com/forum3/viewtopic.php?f=2&t=68311&start=19 |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
my @op_pawn_table = ( |
53
|
|
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, |
54
|
|
|
|
|
|
|
98, 134, 61, 95, 68, 126, 34, -11, |
55
|
|
|
|
|
|
|
-6, 7, 26, 31, 65, 56, 25, -20, |
56
|
|
|
|
|
|
|
-14, 13, 6, 21, 23, 12, 17, -23, |
57
|
|
|
|
|
|
|
-27, -2, -5, 12, 17, 6, 10, -25, |
58
|
|
|
|
|
|
|
-26, -4, -4, -10, 3, 3, 33, -12, |
59
|
|
|
|
|
|
|
# 47, 81, 62, 59, 67, 106, 120, 60 |
60
|
|
|
|
|
|
|
-35, -1, -20, -23, -15, 24, 38, -22, |
61
|
|
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, |
62
|
|
|
|
|
|
|
); |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
my @eg_pawn_table = ( |
65
|
|
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, |
66
|
|
|
|
|
|
|
178, 173, 158, 134, 147, 132, 165, 187, |
67
|
|
|
|
|
|
|
94, 100, 85, 67, 56, 53, 82, 84, |
68
|
|
|
|
|
|
|
32, 24, 13, 5, -2, 4, 17, 17, |
69
|
|
|
|
|
|
|
13, 9, -3, -7, -7, -8, 3, -1, |
70
|
|
|
|
|
|
|
4, 7, -6, 1, 0, -5, -1, -8, |
71
|
|
|
|
|
|
|
13, 8, 8, 10, 13, 0, 2, -7, |
72
|
|
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, |
73
|
|
|
|
|
|
|
); |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
my @op_knight_table = ( |
76
|
|
|
|
|
|
|
-167, -89, -34, -49, 61, -97, -15, -107, |
77
|
|
|
|
|
|
|
-73, -41, 72, 36, 23, 62, 7, -17, |
78
|
|
|
|
|
|
|
-47, 60, 37, 65, 84, 129, 73, 44, |
79
|
|
|
|
|
|
|
-9, 17, 19, 53, 37, 69, 18, 22, |
80
|
|
|
|
|
|
|
-13, 4, 16, 13, 28, 19, 21, -8, |
81
|
|
|
|
|
|
|
-23, -9, 12, 10, 19, 17, 25, -16, |
82
|
|
|
|
|
|
|
-29, -53, -12, -3, -1, 18, -14, -19, |
83
|
|
|
|
|
|
|
-105, -21, -58, -33, -17, -28, -19, -23, |
84
|
|
|
|
|
|
|
); |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
my @eg_knight_table = ( |
87
|
|
|
|
|
|
|
-58, -38, -13, -28, -31, -27, -63, -99, |
88
|
|
|
|
|
|
|
-25, -8, -25, -2, -9, -25, -24, -52, |
89
|
|
|
|
|
|
|
-24, -20, 10, 9, -1, -9, -19, -41, |
90
|
|
|
|
|
|
|
-17, 3, 22, 22, 22, 11, 8, -18, |
91
|
|
|
|
|
|
|
-18, -6, 16, 25, 16, 17, 4, -18, |
92
|
|
|
|
|
|
|
-23, -3, -1, 15, 10, -3, -20, -22, |
93
|
|
|
|
|
|
|
-42, -20, -10, -5, -2, -20, -23, -44, |
94
|
|
|
|
|
|
|
-29, -51, -23, -15, -22, -18, -50, -64, |
95
|
|
|
|
|
|
|
); |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
my @op_bishop_table = ( |
98
|
|
|
|
|
|
|
-29, 4, -82, -37, -25, -42, 7, -8, |
99
|
|
|
|
|
|
|
-26, 16, -18, -13, 30, 59, 18, -47, |
100
|
|
|
|
|
|
|
-16, 37, 43, 40, 35, 50, 37, -2, |
101
|
|
|
|
|
|
|
-4, 5, 19, 50, 37, 37, 7, -2, |
102
|
|
|
|
|
|
|
-6, 13, 13, 26, 34, 12, 10, 4, |
103
|
|
|
|
|
|
|
0, 15, 15, 15, 14, 27, 18, 10, |
104
|
|
|
|
|
|
|
4, 15, 16, 0, 7, 21, 33, 1, |
105
|
|
|
|
|
|
|
-33, -3, -14, -21, -13, -12, -39, -21, |
106
|
|
|
|
|
|
|
); |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
my @eg_bishop_table = ( |
109
|
|
|
|
|
|
|
-14, -21, -11, -8, -7, -9, -17, -24, |
110
|
|
|
|
|
|
|
-8, -4, 7, -12, -3, -13, -4, -14, |
111
|
|
|
|
|
|
|
2, -8, 0, -1, -2, 6, 0, 4, |
112
|
|
|
|
|
|
|
-3, 9, 12, 9, 14, 10, 3, 2, |
113
|
|
|
|
|
|
|
-6, 3, 13, 19, 7, 10, -3, -9, |
114
|
|
|
|
|
|
|
-12, -3, 8, 10, 13, 3, -7, -15, |
115
|
|
|
|
|
|
|
-14, -18, -7, -1, 4, -9, -15, -27, |
116
|
|
|
|
|
|
|
-23, -9, -23, -5, -9, -16, -5, -17, |
117
|
|
|
|
|
|
|
); |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
my @op_rook_table = ( |
120
|
|
|
|
|
|
|
32, 42, 32, 51, 63, 9, 31, 43, |
121
|
|
|
|
|
|
|
27, 32, 58, 62, 80, 67, 26, 44, |
122
|
|
|
|
|
|
|
-5, 19, 26, 36, 17, 45, 61, 16, |
123
|
|
|
|
|
|
|
-24, -11, 7, 26, 24, 35, -8, -20, |
124
|
|
|
|
|
|
|
-36, -26, -12, -1, 9, -7, 6, -23, |
125
|
|
|
|
|
|
|
-45, -25, -16, -17, 3, 0, -5, -33, |
126
|
|
|
|
|
|
|
-44, -16, -20, -9, -1, 11, -6, -71, |
127
|
|
|
|
|
|
|
-19, -13, 1, 17, 16, 7, -37, -26, |
128
|
|
|
|
|
|
|
); |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
my @eg_rook_table = ( |
131
|
|
|
|
|
|
|
13, 10, 18, 15, 12, 12, 8, 5, |
132
|
|
|
|
|
|
|
11, 13, 13, 11, -3, 3, 8, 3, |
133
|
|
|
|
|
|
|
7, 7, 7, 5, 4, -3, -5, -3, |
134
|
|
|
|
|
|
|
4, 3, 13, 1, 2, 1, -1, 2, |
135
|
|
|
|
|
|
|
3, 5, 8, 4, -5, -6, -8, -11, |
136
|
|
|
|
|
|
|
-4, 0, -5, -1, -7, -12, -8, -16, |
137
|
|
|
|
|
|
|
-6, -6, 0, 2, -9, -9, -11, -3, |
138
|
|
|
|
|
|
|
-9, 2, 3, -1, -5, -13, 4, -20, |
139
|
|
|
|
|
|
|
); |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
my @op_queen_table = ( |
142
|
|
|
|
|
|
|
-28, 0, 29, 12, 59, 44, 43, 45, |
143
|
|
|
|
|
|
|
-24, -39, -5, 1, -16, 57, 28, 54, |
144
|
|
|
|
|
|
|
-13, -17, 7, 8, 29, 56, 47, 57, |
145
|
|
|
|
|
|
|
-27, -27, -16, -16, -1, 17, -2, 1, |
146
|
|
|
|
|
|
|
-9, -26, -9, -10, -2, -4, 3, -3, |
147
|
|
|
|
|
|
|
-14, 2, -11, -2, -5, 2, 14, 5, |
148
|
|
|
|
|
|
|
-35, -8, 11, 2, 8, 15, -3, 1, |
149
|
|
|
|
|
|
|
-1, -18, -9, 10, -15, -25, -31, -50, |
150
|
|
|
|
|
|
|
); |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
my @eg_queen_table = ( |
153
|
|
|
|
|
|
|
-9, 22, 22, 27, 27, 19, 10, 20, |
154
|
|
|
|
|
|
|
-17, 20, 32, 41, 58, 25, 30, 0, |
155
|
|
|
|
|
|
|
-20, 6, 9, 49, 47, 35, 19, 9, |
156
|
|
|
|
|
|
|
3, 22, 24, 45, 57, 40, 57, 36, |
157
|
|
|
|
|
|
|
-18, 28, 19, 47, 31, 34, 39, 23, |
158
|
|
|
|
|
|
|
-16, -27, 15, 6, 9, 17, 10, 5, |
159
|
|
|
|
|
|
|
-22, -23, -30, -16, -16, -23, -36, -32, |
160
|
|
|
|
|
|
|
-33, -28, -22, -43, -5, -32, -20, -41, |
161
|
|
|
|
|
|
|
); |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
my @op_king_table = ( |
164
|
|
|
|
|
|
|
-65, 23, 16, -15, -56, -34, 2, 13, |
165
|
|
|
|
|
|
|
29, -1, -20, -7, -8, -4, -38, -29, |
166
|
|
|
|
|
|
|
-9, 24, 2, -16, -20, 6, 22, -22, |
167
|
|
|
|
|
|
|
-17, -20, -12, -27, -30, -25, -14, -36, |
168
|
|
|
|
|
|
|
-49, -1, -27, -39, -46, -44, -33, -51, |
169
|
|
|
|
|
|
|
-14, -14, -22, -46, -44, -30, -15, -27, |
170
|
|
|
|
|
|
|
1, 7, -8, -64, -43, -16, 9, 8, |
171
|
|
|
|
|
|
|
-15, 36, 12, -54, 8, -28, 24, 14, |
172
|
|
|
|
|
|
|
); |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
my @eg_king_table = ( |
175
|
|
|
|
|
|
|
-74, -35, -18, -18, -11, 15, 4, -17, |
176
|
|
|
|
|
|
|
-12, 17, 14, 17, 17, 38, 23, 11, |
177
|
|
|
|
|
|
|
10, 17, 23, 15, 20, 45, 44, 13, |
178
|
|
|
|
|
|
|
-8, 22, 24, 27, 26, 33, 26, 3, |
179
|
|
|
|
|
|
|
-18, -4, 21, 24, 27, 23, 9, -11, |
180
|
|
|
|
|
|
|
-19, -3, 11, 21, 23, 16, 7, -9, |
181
|
|
|
|
|
|
|
-27, -11, 4, 13, 14, 4, -5, -17, |
182
|
|
|
|
|
|
|
-53, -34, -21, -11, -28, -14, -24, -43 |
183
|
|
|
|
|
|
|
); |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
my @op_pesto_table = ( |
186
|
|
|
|
|
|
|
undef, |
187
|
|
|
|
|
|
|
\@op_pawn_table, |
188
|
|
|
|
|
|
|
\@op_knight_table, |
189
|
|
|
|
|
|
|
\@op_bishop_table, |
190
|
|
|
|
|
|
|
\@op_rook_table, |
191
|
|
|
|
|
|
|
\@op_queen_table, |
192
|
|
|
|
|
|
|
\@op_king_table |
193
|
|
|
|
|
|
|
); |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
my @eg_pesto_table = ( |
196
|
|
|
|
|
|
|
undef, |
197
|
|
|
|
|
|
|
\@eg_pawn_table, |
198
|
|
|
|
|
|
|
\@eg_knight_table, |
199
|
|
|
|
|
|
|
\@eg_bishop_table, |
200
|
|
|
|
|
|
|
\@eg_rook_table, |
201
|
|
|
|
|
|
|
\@eg_queen_table, |
202
|
|
|
|
|
|
|
\@eg_king_table |
203
|
|
|
|
|
|
|
); |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
my @op_table; |
206
|
|
|
|
|
|
|
my @eg_table; |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
# Init tables. |
209
|
|
|
|
|
|
|
for (my $piece = CP_PAWN; $piece <= CP_KING; ++$piece) { |
210
|
|
|
|
|
|
|
for (my $shift = 0; $shift < 64; ++$shift) { |
211
|
|
|
|
|
|
|
my $windex = (CP_WHITE << 9) | ($piece << 6) | $shift; |
212
|
|
|
|
|
|
|
my $bindex = (CP_BLACK << 9) | ($piece << 6) | $shift; |
213
|
|
|
|
|
|
|
$op_table[$windex] = $op_value[$piece] + $op_pesto_table[$piece]->[$shift ^ 56]; |
214
|
|
|
|
|
|
|
$eg_table[$windex] = $eg_value[$piece] + $eg_pesto_table[$piece]->[$shift ^ 56]; |
215
|
|
|
|
|
|
|
$op_table[$bindex] = $op_value[$piece] + $op_pesto_table[$piece]->[$shift]; |
216
|
|
|
|
|
|
|
$eg_table[$bindex] = $eg_value[$piece] + $eg_pesto_table[$piece]->[$shift]; |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
my @pieces = (' ', 'P', 'N', 'B', 'R', 'Q', 'K'); |
221
|
|
|
|
|
|
|
for (my $i = 0; $i < @op_table; ++$i) { |
222
|
|
|
|
|
|
|
my $op_score = $op_table[$i]; |
223
|
|
|
|
|
|
|
next if !defined $op_score; |
224
|
|
|
|
|
|
|
my $eg_score = $eg_table[$i]; |
225
|
|
|
|
|
|
|
my $shift = $i & 0x3f; |
226
|
|
|
|
|
|
|
my $piece = ($i >> 6) & 0x7; |
227
|
|
|
|
|
|
|
my $color = ($i >> 9); |
228
|
|
|
|
|
|
|
my $piece_char = $pieces[$piece]; |
229
|
|
|
|
|
|
|
$piece_char = lc $piece_char if $color; |
230
|
|
|
|
|
|
|
my $square = Chess::Plisco->shiftToSquare($shift); |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
# For all combinations of victim and promotion piece, calculate the change in |
234
|
|
|
|
|
|
|
# game phase. These values are positive and meant to be added to the phase; |
235
|
|
|
|
|
|
|
my @move_phase_deltas = (0) x 369; |
236
|
|
|
|
|
|
|
foreach my $victim (CP_NO_PIECE, CP_PAWN .. CP_QUEEN) { |
237
|
|
|
|
|
|
|
foreach my $promote (CP_NO_PIECE, CP_KNIGHT .. CP_QUEEN) { |
238
|
|
|
|
|
|
|
next if $promote && $victim == CP_PAWN; |
239
|
|
|
|
|
|
|
next if !$victim && !$promote; |
240
|
|
|
|
|
|
|
my $delta = -PHASE_INC->[$victim]; |
241
|
|
|
|
|
|
|
if ($promote) { |
242
|
|
|
|
|
|
|
$delta -= (Chess::Plisco::Engine::Position::PAWN_PHASE |
243
|
|
|
|
|
|
|
- PHASE_INC->[$promote]); |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
$move_phase_deltas[($victim << 3) | $promote] = $delta; |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
# Lookup tables for the resulting opening and endgame scores for each |
250
|
|
|
|
|
|
|
# possible move. |
251
|
|
|
|
|
|
|
my @opening_deltas; |
252
|
|
|
|
|
|
|
my @endgame_deltas; |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
foreach my $move (Chess::Plisco->moveNumbers) { |
255
|
|
|
|
|
|
|
my $is_ep; |
256
|
|
|
|
|
|
|
my $color = 1 & ($move >> 21); |
257
|
|
|
|
|
|
|
my $captured = 0x7 & ($move >> 18); |
258
|
|
|
|
|
|
|
if ($captured == CP_KING) { |
259
|
|
|
|
|
|
|
$captured = CP_PAWN; |
260
|
|
|
|
|
|
|
$is_ep = 1; |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
my ($to, $from, $promote, $piece) = ( |
263
|
|
|
|
|
|
|
Chess::Plisco->moveTo($move), |
264
|
|
|
|
|
|
|
Chess::Plisco->moveFrom($move), |
265
|
|
|
|
|
|
|
Chess::Plisco->movePromote($move), |
266
|
|
|
|
|
|
|
Chess::Plisco->movePiece($move), |
267
|
|
|
|
|
|
|
); |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
my $from_index = ($color << 9) | ($piece << 6) | $from; |
270
|
|
|
|
|
|
|
my $to_index = ($color << 9) | ($piece << 6) | $to; |
271
|
|
|
|
|
|
|
my $opening_delta = $op_table[$from_index] - $op_table[$to_index]; |
272
|
|
|
|
|
|
|
my $endgame_delta = $eg_table[$from_index] - $eg_table[$to_index]; |
273
|
|
|
|
|
|
|
if ($is_ep) { |
274
|
|
|
|
|
|
|
my $ep_to; |
275
|
|
|
|
|
|
|
if ($color == CP_WHITE) { |
276
|
|
|
|
|
|
|
$ep_to = $to - 8; |
277
|
|
|
|
|
|
|
} else { |
278
|
|
|
|
|
|
|
$ep_to = $to + 8; |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
my $ep_index = ($color << 9) | (CP_PAWN) << 6 | $ep_to; |
281
|
|
|
|
|
|
|
$opening_delta -= $op_table[$ep_index]; |
282
|
|
|
|
|
|
|
$endgame_delta -= $eg_table[$ep_index]; |
283
|
|
|
|
|
|
|
} elsif ($captured) { |
284
|
|
|
|
|
|
|
# The captured piece must be viewed from the other side. |
285
|
|
|
|
|
|
|
my $captured_index = ((!$color) << 9) | ($captured << 6) | $to; |
286
|
|
|
|
|
|
|
$opening_delta -= $op_table[$captured_index]; |
287
|
|
|
|
|
|
|
$endgame_delta -= $eg_table[$captured_index]; |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
if ($promote) { |
291
|
|
|
|
|
|
|
my $promote_index = ($color << 9) | ($promote << 6) | $to; |
292
|
|
|
|
|
|
|
my $promote_pawn_index = ($color << 9) | (CP_PAWN << 6) | $to; |
293
|
|
|
|
|
|
|
$opening_delta -= $op_table[$promote_index] |
294
|
|
|
|
|
|
|
- $op_table[$promote_pawn_index]; |
295
|
|
|
|
|
|
|
$endgame_delta -= $eg_table[$promote_index] |
296
|
|
|
|
|
|
|
- $eg_table[$promote_pawn_index]; |
297
|
|
|
|
|
|
|
} |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
# Handle castlings. |
300
|
|
|
|
|
|
|
if (CP_KING == $piece && CP_E8 == $from) { |
301
|
|
|
|
|
|
|
if (CP_C8 == $to) { |
302
|
|
|
|
|
|
|
my $rook_a8_index = (CP_BLACK << 9) | (CP_ROOK << 6) | CP_A8; |
303
|
|
|
|
|
|
|
my $rook_d8_index = (CP_BLACK << 9) | (CP_ROOK << 6) | CP_D8; |
304
|
|
|
|
|
|
|
$opening_delta -= $op_table[$rook_d8_index] |
305
|
|
|
|
|
|
|
- $op_table[$rook_a8_index]; |
306
|
|
|
|
|
|
|
$endgame_delta -= $eg_table[$rook_d8_index] |
307
|
|
|
|
|
|
|
- $eg_table[$rook_a8_index]; |
308
|
|
|
|
|
|
|
} elsif (CP_G8 == $to) { |
309
|
|
|
|
|
|
|
my $rook_h8_index = (CP_BLACK << 9) | (CP_ROOK << 6) | CP_H8; |
310
|
|
|
|
|
|
|
my $rook_f8_index = (CP_BLACK << 9) | (CP_ROOK << 6) | CP_F8; |
311
|
|
|
|
|
|
|
$opening_delta -= $op_table[$rook_f8_index] |
312
|
|
|
|
|
|
|
- $op_table[$rook_h8_index]; |
313
|
|
|
|
|
|
|
$endgame_delta -= $eg_table[$rook_f8_index] |
314
|
|
|
|
|
|
|
- $eg_table[$rook_h8_index]; |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
} elsif (CP_KING == $piece && CP_E1 == $from) { |
317
|
|
|
|
|
|
|
if (CP_C1 == $to) { |
318
|
|
|
|
|
|
|
my $rook_a1_index = (CP_WHITE << 9) | (CP_ROOK << 6) | CP_A1; |
319
|
|
|
|
|
|
|
my $rook_d1_index = (CP_WHITE << 9) | (CP_ROOK << 6) | CP_D1; |
320
|
|
|
|
|
|
|
$opening_delta -= $op_table[$rook_d1_index] |
321
|
|
|
|
|
|
|
- $op_table[$rook_a1_index]; |
322
|
|
|
|
|
|
|
$endgame_delta -= $eg_table[$rook_d1_index] |
323
|
|
|
|
|
|
|
- $eg_table[$rook_a1_index]; |
324
|
|
|
|
|
|
|
} elsif (CP_G1 == $to) { |
325
|
|
|
|
|
|
|
my $rook_h1_index = (CP_WHITE << 9) | (CP_ROOK << 6) | CP_H1; |
326
|
|
|
|
|
|
|
my $rook_f1_index = (CP_WHITE << 9) | (CP_ROOK << 6) | CP_F1; |
327
|
|
|
|
|
|
|
$opening_delta -= $op_table[$rook_f1_index] |
328
|
|
|
|
|
|
|
- $op_table[$rook_h1_index]; |
329
|
|
|
|
|
|
|
$endgame_delta -= $eg_table[$rook_f1_index] |
330
|
|
|
|
|
|
|
- $eg_table[$rook_h1_index]; |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
} |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
$opening_deltas[$move] = $color ? $opening_delta : -$opening_delta; |
335
|
|
|
|
|
|
|
$endgame_deltas[$move] = $color ? $endgame_delta : -$endgame_delta; |
336
|
|
|
|
|
|
|
} |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
sub new { |
341
|
111
|
|
|
111
|
1
|
45910
|
my ($class, @args) = @_; |
342
|
|
|
|
|
|
|
|
343
|
111
|
|
|
|
|
390
|
my $self = $class->SUPER::new(@args); |
344
|
|
|
|
|
|
|
|
345
|
111
|
|
|
|
|
173
|
my $op_phase = 0; |
346
|
111
|
|
|
|
|
160
|
my $op_score = 0; |
347
|
111
|
|
|
|
|
136
|
my $eg_score = 0; |
348
|
111
|
|
|
|
|
161
|
my $white = $self->[CP_POS_WHITE_PIECES]; |
349
|
111
|
|
|
|
|
182
|
my $black = $self->[CP_POS_BLACK_PIECES]; |
350
|
|
|
|
|
|
|
|
351
|
111
|
|
|
|
|
234
|
foreach my $piece (CP_PAWN .. CP_KING) { |
352
|
666
|
|
|
|
|
772
|
my $pieces = $self->[$piece]; |
353
|
666
|
|
|
|
|
746
|
my $white_pieces = $pieces & $white; |
354
|
666
|
|
|
|
|
737
|
my $black_pieces = $pieces & $black; |
355
|
666
|
|
|
|
|
810
|
my $phase_inc = PHASE_INC->[$piece]; |
356
|
666
|
|
|
|
|
974
|
while ($white_pieces) { |
357
|
627
|
|
|
|
|
635
|
my $shift = (do { my $B = $white_pieces & -$white_pieces; my $A = $B - 1 - ((($B - 1) >> 1) & 0x5555_5555_5555_5555); my $C = ($A & 0x3333_3333_3333_3333) + (($A >> 2) & 0x3333_3333_3333_3333); my $n = $C + ($C >> 32); $n = ($n & 0x0f0f0f0f) + (($n >> 4) & 0x0f0f0f0f); $n = ($n & 0xffff) + ($n >> 16); $n = ($n & 0xff) + ($n >> 8);}); |
|
627
|
|
|
|
|
756
|
|
|
627
|
|
|
|
|
796
|
|
|
627
|
|
|
|
|
771
|
|
|
627
|
|
|
|
|
713
|
|
|
627
|
|
|
|
|
727
|
|
|
627
|
|
|
|
|
728
|
|
|
627
|
|
|
|
|
758
|
|
358
|
627
|
|
|
|
|
766
|
my $idx = (CP_WHITE << 9) | ($piece << 6) | $shift; |
359
|
627
|
|
|
|
|
754
|
$op_score += $op_table[$idx]; |
360
|
627
|
|
|
|
|
690
|
$eg_score += $eg_table[$idx]; |
361
|
627
|
|
|
|
|
738
|
$white_pieces = (($white_pieces) & (($white_pieces) - 1)); |
362
|
627
|
|
|
|
|
954
|
$op_phase += $phase_inc; |
363
|
|
|
|
|
|
|
} |
364
|
666
|
|
|
|
|
1045
|
while ($black_pieces) { |
365
|
636
|
|
|
|
|
657
|
my $shift = (do { my $B = $black_pieces & -$black_pieces; my $A = $B - 1 - ((($B - 1) >> 1) & 0x5555_5555_5555_5555); my $C = ($A & 0x3333_3333_3333_3333) + (($A >> 2) & 0x3333_3333_3333_3333); my $n = $C + ($C >> 32); $n = ($n & 0x0f0f0f0f) + (($n >> 4) & 0x0f0f0f0f); $n = ($n & 0xffff) + ($n >> 16); $n = ($n & 0xff) + ($n >> 8);}); |
|
636
|
|
|
|
|
700
|
|
|
636
|
|
|
|
|
811
|
|
|
636
|
|
|
|
|
778
|
|
|
636
|
|
|
|
|
781
|
|
|
636
|
|
|
|
|
755
|
|
|
636
|
|
|
|
|
781
|
|
|
636
|
|
|
|
|
777
|
|
366
|
636
|
|
|
|
|
785
|
my $idx = (CP_BLACK << 9) | ($piece << 6) | $shift; |
367
|
636
|
|
|
|
|
755
|
$op_score -= $op_table[$idx]; |
368
|
636
|
|
|
|
|
683
|
$eg_score -= $eg_table[$idx]; |
369
|
636
|
|
|
|
|
720
|
$black_pieces = (($black_pieces) & (($black_pieces) - 1)); |
370
|
636
|
|
|
|
|
986
|
$op_phase += $phase_inc; |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
|
374
|
111
|
|
|
|
|
140
|
$self->[CP_POS_OPENING_SCORE] = $op_score; |
375
|
111
|
|
|
|
|
145
|
$self->[CP_POS_ENDGAME_SCORE] = $eg_score; |
376
|
|
|
|
|
|
|
|
377
|
111
|
|
|
|
|
141
|
$self->[CP_POS_GAME_PHASE] = $op_phase; |
378
|
|
|
|
|
|
|
|
379
|
111
|
|
|
|
|
287
|
return $self; |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
sub doMove { |
383
|
3533635
|
|
|
3533635
|
1
|
4983783
|
my ($self, $move) = @_; |
384
|
|
|
|
|
|
|
|
385
|
3533635
|
100
|
|
|
|
6013953
|
my $state = $self->SUPER::doMove($move) or return; |
386
|
1866910
|
|
|
|
|
2839873
|
($move) = @$state; |
387
|
1866910
|
|
|
|
|
3341132
|
$self->[CP_POS_GAME_PHASE] += $move_phase_deltas[ |
388
|
|
|
|
|
|
|
((($move >> 18) & 0x7) << 3) | (($move >> 12) & 0x7) |
389
|
|
|
|
|
|
|
]; |
390
|
1866910
|
|
|
|
|
3106614
|
my $score_index = ($move & 0x1fffff) | (!(((($self->[CP_POS_INFO] & (1 << 4)) >> 4))) << 21); |
391
|
1866910
|
|
|
|
|
3031018
|
$self->[CP_POS_OPENING_SCORE] += $opening_deltas[$score_index]; |
392
|
1866910
|
|
|
|
|
2706563
|
$self->[CP_POS_ENDGAME_SCORE] += $endgame_deltas[$score_index]; |
393
|
|
|
|
|
|
|
|
394
|
1866910
|
|
|
|
|
4061854
|
return $state; |
395
|
|
|
|
|
|
|
} |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
sub evaluate { |
398
|
742387
|
|
|
742387
|
0
|
1065832
|
my ($self) = @_; |
399
|
|
|
|
|
|
|
|
400
|
742387
|
|
|
|
|
1061118
|
my $material = (($self->[CP_POS_INFO] >> 19)); |
401
|
742387
|
|
|
|
|
913008
|
my $white_pieces = $self->[CP_POS_WHITE_PIECES]; |
402
|
742387
|
|
|
|
|
946532
|
my $black_pieces = $self->[CP_POS_BLACK_PIECES]; |
403
|
742387
|
|
|
|
|
875113
|
my $pawns = $self->[CP_POS_PAWNS]; |
404
|
742387
|
|
|
|
|
982060
|
my $knights = $self->[CP_POS_KNIGHTS]; |
405
|
742387
|
|
|
|
|
1022187
|
my $bishops = $self->[CP_POS_BISHOPS]; |
406
|
742387
|
|
|
|
|
914065
|
my $rooks = $self->[CP_POS_ROOKS]; |
407
|
742387
|
|
|
|
|
893083
|
my $queens = $self->[CP_POS_QUEENS]; |
408
|
742387
|
|
|
|
|
930884
|
my $kings = $self->[CP_POS_KINGS]; |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
# We simply assume that a position without pawns is in general a draw. |
411
|
|
|
|
|
|
|
# If one side is a minor piece ahead, it is considered a draw, when there |
412
|
|
|
|
|
|
|
# are no rooks or queens on the board. Important exception is KBB vs KN. |
413
|
|
|
|
|
|
|
# But in that case the material delta is B + B - N which is greater |
414
|
|
|
|
|
|
|
# than B. On the other hand KBB vs KB is a draw and the material balance |
415
|
|
|
|
|
|
|
# in that case is exactly one bishop. |
416
|
|
|
|
|
|
|
# These simple formulas do not take into account that there may be more |
417
|
|
|
|
|
|
|
# than two knights or bishops for one side on the board but in the |
418
|
|
|
|
|
|
|
# exceptional case that this happens, the result would be close enough |
419
|
|
|
|
|
|
|
# anyway. |
420
|
742387
|
100
|
|
|
|
1230348
|
if (!$pawns) { |
421
|
397
|
|
|
|
|
525
|
my $delta = (do { my $mask = $material >> CP_INT_SIZE * CP_CHAR_BIT - 1; ($material + $mask) ^ $mask;}); |
|
397
|
|
|
|
|
573
|
|
|
397
|
|
|
|
|
639
|
|
422
|
397
|
50
|
100
|
|
|
1831
|
if ($delta < CP_PAWN_VALUE |
|
|
|
66
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
423
|
|
|
|
|
|
|
|| (!$rooks && !$queens |
424
|
|
|
|
|
|
|
&& (($delta <= CP_BISHOP_VALUE) |
425
|
|
|
|
|
|
|
|| ($delta == 2 * CP_KNIGHT_VALUE) |
426
|
|
|
|
|
|
|
|| ($delta == CP_KNIGHT_VALUE + CP_BISHOP_VALUE)))) { |
427
|
37
|
|
|
|
|
147
|
return Chess::Plisco::Engine::Tree::DRAW(); |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
} |
430
|
|
|
|
|
|
|
|
431
|
742350
|
|
|
|
|
998783
|
my $op_phase = $self->[CP_POS_GAME_PHASE]; |
432
|
|
|
|
|
|
|
|
433
|
742350
|
50
|
|
|
|
1247187
|
$op_phase = TOTAL_PHASE if $op_phase > TOTAL_PHASE; |
434
|
742350
|
|
|
|
|
905133
|
my $eg_phase = TOTAL_PHASE - $op_phase; |
435
|
|
|
|
|
|
|
|
436
|
742350
|
|
|
|
|
1163754
|
my $score = ($self->[CP_POS_OPENING_SCORE] * $op_phase |
437
|
|
|
|
|
|
|
+ $self->[CP_POS_ENDGAME_SCORE] * $eg_phase) / TOTAL_PHASE; |
438
|
|
|
|
|
|
|
|
439
|
742350
|
100
|
|
|
|
1717880
|
return (((($self->[CP_POS_INFO] & (1 << 4)) >> 4))) ? -$score : $score; |
440
|
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
sub openingDelta { |
445
|
0
|
|
|
0
|
0
|
|
my ($self, $index) = @_; |
446
|
|
|
|
|
|
|
|
447
|
0
|
|
|
|
|
|
return $opening_deltas[$index]; |
448
|
|
|
|
|
|
|
} |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
sub endgameDelta { |
451
|
0
|
|
|
0
|
0
|
|
my ($self, $index) = @_; |
452
|
|
|
|
|
|
|
|
453
|
0
|
|
|
|
|
|
return $endgame_deltas[$index]; |
454
|
|
|
|
|
|
|
} |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
1; |