├── LICENSE
├── README
├── Web.config
├── archived
├── garbochess-062210-01.js
├── garbochess-062310-01.js
├── garbochess-062410-01.js
├── garbochess-062510-01.js
├── garbochess-071210-01.js
├── garbochess-071610-01.js
└── garbochess-071610-02.js
├── book
└── book.bin
├── chess.html
├── debug.html
├── img
├── bishop_black.png
├── bishop_white.png
├── blank.gif
├── king_black.png
├── king_white.png
├── knight_black.png
├── knight_white.png
├── pawn_black.png
├── pawn_white.png
├── queen_black.png
├── queen_white.png
├── rook_black.png
├── rook_white.png
├── sprites.png
└── transpBlue50.png
├── js
├── bench.js
├── boardui.js
├── garbochess-old.js
├── garbochess.js
├── jquery-1.8.2.min.js
├── jquery-ui-1.8.24.custom.min.js
├── jquery.js
├── testing.js
└── tourney.js
├── rungames.html
├── tests
├── openings.epd
├── openings.js
└── perft-positions.js
├── webpage.sln
└── webpage.suo
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011 Gary Linscott
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions
6 | are met:
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright
10 | notice, this list of conditions and the following disclaimer in the
11 | documentation and/or other materials provided with the distribution.
12 | 3. The name of the author may not be used to endorse or promote products
13 | derived from this software without specific prior written permission.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | Thanks
2 | ------
3 | - Stockfish authors
4 | - Crafty authors
5 |
6 | Version 1.0
7 | -----------
8 | - Rewrote move generation for a big speed-up
9 | - Pure material evaluation
10 | - Null-move + Razoring + LMR in main search
11 | - Hash table
12 |
13 | Version 2.0
14 | -----------
15 | - Mobility evaluation (thanks Fruit)
16 | - Bishop pair
17 | - Rep-draw detection
18 | - Better null-move pruning (thanks Stockfish)
19 | - Better LMR (and again, thanks Stockfish)
20 | - Bugfix with using hash move
21 | - Some speed optimizations
22 |
23 | Version 3.0
24 | -----------
25 | - 604.5/1000 or ~70 ELO better than previous version
26 | - Killer moves
27 | - Tuned PSQ tables/mobility
28 | - Better king eval in endgame (won't stay on back row)
29 | - Show '#' for checkmate
30 | - Improved UI (new game, switch black/white, choose time/move)
31 | - Fixed crashes from using invalid hash moves
32 | - Other small bug fixes
33 | - Speed optimizations
34 |
35 | Version 4.0
36 | -----------
37 | - 594.5/1000 %:59.45 or 66 Elo better than previous
38 | - SEE added (QSearch pruning, losing captures in main search)
39 | - No nullmove in pawn endgames
40 | - Fixed hashtable bugs (RNG was bad)
41 | - Fixed starting position when playing black
42 | - Added ability to analyze position for browsers that support it
43 | - Added support for pasting FEN positions
44 |
45 | Version 5.0
46 | -----------
47 | - Added checks in first ply of q-search (+15)
48 |
49 | Version 5.1
50 | -----------
51 | - Bugfix to hashtable storing (no elo change, but big help in endgames)
52 |
53 | Version 6.0
54 | -----------
55 | - Bonuses for knight attacking pieces (+20)
56 | - Bonus for bishop pins (+40)
57 |
58 | TODO:
59 | -----
60 | - Only extend checks with SEE > 0?
61 | - Single reply to check should be marked as dangerous.
62 |
--------------------------------------------------------------------------------
/Web.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/archived/garbochess-062210-01.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // Perf TODO:
4 | // Merge material updating with psq values
5 | // Put move scoring inline in generator
6 | // Remove need for fliptable in psq tables. Access them by color
7 | // Unroll non-capture move generator
8 |
9 | // Non-perf todo:
10 | // Rep-draw
11 | // Checks in first q?
12 | // SEE?
13 | // Mobility?
14 |
15 | var g_debug = true;
16 | var g_timeout = 20;
17 |
18 | function GetFen() {
19 | var result = "";
20 | for (row = 0; row < 8; row++) {
21 | if (row != 0)
22 | result += '/';
23 | var empty = 0;
24 | for (col = 0; col < 8; col++) {
25 | var piece = g_board[((row + 2) << 4) + col + 4];
26 | if (piece == 0) {
27 | empty++;
28 | }
29 | else {
30 | if (empty != 0)
31 | result += empty;
32 | empty = 0;
33 |
34 | var pieceChar = [" ", "p", "n", "b", "r", "q", "k", " "][(piece & 0x7)];
35 | result += ((piece & colorWhite) != 0) ? pieceChar.toUpperCase() : pieceChar;
36 | }
37 | }
38 | if (empty != 0) {
39 | result += empty;
40 | }
41 | }
42 |
43 | result += g_toMove == colorBlack ? " b" : " w";
44 | result += " ";
45 | if (g_castleRights == 0) {
46 | result += "-";
47 | }
48 | else {
49 | if ((g_castleRights & 1) != 0)
50 | result += "K";
51 | if ((g_castleRights & 2) != 0)
52 | result += "Q";
53 | if ((g_castleRights & 4) != 0)
54 | result += "k";
55 | if ((g_castleRights & 8) != 0)
56 | result += "q";
57 | }
58 |
59 | result += " ";
60 |
61 | if (g_enPassentSquare == -1) {
62 | result += '-';
63 | }
64 | else {
65 | result += FormatSquare(g_enPassentSquare);
66 | }
67 |
68 | return result;
69 | }
70 |
71 | function GetMoveSAN(move, validMoves) {
72 | var from = move & 0xFF;
73 | var to = (move >> 8) & 0xFF;
74 |
75 | if (move & moveflagCastleKing) return "O-O";
76 | if (move & moveflagCastleQueen) return "O-O-O";
77 |
78 | var pieceType = g_board[from] & 0x7;
79 | var result = ["", "", "N", "B", "R", "Q", "K", ""][pieceType];
80 |
81 | var dupe = false, rowDiff = true, colDiff = true;
82 | if (validMoves == null) {
83 | validMoves = GenerateValidMoves();
84 | }
85 | for (var i = 0; i < validMoves.length; i++) {
86 | var moveFrom = validMoves[i] & 0xFF;
87 | var moveTo = (validMoves[i] >> 8) & 0xFF;
88 | if (moveFrom != from &&
89 | moveTo == to &&
90 | (g_board[moveFrom] & 0x7) == pieceType) {
91 | dupe = true;
92 | if ((moveFrom & 0xF0) == (from & 0xF0)) {
93 | rowDiff = false;
94 | }
95 | if ((moveFrom & 0x0F) == (from & 0x0F)) {
96 | colDiff = false;
97 | }
98 | }
99 | }
100 |
101 | if (dupe) {
102 | if (colDiff) {
103 | result += FormatSquare(from).charAt(0);
104 | } else if (rowDiff) {
105 | result += FormatSquare(from).charAt(1);
106 | } else {
107 | result += FormatSquare(from);
108 | }
109 | } else if (pieceType == piecePawn && g_board[to] != 0) {
110 | result += FormatSquare(from).charAt(0);
111 | }
112 |
113 | if (g_board[to] != 0 || (move & moveflagEPC)) {
114 | result += "x";
115 | }
116 |
117 | result += FormatSquare(to);
118 |
119 | if (move & moveflagPromotion) {
120 | if (move & moveflagPromoteBishop) result += "=B";
121 | else if (move & moveflagPromoteKnight) result += "=N";
122 | else if (move & moveflagPromoteQueen) result += "=Q";
123 | else result += "=R";
124 | }
125 |
126 | MakeMove(move);
127 | if (g_inCheck) result += "+";
128 | UnmakeMove(move);
129 |
130 | return result;
131 | }
132 |
133 | function FormatSquare(square) {
134 | var letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
135 | return letters[(square & 0xF) - 4] + ((9 - (square >> 4)) + 1);
136 | }
137 |
138 | function FormatMove(move) {
139 | return FormatSquare(move & 0xFF) + FormatSquare((move >> 8) & 0xFF);
140 | }
141 |
142 | function PVFromHash(move, ply) {
143 | if (ply == 0)
144 | return "";
145 |
146 | var pvString = " " + GetMoveSAN(move);
147 | MakeMove(move);
148 |
149 | var hashNode = g_hashTable[g_hashKey & g_hashMask];
150 | if (hashNode != null && hashNode.lock == g_hashKey && hashNode.bestMove != null) {
151 | pvString += PVFromHash(hashNode.bestMove, ply - 1);
152 | }
153 |
154 | UnmakeMove(move);
155 |
156 | return pvString;
157 | }
158 |
159 | //
160 | // Searching code
161 | //
162 |
163 | var g_startTime;
164 |
165 | var g_nodeCount;
166 | var g_qNodeCount;
167 | var g_searchValid;
168 | var g_globalPly = 0;
169 |
170 | function Search(finishMoveCallback) {
171 | var ply = 99;
172 | var lastEval;
173 | var alpha = minEval;
174 | var beta = maxEval;
175 |
176 | g_globalPly++;
177 | g_nodeCount = 0;
178 | g_qNodeCount = 0;
179 | g_collisions = 0;
180 | g_searchValid = true;
181 |
182 | var bestMove;
183 | var value;
184 |
185 | g_startTime = (new Date()).getTime();
186 |
187 | var i;
188 | for (i = 1; i <= ply && g_searchValid; i++) {
189 | var tmp = AlphaBeta(i, alpha, beta);
190 | if (g_searchValid) {
191 | value = tmp;
192 |
193 | if (value > alpha && value < beta) {
194 | alpha = value - 500;
195 | beta = value + 500;
196 | } else if (alpha != minEval) {
197 | alpha = minEval;
198 | beta = maxEval;
199 | i--;
200 | continue;
201 | }
202 |
203 | //else It's a checkmate or mate score
204 |
205 | if (g_hashTable[g_hashKey & g_hashMask] != null) {
206 | bestMove = g_hashTable[g_hashKey & g_hashMask].bestMove;
207 | } else {
208 | break;
209 | }
210 | }
211 | }
212 |
213 | finishMoveCallback(bestMove, value, (new Date()).getTime() - g_startTime, i);
214 | }
215 |
216 | var minEval = -2000000;
217 | var maxEval = +2000000;
218 |
219 | var minMateBuffer = minEval + 2000;
220 | var maxMateBuffer = maxEval - 2000;
221 |
222 | var materialTable = [0, 850, 3250, 3250, 5500, 9750, 600000];
223 |
224 | var pawnAdj =
225 | [
226 | 0, 0, 0, 0, 0, 0, 0, 0,
227 | 100, 300, 400, 600, 600, 400, 300, 100,
228 | 40, 150, 200, 300, 300, 200, 150, 40,
229 | 15, 75, 100, 150, 150, 100, 75, 15,
230 | 10, 40, 60, 100, 100, 60, 40, 10,
231 | 5, 10, 15, -10, -10, 15, 10, 5,
232 | 0, 0, 0, -80, -80, 0, 0, 0,
233 | 0, 0, 0, 0, 0, 0, 0, 0
234 | ];
235 |
236 | var knightAdj =
237 | [-50, -50, -50, -50, -50, -50, -50, -50,
238 | -50, 0, 0, 0, 0, 0, 0, -50,
239 | -50, 0, 120, 120, 120, 120, 0, -50,
240 | -50, 0, 60, 120, 120, 60, 0, -50,
241 | -50, 0, 60, 120, 120, 60, 0, -50,
242 | -50, 0, 60, 60, 60, 60, 0, -50,
243 | -50, 0, 0, 0, 0, 0, 0, -50,
244 | -50, -60, -50, -50, -50, -50, -60, -50
245 | ];
246 |
247 | var bishopAdj =
248 | [0, 0, 0, 0, 0, 0, 0, 0,
249 | 0, 0, 0, 0, 0, 0, 0, 0,
250 | 0, 0, 40, 40, 40, 40, 0, 0,
251 | 0, 0, 40, 80, 80, 40, 0, 0,
252 | 0, 0, 40, 80, 80, 40, 0, 0,
253 | 0, 0, 60, 40, 40, 60, 0, 0,
254 | 0, 40, 0, 0, 0, 0, 40, 0,
255 | 20, 0, -20, 0, 0, -20, 0, 20
256 | ];
257 |
258 | var rookAdj =
259 | [0, 0, 0, 0, 0, 0, 0, 0,
260 | 100, 100, 100, 100, 100, 100, 100, 100,
261 | 0, 0, 10, 20, 20, 10, 0, 0,
262 | 0, 0, 10, 20, 20, 10, 0, 0,
263 | 0, 0, 10, 20, 20, 10, 0, 0,
264 | 0, 0, 10, 20, 20, 10, 0, 0,
265 | 0, 0, 10, 20, 20, 10, 0, 0,
266 | -10, 0, 10, 20, 20, 10, 0, -10
267 | ];
268 |
269 | var kingAdj =
270 | [0, 0, 0, 0, 0, 0, 0, 0,
271 | -800, -800, -800, -800, -800, -800, -800, -800,
272 | -1500, -1500, -1500, -1500, -1500, -1500, -1500, -1500,
273 | -1200, -1200, -1200, -1200, -1200, -1200, -1200, -1200,
274 | -900, -900, -900, -900, -900, -900, -900, -900,
275 | -600, -600, -600, -600, -600, -600, -600, -600,
276 | -300, -300, -300, -300, -300, -300, -300, -300,
277 | 0, 0, 0, 0, 0, 0, 0, 0
278 | ];
279 |
280 | var emptyAdj =
281 | [0, 0, 0, 0, 0, 0, 0, 0,
282 | 0, 0, 0, 0, 0, 0, 0, 0,
283 | 0, 0, 0, 0, 0, 0, 0, 0,
284 | 0, 0, 0, 0, 0, 0, 0, 0,
285 | 0, 0, 0, 0, 0, 0, 0, 0,
286 | 0, 0, 0, 0, 0, 0, 0, 0,
287 | 0, 0, 0, 0, 0, 0, 0, 0,
288 | 0, 0, 0, 0, 0, 0, 0, 0,
289 | ];
290 |
291 | var pieceSquareAdj = new Array(8);
292 |
293 | // Returns the square flipped
294 | var flipTable = new Array(256);
295 |
296 | function Mobility(color) {
297 | var result = 0;
298 | var from, to, mob, pieceIdx;
299 | var enemy = color == 8 ? 0x10 : 0x8
300 | var mobUnit = color == 8 ? g_mobUnit[0] : g_mobUnit[1];
301 |
302 | // Knight mobility
303 | mob = 0;
304 | pieceIdx = (color | 2) << 4;
305 | from = g_pieceList[pieceIdx++];
306 | while (from != 0) {
307 | mob += mobUnit[g_board[from + 31]];
308 | mob += mobUnit[g_board[from + 33]];
309 | mob += mobUnit[g_board[from + 14]];
310 | mob += mobUnit[g_board[from - 14]];
311 | mob += mobUnit[g_board[from - 31]];
312 | mob += mobUnit[g_board[from - 33]];
313 | mob += mobUnit[g_board[from + 18]];
314 | mob += mobUnit[g_board[from - 18]];
315 | from = g_pieceList[pieceIdx++];
316 | }
317 | result += 70 * mob;
318 |
319 | // Bishop mobility
320 | mob = 0;
321 | pieceIdx = (color | 3) << 4;
322 | from = g_pieceList[pieceIdx++];
323 | while (from != 0) {
324 | to = from - 15; while (g_board[to] == 0) { to -= 15; mob++; } if (g_board[to] & enemy) mob++;
325 | to = from - 17; while (g_board[to] == 0) { to -= 17; mob++; } if (g_board[to] & enemy) mob++;
326 | to = from + 15; while (g_board[to] == 0) { to += 15; mob++; } if (g_board[to] & enemy) mob++;
327 | to = from + 17; while (g_board[to] == 0) { to += 17; mob++; } if (g_board[to] & enemy) mob++;
328 | from = g_pieceList[pieceIdx++];
329 | }
330 | result += 50 * mob;
331 |
332 | // Rook mobility
333 | mob = 0;
334 | pieceIdx = (color | 4) << 4;
335 | from = g_pieceList[pieceIdx++];
336 | while (from != 0) {
337 | to = from - 1; while (g_board[to] == 0) { to--; mob++; } if (g_board[to] & enemy) mob++;
338 | to = from + 1; while (g_board[to] == 0) { to++; mob++; } if (g_board[to] & enemy) mob++;
339 | to = from + 16; while (g_board[to] == 0) { to += 16; mob++; } if (g_board[to] & enemy) mob++;
340 | to = from - 16; while (g_board[to] == 0) { to -= 16; mob++; } if (g_board[to] & enemy) mob++;
341 | from = g_pieceList[pieceIdx++];
342 | }
343 | result += 25 * mob;
344 |
345 | // Queen mobility
346 | mob = 0;
347 | pieceIdx = (color | 5) << 4;
348 | from = g_pieceList[pieceIdx++];
349 | while (from != 0) {
350 | to = from - 15; while (g_board[to] == 0) { to -= 15; mob++; } if (g_board[to] & enemy) mob++;
351 | to = from - 17; while (g_board[to] == 0) { to -= 17; mob++; } if (g_board[to] & enemy) mob++;
352 | to = from + 15; while (g_board[to] == 0) { to += 15; mob++; } if (g_board[to] & enemy) mob++;
353 | to = from + 17; while (g_board[to] == 0) { to += 17; mob++; } if (g_board[to] & enemy) mob++;
354 | to = from - 1; while (g_board[to] == 0) { to--; mob++; } if (g_board[to] & enemy) mob++;
355 | to = from + 1; while (g_board[to] == 0) { to++; mob++; } if (g_board[to] & enemy) mob++;
356 | to = from + 16; while (g_board[to] == 0) { to += 16; mob++; } if (g_board[to] & enemy) mob++;
357 | to = from - 16; while (g_board[to] == 0) { to -= 16; mob++; } if (g_board[to] & enemy) mob++;
358 | from = g_pieceList[pieceIdx++];
359 | }
360 | result += 20 * mob;
361 |
362 | return result;
363 | }
364 |
365 | function Evaluate() {
366 | var curEval = g_baseEval;
367 |
368 | /* var kingPosTerm = 0;
369 | // Black queen gone, then cancel white's penalty for king movement
370 | if (g_pieceList[(colorWhite | pieceQueen) << 4] == 0)
371 | kingPosTerm -= kingAdj[g_pieceList[(colorWhite | pieceKing) << 4]];
372 | // White queen gone, then cancel black's penalty for king movement
373 | if (g_pieceList[pieceQueen << 4] == 0)
374 | kingPosTerm += kingAdj[flipTable[g_pieceList[pieceKing << 4]]];*/
375 |
376 |
377 | var bishopPairTerm = 0;
378 | // Black bishop pair
379 | if (g_pieceCount[pieceBishop] >= 2)
380 | bishopPairTerm -= 500;
381 | // White bishop pair
382 | if (g_pieceCount[pieceBishop | colorWhite] >= 2)
383 | bishopPairTerm += 500;
384 |
385 | var mobility = Mobility(8) - Mobility(0);
386 |
387 | if (g_toMove == 0) {
388 | // Black
389 | curEval -= mobility;
390 | curEval -= bishopPairTerm;
391 | }
392 | else {
393 | curEval += mobility;
394 | curEval += bishopPairTerm;
395 | }
396 |
397 | return curEval;
398 | }
399 |
400 | var historyTable = new Array(32);
401 |
402 | function ScoreMove(move) {
403 | var captured = (move >> 16) & 0x7;
404 | var piece = g_board[move & 0xFF];
405 | var score;
406 | if (captured != 0) {
407 | var pieceType = piece & 0x7;
408 | score = (captured << 5) - pieceType;
409 | if (captured < pieceType)
410 | score -= 1000;
411 | } else {
412 | score = historyTable[piece & 0xF][(move >> 8) & 0xFF];
413 | }
414 | return score;
415 | }
416 |
417 | function QSearch(alpha, beta) {
418 | g_qNodeCount++;
419 |
420 | var realEval = Evaluate();
421 |
422 | if (realEval >= beta)
423 | return realEval;
424 |
425 | if (realEval > alpha)
426 | alpha = realEval;
427 |
428 | var moves = new Array();
429 | GenerateCaptureMoves(moves, null);
430 |
431 | var moveScores = new Array();
432 | for (var i = 0; i < moves.length; i++) {
433 | var captured = (moves[i] >> 16) & 0x7;
434 | var pieceType = g_board[moves[i] & 0xFF] & 0x7;
435 | moveScores[i] = (captured << 5) - pieceType;
436 | }
437 |
438 | for (var i = 0; i < moves.length; i++) {
439 | var bestMove = i;
440 | for (var j = moves.length - 1; j > i; j--) {
441 | if (moveScores[j] > moveScores[bestMove]) {
442 | bestMove = j;
443 | }
444 | }
445 | {
446 | var tmpMove = moves[i];
447 | moves[i] = moves[bestMove];
448 | moves[bestMove] = tmpMove;
449 |
450 | var tmpScore = moveScores[i];
451 | moveScores[i] = moveScores[bestMove];
452 | moveScores[bestMove] = tmpScore;
453 | }
454 |
455 | if (!MakeMove(moves[i])) {
456 | continue;
457 | }
458 |
459 | var value = -QSearch(-beta, -alpha);
460 |
461 | UnmakeMove(moves[i]);
462 |
463 | if (value > realEval) {
464 | if (value >= beta)
465 | return value;
466 |
467 | if (value > alpha)
468 | alpha = value;
469 |
470 | realEval = value;
471 | }
472 | }
473 |
474 | return realEval;
475 | }
476 |
477 | function StoreHash(value, flags, ply, move, force) {
478 | var hashNode = g_hashTable[g_hashKey & g_hashMask];
479 | if (hashNode == null || ply >= hashNode.ply || force) {
480 | g_hashTable[g_hashKey & g_hashMask] = new HashEntry(g_hashKey, value, flags, ply, move);
481 | }
482 | }
483 |
484 | function IsHashMoveValid(hashMove) {
485 | // Do some basic sanity checks
486 | var from = hashMove & 0xFF;
487 | var to = (hashMove >> 8) & 0xFF;
488 | var ourPiece = g_board[from];
489 | var pieceType = ourPiece & 0x7;
490 | if (pieceType < piecePawn || pieceType > pieceKing) return false;
491 | if (ourPiece & colorWhite) {
492 | if (g_toMove == colorWhite) return false;
493 | } else {
494 | if (g_toMove != colorWhite) return false;
495 | }
496 | if (g_inCheck) return false;
497 | if (g_board[to] != ((hashMove >> 16) & 0xFF)) return false;
498 | if (hashMove >> 24) return false;
499 | if (pieceType == piecePawn) {
500 | // TODO - This handles pawn captures, but not pawn pushes
501 | return IsSquareAttackableFrom(to, from);
502 | } else {
503 | // This validates that this piece type can actually make the attack
504 | return IsSquareAttackableFrom(to, from);
505 | }
506 | }
507 |
508 | function IsRepDraw() {
509 | var stop = g_moveCount - 1 - g_move50;
510 | stop = stop < 0 ? 0 : stop;
511 | for (var i = g_moveCount - 5; i >= stop; i -= 2) {
512 | if (g_repMoveStack[i] == g_hashKey)
513 | return true;
514 | }
515 | return false;
516 | }
517 |
518 | function AllCutNode(ply, beta, allowNull) {
519 | if (ply <= 0) {
520 | return QSearch(beta - 1, beta);
521 | }
522 |
523 | if ((g_nodeCount & 127) == 127) {
524 | if ((new Date()).getTime() - g_startTime > g_timeout) {
525 | // Time cutoff
526 | g_searchValid = false;
527 | return beta - 1;
528 | }
529 | }
530 |
531 | g_nodeCount++;
532 |
533 | if (IsRepDraw())
534 | return 0;
535 |
536 | var hashMove = null;
537 | var hashNode = g_hashTable[g_hashKey & g_hashMask];
538 | if (hashNode != null && hashNode.lock == g_hashKey) {
539 | hashMove = hashNode.bestMove;
540 | if (hashNode.depth >= ply) {
541 | if (hashNode.flags == hashflagExact)
542 | return hashNode.value;
543 | if (hashNode.flags == hashflagAlpha && hashNode.value < beta)
544 | return beta - 1;
545 | if (hashNode.flags == hashflagBeta && hashNode.value >= beta)
546 | return beta;
547 | }
548 | }
549 |
550 | // TODO - positional gain?
551 |
552 | if (!g_inCheck &&
553 | allowNull &&
554 | beta > minMateBuffer &&
555 | beta < maxMateBuffer) {
556 | // Try some razoring
557 | if (hashMove == null &&
558 | ply < 4) {
559 | var razorMargin = 2500 + 200 * ply;
560 | if (g_baseEval < beta - razorMargin) {
561 | var razorBeta = beta - razorMargin;
562 | var v = QSearch(razorBeta - 1, razorBeta);
563 | if (v < razorBeta)
564 | return v;
565 | }
566 | }
567 |
568 | // TODO - static null move
569 |
570 | // Null move
571 | if (ply > 1 &&
572 | g_baseEval >= beta - (ply >= 4 ? 2500 : 0)) {
573 | var r = 3 + (ply >= 6 ? 1 : ply / 4);
574 | if (g_baseEval - beta > 1000) r++;
575 |
576 | g_toMove = 8 - g_toMove;
577 | g_baseEval = -g_baseEval;
578 | if (g_toMove)
579 | g_hashKey -= g_zobristBlack;
580 | else
581 | g_hashKey += g_zobristBlack;
582 |
583 | var value = -AllCutNode(ply - r, -(beta - 1), false);
584 |
585 | if (g_toMove)
586 | g_hashKey += g_zobristBlack;
587 | else
588 | g_hashKey -= g_zobristBlack;
589 | g_toMove = 8 - g_toMove;
590 | g_baseEval = -g_baseEval;
591 |
592 | if (value >= beta)
593 | return beta;
594 | }
595 | }
596 |
597 | var moveMade = false;
598 | var moves = new Array();
599 | var moveCount = 0, atMove = -1;
600 |
601 | var moveScores;
602 |
603 | var stage = 0;
604 | var realEval = minEval;
605 | for (; ; ) {
606 | if (++atMove == moveCount) {
607 | stage++;
608 | if (stage == 1) {
609 | if (hashMove != null && IsHashMoveValid(hashMove)) {
610 | moves[0] = hashMove;
611 | moveCount = 1;
612 | }
613 | if (moveCount != 1) {
614 | stage = 2;
615 | }
616 | }
617 |
618 | if (stage == 2) {
619 | GenerateCaptureMoves(moves, null);
620 | moveCount = moves.length;
621 | moveScores = new Array(moveCount);
622 | // Move ordering
623 | for (var i = atMove; i < moveCount; i++) moveScores[i] = ScoreMove(moves[i]);
624 | // No moves, onto next stage
625 | if (atMove == moveCount) stage = 3;
626 | }
627 |
628 | if (stage == 3) {
629 | GenerateAllMoves(moves);
630 | moveCount = moves.length;
631 | moveScores = new Array(moveCount);
632 | // Move ordering
633 | for (var i = atMove; i < moveCount; i++) moveScores[i] = ScoreMove(moves[i]);
634 | // No moves, onto next stage
635 | if (atMove == moveCount) stage = 4;
636 | }
637 |
638 | // TODO: losing captures
639 |
640 | if (stage == 4) break;
641 | }
642 |
643 | var bestMove = atMove;
644 | for (var j = atMove + 1; j < moveCount; j++) {
645 | if (moveScores[j] > moveScores[bestMove]) {
646 | bestMove = j;
647 | }
648 | }
649 |
650 | if (bestMove != atMove) {
651 | var tmpMove = moves[atMove];
652 | moves[atMove] = moves[bestMove];
653 | moves[bestMove] = tmpMove;
654 |
655 | var tmpScore = moveScores[atMove];
656 | moveScores[atMove] = moveScores[bestMove];
657 | moveScores[bestMove] = tmpScore;
658 | }
659 |
660 | var plyToSearch = ply - 1;
661 |
662 | if (!MakeMove(moves[atMove])) {
663 | continue;
664 | }
665 |
666 | var value;
667 | var doFullSearch = true;
668 |
669 | if (g_inCheck) {
670 | // Check extensions
671 | plyToSearch++;
672 | } else {
673 | // Late move reductions
674 | if (stage == 3 && atMove > 5 && ply >= 3) {
675 | var reduced = plyToSearch - (atMove > 14 ? 2 : 1);
676 | value = -AllCutNode(reduced, -(beta - 1), true);
677 | doFullSearch = (value >= beta);
678 | }
679 | }
680 |
681 | if (doFullSearch) {
682 | value = -AllCutNode(plyToSearch, -(beta - 1), true);
683 | }
684 |
685 | moveMade = true;
686 |
687 | UnmakeMove(moves[atMove]);
688 |
689 | if (!g_searchValid) {
690 | return beta - 1;
691 | }
692 |
693 | if (value > realEval) {
694 | if (value >= beta) {
695 | var histPiece = g_board[moves[atMove] & 0xFF] & 0xF;
696 | var histTo = (moves[atMove] >> 8) & 0xFF;
697 | historyTable[histPiece][histTo] += ply * ply;
698 | if (historyTable[histPiece][histTo] > 32767) {
699 | historyTable[histPiece][histTo] >>= 1;
700 | }
701 | StoreHash(value, hashflagBeta, ply, moves[atMove], false);
702 | return value;
703 | }
704 |
705 | realEval = value;
706 | hashMove = moves[atMove];
707 | }
708 | }
709 |
710 | if (!moveMade) {
711 | // If we have no valid moves it's either stalemate or checkmate
712 | if (g_inCheck)
713 | // Checkmate.
714 | return minEval + 1;
715 | else
716 | // Stalemate
717 | return 0;
718 | }
719 |
720 | StoreHash(realEval, hashflagAlpha, ply, hashMove, false);
721 |
722 | return realEval;
723 | }
724 |
725 | function AlphaBeta(ply, alpha, beta) {
726 | if (ply <= 0) {
727 | return QSearch(alpha, beta);
728 | }
729 |
730 | g_nodeCount++;
731 |
732 | if (IsRepDraw())
733 | return 0;
734 |
735 | var hashMove = null;
736 | var hashFlag = hashflagAlpha;
737 | var hashNode = g_hashTable[g_hashKey & g_hashMask];
738 | if (hashNode != null && hashNode.lock == g_hashKey) {
739 | hashMove = hashNode.bestMove;
740 | }
741 |
742 | var inCheck = g_inCheck;
743 |
744 | var moveMade = false;
745 | var moves = new Array();
746 | var moveCount = 0, atMove = -1;
747 |
748 | var moveScores = new Array(256);
749 |
750 | var stage = 0;
751 | var realEval = minEval;
752 | for (; ; ) {
753 | if (++atMove == moveCount) {
754 | stage++;
755 | if (stage == 1) {
756 | if (hashMove != null && IsHashMoveValid(hashMove)) {
757 | moves[0] = hashMove;
758 | moveCount = 1;
759 | }
760 | if (moveCount != 1) {
761 | stage = 2;
762 | }
763 | }
764 |
765 | if (stage == 2) {
766 | GenerateCaptureMoves(moves, null);
767 | moveCount = moves.length;
768 | // Move ordering
769 | for (var i = atMove; i < moveCount; i++) moveScores[i] = ScoreMove(moves[i]);
770 | // No moves, onto next stage
771 | if (atMove == moveCount) stage = 3;
772 | }
773 |
774 | if (stage == 3) {
775 | GenerateAllMoves(moves);
776 | moveCount = moves.length;
777 | // Move ordering
778 | for (var i = atMove; i < moveCount; i++) moveScores[i] = ScoreMove(moves[i]);
779 | // No moves, onto next stage
780 | if (atMove == moveCount) stage = 4;
781 | }
782 |
783 | // TODO: losing captures
784 |
785 | if (stage == 4) break;
786 | }
787 |
788 | var bestMove = atMove;
789 | for (var j = atMove + 1; j < moveCount; j++) {
790 | if (moveScores[j] > moveScores[bestMove]) {
791 | bestMove = j;
792 | }
793 | }
794 | {
795 | var tmpMove = moves[atMove];
796 | moves[atMove] = moves[bestMove];
797 | moves[bestMove] = tmpMove;
798 |
799 | var tmpScore = moveScores[atMove];
800 | moveScores[atMove] = moveScores[bestMove];
801 | moveScores[bestMove] = tmpScore;
802 | }
803 |
804 | var plyToSearch = ply - 1;
805 |
806 | if (!MakeMove(moves[atMove])) {
807 | continue;
808 | }
809 |
810 | if (g_inCheck) {
811 | // Check extensions
812 | plyToSearch++;
813 | }
814 |
815 | var value;
816 | if (moveMade) {
817 | value = -AllCutNode(plyToSearch, -alpha, true);
818 | if (value > alpha) {
819 | value = -AlphaBeta(plyToSearch, -beta, -alpha);
820 | } else {
821 | value = alpha;
822 | }
823 | } else {
824 | value = -AlphaBeta(plyToSearch, -beta, -alpha);
825 | }
826 |
827 | moveMade = true;
828 |
829 | UnmakeMove(moves[atMove]);
830 |
831 | if (!g_searchValid) {
832 | return alpha;
833 | }
834 |
835 | if (value > realEval) {
836 | if (value >= beta) {
837 | var histPiece = g_board[moves[atMove] & 0xFF] & 0xF;
838 | var histTo = (moves[atMove] >> 8) & 0xFF;
839 | historyTable[histPiece][histTo] += ply * ply;
840 | if (historyTable[histPiece][histTo] > 32767) {
841 | historyTable[histPiece][histTo] >>= 1;
842 | }
843 | StoreHash(value, hashflagBeta, ply, moves[atMove], true);
844 | return value;
845 | }
846 |
847 | if (value > alpha) {
848 | hashFlag = hashflagExact;
849 | alpha = value;
850 | }
851 |
852 | realEval = value;
853 | hashMove = moves[atMove];
854 | }
855 | }
856 |
857 | if (!moveMade) {
858 | // If we have no valid moves it's either stalemate or checkmate
859 | if (inCheck)
860 | // Checkmate.
861 | return minEval + 1;
862 | else
863 | // Stalemate
864 | return 0;
865 | }
866 |
867 | StoreHash(realEval, hashFlag, ply, hashMove, true);
868 |
869 | return realEval;
870 | }
871 |
872 | //
873 | // Board code
874 | //
875 |
876 | // This somewhat funky scheme means that a piece is indexed by it's lower 4 bits when accessing in arrays. The fifth bit (black bit)
877 | // is used to allow quick edge testing on the board.
878 | var colorBlack = 0x10;
879 | var colorWhite = 0x08;
880 |
881 | var pieceEmpty = 0x00;
882 | var piecePawn = 0x01;
883 | var pieceKnight = 0x02;
884 | var pieceBishop = 0x03;
885 | var pieceRook = 0x04;
886 | var pieceQueen = 0x05;
887 | var pieceKing = 0x06;
888 |
889 | var g_vectorDelta = new Array(256);
890 |
891 | var g_bishopDeltas = [-15, -17, 15, 17];
892 | var g_knightDeltas = [31, 33, 14, -14, -31, -33, 18, -18];
893 | var g_rookDeltas = [-1, +1, -16, +16];
894 | var g_queenDeltas = [-1, +1, -15, +15, -17, +17, -16, +16];
895 |
896 | var g_castleRightsMask = [
897 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
898 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
899 | 0, 0, 0, 0, 7, 15, 15, 15, 3, 15, 15, 11, 0, 0, 0, 0,
900 | 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0,
901 | 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0,
902 | 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0,
903 | 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0,
904 | 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0,
905 | 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0,
906 | 0, 0, 0, 0, 13, 15, 15, 15, 12, 15, 15, 14, 0, 0, 0, 0,
907 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
908 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
909 |
910 | var moveflagEP = 0x1 << 24;
911 | var moveflagEPC = 0x2 << 24;
912 | var moveflagCastleKing = 0x4 << 24;
913 | var moveflagCastleQueen = 0x8 << 24;
914 | var moveflagPromotion = 0x10 << 24;
915 | var moveflagPromoteKnight = 0x20 << 24;
916 | var moveflagPromoteQueen = 0x40 << 24;
917 | var moveflagPromoteBishop = 0x80 << 24;
918 |
919 | var g_randa = 1103515245, g_randc = 12345, g_rands = 0x1BADF00D;
920 | function getRandomInt() {
921 | g_rands = (g_randa * g_rands + g_randc) & 0xFFFFFFFF;
922 | return g_rands;
923 | }
924 |
925 | function getRandomLong() {
926 | return (getRandomInt() * (1024 * 256)) + getRandomInt();
927 | }
928 |
929 | // Position variables
930 | var g_board = new Array(256); // Sentinel 0x80, pieces are in low 4 bits, 0x8 for color, 0x7 bits for piece type
931 | var g_toMove; // side to move, 0 or 8, 0 = black, 8 = white
932 | var g_castleRights; // bitmask representing castling rights, 1 = wk, 2 = wq, 4 = bk, 8 = bq
933 | var g_enPassentSquare;
934 | var g_baseEval;
935 | var g_hashKey;
936 | var g_inCheck;
937 |
938 | // Utility variables
939 | var g_moveCount = 0;
940 | var g_moveUndoStack = new Array();
941 |
942 | var g_move50 = 0;
943 | var g_repMoveStack = new Array();
944 |
945 | var g_hashSize = 1 << 22;
946 | var g_hashMask = g_hashSize - 1;
947 | var g_hashTable;
948 |
949 | var g_zobrist;
950 | var g_zobristBlack;
951 |
952 | // Evaulation variables
953 | var g_mobUnit;
954 |
955 | function State() {
956 | this.board = new Array(256);
957 | for (var i = 0; i < 256; i++)
958 | this.board[i] = g_board[i];
959 | this.toMove = g_toMove;
960 | this.castleRights = g_castleRights;
961 | this.enPassentSquare = g_enPassentSquare;
962 | this.baseEval = g_baseEval;
963 | this.hashKey = g_hashKey;
964 | this.inCheck = g_inCheck;
965 | }
966 |
967 | function DebugValidate() {
968 | // Validate that pieceLists are correct
969 | for (var piece = 0; piece < 0xF; piece++) {
970 | for (var i = 0; i < g_pieceCount[piece]; i++) {
971 | var boardPiece = piece < 0x8 ? (piece | colorBlack) : piece;
972 | if (g_pieceList[(piece << 4) | i] == 0)
973 | return 1;
974 | if (g_board[g_pieceList[(piece << 4) | i]] != boardPiece)
975 | return 2;
976 | }
977 | for (var i = g_pieceCount[piece]; i < 16; i++) {
978 | if (g_pieceList[(piece << 4) | i] != 0)
979 | return 3;
980 | }
981 | }
982 |
983 | // Validate that board matches pieceList
984 | for (var i = 0; i < 256; i++) {
985 | var row = i >> 4;
986 | var col = i & 0xF;
987 | if (row >= 2 && row < 10 && col >= 4 && col < 12) {
988 | if (!(g_board[i] == 0 ||
989 | (g_board[i] & (colorBlack | colorWhite)) != 0)) {
990 | return 4;
991 | } else if (g_board[i] != 0) {
992 | if (g_pieceList[((g_board[i] & 0xF) << 4) | g_pieceIndex[i]] != i)
993 | return 6;
994 | }
995 | } else {
996 | if (g_board[i] != 0x80)
997 | return 5;
998 | }
999 | }
1000 |
1001 | if (SetHash() != g_hashKey) {
1002 | return 6;
1003 | }
1004 |
1005 | return 0;
1006 | }
1007 |
1008 | State.prototype.CompareTo = function (other) {
1009 | for (var i = 0; i < 256; i++)
1010 | if (this.board[i] != other.board[i])
1011 | return 1;
1012 | if (this.toMove != other.toMove)
1013 | return 3;
1014 | if (this.castleRights != other.castleRights)
1015 | return 4;
1016 | if (this.enPassentSquare != other.enPassentSquare)
1017 | return 5;
1018 | if (this.baseEval != other.baseEval)
1019 | return 6;
1020 | if (this.hashKey != other.hashKey)
1021 | return 7;
1022 | if (this.inCheck != other.inCheck)
1023 | return 8;
1024 | return 0;
1025 | }
1026 |
1027 | var hashflagAlpha = 1;
1028 | var hashflagBeta = 2;
1029 | var hashflagExact = 3;
1030 |
1031 | function HashEntry(lock, value, flags, depth, bestMove, globalPly) {
1032 | this.lock = lock;
1033 | this.value = value;
1034 | this.flags = flags;
1035 | this.depth = depth;
1036 | this.bestMove = bestMove;
1037 | }
1038 |
1039 | function MakeSquare(row, column) {
1040 | return ((row + 2) << 4) | (column + 4);
1041 | }
1042 |
1043 | function MakeTable(table) {
1044 | var result = new Array(256);
1045 | for (var i = 0; i < 256; i++) {
1046 | result[i] = 0;
1047 | }
1048 | for (var row = 0; row < 8; row++) {
1049 | for (var col = 0; col < 8; col++) {
1050 | result[MakeSquare(row, col)] = table[row * 8 + col];
1051 | }
1052 | }
1053 | return result;
1054 | }
1055 |
1056 | function ResetGame() {
1057 | g_hashTable = new Array(g_hashSize);
1058 |
1059 | for (var i = 0; i < 32; i++) {
1060 | historyTable[i] = new Array(256);
1061 | for (var j = 0; j < 256; j++)
1062 | historyTable[i][j] = 0;
1063 | }
1064 | g_zobrist = new Array(256);
1065 | for (var i = 0; i < 256; i++) {
1066 | g_zobrist[i] = new Array(16);
1067 | for (var j = 0; j < 16; j++) {
1068 | g_zobrist[i][j] = getRandomLong();
1069 | }
1070 | }
1071 | g_zobristBlack = getRandomLong();
1072 |
1073 | for (var row = 0; row < 8; row++) {
1074 | for (var col = 0; col < 8; col++) {
1075 | var square = MakeSquare(row, col);
1076 | flipTable[square] = MakeSquare(7 - row, col);
1077 | }
1078 | }
1079 |
1080 | pieceSquareAdj[piecePawn] = MakeTable(pawnAdj);
1081 | pieceSquareAdj[pieceKnight] = MakeTable(knightAdj);
1082 | pieceSquareAdj[pieceBishop] = MakeTable(bishopAdj);
1083 | pieceSquareAdj[pieceRook] = MakeTable(rookAdj);
1084 | pieceSquareAdj[pieceQueen] = MakeTable(emptyAdj);
1085 | pieceSquareAdj[pieceKing] = MakeTable(kingAdj);
1086 |
1087 | var pieceDeltas = [[], [], g_knightDeltas, g_bishopDeltas, g_rookDeltas, g_queenDeltas, g_queenDeltas];
1088 |
1089 | for (var i = 0; i < 256; i++) {
1090 | g_vectorDelta[i] = new Object();
1091 | g_vectorDelta[i].delta = 0;
1092 | g_vectorDelta[i].pieceMask = new Array(2);
1093 | g_vectorDelta[i].pieceMask[0] = 0;
1094 | g_vectorDelta[i].pieceMask[1] = 0;
1095 | }
1096 |
1097 | // Initialize the vector delta table
1098 | for (var row = 0; row < 0x80; row += 0x10)
1099 | for (var col = 0; col < 0x8; col++) {
1100 | var square = row | col;
1101 |
1102 | // Pawn moves
1103 | var index = square - (square - 17) + 128;
1104 | g_vectorDelta[index].pieceMask[colorWhite >> 3] |= (1 << piecePawn);
1105 | index = square - (square - 15) + 128;
1106 | g_vectorDelta[index].pieceMask[colorWhite >> 3] |= (1 << piecePawn);
1107 |
1108 | index = square - (square + 17) + 128;
1109 | g_vectorDelta[index].pieceMask[0] |= (1 << piecePawn);
1110 | index = square - (square + 15) + 128;
1111 | g_vectorDelta[index].pieceMask[0] |= (1 << piecePawn);
1112 |
1113 | for (var i = pieceKnight; i <= pieceKing; i++) {
1114 | for (var dir = 0; dir < pieceDeltas[i].length; dir++) {
1115 | var target = square + pieceDeltas[i][dir];
1116 | while (!(target & 0x88)) {
1117 | index = square - target + 128;
1118 |
1119 | g_vectorDelta[index].pieceMask[colorWhite >> 3] |= (1 << i);
1120 | g_vectorDelta[index].pieceMask[0] |= (1 << i);
1121 |
1122 | var flip = -1;
1123 | if (square < target)
1124 | flip = 1;
1125 |
1126 | if ((square & 0xF0) == (target & 0xF0)) {
1127 | // On the same row
1128 | g_vectorDelta[index].delta = flip * 1;
1129 | } else if ((square & 0x0F) == (target & 0x0F)) {
1130 | // On the same column
1131 | g_vectorDelta[index].delta = flip * 16;
1132 | } else if ((square % 15) == (target % 15)) {
1133 | g_vectorDelta[index].delta = flip * 15;
1134 | } else if ((square % 17) == (target % 17)) {
1135 | g_vectorDelta[index].delta = flip * 17;
1136 | }
1137 |
1138 | if (i == pieceKnight) {
1139 | g_vectorDelta[index].delta = pieceDeltas[i][dir];
1140 | break;
1141 | }
1142 |
1143 | if (i == pieceKing)
1144 | break;
1145 |
1146 | target += pieceDeltas[i][dir];
1147 | }
1148 | }
1149 | }
1150 | }
1151 |
1152 | InitializeEval();
1153 | InitializeFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
1154 | }
1155 |
1156 | function InitializeEval() {
1157 | g_mobUnit = new Array(2);
1158 | for (var i = 0; i < 2; i++) {
1159 | g_mobUnit[i] = new Array();
1160 | var enemy = i == 0 ? 0x10 : 8;
1161 | var friend = i == 0 ? 8 : 0x10;
1162 | g_mobUnit[i][0] = 1;
1163 | g_mobUnit[i][0x80] = 0;
1164 | g_mobUnit[i][enemy | piecePawn] = 1;
1165 | g_mobUnit[i][enemy | pieceBishop] = 1;
1166 | g_mobUnit[i][enemy | pieceKnight] = 1;
1167 | g_mobUnit[i][enemy | pieceRook] = 1;
1168 | g_mobUnit[i][enemy | pieceQueen] = 1;
1169 | g_mobUnit[i][enemy | pieceKing] = 1;
1170 | g_mobUnit[i][friend | piecePawn] = 0;
1171 | g_mobUnit[i][friend | pieceBishop] = 0;
1172 | g_mobUnit[i][friend | pieceKnight] = 0;
1173 | g_mobUnit[i][friend | pieceRook] = 0;
1174 | g_mobUnit[i][friend | pieceQueen] = 0;
1175 | g_mobUnit[i][friend | pieceKing] = 0;
1176 | }
1177 | }
1178 |
1179 | function SetHash() {
1180 | var hashKey = 0;
1181 | for (var i = 0; i < 256; i++) {
1182 | var piece = g_board[i];
1183 | if (piece & 0x18) {
1184 | hashKey += g_zobrist[i][piece & 0xF];
1185 | }
1186 | }
1187 |
1188 | if (!g_toMove)
1189 | hashKey += g_zobristBlack;
1190 | return hashKey;
1191 | }
1192 |
1193 | function InitializeFromFen(fen) {
1194 | var chunks = fen.split(' ');
1195 |
1196 | for (var i = 0; i < 256; i++)
1197 | g_board[i] = 0x80;
1198 |
1199 | var row = 0;
1200 | var col = 0;
1201 |
1202 | var pieces = chunks[0];
1203 | for (var i = 0; i < pieces.length; i++) {
1204 | var c = pieces.charAt(i);
1205 |
1206 | if (c == '/') {
1207 | row++;
1208 | col = 0;
1209 | }
1210 | else {
1211 | if (c >= '0' && c <= '9') {
1212 | for (var j = 0; j < parseInt(c); j++) {
1213 | g_board[((row + 2) * 0x10) + (col + 4)] = 0;
1214 | col++;
1215 | }
1216 | }
1217 | else {
1218 | var isBlack = c >= 'a' && c <= 'z';
1219 | var piece = isBlack ? colorBlack : colorWhite;
1220 | if (!isBlack)
1221 | c = pieces.toLowerCase().charAt(i);
1222 | switch (c) {
1223 | case 'p':
1224 | piece |= piecePawn;
1225 | break;
1226 | case 'b':
1227 | piece |= pieceBishop;
1228 | break;
1229 | case 'n':
1230 | piece |= pieceKnight;
1231 | break;
1232 | case 'r':
1233 | piece |= pieceRook;
1234 | break;
1235 | case 'q':
1236 | piece |= pieceQueen;
1237 | break;
1238 | case 'k':
1239 | piece |= pieceKing;
1240 | break;
1241 | }
1242 |
1243 | g_board[((row + 2) * 0x10) + (col + 4)] = piece;
1244 | col++;
1245 | }
1246 | }
1247 | }
1248 |
1249 | InitializePieceList();
1250 |
1251 | g_toMove = chunks[1].charAt(0) == 'w' ? colorWhite : 0;
1252 |
1253 | g_castleRights = 0;
1254 | if (chunks[2].indexOf('K') != -1)
1255 | g_castleRights |= 1;
1256 | if (chunks[2].indexOf('Q') != -1)
1257 | g_castleRights |= 2;
1258 | if (chunks[2].indexOf('k') != -1)
1259 | g_castleRights |= 4;
1260 | if (chunks[2].indexOf('q') != -1)
1261 | g_castleRights |= 8;
1262 |
1263 | g_enPassentSquare = -1;
1264 | if (chunks[3].indexOf('-') == -1) {
1265 | g_enPassentSquare = parseInt(chunks[3], 16);
1266 | }
1267 |
1268 | g_hashKey = SetHash();
1269 |
1270 | g_move50 = 0;
1271 | g_baseEval = 0;
1272 | for (var i = 0; i < 256; i++) {
1273 | if (g_board[i] & colorWhite) {
1274 | g_baseEval += pieceSquareAdj[g_board[i] & 0x7][i];
1275 | } else if (g_board[i] & colorBlack) {
1276 | g_baseEval -= pieceSquareAdj[g_board[i] & 0x7][flipTable[i]];
1277 | }
1278 | }
1279 |
1280 | g_inCheck = IsSquareAttackable(g_pieceList[(g_toMove | pieceKing) << 4], 8 - g_toMove);
1281 | }
1282 |
1283 | var g_pieceIndex = new Array(256);
1284 | var g_pieceList = new Array(2 * 8 * 16);
1285 | var g_pieceCount = new Array(2 * 8);
1286 |
1287 | function InitializePieceList() {
1288 | for (var i = 0; i < 16; i++) {
1289 | g_pieceCount[i] = 0;
1290 | for (var j = 0; j < 16; j++) {
1291 | // 0 is used as the terminator for piece lists
1292 | g_pieceList[(i << 4) | j] = 0;
1293 | }
1294 | }
1295 |
1296 | for (var i = 0; i < 256; i++) {
1297 | g_pieceIndex[i] = 0;
1298 | if (g_board[i] & (colorWhite | colorBlack)) {
1299 | var piece = g_board[i] & 0xF;
1300 |
1301 | g_pieceList[(piece << 4) | g_pieceCount[piece]] = i;
1302 | g_pieceIndex[i] = g_pieceCount[piece];
1303 | g_pieceCount[piece]++;
1304 | }
1305 | }
1306 | }
1307 |
1308 | function MakeMove(move) {
1309 | var me = g_toMove >> 3;
1310 | var otherColor = 8 - g_toMove;
1311 |
1312 | g_enPassentSquare = -1;
1313 |
1314 | var flags = move & 0xFF000000;
1315 | var captured = (move >> 16) & 0xFF;
1316 | var to = (move >> 8) & 0xFF;
1317 | var from = move & 0xFF;
1318 | var piece = g_board[from];
1319 | var epcEnd = to;
1320 |
1321 | g_moveUndoStack[g_moveCount] = new UndoHistory(g_enPassentSquare, g_castleRights, g_inCheck, g_baseEval, g_hashKey, g_move50);
1322 | g_moveCount++;
1323 |
1324 | if (flags) {
1325 | if (flags & moveflagEP) {
1326 | if (me)
1327 | g_enPassentSquare = to + 0x10;
1328 | else
1329 | g_enPassentSquare = to - 0x10;
1330 | }
1331 | else if (flags & moveflagEPC) {
1332 | if (me)
1333 | epcEnd = to + 0x10;
1334 | else
1335 | epcEnd = to - 0x10;
1336 |
1337 | g_board[epcEnd] = pieceEmpty;
1338 | } else if (flags & moveflagCastleKing) {
1339 | if (IsSquareAttackable(from + 1, otherColor) ||
1340 | IsSquareAttackable(from + 2, otherColor)) {
1341 | g_moveCount--;
1342 | return false;
1343 | }
1344 |
1345 | var rook = g_board[to + 1];
1346 |
1347 | g_hashKey -= g_zobrist[to + 1][rook & 0xF];
1348 | g_hashKey += g_zobrist[to - 1][rook & 0xF];
1349 |
1350 | g_board[to - 1] = rook;
1351 | g_board[to + 1] = pieceEmpty;
1352 |
1353 | g_baseEval -= pieceSquareAdj[rook & 0x7][me == 0 ? flipTable[to + 1] : (to + 1)];
1354 | g_baseEval += pieceSquareAdj[rook & 0x7][me == 0 ? flipTable[to - 1] : (to - 1)];
1355 |
1356 | var rookIndex = g_pieceIndex[to + 1];
1357 | g_pieceIndex[to - 1] = rookIndex;
1358 | g_pieceList[((rook & 0xF) << 4) | rookIndex] = to - 1;
1359 | } else if (flags & moveflagCastleQueen) {
1360 | if (IsSquareAttackable(from - 1, otherColor) ||
1361 | IsSquareAttackable(from - 2, otherColor)) {
1362 | g_moveCount--;
1363 | return false;
1364 | }
1365 |
1366 | var rook = g_board[to - 2];
1367 |
1368 | g_hashKey -= g_zobrist[to - 2][rook & 0xF];
1369 | g_hashKey += g_zobrist[to + 1][rook & 0xF];
1370 |
1371 | g_board[to + 1] = rook;
1372 | g_board[to - 2] = pieceEmpty;
1373 |
1374 | g_baseEval -= pieceSquareAdj[rook & 0x7][me == 0 ? flipTable[to - 2] : (to - 2)];
1375 | g_baseEval += pieceSquareAdj[rook & 0x7][me == 0 ? flipTable[to + 1] : (to + 1)];
1376 |
1377 | var rookIndex = g_pieceIndex[to - 2];
1378 | g_pieceIndex[to + 1] = rookIndex;
1379 | g_pieceList[((rook & 0xF) << 4) | rookIndex] = to + 1;
1380 | }
1381 | }
1382 |
1383 | if (captured) {
1384 | // Remove our piece from the piece list
1385 | var capturedType = captured & 0xF;
1386 | g_pieceCount[capturedType]--;
1387 | var lastPieceSquare = g_pieceList[(capturedType << 4) | g_pieceCount[capturedType]];
1388 | g_pieceIndex[lastPieceSquare] = g_pieceIndex[epcEnd];
1389 | g_pieceList[(capturedType << 4) | g_pieceIndex[lastPieceSquare]] = lastPieceSquare;
1390 | g_pieceList[(capturedType << 4) | g_pieceCount[capturedType]] = 0;
1391 |
1392 | g_baseEval += materialTable[captured & 0x7];
1393 | g_baseEval += pieceSquareAdj[captured & 0x7][me ? flipTable[epcEnd] : epcEnd];
1394 |
1395 | g_hashKey -= g_zobrist[epcEnd][capturedType];
1396 | g_move50 = 0;
1397 | } else if ((piece & 0x7) == piecePawn) {
1398 | g_move50 = 0;
1399 | }
1400 |
1401 | g_hashKey -= g_zobrist[from][piece & 0xF];
1402 | g_hashKey += g_zobrist[to][piece & 0xF];
1403 | if (g_toMove) {
1404 | g_hashKey += g_zobristBlack;
1405 | }
1406 | else {
1407 | g_hashKey -= g_zobristBlack;
1408 | }
1409 |
1410 | g_castleRights &= g_castleRightsMask[from] & g_castleRightsMask[to];
1411 |
1412 | g_baseEval -= pieceSquareAdj[piece & 0x7][me == 0 ? flipTable[from] : from];
1413 |
1414 | // Move our piece in the piece list
1415 | g_pieceIndex[to] = g_pieceIndex[from];
1416 | g_pieceList[((piece & 0xF) << 4) | g_pieceIndex[to]] = to;
1417 |
1418 | if (flags & moveflagPromotion) {
1419 | var newPiece = piece & (~0x7);
1420 | if (flags & moveflagPromoteKnight)
1421 | newPiece |= pieceKnight;
1422 | else if (flags & moveflagPromoteQueen)
1423 | newPiece |= pieceQueen;
1424 | else if (flags & moveflagPromoteBishop)
1425 | newPiece |= pieceBishop;
1426 | else
1427 | newPiece |= pieceRook;
1428 |
1429 | g_hashKey -= g_zobrist[to][piece & 0xF];
1430 | g_board[to] = newPiece;
1431 | g_hashKey += g_zobrist[to][newPiece & 0xF];
1432 |
1433 | g_baseEval += pieceSquareAdj[newPiece & 0x7][me == 0 ? flipTable[to] : to];
1434 | g_baseEval -= materialTable[piecePawn];
1435 | g_baseEval += materialTable[newPiece & 0x7];
1436 |
1437 | var pawnType = piece & 0xF;
1438 | var promoteType = newPiece & 0xF;
1439 |
1440 | g_pieceCount[pawnType]--;
1441 |
1442 | var lastPawnSquare = g_pieceList[(pawnType << 4) | g_pieceCount[pawnType]];
1443 | g_pieceIndex[lastPawnSquare] = g_pieceIndex[to];
1444 | g_pieceList[(pawnType << 4) | g_pieceIndex[lastPawnSquare]] = lastPawnSquare;
1445 | g_pieceList[(pawnType << 4) | g_pieceCount[pawnType]] = 0;
1446 | g_pieceIndex[to] = g_pieceCount[promoteType];
1447 | g_pieceList[(promoteType << 4) | g_pieceIndex[to]] = to;
1448 | g_pieceCount[promoteType]++;
1449 | } else {
1450 | g_board[to] = g_board[from];
1451 |
1452 | g_baseEval += pieceSquareAdj[piece & 0x7][me == 0 ? flipTable[to] : to];
1453 | }
1454 | g_board[from] = pieceEmpty;
1455 |
1456 | g_toMove = otherColor;
1457 | g_baseEval = -g_baseEval;
1458 |
1459 | if ((piece & 0x7) == pieceKing || g_inCheck) {
1460 | if (IsSquareAttackable(g_pieceList[(pieceKing | (8 - g_toMove)) << 4], otherColor)) {
1461 | UnmakeMove(move);
1462 | return false;
1463 | }
1464 | } else {
1465 | var kingPos = g_pieceList[(pieceKing | (8 - g_toMove)) << 4];
1466 |
1467 | if (ExposesCheck(from, kingPos)) {
1468 | UnmakeMove(move);
1469 | return false;
1470 | }
1471 |
1472 | if (epcEnd != to) {
1473 | if (ExposesCheck(epcEnd, kingPos)) {
1474 | UnmakeMove(move);
1475 | return false;
1476 | }
1477 | }
1478 | }
1479 |
1480 | g_inCheck = false;
1481 |
1482 | if (flags <= moveflagEPC) {
1483 | var theirKingPos = g_pieceList[(pieceKing | g_toMove) << 4];
1484 |
1485 | // First check if the piece we moved can attack the enemy king
1486 | g_inCheck = IsSquareAttackableFrom(theirKingPos, to);
1487 |
1488 | if (!g_inCheck) {
1489 | // Now check if the square we moved from exposes check on the enemy king
1490 | g_inCheck = ExposesCheck(from, theirKingPos);
1491 |
1492 | if (!g_inCheck) {
1493 | // Finally, ep. capture can cause another square to be exposed
1494 | if (epcEnd != to) {
1495 | g_inCheck = ExposesCheck(epcEnd, theirKingPos);
1496 | }
1497 | }
1498 | }
1499 | }
1500 | else {
1501 | // Castle or promotion, slow check
1502 | g_inCheck = IsSquareAttackable(g_pieceList[(pieceKing | g_toMove) << 4], 8 - g_toMove);
1503 | }
1504 |
1505 | g_repMoveStack[g_moveCount - 1] = g_hashKey;
1506 | g_move50++;
1507 |
1508 | return true;
1509 | }
1510 |
1511 | function UnmakeMove(move) {
1512 | g_toMove = 8 - g_toMove;
1513 | g_baseEval = -g_baseEval;
1514 |
1515 | g_moveCount--;
1516 | g_enPassentSquare = g_moveUndoStack[g_moveCount].ep;
1517 | g_castleRights = g_moveUndoStack[g_moveCount].castleRights;
1518 | g_inCheck = g_moveUndoStack[g_moveCount].inCheck;
1519 | g_baseEval = g_moveUndoStack[g_moveCount].baseEval;
1520 | g_hashKey = g_moveUndoStack[g_moveCount].hashKey;
1521 | g_move50 = g_moveUndoStack[g_moveCount].move50;
1522 |
1523 | var otherColor = 8 - g_toMove;
1524 | var me = g_toMove >> 3;
1525 | var them = otherColor >> 3;
1526 |
1527 | var flags = move & 0xFF000000;
1528 | var captured = (move >> 16) & 0xFF;
1529 | var to = (move >> 8) & 0xFF;
1530 | var from = move & 0xFF;
1531 |
1532 | var piece = g_board[to];
1533 |
1534 | if (flags) {
1535 | if (flags & moveflagCastleKing) {
1536 | var rook = g_board[to - 1];
1537 | g_board[to + 1] = rook;
1538 | g_board[to - 1] = pieceEmpty;
1539 |
1540 | var rookIndex = g_pieceIndex[to - 1];
1541 | g_pieceIndex[to + 1] = rookIndex;
1542 | g_pieceList[((rook & 0xF) << 4) | rookIndex] = to + 1;
1543 | }
1544 | else if (flags & moveflagCastleQueen) {
1545 | var rook = g_board[to + 1];
1546 | g_board[to - 2] = rook;
1547 | g_board[to + 1] = pieceEmpty;
1548 |
1549 | var rookIndex = g_pieceIndex[to + 1];
1550 | g_pieceIndex[to - 2] = rookIndex;
1551 | g_pieceList[((rook & 0xF) << 4) | rookIndex] = to - 2;
1552 | }
1553 | }
1554 |
1555 | if (flags & moveflagPromotion) {
1556 | piece = (g_board[to] & (~0x7)) | piecePawn;
1557 | g_board[from] = piece;
1558 |
1559 | var pawnType = g_board[from] & 0xF;
1560 | var promoteType = g_board[to] & 0xF;
1561 |
1562 | g_pieceCount[promoteType]--;
1563 |
1564 | var lastPromoteSquare = g_pieceList[(promoteType << 4) | g_pieceCount[promoteType]];
1565 | g_pieceIndex[lastPromoteSquare] = g_pieceIndex[to];
1566 | g_pieceList[(promoteType << 4) | g_pieceIndex[lastPromoteSquare]] = lastPromoteSquare;
1567 | g_pieceList[(promoteType << 4) | g_pieceCount[promoteType]] = 0;
1568 | g_pieceIndex[to] = g_pieceCount[pawnType];
1569 | g_pieceList[(pawnType << 4) | g_pieceIndex[to]] = to;
1570 | g_pieceCount[pawnType]++;
1571 | }
1572 | else {
1573 | g_board[from] = g_board[to];
1574 | }
1575 |
1576 | var epcEnd = to;
1577 | if (flags & moveflagEPC) {
1578 | if (g_toMove == colorWhite)
1579 | epcEnd = to + 0x10;
1580 | else
1581 | epcEnd = to - 0x10;
1582 | g_board[to] = pieceEmpty;
1583 | }
1584 |
1585 | g_board[epcEnd] = captured;
1586 |
1587 | // Move our piece in the piece list
1588 | g_pieceIndex[from] = g_pieceIndex[to];
1589 | g_pieceList[((piece & 0xF) << 4) | g_pieceIndex[from]] = from;
1590 |
1591 | if (captured) {
1592 | // Restore our piece to the piece list
1593 | var captureType = captured & 0xF;
1594 | g_pieceIndex[epcEnd] = g_pieceCount[captureType];
1595 | g_pieceList[(captureType << 4) | g_pieceCount[captureType]] = epcEnd;
1596 | g_pieceCount[captureType]++;
1597 | }
1598 | }
1599 |
1600 | function ExposesCheck(from, kingPos) {
1601 | var index = kingPos - from + 128;
1602 | // If a queen can't reach it, nobody can!
1603 | if ((g_vectorDelta[index].pieceMask[0] & (1 << (pieceQueen))) != 0) {
1604 | var delta = g_vectorDelta[index].delta;
1605 | var pos = kingPos + delta;
1606 | while (g_board[pos] == 0) pos += delta;
1607 |
1608 | var piece = g_board[pos];
1609 | if (((piece & (g_board[kingPos] ^ 0x18)) & 0x18) == 0)
1610 | return false;
1611 |
1612 | // Now see if the piece can actually attack the king
1613 | var backwardIndex = pos - kingPos + 128;
1614 | return (g_vectorDelta[backwardIndex].pieceMask[(piece >> 3) & 1] & (1 << (piece & 0x7))) != 0;
1615 | }
1616 | return false;
1617 | }
1618 |
1619 | function IsSquareAttackableFrom(target, from) {
1620 | var index = from - target + 128;
1621 | var piece = g_board[from];
1622 | if (g_vectorDelta[index].pieceMask[(piece >> 3) & 1] & (1 << (piece & 0x7))) {
1623 | // Yes, this square is pseudo-attackable. Now, check for real attack
1624 | var inc = g_vectorDelta[index].delta;
1625 | do {
1626 | from += inc;
1627 | if (from == target)
1628 | return true;
1629 | } while (g_board[from] == 0);
1630 | }
1631 |
1632 | return false;
1633 | }
1634 |
1635 | function IsSquareAttackable(target, color) {
1636 | // Attackable by pawns?
1637 | var inc = color ? -16 : 16;
1638 | var pawn = (color ? colorWhite : colorBlack) | 1;
1639 | if (g_board[target - (inc - 1)] == pawn)
1640 | return true;
1641 | if (g_board[target - (inc + 1)] == pawn)
1642 | return true;
1643 |
1644 | // Attackable by pieces?
1645 | for (var i = 2; i <= 6; i++) {
1646 | var index = (color | i) << 4;
1647 | var square = g_pieceList[index];
1648 | while (square != 0) {
1649 | if (IsSquareAttackableFrom(target, square))
1650 | return true;
1651 | square = g_pieceList[++index];
1652 | }
1653 | }
1654 | return false;
1655 | }
1656 |
1657 | function GenerateMove(from, to, captured) {
1658 | return from | (to << 8);
1659 | }
1660 |
1661 | function GenerateMove(from, to, captured) {
1662 | return from | (to << 8) | (captured << 16);
1663 | }
1664 |
1665 | function GenerateMove(from, to, captured, flags) {
1666 | return from | (to << 8) | (captured << 16) | flags;
1667 | }
1668 |
1669 | function GenerateValidMoves() {
1670 | var moveList = new Array();
1671 | var allMoves = new Array();
1672 | GenerateCaptureMoves(allMoves, null);
1673 | GenerateAllMoves(allMoves);
1674 |
1675 | for (var i = allMoves.length - 1; i >= 0; i--) {
1676 | if (MakeMove(allMoves[i])) {
1677 | moveList[moveList.length] = allMoves[i];
1678 | UnmakeMove(allMoves[i]);
1679 | }
1680 | }
1681 |
1682 | return moveList;
1683 | }
1684 |
1685 | function GenerateAllMoves(moveStack) {
1686 | var from, to, piece, pieceIdx;
1687 |
1688 | // Pawn quiet moves
1689 | pieceIdx = (g_toMove | 1) << 4;
1690 | from = g_pieceList[pieceIdx++];
1691 | while (from != 0) {
1692 | GeneratePawnMoves(moveStack, from);
1693 | from = g_pieceList[pieceIdx++];
1694 | }
1695 |
1696 | // Knight quiet moves
1697 | pieceIdx = (g_toMove | 2) << 4;
1698 | from = g_pieceList[pieceIdx++];
1699 | while (from != 0) {
1700 | to = from + 31; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1701 | to = from + 33; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1702 | to = from + 14; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1703 | to = from - 14; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1704 | to = from - 31; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1705 | to = from - 33; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1706 | to = from + 18; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1707 | to = from - 18; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1708 | from = g_pieceList[pieceIdx++];
1709 | }
1710 |
1711 | // Bishop quiet moves
1712 | pieceIdx = (g_toMove | 3) << 4;
1713 | from = g_pieceList[pieceIdx++];
1714 | while (from != 0) {
1715 | to = from - 15; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to -= 15; }
1716 | to = from - 17; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to -= 17; }
1717 | to = from + 15; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to += 15; }
1718 | to = from + 17; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to += 17; }
1719 | from = g_pieceList[pieceIdx++];
1720 | }
1721 |
1722 | // Rook quiet moves
1723 | pieceIdx = (g_toMove | 4) << 4;
1724 | from = g_pieceList[pieceIdx++];
1725 | while (from != 0) {
1726 | to = from - 1; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to--; }
1727 | to = from + 1; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to++; }
1728 | to = from + 16; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to += 16; }
1729 | to = from - 16; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to -= 16; }
1730 | from = g_pieceList[pieceIdx++];
1731 | }
1732 |
1733 | // Queen quiet moves
1734 | pieceIdx = (g_toMove | 5) << 4;
1735 | from = g_pieceList[pieceIdx++];
1736 | while (from != 0) {
1737 | to = from - 15; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to -= 15; }
1738 | to = from - 17; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to -= 17; }
1739 | to = from + 15; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to += 15; }
1740 | to = from + 17; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to += 17; }
1741 | to = from - 1; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to--; }
1742 | to = from + 1; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to++; }
1743 | to = from + 16; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to += 16; }
1744 | to = from - 16; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to -= 16; }
1745 | from = g_pieceList[pieceIdx++];
1746 | }
1747 |
1748 | // King quiet moves
1749 | {
1750 | pieceIdx = (g_toMove | 6) << 4;
1751 | from = g_pieceList[pieceIdx];
1752 | to = from - 15; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1753 | to = from - 17; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1754 | to = from + 15; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1755 | to = from + 17; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1756 | to = from - 1; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1757 | to = from + 1; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1758 | to = from - 16; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1759 | to = from + 16; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1760 |
1761 | if (!g_inCheck) {
1762 | var castleRights = g_castleRights;
1763 | if (!g_toMove)
1764 | castleRights >>= 2;
1765 | if (castleRights & 1) {
1766 | // Kingside castle
1767 | if (g_board[from + 1] == pieceEmpty && g_board[from + 2] == pieceEmpty) {
1768 | moveStack[moveStack.length] = GenerateMove(from, from + 0x02, pieceEmpty, moveflagCastleKing);
1769 | }
1770 | }
1771 | if (castleRights & 2) {
1772 | // Queenside castle
1773 | if (g_board[from - 1] == pieceEmpty && g_board[from - 2] == pieceEmpty && g_board[from - 3] == pieceEmpty) {
1774 | moveStack[moveStack.length] = GenerateMove(from, from - 0x02, pieceEmpty, moveflagCastleQueen);
1775 | }
1776 | }
1777 | }
1778 | }
1779 | }
1780 |
1781 | function GenerateCaptureMoves(moveStack, moveScores) {
1782 | var from, to, piece, pieceIdx;
1783 | var inc = (g_toMove == 8) ? -16 : 16;
1784 | var enemy = g_toMove == 8 ? 0x10 : 0x8;
1785 |
1786 | // Pawn captures
1787 | pieceIdx = (g_toMove | 1) << 4;
1788 | from = g_pieceList[pieceIdx++];
1789 | while (from != 0) {
1790 | to = from + inc - 1;
1791 | if (g_board[to] & enemy) {
1792 | MovePawnTo(moveStack, from, to, g_board[to]);
1793 | }
1794 |
1795 | to = from + inc + 1;
1796 | if (g_board[to] & enemy) {
1797 | MovePawnTo(moveStack, from, to, g_board[to]);
1798 | }
1799 |
1800 | from = g_pieceList[pieceIdx++];
1801 | }
1802 |
1803 | if (g_enPassentSquare != -1) {
1804 | var inc = (g_toMove == colorWhite) ? -16 : 16;
1805 | var pawn = g_toMove | piecePawn;
1806 |
1807 | var from = g_enPassentSquare - (inc + 1);
1808 | if ((g_board[from] & 0xF) == pawn) {
1809 | moveStack[moveStack.length] = GenerateMove(from, g_enPassentSquare, g_board[g_enPassentSquare - inc], moveflagEPC);
1810 | }
1811 |
1812 | from = g_enPassentSquare - (inc - 1);
1813 | if ((g_board[from] & 0xF) == pawn) {
1814 | moveStack[moveStack.length] = GenerateMove(from, g_enPassentSquare, g_board[g_enPassentSquare - inc], moveflagEPC);
1815 | }
1816 | }
1817 |
1818 | // Knight captures
1819 | pieceIdx = (g_toMove | 2) << 4;
1820 | from = g_pieceList[pieceIdx++];
1821 | while (from != 0) {
1822 | to = from + 31; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1823 | to = from + 33; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1824 | to = from + 14; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1825 | to = from - 14; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1826 | to = from - 31; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1827 | to = from - 33; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1828 | to = from + 18; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1829 | to = from - 18; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1830 | from = g_pieceList[pieceIdx++];
1831 | }
1832 |
1833 | // Bishop captures
1834 | pieceIdx = (g_toMove | 3) << 4;
1835 | from = g_pieceList[pieceIdx++];
1836 | while (from != 0) {
1837 | to = from; do { to -= 15; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1838 | to = from; do { to -= 17; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1839 | to = from; do { to += 15; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1840 | to = from; do { to += 17; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1841 | from = g_pieceList[pieceIdx++];
1842 | }
1843 |
1844 | // Rook captures
1845 | pieceIdx = (g_toMove | 4) << 4;
1846 | from = g_pieceList[pieceIdx++];
1847 | while (from != 0) {
1848 | to = from; do { to--; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1849 | to = from; do { to++; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1850 | to = from; do { to -= 16; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1851 | to = from; do { to += 16; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1852 | from = g_pieceList[pieceIdx++];
1853 | }
1854 |
1855 | // Queen captures
1856 | pieceIdx = (g_toMove | 5) << 4;
1857 | from = g_pieceList[pieceIdx++];
1858 | while (from != 0) {
1859 | to = from; do { to -= 15; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1860 | to = from; do { to -= 17; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1861 | to = from; do { to += 15; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1862 | to = from; do { to += 17; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1863 | to = from; do { to--; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1864 | to = from; do { to++; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1865 | to = from; do { to -= 16; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1866 | to = from; do { to += 16; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1867 | from = g_pieceList[pieceIdx++];
1868 | }
1869 |
1870 | // King captures
1871 | {
1872 | pieceIdx = (g_toMove | 6) << 4;
1873 | from = g_pieceList[pieceIdx];
1874 | to = from - 15; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1875 | to = from - 17; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1876 | to = from + 15; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1877 | to = from + 17; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1878 | to = from - 1; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1879 | to = from + 1; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1880 | to = from - 16; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1881 | to = from + 16; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1882 | }
1883 | }
1884 |
1885 | function MovePawnTo(moveStack, start, square, captured) {
1886 | var row = square & 0xF0;
1887 | if ((row == 0x90) || (row == 0x20)) {
1888 | moveStack[moveStack.length] = GenerateMove(start, square, captured, moveflagPromotion | moveflagPromoteQueen);
1889 | moveStack[moveStack.length] = GenerateMove(start, square, captured, moveflagPromotion | moveflagPromoteKnight);
1890 | moveStack[moveStack.length] = GenerateMove(start, square, captured, moveflagPromotion | moveflagPromoteBishop);
1891 | moveStack[moveStack.length] = GenerateMove(start, square, captured, moveflagPromotion);
1892 | }
1893 | else {
1894 | moveStack[moveStack.length] = GenerateMove(start, square, captured, 0);
1895 | }
1896 | }
1897 |
1898 | function GeneratePawnMoves(moveStack, from) {
1899 | var piece = g_board[from];
1900 | var color = piece & colorWhite;
1901 | var inc = (color == colorWhite) ? -16 : 16;
1902 |
1903 | // Quiet pawn moves
1904 | var to = from + inc;
1905 | if (g_board[to] == 0) {
1906 | MovePawnTo(moveStack, from, to, pieceEmpty);
1907 |
1908 | // Check if we can do a 2 square jump
1909 | if ((((from & 0xF0) == 0x30) && color != colorWhite) ||
1910 | (((from & 0xF0) == 0x80) && color == colorWhite)) {
1911 | to += inc;
1912 | if (g_board[to] == 0) {
1913 | moveStack[moveStack.length] = GenerateMove(from, to, pieceEmpty, moveflagEP);
1914 | }
1915 | }
1916 | }
1917 | }
1918 |
1919 | function UndoHistory(ep, castleRights, inCheck, baseEval, hashKey, move50) {
1920 | this.ep = ep;
1921 | this.castleRights = castleRights;
1922 | this.inCheck = inCheck;
1923 | this.baseEval = baseEval;
1924 | this.hashKey = hashKey;
1925 | this.move50 = move50;
1926 | }
1927 |
1928 | function FinishMove(bestMove, value, timeTaken, ply) {
1929 | if (bestMove != null) {
1930 | MakeMove(bestMove);
1931 | postMessage(bestMove.toString("16"));
1932 | }
1933 | }
1934 |
1935 | var needsReset = true;
1936 | onmessage = function (e) {
1937 | if (e.data == "go" || needsReset) {
1938 | ResetGame();
1939 | needsReset = false;
1940 | }
1941 | if (e.data.match("^position") == "position") {
1942 | ResetGame();
1943 | InitializeFromFen(e.data.substr(9, e.data.length - 9));
1944 | } else if (e.data == "search") {
1945 | Search(FinishMove);
1946 | } else {
1947 | MakeMove(parseInt(e.data, 16));
1948 | }
1949 | }
1950 |
1951 |
--------------------------------------------------------------------------------
/archived/garbochess-062310-01.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // Vs current best -> 587/1057 %:55.53453169347209
4 |
5 | // Perf TODO:
6 | // Merge material updating with psq values
7 | // Put move scoring inline in generator
8 | // Remove need for fliptable in psq tables. Access them by color
9 | // Unroll non-capture move generator
10 |
11 | // Non-perf todo:
12 | // Killer moves!
13 | // SEE!
14 | // Checks in first q?
15 | // Pawn eval.
16 | // Better king evaluation
17 |
18 | var g_debug = true;
19 | var g_timeout = 20;
20 |
21 | function GetFen(){
22 | var result = "";
23 | for (row = 0; row < 8; row++) {
24 | if (row != 0)
25 | result += '/';
26 | var empty = 0;
27 | for (col = 0; col < 8; col++) {
28 | var piece = g_board[((row + 2) << 4) + col + 4];
29 | if (piece == 0) {
30 | empty++;
31 | }
32 | else {
33 | if (empty != 0)
34 | result += empty;
35 | empty = 0;
36 |
37 | var pieceChar = [" ", "p", "n", "b", "r", "q", "k", " "][(piece & 0x7)];
38 | result += ((piece & colorWhite) != 0) ? pieceChar.toUpperCase() : pieceChar;
39 | }
40 | }
41 | if (empty != 0) {
42 | result += empty;
43 | }
44 | }
45 |
46 | result += g_toMove == colorBlack ? " b" : " w";
47 | result += " ";
48 | if (g_castleRights == 0) {
49 | result += "-";
50 | }
51 | else {
52 | if ((g_castleRights & 1) != 0)
53 | result += "K";
54 | if ((g_castleRights & 2) != 0)
55 | result += "Q";
56 | if ((g_castleRights & 4) != 0)
57 | result += "k";
58 | if ((g_castleRights & 8) != 0)
59 | result += "q";
60 | }
61 |
62 | result += " ";
63 |
64 | if (g_enPassentSquare == -1) {
65 | result += '-';
66 | }
67 | else {
68 | result += FormatSquare(g_enPassentSquare);
69 | }
70 |
71 | return result;
72 | }
73 |
74 | function GetMoveSAN(move, validMoves) {
75 | var from = move & 0xFF;
76 | var to = (move >> 8) & 0xFF;
77 |
78 | if (move & moveflagCastleKing) return "O-O";
79 | if (move & moveflagCastleQueen) return "O-O-O";
80 |
81 | var pieceType = g_board[from] & 0x7;
82 | var result = ["", "", "N", "B", "R", "Q", "K", ""][pieceType];
83 |
84 | var dupe = false, rowDiff = true, colDiff = true;
85 | if (validMoves == null) {
86 | validMoves = GenerateValidMoves();
87 | }
88 | for (var i = 0; i < validMoves.length; i++) {
89 | var moveFrom = validMoves[i] & 0xFF;
90 | var moveTo = (validMoves[i] >> 8) & 0xFF;
91 | if (moveFrom != from &&
92 | moveTo == to &&
93 | (g_board[moveFrom] & 0x7) == pieceType) {
94 | dupe = true;
95 | if ((moveFrom & 0xF0) == (from & 0xF0)) {
96 | rowDiff = false;
97 | }
98 | if ((moveFrom & 0x0F) == (from & 0x0F)) {
99 | colDiff = false;
100 | }
101 | }
102 | }
103 |
104 | if (dupe) {
105 | if (colDiff) {
106 | result += FormatSquare(from).charAt(0);
107 | } else if (rowDiff) {
108 | result += FormatSquare(from).charAt(1);
109 | } else {
110 | result += FormatSquare(from);
111 | }
112 | } else if (pieceType == piecePawn && g_board[to] != 0) {
113 | result += FormatSquare(from).charAt(0);
114 | }
115 |
116 | if (g_board[to] != 0 || (move & moveflagEPC)) {
117 | result += "x";
118 | }
119 |
120 | result += FormatSquare(to);
121 |
122 | if (move & moveflagPromotion) {
123 | if (move & moveflagPromoteBishop) result += "=B";
124 | else if (move & moveflagPromoteKnight) result += "=N";
125 | else if (move & moveflagPromoteQueen) result += "=Q";
126 | else result += "=R";
127 | }
128 |
129 | MakeMove(move);
130 | if (g_inCheck) {
131 | result += GenerateValidMoves().length == 0 ? "#" : "+";
132 | }
133 | UnmakeMove(move);
134 |
135 | return result;
136 | }
137 |
138 | function FormatSquare(square) {
139 | var letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
140 | return letters[(square & 0xF) - 4] + ((9 - (square >> 4)) + 1);
141 | }
142 |
143 | function FormatMove(move) {
144 | return FormatSquare(move & 0xFF) + FormatSquare((move >> 8) & 0xFF);
145 | }
146 |
147 | function PVFromHash(move, ply) {
148 | if (ply == 0)
149 | return "";
150 |
151 | var pvString = " " + GetMoveSAN(move);
152 | MakeMove(move);
153 |
154 | var hashNode = g_hashTable[g_hashKey & g_hashMask];
155 | if (hashNode != null && hashNode.lock == g_hashKey && hashNode.bestMove != null) {
156 | pvString += PVFromHash(hashNode.bestMove, ply - 1);
157 | }
158 |
159 | UnmakeMove(move);
160 |
161 | return pvString;
162 | }
163 |
164 | //
165 | // Searching code
166 | //
167 |
168 | var g_startTime;
169 |
170 | var g_nodeCount;
171 | var g_qNodeCount;
172 | var g_searchValid;
173 | var g_globalPly = 0;
174 |
175 | function Search(finishMoveCallback, maxPly) {
176 | var lastEval;
177 | var alpha = minEval;
178 | var beta = maxEval;
179 |
180 | g_globalPly++;
181 | g_nodeCount = 0;
182 | g_qNodeCount = 0;
183 | g_collisions = 0;
184 | g_searchValid = true;
185 |
186 | var bestMove;
187 | var value;
188 |
189 | g_startTime = (new Date()).getTime();
190 |
191 | var i;
192 | for (i = 1; i <= maxPly && g_searchValid; i++) {
193 | var tmp = AlphaBeta(i, 0, alpha, beta);
194 | if (!g_searchValid) break;
195 |
196 | value = tmp;
197 |
198 | if (value > alpha && value < beta) {
199 | alpha = value - 500;
200 | beta = value + 500;
201 | } else if (alpha != minEval) {
202 | alpha = minEval;
203 | beta = maxEval;
204 | i--;
205 | continue;
206 | }
207 |
208 | if (g_hashTable[g_hashKey & g_hashMask] != null) {
209 | bestMove = g_hashTable[g_hashKey & g_hashMask].bestMove;
210 | }
211 | }
212 |
213 | finishMoveCallback(bestMove, value, (new Date()).getTime() - g_startTime, i - 1);
214 | }
215 |
216 | var minEval = -2000000;
217 | var maxEval = +2000000;
218 |
219 | var minMateBuffer = minEval + 2000;
220 | var maxMateBuffer = maxEval - 2000;
221 |
222 | var materialTable = [0, 800, 3350, 3450, 5000, 9750, 600000];
223 |
224 | var pawnAdj =
225 | [
226 | 0, 0, 0, 0, 0, 0, 0, 0,
227 | -75, 5, 35, 177, 177, 35, 5, -75,
228 | -80, 0, 30, 176, 176, 30, 0, -80,
229 | -85, -5, 25, 175, 175, 25, -5, -85,
230 | -90, -10, 20, 125, 125, 20, -10, -90,
231 | -95, -15, 15, 75, 75, 15, -15, -95,
232 | -100, -20, 10, 70, 70, 10, -20, -100,
233 | 0, 0, 0, 0, 0, 0, 0, 0
234 | ];
235 |
236 | var knightAdj =
237 | [-200, -100, -50, -50, -50, -50, -100, -200,
238 | -100, 0, 0, 0, 0, 0, 0, -100,
239 | -50, 0, 60, 60, 60, 60, 0, -50,
240 | -50, 0, 30, 60, 60, 30, 0, -50,
241 | -50, 0, 30, 60, 60, 30, 0, -50,
242 | -50, 0, 30, 30, 30, 30, 0, -50,
243 | -100, 0, 0, 0, 0, 0, 0, -100,
244 | -200, -50, -25, -25, -25, -25, -50, -200
245 | ];
246 |
247 | var bishopAdj =
248 | [ -50,-50,-25,-10,-10,-25,-50,-50,
249 | -50,-25,-10, 0, 0,-10,-25,-50,
250 | -25,-10, 0, 25, 25, 0,-10,-25,
251 | -10, 0, 25, 40, 40, 25, 0,-10,
252 | -10, 0, 25, 40, 40, 25, 0,-10,
253 | -25,-10, 0, 25, 25, 0,-10,-25,
254 | -50,-25,-10, 0, 0,-10,-25,-50,
255 | -50,-50,-25,-10,-10,-25,-50,-50
256 | ];
257 |
258 | var rookAdj =
259 | [ -60, -30, -10, 20, 20, -10, -30, -60,
260 | 40, 70, 90,120,120, 90, 70, 40,
261 | -60, -30, -10, 20, 20, -10, -30, -60,
262 | -60, -30, -10, 20, 20, -10, -30, -60,
263 | -60, -30, -10, 20, 20, -10, -30, -60,
264 | -60, -30, -10, 20, 20, -10, -30, -60,
265 | -60, -30, -10, 20, 20, -10, -30, -60,
266 | -60, -30, -10, 20, 20, -10, -30, -60
267 | ];
268 |
269 | var kingAdj =
270 | [ 50, 150, -25, -125, -125, -25, 150, 50,
271 | 50, 150, -25, -125, -125, -25, 150, 50,
272 | 50, 150, -25, -125, -125, -25, 150, 50,
273 | 50, 150, -25, -125, -125, -25, 150, 50,
274 | 50, 150, -25, -125, -125, -25, 150, 50,
275 | 50, 150, -25, -125, -125, -25, 150, 50,
276 | 50, 150, -25, -125, -125, -25, 150, 50,
277 | 150, 250, 75, -25, -25, 75, 250, 150
278 | ];
279 |
280 | var emptyAdj =
281 | [0, 0, 0, 0, 0, 0, 0, 0,
282 | 0, 0, 0, 0, 0, 0, 0, 0,
283 | 0, 0, 0, 0, 0, 0, 0, 0,
284 | 0, 0, 0, 0, 0, 0, 0, 0,
285 | 0, 0, 0, 0, 0, 0, 0, 0,
286 | 0, 0, 0, 0, 0, 0, 0, 0,
287 | 0, 0, 0, 0, 0, 0, 0, 0,
288 | 0, 0, 0, 0, 0, 0, 0, 0,
289 | ];
290 |
291 | var pieceSquareAdj = new Array(8);
292 |
293 | // Returns the square flipped
294 | var flipTable = new Array(256);
295 |
296 | function PawnEval(color) {
297 | var pieceIdx = (color | 1) << 4;
298 | var from = g_pieceList[pieceIdx++];
299 | while (from != 0) {
300 | from = g_pieceList[pieceIdx++];
301 | }
302 | }
303 |
304 | function Mobility(color) {
305 | var result = 0;
306 | var from, to, mob, pieceIdx;
307 | var enemy = color == 8 ? 0x10 : 0x8
308 | var mobUnit = color == 8 ? g_mobUnit[0] : g_mobUnit[1];
309 |
310 | // Knight mobility
311 | mob = -3;
312 | pieceIdx = (color | 2) << 4;
313 | from = g_pieceList[pieceIdx++];
314 | while (from != 0) {
315 | mob += mobUnit[g_board[from + 31]];
316 | mob += mobUnit[g_board[from + 33]];
317 | mob += mobUnit[g_board[from + 14]];
318 | mob += mobUnit[g_board[from - 14]];
319 | mob += mobUnit[g_board[from - 31]];
320 | mob += mobUnit[g_board[from - 33]];
321 | mob += mobUnit[g_board[from + 18]];
322 | mob += mobUnit[g_board[from - 18]];
323 | from = g_pieceList[pieceIdx++];
324 | }
325 | result += 65 * mob;
326 |
327 | // Bishop mobility
328 | mob = -4;
329 | pieceIdx = (color | 3) << 4;
330 | from = g_pieceList[pieceIdx++];
331 | while (from != 0) {
332 | to = from - 15; while (g_board[to] == 0) { to -= 15; mob++; } if (g_board[to] & enemy) mob++;
333 | to = from - 17; while (g_board[to] == 0) { to -= 17; mob++; } if (g_board[to] & enemy) mob++;
334 | to = from + 15; while (g_board[to] == 0) { to += 15; mob++; } if (g_board[to] & enemy) mob++;
335 | to = from + 17; while (g_board[to] == 0) { to += 17; mob++; } if (g_board[to] & enemy) mob++;
336 | from = g_pieceList[pieceIdx++];
337 | }
338 | result += 50 * mob;
339 |
340 | // Rook mobility
341 | mob = -4;
342 | pieceIdx = (color | 4) << 4;
343 | from = g_pieceList[pieceIdx++];
344 | while (from != 0) {
345 | to = from - 1; while (g_board[to] == 0) { to--; mob++;} if (g_board[to] & enemy) mob++;
346 | to = from + 1; while (g_board[to] == 0) { to++; mob++; } if (g_board[to] & enemy) mob++;
347 | to = from + 16; while (g_board[to] == 0) { to += 16; mob++; } if (g_board[to] & enemy) mob++;
348 | to = from - 16; while (g_board[to] == 0) { to -= 16; mob++; } if (g_board[to] & enemy) mob++;
349 | from = g_pieceList[pieceIdx++];
350 | }
351 | result += 25 * mob;
352 |
353 | // Queen mobility
354 | mob = -2;
355 | pieceIdx = (color | 5) << 4;
356 | from = g_pieceList[pieceIdx++];
357 | while (from != 0) {
358 | to = from - 15; while (g_board[to] == 0) { to -= 15; mob++; } if (g_board[to] & enemy) mob++;
359 | to = from - 17; while (g_board[to] == 0) { to -= 17; mob++; } if (g_board[to] & enemy) mob++;
360 | to = from + 15; while (g_board[to] == 0) { to += 15; mob++; } if (g_board[to] & enemy) mob++;
361 | to = from + 17; while (g_board[to] == 0) { to += 17; mob++; } if (g_board[to] & enemy) mob++;
362 | to = from - 1; while (g_board[to] == 0) { to--; mob++; } if (g_board[to] & enemy) mob++;
363 | to = from + 1; while (g_board[to] == 0) { to++; mob++; } if (g_board[to] & enemy) mob++;
364 | to = from + 16; while (g_board[to] == 0) { to += 16; mob++; } if (g_board[to] & enemy) mob++;
365 | to = from - 16; while (g_board[to] == 0) { to -= 16; mob++; } if (g_board[to] & enemy) mob++;
366 | from = g_pieceList[pieceIdx++];
367 | }
368 | result += 22 * mob;
369 |
370 | return result;
371 | }
372 |
373 | function Evaluate() {
374 | var curEval = g_baseEval;
375 |
376 | var evalAdjust = 0;
377 | // Black queen gone, then cancel white's penalty for king movement
378 | if (g_pieceList[pieceQueen << 4] == 0)
379 | evalAdjust -= pieceSquareAdj[pieceKing][g_pieceList[(colorWhite | pieceKing) << 4]];
380 | // White queen gone, then cancel black's penalty for king movement
381 | if (g_pieceList[(colorWhite | pieceQueen) << 4] == 0)
382 | evalAdjust += pieceSquareAdj[pieceKing][flipTable[g_pieceList[pieceKing << 4]]];
383 |
384 | // Black bishop pair
385 | if (g_pieceCount[pieceBishop] >= 2)
386 | evalAdjust -= 500;
387 | // White bishop pair
388 | if (g_pieceCount[pieceBishop | colorWhite] >= 2)
389 | evalAdjust += 500;
390 |
391 | var mobility = Mobility(8) - Mobility(0);
392 |
393 | if (g_toMove == 0) {
394 | // Black
395 | curEval -= mobility;
396 | curEval -= evalAdjust;
397 | }
398 | else {
399 | curEval += mobility;
400 | curEval += evalAdjust;
401 | }
402 |
403 | return curEval;
404 | }
405 |
406 | var historyTable = new Array(32);
407 |
408 | function ScoreMove(move){
409 | var captured = (move >> 16) & 0x7;
410 | var piece = g_board[move & 0xFF];
411 | var score;
412 | if (captured != 0) {
413 | var pieceType = piece & 0x7;
414 | score = (captured << 5) - pieceType;
415 | } else {
416 | score = historyTable[piece & 0xF][(move >> 8) & 0xFF];
417 | }
418 | return score;
419 | }
420 |
421 | function QSearch(alpha, beta, ply) {
422 | var checkStopDepth = -3;
423 |
424 | g_qNodeCount++;
425 |
426 | //var realEval = g_inCheck ? (minEval + 1) : Evaluate();
427 | var realEval = Evaluate();
428 |
429 | if (realEval >= beta)
430 | return realEval;
431 |
432 | if (realEval > alpha)
433 | alpha = realEval;
434 |
435 | var moves = new Array();
436 | var moveScores = new Array();
437 | var wasInCheck = g_inCheck;
438 |
439 | /*if (ply == 0 || (wasInCheck && ply >= checkStopDepth)) {
440 | // TODO: Fast check escape generator and fast checking moves generator
441 | GenerateCaptureMoves(moves, null);
442 | GenerateAllMoves(moves);
443 |
444 | for (var i = 0; i < moves.length; i++) {
445 | moveScores[i] = ScoreMove(moves[i]);
446 | }
447 | } else */{
448 | GenerateCaptureMoves(moves, null);
449 |
450 | for (var i = 0; i < moves.length; i++) {
451 | var captured = (moves[i] >> 16) & 0x7;
452 | var pieceType = g_board[moves[i] & 0xFF] & 0x7;
453 |
454 | moveScores[i] = (captured << 5) - pieceType;
455 | }
456 | }
457 |
458 | for (var i = 0; i < moves.length; i++) {
459 | var bestMove = i;
460 | for (var j = moves.length - 1; j > i; j--) {
461 | if (moveScores[j] > moveScores[bestMove]) {
462 | bestMove = j;
463 | }
464 | }
465 | {
466 | var tmpMove = moves[i];
467 | moves[i] = moves[bestMove];
468 | moves[bestMove] = tmpMove;
469 |
470 | var tmpScore = moveScores[i];
471 | moveScores[i] = moveScores[bestMove];
472 | moveScores[bestMove] = tmpScore;
473 | }
474 |
475 | if (!MakeMove(moves[i])) {
476 | continue;
477 | }
478 |
479 | /* if (ply == 0 && !wasInCheck) {
480 | // Search captures and checks at ply 0 of q-search
481 | if (!(g_inCheck || (moves[i] & 0xFF0000))) {
482 | UnmakeMove(moves[i]);
483 | continue;
484 | }
485 | }*/
486 |
487 | var value = -QSearch(-beta, -alpha, ply - 1);
488 |
489 | UnmakeMove(moves[i]);
490 |
491 | if (value > realEval) {
492 | if (value >= beta)
493 | return value;
494 |
495 | if (value > alpha)
496 | alpha = value;
497 |
498 | realEval = value;
499 | }
500 | }
501 |
502 | return realEval;
503 | }
504 |
505 | function StoreHash(value, flags, ply, move, force){
506 | var hashNode = g_hashTable[g_hashKey & g_hashMask];
507 | if (hashNode == null || ply >= hashNode.ply || force) {
508 | g_hashTable[g_hashKey & g_hashMask] = new HashEntry(g_hashKey, value, flags, ply, move);
509 | }
510 | }
511 |
512 | function IsHashMoveValid(hashMove) {
513 | // Do some basic sanity checks
514 | var from = hashMove & 0xFF;
515 | var to = (hashMove >> 8) & 0xFF;
516 | var ourPiece = g_board[from];
517 | var pieceType = ourPiece & 0x7;
518 | if (pieceType < piecePawn || pieceType > pieceKing) return false;
519 | if ((ourPiece & colorWhite) != 0) {
520 | if (g_toMove != colorWhite) return false;
521 | } else {
522 | if (g_toMove == colorWhite) return false;
523 | }
524 | if (g_inCheck) return false;
525 | if (g_board[to] != ((hashMove >> 16) & 0xFF)) return false;
526 | if (hashMove >> 24) return false;
527 | if (pieceType == piecePawn) {
528 | // TODO - This handles pawn captures, but not pawn pushes
529 | return IsSquareAttackableFrom(to, from);
530 | } else {
531 | // This validates that this piece type can actually make the attack
532 | return IsSquareAttackableFrom(to, from);
533 | }
534 | }
535 |
536 | function IsRepDraw() {
537 | var stop = g_moveCount - 1 - g_move50;
538 | stop = stop < 0 ? 0 : stop;
539 | for (var i = g_moveCount - 5; i >= stop; i -= 2) {
540 | if (g_repMoveStack[i] == g_hashKey)
541 | return true;
542 | }
543 | return false;
544 | }
545 |
546 | function MovePicker(hashMove, depth, killer1, killer2) {
547 | this.hashMove = hashMove;
548 | this.depth = depth;
549 | this.killer1 = killer1;
550 | this.killer2 = killer2;
551 |
552 | this.moves = new Array();
553 | this.moveCount = 0;
554 | this.atMove = -1;
555 | this.moveScores = null;
556 | this.stage = 0;
557 |
558 | this.nextMove = function () {
559 | if (++this.atMove == this.moveCount) {
560 | this.stage++;
561 | if (this.stage == 1) {
562 | if (this.hashMove != null && IsHashMoveValid(hashMove)) {
563 | this.moves[0] = hashMove;
564 | this.moveCount = 1;
565 | }
566 | if (this.moveCount != 1) {
567 | this.hashMove = null;
568 | this.stage++;
569 | }
570 | }
571 |
572 | if (this.stage == 2) {
573 | GenerateCaptureMoves(this.moves, null);
574 | this.moveCount = this.moves.length;
575 | this.moveScores = new Array(this.moveCount);
576 | // Move ordering
577 | for (var i = this.atMove; i < this.moveCount; i++) {
578 | var captured = (this.moves[i] >> 16) & 0x7;
579 | var pieceType = g_board[this.moves[i] & 0xFF] & 0x7;
580 | this.moveScores[i] = (captured << 5) - pieceType;
581 | }
582 | // No moves, onto next stage
583 | if (this.atMove == this.moveCount) this.stage++;
584 | }
585 |
586 | if (this.stage == 3) {
587 | if (IsHashMoveValid(this.killer1) &&
588 | this.killer1 != this.hashMove) {
589 | this.moves[this.moves.length] = this.killer1;
590 | this.moveCount = this.moves.length;
591 | } else {
592 | this.killer1 = 0;
593 | this.stage++;
594 | }
595 | }
596 |
597 | if (this.stage == 4) {
598 | if (IsHashMoveValid(this.killer2) &&
599 | this.killer2 != this.hashMove) {
600 | this.moves[this.moves.length] = this.killer2;
601 | this.moveCount = this.moves.length;
602 | } else {
603 | this.killer2 = 0;
604 | this.stage++;
605 | }
606 | }
607 |
608 | if (this.stage == 5) {
609 | GenerateAllMoves(this.moves);
610 | this.moveCount = this.moves.length;
611 | // Move ordering
612 | for (var i = this.atMove; i < this.moveCount; i++) this.moveScores[i] = ScoreMove(this.moves[i]);
613 | // No moves, onto next stage
614 | if (this.atMove == this.moveCount) this.stage++;
615 | }
616 |
617 | if (this.stage == 6)
618 | return 0;
619 | }
620 |
621 | var bestMove = this.atMove;
622 | for (var j = this.atMove + 1; j < this.moveCount; j++) {
623 | if (this.moveScores[j] > this.moveScores[bestMove]) {
624 | bestMove = j;
625 | }
626 | }
627 |
628 | if (bestMove != this.atMove) {
629 | var tmpMove = this.moves[this.atMove];
630 | this.moves[this.atMove] = this.moves[bestMove];
631 | this.moves[bestMove] = tmpMove;
632 |
633 | var tmpScore = this.moveScores[this.atMove];
634 | this.moveScores[this.atMove] = this.moveScores[bestMove];
635 | this.moveScores[bestMove] = tmpScore;
636 | }
637 |
638 | var candidateMove = this.moves[this.atMove];
639 | if ((this.stage > 1 && candidateMove == this.hashMove) ||
640 | (this.stage > 3 && candidateMove == this.killer1) ||
641 | (this.stage > 4 && candidateMove == this.killer2)) {
642 | return this.nextMove();
643 | }
644 |
645 | return this.moves[this.atMove];
646 | }
647 | }
648 |
649 | function AllCutNode(ply, depth, beta, allowNull) {
650 | if (ply <= 0) {
651 | return QSearch(beta - 1, beta, 0);
652 | }
653 |
654 | if ((g_nodeCount & 127) == 127) {
655 | if ((new Date()).getTime() - g_startTime > g_timeout) {
656 | // Time cutoff
657 | g_searchValid = false;
658 | return beta - 1;
659 | }
660 | }
661 |
662 | g_nodeCount++;
663 |
664 | if (IsRepDraw())
665 | return 0;
666 |
667 | var hashMove = null;
668 | var hashNode = g_hashTable[g_hashKey & g_hashMask];
669 | if (hashNode != null && hashNode.lock == g_hashKey) {
670 | hashMove = hashNode.bestMove;
671 | if (hashNode.hashDepth >= ply) {
672 | if (hashNode.flags == hashflagExact)
673 | return hashNode.value;
674 | if (hashNode.flags == hashflagAlpha && hashNode.value < beta)
675 | return beta - 1;
676 | if (hashNode.flags == hashflagBeta && hashNode.value >= beta)
677 | return beta;
678 | }
679 | }
680 |
681 | // TODO - positional gain?
682 |
683 | if (!g_inCheck &&
684 | allowNull &&
685 | beta > minMateBuffer &&
686 | beta < maxMateBuffer) {
687 | // Try some razoring
688 | if (hashMove == null &&
689 | ply < 4) {
690 | var razorMargin = 2500 + 200 * ply;
691 | if (g_baseEval < beta - razorMargin) {
692 | var razorBeta = beta - razorMargin;
693 | var v = QSearch(razorBeta - 1, razorBeta, 0);
694 | if (v < razorBeta)
695 | return v;
696 | }
697 | }
698 |
699 | // TODO - static null move
700 |
701 | // Null move
702 | if (ply > 1 &&
703 | g_baseEval >= beta - (ply >= 4 ? 2500 : 0)) {
704 | var r = 3 + (ply >= 5 ? 1 : ply / 4);
705 | if (g_baseEval - beta > 1500) r++;
706 |
707 | g_toMove = 8 - g_toMove;
708 | g_baseEval = -g_baseEval;
709 | if (g_toMove)
710 | g_hashKey -= g_zobristBlack;
711 | else
712 | g_hashKey += g_zobristBlack;
713 |
714 | var value = -AllCutNode(ply - r, depth + 1, -(beta - 1), false);
715 |
716 | if (g_toMove)
717 | g_hashKey += g_zobristBlack;
718 | else
719 | g_hashKey -= g_zobristBlack;
720 | g_toMove = 8 - g_toMove;
721 | g_baseEval = -g_baseEval;
722 |
723 | if (value >= beta)
724 | return beta;
725 | }
726 | }
727 |
728 | var moveMade = false;
729 | var realEval = minEval;
730 |
731 | var movePicker = new MovePicker(hashMove, depth, g_killers[depth][0], g_killers[depth][1]);
732 |
733 | for (;;) {
734 | var currentMove = movePicker.nextMove();
735 | if (currentMove == 0) {
736 | break;
737 | }
738 |
739 | var plyToSearch = ply - 1;
740 |
741 | if (!MakeMove(currentMove)) {
742 | continue;
743 | }
744 |
745 | var value;
746 | var doFullSearch = true;
747 |
748 | if (g_inCheck) {
749 | // Check extensions
750 | plyToSearch++;
751 | } else {
752 | // Late move reductions
753 | if (movePicker.stage == 5 && movePicker.atMove > 5 && ply >= 3) {
754 | var reduced = plyToSearch - (movePicker.atMove > 14 ? 2 : 1);
755 | value = -AllCutNode(reduced, depth + 1, -(beta - 1), true);
756 | doFullSearch = (value >= beta);
757 | }
758 | }
759 |
760 | if (doFullSearch) {
761 | value = -AllCutNode(plyToSearch, depth + 1, -(beta - 1), true);
762 | }
763 |
764 | moveMade = true;
765 |
766 | UnmakeMove(currentMove);
767 |
768 | if (!g_searchValid) {
769 | return beta - 1;
770 | }
771 |
772 | if (value > realEval) {
773 | if (value >= beta) {
774 | var histTo = (currentMove >> 8) & 0xFF;
775 | if (g_board[histTo] == 0) {
776 | var histPiece = g_board[currentMove & 0xFF] & 0xF;
777 | historyTable[histPiece][histTo] += ply * ply;
778 | if (historyTable[histPiece][histTo] > 32767) {
779 | historyTable[histPiece][histTo] >>= 1;
780 | }
781 |
782 | if (g_killers[depth][0] != currentMove) {
783 | g_killers[depth][1] = g_killers[depth][0];
784 | g_killers[depth][0] = currentMove;
785 | }
786 | }
787 |
788 | StoreHash(value, hashflagBeta, ply, currentMove, false);
789 | return value;
790 | }
791 |
792 | realEval = value;
793 | hashMove = currentMove;
794 | }
795 | }
796 |
797 | if (!moveMade) {
798 | // If we have no valid moves it's either stalemate or checkmate
799 | if (g_inCheck)
800 | // Checkmate.
801 | return minEval + 1;
802 | else
803 | // Stalemate
804 | return 0;
805 | }
806 |
807 | StoreHash(realEval, hashflagAlpha, ply, hashMove, false);
808 |
809 | return realEval;
810 | }
811 |
812 | function AlphaBeta(ply, depth, alpha, beta) {
813 | if (ply <= 0) {
814 | return QSearch(alpha, beta, 0);
815 | }
816 |
817 | g_nodeCount++;
818 |
819 | if (IsRepDraw())
820 | return 0;
821 |
822 | var hashMove = null;
823 | var hashFlag = hashflagAlpha;
824 | var hashNode = g_hashTable[g_hashKey & g_hashMask];
825 | if (hashNode != null && hashNode.lock == g_hashKey) {
826 | hashMove = hashNode.bestMove;
827 | }
828 |
829 | var inCheck = g_inCheck;
830 |
831 | var moveMade = false;
832 | var realEval = minEval;
833 |
834 | var movePicker = new MovePicker(hashMove, depth, g_killers[depth][0], g_killers[depth][1]);
835 |
836 | for (;;) {
837 | var currentMove = movePicker.nextMove();
838 | if (currentMove == 0) {
839 | break;
840 | }
841 |
842 | var plyToSearch = ply - 1;
843 |
844 | if (!MakeMove(currentMove)) {
845 | continue;
846 | }
847 |
848 | if (g_inCheck) {
849 | // Check extensions
850 | plyToSearch++;
851 | }
852 |
853 | var value;
854 | if (moveMade) {
855 | value = -AllCutNode(plyToSearch, depth + 1, -alpha, true);
856 | if (value > alpha) {
857 | value = -AlphaBeta(plyToSearch, depth + 1, -beta, -alpha);
858 | }
859 | } else {
860 | value = -AlphaBeta(plyToSearch, depth + 1, -beta, -alpha);
861 | }
862 |
863 | moveMade = true;
864 |
865 | UnmakeMove(currentMove);
866 |
867 | if (!g_searchValid) {
868 | return alpha;
869 | }
870 |
871 | if (value > realEval) {
872 | if (value >= beta) {
873 | var histTo = (currentMove >> 8) & 0xFF;
874 | if (g_board[histTo] == 0) {
875 | var histPiece = g_board[currentMove & 0xFF] & 0xF;
876 | historyTable[histPiece][histTo] += ply * ply;
877 | if (historyTable[histPiece][histTo] > 32767) {
878 | historyTable[histPiece][histTo] >>= 1;
879 | }
880 |
881 | if (g_killers[depth][0] != currentMove) {
882 | g_killers[depth][1] = g_killers[depth][0];
883 | g_killers[depth][0] = currentMove;
884 | }
885 | }
886 |
887 | StoreHash(value, hashflagBeta, ply, currentMove, false);
888 | return value;
889 | }
890 |
891 | if (value > alpha) {
892 | hashFlag = hashflagExact;
893 | alpha = value;
894 | }
895 |
896 | realEval = value;
897 | hashMove = currentMove;
898 | }
899 | }
900 |
901 | if (!moveMade) {
902 | // If we have no valid moves it's either stalemate or checkmate
903 | if (inCheck)
904 | // Checkmate.
905 | return minEval + 1;
906 | else
907 | // Stalemate
908 | return 0;
909 | }
910 |
911 | StoreHash(realEval, hashFlag, ply, hashMove, true);
912 |
913 | return realEval;
914 | }
915 |
916 | //
917 | // Board code
918 | //
919 |
920 | // This somewhat funky scheme means that a piece is indexed by it's lower 4 bits when accessing in arrays. The fifth bit (black bit)
921 | // is used to allow quick edge testing on the board.
922 | var colorBlack = 0x10;
923 | var colorWhite = 0x08;
924 |
925 | var pieceEmpty = 0x00;
926 | var piecePawn = 0x01;
927 | var pieceKnight = 0x02;
928 | var pieceBishop = 0x03;
929 | var pieceRook = 0x04;
930 | var pieceQueen = 0x05;
931 | var pieceKing = 0x06;
932 |
933 | var g_vectorDelta = new Array(256);
934 |
935 | var g_bishopDeltas = [-15, -17, 15, 17];
936 | var g_knightDeltas = [31, 33, 14, -14, -31, -33, 18, -18];
937 | var g_rookDeltas = [-1, +1, -16, +16];
938 | var g_queenDeltas = [-1, +1, -15, +15, -17, +17, -16, +16];
939 |
940 | var g_castleRightsMask = [
941 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
942 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
943 | 0, 0, 0, 0, 7,15,15,15, 3,15,15,11, 0, 0, 0, 0,
944 | 0, 0, 0, 0,15,15,15,15,15,15,15,15, 0, 0, 0, 0,
945 | 0, 0, 0, 0,15,15,15,15,15,15,15,15, 0, 0, 0, 0,
946 | 0, 0, 0, 0,15,15,15,15,15,15,15,15, 0, 0, 0, 0,
947 | 0, 0, 0, 0,15,15,15,15,15,15,15,15, 0, 0, 0, 0,
948 | 0, 0, 0, 0,15,15,15,15,15,15,15,15, 0, 0, 0, 0,
949 | 0, 0, 0, 0,15,15,15,15,15,15,15,15, 0, 0, 0, 0,
950 | 0, 0, 0, 0,13,15,15,15,12,15,15,14, 0, 0, 0, 0,
951 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
952 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
953 |
954 | var moveflagEP = 0x1 << 24;
955 | var moveflagEPC = 0x2 << 24;
956 | var moveflagCastleKing = 0x4 << 24;
957 | var moveflagCastleQueen = 0x8 << 24;
958 | var moveflagPromotion = 0x10 << 24;
959 | var moveflagPromoteKnight = 0x20 << 24;
960 | var moveflagPromoteQueen = 0x40 << 24;
961 | var moveflagPromoteBishop = 0x80 << 24;
962 |
963 | var g_randa = 1103515245, g_randc = 12345, g_rands = 0x1BADF00D;
964 | function getRandomInt() {
965 | g_rands = (g_randa * g_rands + g_randc) & 0xFFFFFFFF;
966 | return g_rands;
967 | }
968 |
969 | function getRandomLong(){
970 | return (getRandomInt() * (1024 * 256)) + getRandomInt();
971 | }
972 |
973 | // Position variables
974 | var g_board = new Array(256); // Sentinel 0x80, pieces are in low 4 bits, 0x8 for color, 0x7 bits for piece type
975 | var g_toMove; // side to move, 0 or 8, 0 = black, 8 = white
976 | var g_castleRights; // bitmask representing castling rights, 1 = wk, 2 = wq, 4 = bk, 8 = bq
977 | var g_enPassentSquare;
978 | var g_baseEval;
979 | var g_hashKey;
980 | var g_inCheck;
981 |
982 | // Utility variables
983 | var g_moveCount = 0;
984 | var g_moveUndoStack = new Array();
985 |
986 | var g_move50 = 0;
987 | var g_repMoveStack = new Array();
988 |
989 | var g_hashSize = 1 << 22;
990 | var g_hashMask = g_hashSize - 1;
991 | var g_hashTable;
992 |
993 | var g_killers;
994 |
995 | var g_zobrist;
996 | var g_zobristBlack;
997 |
998 | // Evaulation variables
999 | var g_mobUnit;
1000 |
1001 | function State() {
1002 | this.board = new Array(256);
1003 | for (var i = 0; i < 256; i++)
1004 | this.board[i] = g_board[i];
1005 | this.toMove = g_toMove;
1006 | this.castleRights = g_castleRights;
1007 | this.enPassentSquare = g_enPassentSquare;
1008 | this.baseEval = g_baseEval;
1009 | this.hashKey = g_hashKey;
1010 | this.inCheck = g_inCheck;
1011 | }
1012 |
1013 | function DebugValidate() {
1014 | // Validate that pieceLists are correct
1015 | for (var piece = 0; piece < 0xF; piece++) {
1016 | for (var i = 0; i < g_pieceCount[piece]; i++) {
1017 | var boardPiece = piece < 0x8 ? (piece | colorBlack) : piece;
1018 | if (g_pieceList[(piece << 4) | i] == 0)
1019 | return 1;
1020 | if (g_board[g_pieceList[(piece << 4) | i]] != boardPiece)
1021 | return 2;
1022 | }
1023 | for (var i = g_pieceCount[piece]; i < 16; i++) {
1024 | if (g_pieceList[(piece << 4) | i] != 0)
1025 | return 3;
1026 | }
1027 | }
1028 |
1029 | // Validate that board matches pieceList
1030 | for (var i = 0; i < 256; i++) {
1031 | var row = i >> 4;
1032 | var col = i & 0xF;
1033 | if (row >= 2 && row < 10 && col >= 4 && col < 12) {
1034 | if (!(g_board[i] == 0 ||
1035 | (g_board[i] & (colorBlack | colorWhite)) != 0)) {
1036 | return 4;
1037 | } else if (g_board[i] != 0) {
1038 | if (g_pieceList[((g_board[i] & 0xF) << 4) | g_pieceIndex[i]] != i)
1039 | return 6;
1040 | }
1041 | } else {
1042 | if (g_board[i] != 0x80)
1043 | return 5;
1044 | }
1045 | }
1046 |
1047 | if (SetHash() != g_hashKey) {
1048 | return 6;
1049 | }
1050 |
1051 | return 0;
1052 | }
1053 |
1054 | State.prototype.CompareTo = function (other) {
1055 | for (var i = 0; i < 256; i++)
1056 | if (this.board[i] != other.board[i])
1057 | return 1;
1058 | if (this.toMove != other.toMove)
1059 | return 3;
1060 | if (this.castleRights != other.castleRights)
1061 | return 4;
1062 | if (this.enPassentSquare != other.enPassentSquare)
1063 | return 5;
1064 | if (this.baseEval != other.baseEval)
1065 | return 6;
1066 | if (this.hashKey != other.hashKey)
1067 | return 7;
1068 | if (this.inCheck != other.inCheck)
1069 | return 8;
1070 | return 0;
1071 | }
1072 |
1073 | var hashflagAlpha = 1;
1074 | var hashflagBeta = 2;
1075 | var hashflagExact = 3;
1076 |
1077 | function HashEntry(lock, value, flags, hashDepth, bestMove, globalPly) {
1078 | this.lock = lock;
1079 | this.value = value;
1080 | this.flags = flags;
1081 | this.hashDepth = hashDepth;
1082 | this.bestMove = bestMove;
1083 | }
1084 |
1085 | function MakeSquare(row, column) {
1086 | return ((row + 2) << 4) | (column + 4);
1087 | }
1088 |
1089 | function MakeTable(table) {
1090 | var result = new Array(256);
1091 | for (var i = 0; i < 256; i++) {
1092 | result[i] = 0;
1093 | }
1094 | for (var row = 0; row < 8; row++) {
1095 | for (var col = 0; col < 8; col++) {
1096 | result[MakeSquare(row, col)] = table[row * 8 + col];
1097 | }
1098 | }
1099 | return result;
1100 | }
1101 |
1102 | function ResetGame() {
1103 | g_killers = new Array(128);
1104 | for (var i = 0; i < 128; i++) {
1105 | g_killers[i] = [0, 0];
1106 | }
1107 |
1108 | g_hashTable = new Array(g_hashSize);
1109 |
1110 | for (var i = 0; i < 32; i++) {
1111 | historyTable[i] = new Array(256);
1112 | for (var j = 0; j < 256; j++)
1113 | historyTable[i][j] = 0;
1114 | }
1115 | g_zobrist = new Array(256);
1116 | for (var i = 0; i < 256; i++) {
1117 | g_zobrist[i] = new Array(16);
1118 | for (var j = 0; j < 16; j++) {
1119 | g_zobrist[i][j] = getRandomLong();
1120 | }
1121 | }
1122 | g_zobristBlack = getRandomLong();
1123 |
1124 | for (var row = 0; row < 8; row++) {
1125 | for (var col = 0; col < 8; col++) {
1126 | var square = MakeSquare(row, col);
1127 | flipTable[square] = MakeSquare(7 - row, col);
1128 | }
1129 | }
1130 |
1131 | pieceSquareAdj[piecePawn] = MakeTable(pawnAdj);
1132 | pieceSquareAdj[pieceKnight] = MakeTable(knightAdj);
1133 | pieceSquareAdj[pieceBishop] = MakeTable(bishopAdj);
1134 | pieceSquareAdj[pieceRook] = MakeTable(rookAdj);
1135 | pieceSquareAdj[pieceQueen] = MakeTable(emptyAdj);
1136 | pieceSquareAdj[pieceKing] = MakeTable(kingAdj);
1137 |
1138 | var pieceDeltas = [[], [], g_knightDeltas, g_bishopDeltas, g_rookDeltas, g_queenDeltas, g_queenDeltas];
1139 |
1140 | for (var i = 0; i < 256; i++) {
1141 | g_vectorDelta[i] = new Object();
1142 | g_vectorDelta[i].delta = 0;
1143 | g_vectorDelta[i].pieceMask = new Array(2);
1144 | g_vectorDelta[i].pieceMask[0] = 0;
1145 | g_vectorDelta[i].pieceMask[1] = 0;
1146 | }
1147 |
1148 | // Initialize the vector delta table
1149 | for (var row = 0; row < 0x80; row += 0x10)
1150 | for (var col = 0; col < 0x8; col++) {
1151 | var square = row | col;
1152 |
1153 | // Pawn moves
1154 | var index = square - (square - 17) + 128;
1155 | g_vectorDelta[index].pieceMask[colorWhite >> 3] |= (1 << piecePawn);
1156 | index = square - (square - 15) + 128;
1157 | g_vectorDelta[index].pieceMask[colorWhite >> 3] |= (1 << piecePawn);
1158 |
1159 | index = square - (square + 17) + 128;
1160 | g_vectorDelta[index].pieceMask[0] |= (1 << piecePawn);
1161 | index = square - (square + 15) + 128;
1162 | g_vectorDelta[index].pieceMask[0] |= (1 << piecePawn);
1163 |
1164 | for (var i = pieceKnight; i <= pieceKing; i++) {
1165 | for (var dir = 0; dir < pieceDeltas[i].length; dir++) {
1166 | var target = square + pieceDeltas[i][dir];
1167 | while (!(target & 0x88)) {
1168 | index = square - target + 128;
1169 |
1170 | g_vectorDelta[index].pieceMask[colorWhite >> 3] |= (1 << i);
1171 | g_vectorDelta[index].pieceMask[0] |= (1 << i);
1172 |
1173 | var flip = -1;
1174 | if (square < target)
1175 | flip = 1;
1176 |
1177 | if ((square & 0xF0) == (target & 0xF0)) {
1178 | // On the same row
1179 | g_vectorDelta[index].delta = flip * 1;
1180 | } else if ((square & 0x0F) == (target & 0x0F)) {
1181 | // On the same column
1182 | g_vectorDelta[index].delta = flip * 16;
1183 | } else if ((square % 15) == (target % 15)) {
1184 | g_vectorDelta[index].delta = flip * 15;
1185 | } else if ((square % 17) == (target % 17)) {
1186 | g_vectorDelta[index].delta = flip * 17;
1187 | }
1188 |
1189 | if (i == pieceKnight) {
1190 | g_vectorDelta[index].delta = pieceDeltas[i][dir];
1191 | break;
1192 | }
1193 |
1194 | if (i == pieceKing)
1195 | break;
1196 |
1197 | target += pieceDeltas[i][dir];
1198 | }
1199 | }
1200 | }
1201 | }
1202 |
1203 | InitializeEval();
1204 | InitializeFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
1205 | }
1206 |
1207 | function InitializeEval() {
1208 | g_mobUnit = new Array(2);
1209 | for (var i = 0; i < 2; i++) {
1210 | g_mobUnit[i] = new Array();
1211 | var enemy = i == 0 ? 0x10 : 8;
1212 | var friend = i == 0 ? 8 : 0x10;
1213 | g_mobUnit[i][0] = 1;
1214 | g_mobUnit[i][0x80] = 0;
1215 | g_mobUnit[i][enemy | piecePawn] = 1;
1216 | g_mobUnit[i][enemy | pieceBishop] = 1;
1217 | g_mobUnit[i][enemy | pieceKnight] = 1;
1218 | g_mobUnit[i][enemy | pieceRook] = 1;
1219 | g_mobUnit[i][enemy | pieceQueen] = 1;
1220 | g_mobUnit[i][enemy | pieceKing] = 1;
1221 | g_mobUnit[i][friend | piecePawn] = 0;
1222 | g_mobUnit[i][friend | pieceBishop] = 0;
1223 | g_mobUnit[i][friend | pieceKnight] = 0;
1224 | g_mobUnit[i][friend | pieceRook] = 0;
1225 | g_mobUnit[i][friend | pieceQueen] = 0;
1226 | g_mobUnit[i][friend | pieceKing] = 0;
1227 | }
1228 | }
1229 |
1230 | function SetHash(){
1231 | var hashKey = 0;
1232 | for (var i = 0; i < 256; i++) {
1233 | var piece = g_board[i];
1234 | if (piece & 0x18) {
1235 | hashKey += g_zobrist[i][piece & 0xF];
1236 | }
1237 | }
1238 |
1239 | if (!g_toMove)
1240 | hashKey += g_zobristBlack;
1241 | return hashKey;
1242 | }
1243 |
1244 | function InitializeFromFen(fen){
1245 | var chunks = fen.split(' ');
1246 |
1247 | for (var i = 0; i < 256; i++)
1248 | g_board[i] = 0x80;
1249 |
1250 | var row = 0;
1251 | var col = 0;
1252 |
1253 | var pieces = chunks[0];
1254 | for (var i = 0; i < pieces.length; i++) {
1255 | var c = pieces.charAt(i);
1256 |
1257 | if (c == '/') {
1258 | row++;
1259 | col = 0;
1260 | }
1261 | else {
1262 | if (c >= '0' && c <= '9') {
1263 | for (var j = 0; j < parseInt(c); j++) {
1264 | g_board[((row + 2) * 0x10) + (col + 4)] = 0;
1265 | col++;
1266 | }
1267 | }
1268 | else {
1269 | var isBlack = c >= 'a' && c <= 'z';
1270 | var piece = isBlack ? colorBlack : colorWhite;
1271 | if (!isBlack)
1272 | c = pieces.toLowerCase().charAt(i);
1273 | switch (c) {
1274 | case 'p':
1275 | piece |= piecePawn;
1276 | break;
1277 | case 'b':
1278 | piece |= pieceBishop;
1279 | break;
1280 | case 'n':
1281 | piece |= pieceKnight;
1282 | break;
1283 | case 'r':
1284 | piece |= pieceRook;
1285 | break;
1286 | case 'q':
1287 | piece |= pieceQueen;
1288 | break;
1289 | case 'k':
1290 | piece |= pieceKing;
1291 | break;
1292 | }
1293 |
1294 | g_board[((row + 2) * 0x10) + (col + 4)] = piece;
1295 | col++;
1296 | }
1297 | }
1298 | }
1299 |
1300 | InitializePieceList();
1301 |
1302 | g_toMove = chunks[1].charAt(0) == 'w' ? colorWhite : 0;
1303 |
1304 | g_castleRights = 0;
1305 | if (chunks[2].indexOf('K') != -1)
1306 | g_castleRights |= 1;
1307 | if (chunks[2].indexOf('Q') != -1)
1308 | g_castleRights |= 2;
1309 | if (chunks[2].indexOf('k') != -1)
1310 | g_castleRights |= 4;
1311 | if (chunks[2].indexOf('q') != -1)
1312 | g_castleRights |= 8;
1313 |
1314 | g_enPassentSquare = -1;
1315 | if (chunks[3].indexOf('-') == -1) {
1316 | g_enPassentSquare = parseInt(chunks[3], 16);
1317 | }
1318 |
1319 | g_hashKey = SetHash();
1320 |
1321 | g_baseEval = 0;
1322 | for (var i = 0; i < 256; i++) {
1323 | if (g_board[i] & colorWhite) {
1324 | g_baseEval += pieceSquareAdj[g_board[i] & 0x7][i];
1325 | } else if (g_board[i] & colorBlack) {
1326 | g_baseEval -= pieceSquareAdj[g_board[i] & 0x7][flipTable[i]];
1327 | }
1328 | }
1329 |
1330 | g_move50 = 0;
1331 | g_inCheck = IsSquareAttackable(g_pieceList[(g_toMove | pieceKing) << 4], 8 - g_toMove);
1332 | }
1333 |
1334 | var g_pieceIndex = new Array(256);
1335 | var g_pieceList = new Array(2 * 8 * 16);
1336 | var g_pieceCount = new Array(2 * 8);
1337 |
1338 | function InitializePieceList() {
1339 | for (var i = 0; i < 16; i++) {
1340 | g_pieceCount[i] = 0;
1341 | for (var j = 0; j < 16; j++) {
1342 | // 0 is used as the terminator for piece lists
1343 | g_pieceList[(i << 4) | j] = 0;
1344 | }
1345 | }
1346 |
1347 | for (var i = 0; i < 256; i++) {
1348 | g_pieceIndex[i] = 0;
1349 | if (g_board[i] & (colorWhite | colorBlack)) {
1350 | var piece = g_board[i] & 0xF;
1351 |
1352 | g_pieceList[(piece << 4) | g_pieceCount[piece]] = i;
1353 | g_pieceIndex[i] = g_pieceCount[piece];
1354 | g_pieceCount[piece]++;
1355 | }
1356 | }
1357 | }
1358 |
1359 | function MakeMove(move){
1360 | var me = g_toMove >> 3;
1361 | var otherColor = 8 - g_toMove;
1362 |
1363 | g_enPassentSquare = -1;
1364 |
1365 | var flags = move & 0xFF000000;
1366 | var captured = (move >> 16) & 0xFF;
1367 | var to = (move >> 8) & 0xFF;
1368 | var from = move & 0xFF;
1369 | var piece = g_board[from];
1370 | var epcEnd = to;
1371 |
1372 | g_moveUndoStack[g_moveCount] = new UndoHistory(g_enPassentSquare, g_castleRights, g_inCheck, g_baseEval, g_hashKey, g_move50);
1373 | g_moveCount++;
1374 |
1375 | if (flags) {
1376 | if (flags & moveflagEP) {
1377 | if (me)
1378 | g_enPassentSquare = to + 0x10;
1379 | else
1380 | g_enPassentSquare = to - 0x10;
1381 | }
1382 | else if (flags & moveflagEPC) {
1383 | if (me)
1384 | epcEnd = to + 0x10;
1385 | else
1386 | epcEnd = to - 0x10;
1387 |
1388 | g_board[epcEnd] = pieceEmpty;
1389 | } else if (flags & moveflagCastleKing) {
1390 | if (IsSquareAttackable(from + 1, otherColor) ||
1391 | IsSquareAttackable(from + 2, otherColor)) {
1392 | g_moveCount--;
1393 | return false;
1394 | }
1395 |
1396 | var rook = g_board[to + 1];
1397 |
1398 | g_hashKey -= g_zobrist[to + 1][rook & 0xF];
1399 | g_hashKey += g_zobrist[to - 1][rook & 0xF];
1400 |
1401 | g_board[to - 1] = rook;
1402 | g_board[to + 1] = pieceEmpty;
1403 |
1404 | g_baseEval -= pieceSquareAdj[rook & 0x7][me == 0 ? flipTable[to + 1] : (to + 1)];
1405 | g_baseEval += pieceSquareAdj[rook & 0x7][me == 0 ? flipTable[to - 1] : (to - 1)];
1406 |
1407 | var rookIndex = g_pieceIndex[to + 1];
1408 | g_pieceIndex[to - 1] = rookIndex;
1409 | g_pieceList[((rook & 0xF) << 4) | rookIndex] = to - 1;
1410 | } else if (flags & moveflagCastleQueen) {
1411 | if (IsSquareAttackable(from - 1, otherColor) ||
1412 | IsSquareAttackable(from - 2, otherColor)) {
1413 | g_moveCount--;
1414 | return false;
1415 | }
1416 |
1417 | var rook = g_board[to - 2];
1418 |
1419 | g_hashKey -= g_zobrist[to - 2][rook & 0xF];
1420 | g_hashKey += g_zobrist[to + 1][rook & 0xF];
1421 |
1422 | g_board[to + 1] = rook;
1423 | g_board[to - 2] = pieceEmpty;
1424 |
1425 | g_baseEval -= pieceSquareAdj[rook & 0x7][me == 0 ? flipTable[to - 2] : (to - 2)];
1426 | g_baseEval += pieceSquareAdj[rook & 0x7][me == 0 ? flipTable[to + 1] : (to + 1)];
1427 |
1428 | var rookIndex = g_pieceIndex[to - 2];
1429 | g_pieceIndex[to + 1] = rookIndex;
1430 | g_pieceList[((rook & 0xF) << 4) | rookIndex] = to + 1;
1431 | }
1432 | }
1433 |
1434 | if (captured) {
1435 | // Remove our piece from the piece list
1436 | var capturedType = captured & 0xF;
1437 | g_pieceCount[capturedType]--;
1438 | var lastPieceSquare = g_pieceList[(capturedType << 4) | g_pieceCount[capturedType]];
1439 | g_pieceIndex[lastPieceSquare] = g_pieceIndex[epcEnd];
1440 | g_pieceList[(capturedType << 4) | g_pieceIndex[lastPieceSquare]] = lastPieceSquare;
1441 | g_pieceList[(capturedType << 4) | g_pieceCount[capturedType]] = 0;
1442 |
1443 | g_baseEval += materialTable[captured & 0x7];
1444 | g_baseEval += pieceSquareAdj[captured & 0x7][me ? flipTable[epcEnd] : epcEnd];
1445 |
1446 | g_hashKey -= g_zobrist[epcEnd][capturedType];
1447 | g_move50 = 0;
1448 | } else if ((piece & 0x7) == piecePawn) {
1449 | g_move50 = 0;
1450 | }
1451 |
1452 | g_hashKey -= g_zobrist[from][piece & 0xF];
1453 | g_hashKey += g_zobrist[to][piece & 0xF];
1454 | if (g_toMove) {
1455 | g_hashKey += g_zobristBlack;
1456 | }
1457 | else {
1458 | g_hashKey -= g_zobristBlack;
1459 | }
1460 |
1461 | g_castleRights &= g_castleRightsMask[from] & g_castleRightsMask[to];
1462 |
1463 | g_baseEval -= pieceSquareAdj[piece & 0x7][me == 0 ? flipTable[from] : from];
1464 |
1465 | // Move our piece in the piece list
1466 | g_pieceIndex[to] = g_pieceIndex[from];
1467 | g_pieceList[((piece & 0xF) << 4) | g_pieceIndex[to]] = to;
1468 |
1469 | if (flags & moveflagPromotion) {
1470 | var newPiece = piece & (~0x7);
1471 | if (flags & moveflagPromoteKnight)
1472 | newPiece |= pieceKnight;
1473 | else if (flags & moveflagPromoteQueen)
1474 | newPiece |= pieceQueen;
1475 | else if (flags & moveflagPromoteBishop)
1476 | newPiece |= pieceBishop;
1477 | else
1478 | newPiece |= pieceRook;
1479 |
1480 | g_hashKey -= g_zobrist[to][piece & 0xF];
1481 | g_board[to] = newPiece;
1482 | g_hashKey += g_zobrist[to][newPiece & 0xF];
1483 |
1484 | g_baseEval += pieceSquareAdj[newPiece & 0x7][me == 0 ? flipTable[to] : to];
1485 | g_baseEval -= materialTable[piecePawn];
1486 | g_baseEval += materialTable[newPiece & 0x7];
1487 |
1488 | var pawnType = piece & 0xF;
1489 | var promoteType = newPiece & 0xF;
1490 |
1491 | g_pieceCount[pawnType]--;
1492 |
1493 | var lastPawnSquare = g_pieceList[(pawnType << 4) | g_pieceCount[pawnType]];
1494 | g_pieceIndex[lastPawnSquare] = g_pieceIndex[to];
1495 | g_pieceList[(pawnType << 4) | g_pieceIndex[lastPawnSquare]] = lastPawnSquare;
1496 | g_pieceList[(pawnType << 4) | g_pieceCount[pawnType]] = 0;
1497 | g_pieceIndex[to] = g_pieceCount[promoteType];
1498 | g_pieceList[(promoteType << 4) | g_pieceIndex[to]] = to;
1499 | g_pieceCount[promoteType]++;
1500 | } else {
1501 | g_board[to] = g_board[from];
1502 |
1503 | g_baseEval += pieceSquareAdj[piece & 0x7][me == 0 ? flipTable[to] : to];
1504 | }
1505 | g_board[from] = pieceEmpty;
1506 |
1507 | g_toMove = otherColor;
1508 | g_baseEval = -g_baseEval;
1509 |
1510 | if ((piece & 0x7) == pieceKing || g_inCheck) {
1511 | if (IsSquareAttackable(g_pieceList[(pieceKing | (8 - g_toMove)) << 4], otherColor)) {
1512 | UnmakeMove(move);
1513 | return false;
1514 | }
1515 | } else {
1516 | var kingPos = g_pieceList[(pieceKing | (8 - g_toMove)) << 4];
1517 |
1518 | if (ExposesCheck(from, kingPos)) {
1519 | UnmakeMove(move);
1520 | return false;
1521 | }
1522 |
1523 | if (epcEnd != to) {
1524 | if (ExposesCheck(epcEnd, kingPos)) {
1525 | UnmakeMove(move);
1526 | return false;
1527 | }
1528 | }
1529 | }
1530 |
1531 | g_inCheck = false;
1532 |
1533 | if (flags <= moveflagEPC) {
1534 | var theirKingPos = g_pieceList[(pieceKing | g_toMove) << 4];
1535 |
1536 | // First check if the piece we moved can attack the enemy king
1537 | g_inCheck = IsSquareAttackableFrom(theirKingPos, to);
1538 |
1539 | if (!g_inCheck) {
1540 | // Now check if the square we moved from exposes check on the enemy king
1541 | g_inCheck = ExposesCheck(from, theirKingPos);
1542 |
1543 | if (!g_inCheck) {
1544 | // Finally, ep. capture can cause another square to be exposed
1545 | if (epcEnd != to) {
1546 | g_inCheck = ExposesCheck(epcEnd, theirKingPos);
1547 | }
1548 | }
1549 | }
1550 | }
1551 | else {
1552 | // Castle or promotion, slow check
1553 | g_inCheck = IsSquareAttackable(g_pieceList[(pieceKing | g_toMove) << 4], 8 - g_toMove);
1554 | }
1555 |
1556 | g_repMoveStack[g_moveCount - 1] = g_hashKey;
1557 | g_move50++;
1558 |
1559 | return true;
1560 | }
1561 |
1562 | function UnmakeMove(move){
1563 | g_toMove = 8 - g_toMove;
1564 | g_baseEval = -g_baseEval;
1565 |
1566 | g_moveCount--;
1567 | g_enPassentSquare = g_moveUndoStack[g_moveCount].ep;
1568 | g_castleRights = g_moveUndoStack[g_moveCount].castleRights;
1569 | g_inCheck = g_moveUndoStack[g_moveCount].inCheck;
1570 | g_baseEval = g_moveUndoStack[g_moveCount].baseEval;
1571 | g_hashKey = g_moveUndoStack[g_moveCount].hashKey;
1572 | g_move50 = g_moveUndoStack[g_moveCount].move50;
1573 |
1574 | var otherColor = 8 - g_toMove;
1575 | var me = g_toMove >> 3;
1576 | var them = otherColor >> 3;
1577 |
1578 | var flags = move & 0xFF000000;
1579 | var captured = (move >> 16) & 0xFF;
1580 | var to = (move >> 8) & 0xFF;
1581 | var from = move & 0xFF;
1582 |
1583 | var piece = g_board[to];
1584 |
1585 | if (flags) {
1586 | if (flags & moveflagCastleKing) {
1587 | var rook = g_board[to - 1];
1588 | g_board[to + 1] = rook;
1589 | g_board[to - 1] = pieceEmpty;
1590 |
1591 | var rookIndex = g_pieceIndex[to - 1];
1592 | g_pieceIndex[to + 1] = rookIndex;
1593 | g_pieceList[((rook & 0xF) << 4) | rookIndex] = to + 1;
1594 | }
1595 | else if (flags & moveflagCastleQueen) {
1596 | var rook = g_board[to + 1];
1597 | g_board[to - 2] = rook;
1598 | g_board[to + 1] = pieceEmpty;
1599 |
1600 | var rookIndex = g_pieceIndex[to + 1];
1601 | g_pieceIndex[to - 2] = rookIndex;
1602 | g_pieceList[((rook & 0xF) << 4) | rookIndex] = to - 2;
1603 | }
1604 | }
1605 |
1606 | if (flags & moveflagPromotion) {
1607 | piece = (g_board[to] & (~0x7)) | piecePawn;
1608 | g_board[from] = piece;
1609 |
1610 | var pawnType = g_board[from] & 0xF;
1611 | var promoteType = g_board[to] & 0xF;
1612 |
1613 | g_pieceCount[promoteType]--;
1614 |
1615 | var lastPromoteSquare = g_pieceList[(promoteType << 4) | g_pieceCount[promoteType]];
1616 | g_pieceIndex[lastPromoteSquare] = g_pieceIndex[to];
1617 | g_pieceList[(promoteType << 4) | g_pieceIndex[lastPromoteSquare]] = lastPromoteSquare;
1618 | g_pieceList[(promoteType << 4) | g_pieceCount[promoteType]] = 0;
1619 | g_pieceIndex[to] = g_pieceCount[pawnType];
1620 | g_pieceList[(pawnType << 4) | g_pieceIndex[to]] = to;
1621 | g_pieceCount[pawnType]++;
1622 | }
1623 | else {
1624 | g_board[from] = g_board[to];
1625 | }
1626 |
1627 | var epcEnd = to;
1628 | if (flags & moveflagEPC) {
1629 | if (g_toMove == colorWhite)
1630 | epcEnd = to + 0x10;
1631 | else
1632 | epcEnd = to - 0x10;
1633 | g_board[to] = pieceEmpty;
1634 | }
1635 |
1636 | g_board[epcEnd] = captured;
1637 |
1638 | // Move our piece in the piece list
1639 | g_pieceIndex[from] = g_pieceIndex[to];
1640 | g_pieceList[((piece & 0xF) << 4) | g_pieceIndex[from]] = from;
1641 |
1642 | if (captured) {
1643 | // Restore our piece to the piece list
1644 | var captureType = captured & 0xF;
1645 | g_pieceIndex[epcEnd] = g_pieceCount[captureType];
1646 | g_pieceList[(captureType << 4) | g_pieceCount[captureType]] = epcEnd;
1647 | g_pieceCount[captureType]++;
1648 | }
1649 | }
1650 |
1651 | function ExposesCheck(from, kingPos){
1652 | var index = kingPos - from + 128;
1653 | // If a queen can't reach it, nobody can!
1654 | if ((g_vectorDelta[index].pieceMask[0] & (1 << (pieceQueen))) != 0) {
1655 | var delta = g_vectorDelta[index].delta;
1656 | var pos = kingPos + delta;
1657 | while (g_board[pos] == 0) pos += delta;
1658 |
1659 | var piece = g_board[pos];
1660 | if (((piece & (g_board[kingPos] ^ 0x18)) & 0x18) == 0)
1661 | return false;
1662 |
1663 | // Now see if the piece can actually attack the king
1664 | var backwardIndex = pos - kingPos + 128;
1665 | return (g_vectorDelta[backwardIndex].pieceMask[(piece >> 3) & 1] & (1 << (piece & 0x7))) != 0;
1666 | }
1667 | return false;
1668 | }
1669 |
1670 | function IsSquareAttackableFrom(target, from){
1671 | var index = from - target + 128;
1672 | var piece = g_board[from];
1673 | if (g_vectorDelta[index].pieceMask[(piece >> 3) & 1] & (1 << (piece & 0x7))) {
1674 | // Yes, this square is pseudo-attackable. Now, check for real attack
1675 | var inc = g_vectorDelta[index].delta;
1676 | do {
1677 | from += inc;
1678 | if (from == target)
1679 | return true;
1680 | } while (g_board[from] == 0);
1681 | }
1682 |
1683 | return false;
1684 | }
1685 |
1686 | function IsSquareAttackable(target, color) {
1687 | // Attackable by pawns?
1688 | var inc = color ? -16 : 16;
1689 | var pawn = (color ? colorWhite : colorBlack) | 1;
1690 | if (g_board[target - (inc - 1)] == pawn)
1691 | return true;
1692 | if (g_board[target - (inc + 1)] == pawn)
1693 | return true;
1694 |
1695 | // Attackable by pieces?
1696 | for (var i = 2; i <= 6; i++) {
1697 | var index = (color | i) << 4;
1698 | var square = g_pieceList[index];
1699 | while (square != 0) {
1700 | if (IsSquareAttackableFrom(target, square))
1701 | return true;
1702 | square = g_pieceList[++index];
1703 | }
1704 | }
1705 | return false;
1706 | }
1707 |
1708 | function GenerateMove(from, to, captured){
1709 | return from | (to << 8);
1710 | }
1711 |
1712 | function GenerateMove(from, to, captured){
1713 | return from | (to << 8) | (captured << 16);
1714 | }
1715 |
1716 | function GenerateMove(from, to, captured, flags) {
1717 | return from | (to << 8) | (captured << 16) | flags;
1718 | }
1719 |
1720 | function GenerateValidMoves() {
1721 | var moveList = new Array();
1722 | var allMoves = new Array();
1723 | GenerateCaptureMoves(allMoves, null);
1724 | GenerateAllMoves(allMoves);
1725 |
1726 | for (var i = allMoves.length - 1; i >= 0; i--) {
1727 | if (MakeMove(allMoves[i])) {
1728 | moveList[moveList.length] = allMoves[i];
1729 | UnmakeMove(allMoves[i]);
1730 | }
1731 | }
1732 |
1733 | return moveList;
1734 | }
1735 |
1736 | function GenerateAllMoves(moveStack) {
1737 | var from, to, piece, pieceIdx;
1738 |
1739 | // Pawn quiet moves
1740 | pieceIdx = (g_toMove | 1) << 4;
1741 | from = g_pieceList[pieceIdx++];
1742 | while (from != 0) {
1743 | GeneratePawnMoves(moveStack, from);
1744 | from = g_pieceList[pieceIdx++];
1745 | }
1746 |
1747 | // Knight quiet moves
1748 | pieceIdx = (g_toMove | 2) << 4;
1749 | from = g_pieceList[pieceIdx++];
1750 | while (from != 0) {
1751 | to = from + 31; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1752 | to = from + 33; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1753 | to = from + 14; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1754 | to = from - 14; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1755 | to = from - 31; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1756 | to = from - 33; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1757 | to = from + 18; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1758 | to = from - 18; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1759 | from = g_pieceList[pieceIdx++];
1760 | }
1761 |
1762 | // Bishop quiet moves
1763 | pieceIdx = (g_toMove | 3) << 4;
1764 | from = g_pieceList[pieceIdx++];
1765 | while (from != 0) {
1766 | to = from - 15; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to -= 15; }
1767 | to = from - 17; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to -= 17; }
1768 | to = from + 15; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to += 15; }
1769 | to = from + 17; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to += 17; }
1770 | from = g_pieceList[pieceIdx++];
1771 | }
1772 |
1773 | // Rook quiet moves
1774 | pieceIdx = (g_toMove | 4) << 4;
1775 | from = g_pieceList[pieceIdx++];
1776 | while (from != 0) {
1777 | to = from - 1; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to--; }
1778 | to = from + 1; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to++; }
1779 | to = from + 16; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to += 16; }
1780 | to = from - 16; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to -= 16; }
1781 | from = g_pieceList[pieceIdx++];
1782 | }
1783 |
1784 | // Queen quiet moves
1785 | pieceIdx = (g_toMove | 5) << 4;
1786 | from = g_pieceList[pieceIdx++];
1787 | while (from != 0) {
1788 | to = from - 15; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to -= 15; }
1789 | to = from - 17; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to -= 17; }
1790 | to = from + 15; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to += 15; }
1791 | to = from + 17; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to += 17; }
1792 | to = from - 1; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to--; }
1793 | to = from + 1; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to++; }
1794 | to = from + 16; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to += 16; }
1795 | to = from - 16; while (g_board[to] == 0) { moveStack[moveStack.length] = GenerateMove(from, to); to -= 16; }
1796 | from = g_pieceList[pieceIdx++];
1797 | }
1798 |
1799 | // King quiet moves
1800 | {
1801 | pieceIdx = (g_toMove | 6) << 4;
1802 | from = g_pieceList[pieceIdx];
1803 | to = from - 15; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1804 | to = from - 17; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1805 | to = from + 15; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1806 | to = from + 17; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1807 | to = from - 1; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1808 | to = from + 1; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1809 | to = from - 16; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1810 | to = from + 16; if (g_board[to] == 0) moveStack[moveStack.length] = GenerateMove(from, to);
1811 |
1812 | if (!g_inCheck) {
1813 | var castleRights = g_castleRights;
1814 | if (!g_toMove)
1815 | castleRights >>= 2;
1816 | if (castleRights & 1) {
1817 | // Kingside castle
1818 | if (g_board[from + 1] == pieceEmpty && g_board[from + 2] == pieceEmpty) {
1819 | moveStack[moveStack.length] = GenerateMove(from, from + 0x02, pieceEmpty, moveflagCastleKing);
1820 | }
1821 | }
1822 | if (castleRights & 2) {
1823 | // Queenside castle
1824 | if (g_board[from - 1] == pieceEmpty && g_board[from - 2] == pieceEmpty && g_board[from - 3] == pieceEmpty) {
1825 | moveStack[moveStack.length] = GenerateMove(from, from - 0x02, pieceEmpty, moveflagCastleQueen);
1826 | }
1827 | }
1828 | }
1829 | }
1830 | }
1831 |
1832 | function GenerateCaptureMoves(moveStack, moveScores) {
1833 | var from, to, piece, pieceIdx;
1834 | var inc = (g_toMove == 8) ? -16 : 16;
1835 | var enemy = g_toMove == 8 ? 0x10 : 0x8;
1836 |
1837 | // Pawn captures
1838 | pieceIdx = (g_toMove | 1) << 4;
1839 | from = g_pieceList[pieceIdx++];
1840 | while (from != 0) {
1841 | to = from + inc - 1;
1842 | if (g_board[to] & enemy) {
1843 | MovePawnTo(moveStack, from, to, g_board[to]);
1844 | }
1845 |
1846 | to = from + inc + 1;
1847 | if (g_board[to] & enemy) {
1848 | MovePawnTo(moveStack, from, to, g_board[to]);
1849 | }
1850 |
1851 | from = g_pieceList[pieceIdx++];
1852 | }
1853 |
1854 | if (g_enPassentSquare != -1) {
1855 | var inc = (g_toMove == colorWhite) ? -16 : 16;
1856 | var pawn = g_toMove | piecePawn;
1857 |
1858 | var from = g_enPassentSquare - (inc + 1);
1859 | if ((g_board[from] & 0xF) == pawn) {
1860 | moveStack[moveStack.length] = GenerateMove(from, g_enPassentSquare, g_board[g_enPassentSquare - inc], moveflagEPC);
1861 | }
1862 |
1863 | from = g_enPassentSquare - (inc - 1);
1864 | if ((g_board[from] & 0xF) == pawn) {
1865 | moveStack[moveStack.length] = GenerateMove(from, g_enPassentSquare, g_board[g_enPassentSquare - inc], moveflagEPC);
1866 | }
1867 | }
1868 |
1869 | // Knight captures
1870 | pieceIdx = (g_toMove | 2) << 4;
1871 | from = g_pieceList[pieceIdx++];
1872 | while (from != 0) {
1873 | to = from + 31; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1874 | to = from + 33; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1875 | to = from + 14; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1876 | to = from - 14; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1877 | to = from - 31; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1878 | to = from - 33; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1879 | to = from + 18; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1880 | to = from - 18; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1881 | from = g_pieceList[pieceIdx++];
1882 | }
1883 |
1884 | // Bishop captures
1885 | pieceIdx = (g_toMove | 3) << 4;
1886 | from = g_pieceList[pieceIdx++];
1887 | while (from != 0) {
1888 | to = from; do { to -= 15; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1889 | to = from; do { to -= 17; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1890 | to = from; do { to += 15; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1891 | to = from; do { to += 17; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1892 | from = g_pieceList[pieceIdx++];
1893 | }
1894 |
1895 | // Rook captures
1896 | pieceIdx = (g_toMove | 4) << 4;
1897 | from = g_pieceList[pieceIdx++];
1898 | while (from != 0) {
1899 | to = from; do { to--; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1900 | to = from; do { to++; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1901 | to = from; do { to -= 16; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1902 | to = from; do { to += 16; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1903 | from = g_pieceList[pieceIdx++];
1904 | }
1905 |
1906 | // Queen captures
1907 | pieceIdx = (g_toMove | 5) << 4;
1908 | from = g_pieceList[pieceIdx++];
1909 | while (from != 0) {
1910 | to = from; do { to -= 15; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1911 | to = from; do { to -= 17; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1912 | to = from; do { to += 15; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1913 | to = from; do { to += 17; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1914 | to = from; do { to--; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1915 | to = from; do { to++; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1916 | to = from; do { to -= 16; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1917 | to = from; do { to += 16; } while (g_board[to] == 0); if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1918 | from = g_pieceList[pieceIdx++];
1919 | }
1920 |
1921 | // King captures
1922 | {
1923 | pieceIdx = (g_toMove | 6) << 4;
1924 | from = g_pieceList[pieceIdx];
1925 | to = from - 15; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1926 | to = from - 17; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1927 | to = from + 15; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1928 | to = from + 17; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1929 | to = from - 1; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1930 | to = from + 1; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1931 | to = from - 16; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1932 | to = from + 16; if (g_board[to] & enemy) moveStack[moveStack.length] = GenerateMove(from, to, g_board[to]);
1933 | }
1934 | }
1935 |
1936 | function MovePawnTo(moveStack, start, square, captured) {
1937 | var row = square & 0xF0;
1938 | if ((row == 0x90) || (row == 0x20)) {
1939 | moveStack[moveStack.length] = GenerateMove(start, square, captured, moveflagPromotion | moveflagPromoteQueen);
1940 | moveStack[moveStack.length] = GenerateMove(start, square, captured, moveflagPromotion | moveflagPromoteKnight);
1941 | moveStack[moveStack.length] = GenerateMove(start, square, captured, moveflagPromotion | moveflagPromoteBishop);
1942 | moveStack[moveStack.length] = GenerateMove(start, square, captured, moveflagPromotion);
1943 | }
1944 | else {
1945 | moveStack[moveStack.length] = GenerateMove(start, square, captured, 0);
1946 | }
1947 | }
1948 |
1949 | function GeneratePawnMoves(moveStack, from) {
1950 | var piece = g_board[from];
1951 | var color = piece & colorWhite;
1952 | var inc = (color == colorWhite) ? -16 : 16;
1953 |
1954 | // Quiet pawn moves
1955 | var to = from + inc;
1956 | if (g_board[to] == 0) {
1957 | MovePawnTo(moveStack, from, to, pieceEmpty);
1958 |
1959 | // Check if we can do a 2 square jump
1960 | if ((((from & 0xF0) == 0x30) && color != colorWhite) ||
1961 | (((from & 0xF0) == 0x80) && color == colorWhite)) {
1962 | to += inc;
1963 | if (g_board[to] == 0) {
1964 | moveStack[moveStack.length] = GenerateMove(from, to, pieceEmpty, moveflagEP);
1965 | }
1966 | }
1967 | }
1968 | }
1969 |
1970 | function UndoHistory(ep, castleRights, inCheck, baseEval, hashKey, move50) {
1971 | this.ep = ep;
1972 | this.castleRights = castleRights;
1973 | this.inCheck = inCheck;
1974 | this.baseEval = baseEval;
1975 | this.hashKey = hashKey;
1976 | this.move50 = move50;
1977 | }
1978 |
1979 | //////////////////////////////////////////////////
1980 | // Test Harness
1981 | //////////////////////////////////////////////////
1982 | function FinishMoveLocalTesting(bestMove, value, timeTaken, ply) {
1983 | if (bestMove != null) {
1984 | MakeMove(bestMove);
1985 | postMessage(bestMove.toString("16"));
1986 | }
1987 | }
1988 |
1989 | var needsReset = true;
1990 | onmessage = function (e) {
1991 | if (e.data == "go" || needsReset) {
1992 | ResetGame();
1993 | needsReset = false;
1994 | }
1995 | if (e.data.match("^position") == "position") {
1996 | ResetGame();
1997 | InitializeFromFen(e.data.substr(9, e.data.length - 9));
1998 | } else if (e.data == "search") {
1999 | Search(FinishMoveLocalTesting, 99);
2000 | } else {
2001 | MakeMove(parseInt(e.data, 16));
2002 | }
2003 | }
--------------------------------------------------------------------------------
/book/book.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glinscott/Garbochess-JS/4ff0f9e49a0041545b645e25d3d2da7af3b36a90/book/book.bin
--------------------------------------------------------------------------------
/chess.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | GarboChess Javascript
5 |
6 |
7 |
8 |
9 |
15 |
38 |
39 |
40 |
41 |
42 |
New game
43 |
47 | Time per move:
ms
48 |
Undo
49 |
50 |
51 |
52 |
53 |
54 |
57 | FEN:
58 |
59 |
60 |
About
61 |
It started as a feasibility project to see how Javascript could do in Chess, then languished until Chrome came along.
62 |
Back to Projects
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/debug.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Test harness
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
33 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | output
45 |
46 | FEN:
47 |
48 |
49 |
--------------------------------------------------------------------------------
/img/bishop_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glinscott/Garbochess-JS/4ff0f9e49a0041545b645e25d3d2da7af3b36a90/img/bishop_black.png
--------------------------------------------------------------------------------
/img/bishop_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glinscott/Garbochess-JS/4ff0f9e49a0041545b645e25d3d2da7af3b36a90/img/bishop_white.png
--------------------------------------------------------------------------------
/img/blank.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glinscott/Garbochess-JS/4ff0f9e49a0041545b645e25d3d2da7af3b36a90/img/blank.gif
--------------------------------------------------------------------------------
/img/king_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glinscott/Garbochess-JS/4ff0f9e49a0041545b645e25d3d2da7af3b36a90/img/king_black.png
--------------------------------------------------------------------------------
/img/king_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glinscott/Garbochess-JS/4ff0f9e49a0041545b645e25d3d2da7af3b36a90/img/king_white.png
--------------------------------------------------------------------------------
/img/knight_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glinscott/Garbochess-JS/4ff0f9e49a0041545b645e25d3d2da7af3b36a90/img/knight_black.png
--------------------------------------------------------------------------------
/img/knight_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glinscott/Garbochess-JS/4ff0f9e49a0041545b645e25d3d2da7af3b36a90/img/knight_white.png
--------------------------------------------------------------------------------
/img/pawn_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glinscott/Garbochess-JS/4ff0f9e49a0041545b645e25d3d2da7af3b36a90/img/pawn_black.png
--------------------------------------------------------------------------------
/img/pawn_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glinscott/Garbochess-JS/4ff0f9e49a0041545b645e25d3d2da7af3b36a90/img/pawn_white.png
--------------------------------------------------------------------------------
/img/queen_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glinscott/Garbochess-JS/4ff0f9e49a0041545b645e25d3d2da7af3b36a90/img/queen_black.png
--------------------------------------------------------------------------------
/img/queen_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glinscott/Garbochess-JS/4ff0f9e49a0041545b645e25d3d2da7af3b36a90/img/queen_white.png
--------------------------------------------------------------------------------
/img/rook_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glinscott/Garbochess-JS/4ff0f9e49a0041545b645e25d3d2da7af3b36a90/img/rook_black.png
--------------------------------------------------------------------------------
/img/rook_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glinscott/Garbochess-JS/4ff0f9e49a0041545b645e25d3d2da7af3b36a90/img/rook_white.png
--------------------------------------------------------------------------------
/img/sprites.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glinscott/Garbochess-JS/4ff0f9e49a0041545b645e25d3d2da7af3b36a90/img/sprites.png
--------------------------------------------------------------------------------
/img/transpBlue50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glinscott/Garbochess-JS/4ff0f9e49a0041545b645e25d3d2da7af3b36a90/img/transpBlue50.png
--------------------------------------------------------------------------------
/js/bench.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | /* Initial bench scores:
4 | Ply:5 Score:254 Nodes:11120 NPS:45950 Nc3 d5 d4 Nf6 Bf4
5 | Ply:5 Score:281 Nodes:24707 NPS:51365 Rad8 Nc3 f5 a3 Bd6
6 | Ply:5 Score:741 Nodes:23628 NPS:55334 Qa5 Rd1 b5 Qc3 Qxa2
7 | Ply:5 Score:188 Nodes:32137 NPS:65054 Bd3 Rxf8+ Rxf8 dxe5 Nxe5 Bf4
8 | Ply:5 Score:509 Nodes:9584 NPS:59160 Nxc6 bxc6 Ne7+ Kh8 Nxc6 Qb7
9 | Ply:5 Score:127 Nodes:15516 NPS:52067 a3 Bd7 Rd2 Qe8 Qg5
10 | Ply:5 Score:106 Nodes:8733 NPS:45722 Qxb2 Qd2 Red8 Rab1 Qa3
11 | Ply:5 Score:1155 Nodes:43826 NPS:66202 Bg3 Bg4 f3 Be6 Nxd4
12 | Ply:5 Score:410 Nodes:27018 NPS:43931 Bc2 g6 Qg4 Nc5 Qh4
13 | Ply:5 Score:873 Nodes:15928 NPS:41805 Qg3 Qxe5 Rxg4 Nxg4 Qxg4
14 | Ply:5 Score:-249 Nodes:59916 NPS:72362 Ng4 O-O a6 f5 gxf5
15 | Ply:5 Score:238 Nodes:19851 NPS:65950 Rc1 Bd7 Qd3 Bb6 e5
16 | Ply:5 Score:911 Nodes:19888 NPS:65206 Bd6 Rb2 Rf7 Qh5 Bxc4
17 | Ply:5 Score:-317 Nodes:12639 NPS:63512 Qb5 Bg6 Bg5 f6 Bf4
18 | Ply:5 Score:870 Nodes:31307 NPS:83932 Ra3 R5b2 Ke7 Rc1 Rc8
19 | Ply:5 Score:715 Nodes:8779 NPS:61823 Qe7 Qb1 Qc7 Qc2 Bc6
20 | 364577 NPS:59756
21 |
22 | Ply:6 Score:13 Nodes:39972 NPS:97255 e4 d5 exd5 Qxd5 Nc3 Qd6
23 | Ply:6 Score:192 Nodes:129475 NPS:116017 Rfd8 a3 Ba5 Nc5 Nxd4 Bxh7+ Kxh7
24 | Ply:6 Score:727 Nodes:58792 NPS:110303 Qa5 a4 a6 Rf1 b5 axb5
25 | Ply:6 Score:127 Nodes:58919 NPS:132700 Bd3 Rxf8+ Rxf8 dxe5 Nxe5 Bf4 Re8
26 | Ply:6 Score:8 Nodes:73180 NPS:113987 Nxc6 bxc6 Ne7+ Kh8 Kb1 Qb7 Nf5
27 | Ply:6 Score:12 Nodes:53598 NPS:95031 a3 Bd7 d4 Nc6 dxe5 Nxe5
28 | Ply:6 Score:17 Nodes:31916 NPS:89651 Qxb2 Qd2 Qb6+ d4 Rad8 e5 Ng4
29 | Ply:6 Score:1220 Nodes:91765 NPS:140743 Bg3 Bd7 Nxd4 Bf6 Re1+ Nge7 Nd6+ Kf8
30 | Ply:6 Score:273 Nodes:40782 NPS:90626 Bc2 g6 Qe2 Nc6 Bh6 Ng7
31 | Ply:6 Score:873 Nodes:30305 NPS:102728 Qg3 Qxe5 Rxg4 Nxg4 Qxg4 Qxb2
32 | Ply:6 Score:-278 Nodes:202802 NPS:120571 Ng4 Qa4 a6 Na7 Bxd5 cxd5
33 | Ply:6 Score:152 Nodes:90124 NPS:97431 a4 Rb8 Qd3 bxa4 Rxa4 Rb5
34 | Ply:6 Score:824 Nodes:59203 NPS:110659 Bd6 Rb2 Ba3 Rb3 Bd6 Qg4
35 | Ply:6 Score:-387 Nodes:33173 NPS:102385 Qb5 Qg4 f3 exf3 Nxf3 Bg6
36 | Ply:6 Score:865 Nodes:75147 NPS:128456 Rg8 a4 g5 Rd1 Rc4 a5
37 | Ply:6 Score:638 Nodes:26820 NPS:98602 Qe7 Qa2 Qc7 f3 Bc6 Qc2
38 | 1095973 NPS:111993
39 | */
40 |
41 | var PerfTests = new Object();
42 |
43 | PerfTests.g_benchPositions =
44 | [
45 | "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
46 | "r4rk1/1b2qppp/p1n1p3/1p6/1b1PN3/3BRN2/PP3PPP/R2Q2K1 b - - 7 16",
47 | "4r1k1/ppq3pp/3b4/2pP4/2Q1p3/4B1P1/PP5P/R5K1 b - - 0 20",
48 | "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19",
49 | "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14",
50 | "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14",
51 | "r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15",
52 | "r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R w kq - 0 13",
53 | "r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1 w - - 1 16",
54 | "4r1k1/r1q2ppp/ppp2n2/4P3/5Rb1/1N1BQ3/PPP3PP/R5K1 w - - 1 17",
55 | "2rqkb1r/ppp2p2/2npb1p1/1N1Nn2p/2P1PP2/8/PP2B1PP/R1BQK2R b KQ - 0 11",
56 | "r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16",
57 | "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22",
58 | "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18",
59 | "4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - 3 22",
60 | "3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26"
61 | ];
62 |
63 | PerfTests.finishTest = function (bestMove, value, timeTaken, ply) {
64 | var totalNodes = g_nodeCount + g_qNodeCount;
65 | var pv = "Ply:" + ply + " Score:" + value + " Nodes:" + totalNodes + " NPS:" + ((totalNodes / (timeTaken / 1000)) | 0) + " " + PVFromHash(bestMove, 15);
66 | PerfTests.totalNodes += totalNodes;
67 | PerfTests.totalTime += timeTaken;
68 |
69 | var pgnTextBox = document.getElementById("PgnTextBox");
70 | pgnTextBox.value += pv + "\n";
71 | if (PerfTests.currentTest >= PerfTests.g_benchPositions.length) {
72 | pgnTextBox.value += PerfTests.totalNodes + " NPS:" + ((PerfTests.totalNodes / (PerfTests.totalTime / 1000)) | 0);
73 | }
74 | }
75 |
76 | PerfTests.currentTest = 0;
77 | PerfTests.totalNodes = 0;
78 | PerfTests.totalTime = 0;
79 |
80 | PerfTests.benchmark = function () {
81 | PerfTests.currentTest = 0;
82 | PerfTests.totalNodes = 0;
83 | PerfTests.totalTime = 0;
84 | ResetGame();
85 | PerfTests.benchmarkInner();
86 | }
87 |
88 | PerfTests.benchmarkInner = function () {
89 | if (PerfTests.currentTest < PerfTests.g_benchPositions.length)
90 | setTimeout(function () {
91 | InitializeFromFen(PerfTests.g_benchPositions[PerfTests.currentTest++]);
92 | Search(PerfTests.finishTest, 6, null);
93 | PerfTests.benchmarkInner();
94 | }, 100);
95 | }
--------------------------------------------------------------------------------
/js/boardui.js:
--------------------------------------------------------------------------------
1 | var g_startOffset = null;
2 | var g_selectedPiece = null;
3 | var moveNumber = 1;
4 |
5 | var g_allMoves = [];
6 | var g_playerWhite = true;
7 | var g_changingFen = false;
8 | var g_analyzing = false;
9 |
10 | var g_uiBoard;
11 | var g_cellSize = 45;
12 |
13 | function UINewGame() {
14 | moveNumber = 1;
15 |
16 | var pgnTextBox = document.getElementById("PgnTextBox");
17 | pgnTextBox.value = "";
18 |
19 | EnsureAnalysisStopped();
20 | ResetGame();
21 | if (InitializeBackgroundEngine()) {
22 | g_backgroundEngine.postMessage("go");
23 | }
24 | g_allMoves = [];
25 | RedrawBoard();
26 |
27 | if (!g_playerWhite) {
28 | SearchAndRedraw();
29 | }
30 | }
31 |
32 | function EnsureAnalysisStopped() {
33 | if (g_analyzing && g_backgroundEngine != null) {
34 | g_backgroundEngine.terminate();
35 | g_backgroundEngine = null;
36 | }
37 | }
38 |
39 | function UIAnalyzeToggle() {
40 | if (InitializeBackgroundEngine()) {
41 | if (!g_analyzing) {
42 | g_backgroundEngine.postMessage("analyze");
43 | } else {
44 | EnsureAnalysisStopped();
45 | }
46 | g_analyzing = !g_analyzing;
47 | document.getElementById("AnalysisToggleLink").innerText = g_analyzing ? "Analysis: On" : "Analysis: Off";
48 | } else {
49 | alert("Your browser must support web workers for analysis - (chrome4, ff4, safari)");
50 | }
51 | }
52 |
53 | function UIChangeFEN() {
54 | if (!g_changingFen) {
55 | var fenTextBox = document.getElementById("FenTextBox");
56 | var result = InitializeFromFen(fenTextBox.value);
57 | if (result.length != 0) {
58 | UpdatePVDisplay(result);
59 | return;
60 | } else {
61 | UpdatePVDisplay('');
62 | }
63 | g_allMoves = [];
64 |
65 | EnsureAnalysisStopped();
66 | InitializeBackgroundEngine();
67 |
68 | g_playerWhite = !!g_toMove;
69 | g_backgroundEngine.postMessage("position " + GetFen());
70 |
71 | RedrawBoard();
72 | }
73 | }
74 |
75 | function UIChangeStartPlayer() {
76 | g_playerWhite = !g_playerWhite;
77 | RedrawBoard();
78 | }
79 |
80 | function UpdatePgnTextBox(move) {
81 | var pgnTextBox = document.getElementById("PgnTextBox");
82 | if (g_toMove != 0) {
83 | pgnTextBox.value += moveNumber + ". ";
84 | moveNumber++;
85 | }
86 | pgnTextBox.value += GetMoveSAN(move) + " ";
87 | }
88 |
89 | function UIChangeTimePerMove() {
90 | var timePerMove = document.getElementById("TimePerMove");
91 | g_timeout = parseInt(timePerMove.value, 10);
92 | }
93 |
94 | function FinishMove(bestMove, value, timeTaken, ply) {
95 | if (bestMove != null) {
96 | UIPlayMove(bestMove, BuildPVMessage(bestMove, value, timeTaken, ply));
97 | } else {
98 | alert("Checkmate!");
99 | }
100 | }
101 |
102 | function UIPlayMove(move, pv) {
103 | UpdatePgnTextBox(move);
104 |
105 | g_allMoves[g_allMoves.length] = move;
106 | MakeMove(move);
107 |
108 | UpdatePVDisplay(pv);
109 |
110 | UpdateFromMove(move);
111 | }
112 |
113 | function UIUndoMove() {
114 | if (g_allMoves.length == 0) {
115 | return;
116 | }
117 |
118 | if (g_backgroundEngine != null) {
119 | g_backgroundEngine.terminate();
120 | g_backgroundEngine = null;
121 | }
122 |
123 | UnmakeMove(g_allMoves[g_allMoves.length - 1]);
124 | g_allMoves.pop();
125 |
126 | if (g_playerWhite != !!g_toMove && g_allMoves.length != 0) {
127 | UnmakeMove(g_allMoves[g_allMoves.length - 1]);
128 | g_allMoves.pop();
129 | }
130 |
131 | RedrawBoard();
132 | }
133 |
134 | function UpdatePVDisplay(pv) {
135 | if (pv != null) {
136 | var outputDiv = document.getElementById("output");
137 | if (outputDiv.firstChild != null) {
138 | outputDiv.removeChild(outputDiv.firstChild);
139 | }
140 | outputDiv.appendChild(document.createTextNode(pv));
141 | }
142 | }
143 |
144 | function SearchAndRedraw() {
145 | if (g_analyzing) {
146 | EnsureAnalysisStopped();
147 | InitializeBackgroundEngine();
148 | g_backgroundEngine.postMessage("position " + GetFen());
149 | g_backgroundEngine.postMessage("analyze");
150 | return;
151 | }
152 |
153 | if (InitializeBackgroundEngine()) {
154 | g_backgroundEngine.postMessage("search " + g_timeout);
155 | } else {
156 | Search(FinishMove, 99, null);
157 | }
158 | }
159 |
160 | var g_backgroundEngineValid = true;
161 | var g_backgroundEngine;
162 |
163 | function InitializeBackgroundEngine() {
164 | if (!g_backgroundEngineValid) {
165 | return false;
166 | }
167 |
168 | if (g_backgroundEngine == null) {
169 | g_backgroundEngineValid = true;
170 | try {
171 | g_backgroundEngine = new Worker("js/garbochess.js");
172 | g_backgroundEngine.onmessage = function (e) {
173 | if (e.data.match("^pv") == "pv") {
174 | UpdatePVDisplay(e.data.substr(3, e.data.length - 3));
175 | } else if (e.data.match("^message") == "message") {
176 | EnsureAnalysisStopped();
177 | UpdatePVDisplay(e.data.substr(8, e.data.length - 8));
178 | } else {
179 | UIPlayMove(GetMoveFromString(e.data), null);
180 | }
181 | }
182 | g_backgroundEngine.error = function (e) {
183 | alert("Error from background worker:" + e.message);
184 | }
185 | g_backgroundEngine.postMessage("position " + GetFen());
186 | } catch (error) {
187 | g_backgroundEngineValid = false;
188 | }
189 | }
190 |
191 | return g_backgroundEngineValid;
192 | }
193 |
194 | function UpdateFromMove(move) {
195 | var fromX = (move & 0xF) - 4;
196 | var fromY = ((move >> 4) & 0xF) - 2;
197 | var toX = ((move >> 8) & 0xF) - 4;
198 | var toY = ((move >> 12) & 0xF) - 2;
199 |
200 | if (!g_playerWhite) {
201 | fromY = 7 - fromY;
202 | toY = 7 - toY;
203 | fromX = 7 - fromX;
204 | toX = 7 - toX;
205 | }
206 |
207 | if ((move & moveflagCastleKing) ||
208 | (move & moveflagCastleQueen) ||
209 | (move & moveflagEPC) ||
210 | (move & moveflagPromotion)) {
211 | RedrawPieces();
212 | } else {
213 | var fromSquare = g_uiBoard[fromY * 8 + fromX];
214 | $(g_uiBoard[toY * 8 + toX])
215 | .empty()
216 | .append($(fromSquare).children());
217 | }
218 | }
219 |
220 | function RedrawPieces() {
221 | for (y = 0; y < 8; ++y) {
222 | for (x = 0; x < 8; ++x) {
223 | var td = g_uiBoard[y * 8 + x];
224 | var pieceY = g_playerWhite ? y : 7 - y;
225 | var piece = g_board[((pieceY + 2) * 0x10) + (g_playerWhite ? x : 7 - x) + 4];
226 | var pieceName = null;
227 | switch (piece & 0x7) {
228 | case piecePawn: pieceName = "pawn"; break;
229 | case pieceKnight: pieceName = "knight"; break;
230 | case pieceBishop: pieceName = "bishop"; break;
231 | case pieceRook: pieceName = "rook"; break;
232 | case pieceQueen: pieceName = "queen"; break;
233 | case pieceKing: pieceName = "king"; break;
234 | }
235 | if (pieceName != null) {
236 | pieceName += "_";
237 | pieceName += (piece & 0x8) ? "white" : "black";
238 | }
239 |
240 | if (pieceName != null) {
241 | var img = document.createElement("div");
242 | $(img).addClass('sprite-' + pieceName);
243 | img.style.backgroundImage = "url('img/sprites.png')";
244 | img.width = g_cellSize;
245 | img.height = g_cellSize;
246 | var divimg = document.createElement("div");
247 | divimg.appendChild(img);
248 |
249 | $(divimg).draggable({ start: function (e, ui) {
250 | if (g_selectedPiece === null) {
251 | g_selectedPiece = this;
252 | var offset = $(this).closest('table').offset();
253 | g_startOffset = {
254 | left: e.pageX - offset.left,
255 | top: e.pageY - offset.top
256 | };
257 | } else {
258 | return g_selectedPiece == this;
259 | }
260 | }});
261 |
262 | $(divimg).mousedown(function(e) {
263 | if (g_selectedPiece === null) {
264 | var offset = $(this).closest('table').offset();
265 | g_startOffset = {
266 | left: e.pageX - offset.left,
267 | top: e.pageY - offset.top
268 | };
269 | e.stopPropagation();
270 | g_selectedPiece = this;
271 | g_selectedPiece.style.backgroundImage = "url('img/transpBlue50.png')";
272 | } else if (g_selectedPiece === this) {
273 | g_selectedPiece.style.backgroundImage = null;
274 | g_selectedPiece = null;
275 | }
276 | });
277 |
278 | $(td).empty().append(divimg);
279 | } else {
280 | $(td).empty();
281 | }
282 | }
283 | }
284 | }
285 |
286 | function RedrawBoard() {
287 | var div = $("#board")[0];
288 |
289 | var table = document.createElement("table");
290 | table.cellPadding = "0px";
291 | table.cellSpacing = "0px";
292 | $(table).addClass('no-highlight');
293 |
294 | var tbody = document.createElement("tbody");
295 |
296 | g_uiBoard = [];
297 |
298 | var dropPiece = function (e, ui) {
299 | var endX = e.pageX - $(table).offset().left;
300 | var endY = e.pageY - $(table).offset().top;
301 |
302 | endX = Math.floor(endX / g_cellSize);
303 | endY = Math.floor(endY / g_cellSize);
304 |
305 | var startX = Math.floor(g_startOffset.left / g_cellSize);
306 | var startY = Math.floor(g_startOffset.top / g_cellSize);
307 |
308 | if (!g_playerWhite) {
309 | startY = 7 - startY;
310 | endY = 7 - endY;
311 | startX = 7 - startX;
312 | endX = 7 - endX;
313 | }
314 |
315 | var moves = GenerateValidMoves();
316 | var move = null;
317 | for (var i = 0; i < moves.length; i++) {
318 | if ((moves[i] & 0xFF) == MakeSquare(startY, startX) &&
319 | ((moves[i] >> 8) & 0xFF) == MakeSquare(endY, endX)) {
320 | move = moves[i];
321 | }
322 | }
323 |
324 | if (!g_playerWhite) {
325 | startY = 7 - startY;
326 | endY = 7 - endY;
327 | startX = 7 - startX;
328 | endX = 7 - endX;
329 | }
330 |
331 | g_selectedPiece.style.left = 0;
332 | g_selectedPiece.style.top = 0;
333 |
334 | if (!(startX == endX && startY == endY) && move != null) {
335 | UpdatePgnTextBox(move);
336 |
337 | if (InitializeBackgroundEngine()) {
338 | g_backgroundEngine.postMessage(FormatMove(move));
339 | }
340 |
341 | g_allMoves[g_allMoves.length] = move;
342 | MakeMove(move);
343 |
344 | UpdateFromMove(move);
345 |
346 | g_selectedPiece.style.backgroundImage = null;
347 | g_selectedPiece = null;
348 |
349 | var fen = GetFen();
350 | document.getElementById("FenTextBox").value = fen;
351 |
352 | setTimeout("SearchAndRedraw()", 0);
353 | } else {
354 | g_selectedPiece.style.backgroundImage = null;
355 | g_selectedPiece = null;
356 | }
357 | };
358 |
359 | for (y = 0; y < 8; ++y) {
360 | var tr = document.createElement("tr");
361 |
362 | for (x = 0; x < 8; ++x) {
363 | var td = document.createElement("td");
364 | td.style.width = g_cellSize + "px";
365 | td.style.height = g_cellSize + "px";
366 | td.style.backgroundColor = ((y ^ x) & 1) ? "#D18947" : "#FFCE9E";
367 | tr.appendChild(td);
368 | g_uiBoard[y * 8 + x] = td;
369 | }
370 |
371 | tbody.appendChild(tr);
372 | }
373 |
374 | table.appendChild(tbody);
375 |
376 | $('body').droppable({ drop: dropPiece });
377 | $(table).mousedown(function(e) {
378 | if (g_selectedPiece !== null) {
379 | dropPiece(e);
380 | }
381 | });
382 |
383 | RedrawPieces();
384 |
385 | $(div).empty();
386 | div.appendChild(table);
387 |
388 | g_changingFen = true;
389 | document.getElementById("FenTextBox").value = GetFen();
390 | g_changingFen = false;
391 | }
392 |
--------------------------------------------------------------------------------
/js/testing.js:
--------------------------------------------------------------------------------
1 | function RunPerft(){
2 | ResetGame();
3 |
4 | for (var i = 0; i < g_perfTTable.length; i++) {
5 | InitializeFromFen(g_perfTTable[i][0]);
6 | for (var j = 1; j <= 3; j++) {
7 | var perftj = Perft(j);
8 | if (g_perfTTable[i][j] != perftj) {
9 | alert(g_perfTTable[i][0] + " " + g_perfTTable[i][j] + " " + perftj);
10 | }
11 | }
12 | }
13 | }
14 |
15 | function Perft(depth) {
16 | if (depth == 0)
17 | return 1;
18 | var moves = new Array();
19 | GenerateCaptureMoves(moves, null);
20 | GenerateAllMoves(moves);
21 | var result = 0;
22 | for (var i = 0; i < moves.length; i++) {
23 | if (!MakeMove(moves[i])) {
24 | // if (DebugValidate() != 0)
25 | // { alert(moves[i]); }
26 | continue;
27 | }
28 | // if (DebugValidate() != 0)
29 | // { alert(moves[i]); }
30 | result += Perft(depth - 1);
31 | UnmakeMove(moves[i]);
32 | // if (DebugValidate() != 0)
33 | // { alert(moves[i]); }
34 | }
35 | return result;
36 | }
37 |
38 | function DebugCheckMove(hashMove) {
39 | var moves = new Array();
40 | GenerateCaptureMoves(moves, null);
41 | GenerateAllMoves(moves);
42 | for (var i = 0; i < moves.length; i++) {
43 | if (moves[i] == hashMove)
44 | return true;
45 | }
46 | return false;
47 | }
48 |
49 | function State() {
50 | this.board = new Array(256);
51 | for (var i = 0; i < 256; i++)
52 | this.board[i] = g_board[i];
53 | this.toMove = g_toMove;
54 | this.castleRights = g_castleRights;
55 | this.enPassentSquare = g_enPassentSquare;
56 | this.baseEval = g_baseEval;
57 | this.hashKeyLow = g_hashKeyLow;
58 | this.hashKeyHigh = g_hashKeyHigh;
59 | this.inCheck = g_inCheck;
60 | }
61 |
62 | function DebugValidate() {
63 | // Validate that pieceLists are correct
64 | for (var piece = 0; piece < 0xF; piece++) {
65 | for (var i = 0; i < g_pieceCount[piece]; i++) {
66 | var boardPiece = piece < 0x8 ? (piece | colorBlack) : piece;
67 | if (g_pieceList[(piece << 4) | i] == 0)
68 | return 1;
69 | if (g_board[g_pieceList[(piece << 4) | i]] != boardPiece)
70 | return 2;
71 | }
72 | for (var i = g_pieceCount[piece]; i < 16; i++) {
73 | if (g_pieceList[(piece << 4) | i] != 0)
74 | return 3;
75 | }
76 | }
77 |
78 | // Validate that board matches pieceList
79 | for (var i = 0; i < 256; i++) {
80 | var row = i >> 4;
81 | var col = i & 0xF;
82 | if (row >= 2 && row < 10 && col >= 4 && col < 12) {
83 | if (!(g_board[i] == 0 ||
84 | (g_board[i] & (colorBlack | colorWhite)) != 0)) {
85 | return 4;
86 | } else if (g_board[i] != 0) {
87 | if (g_pieceList[((g_board[i] & 0xF) << 4) | g_pieceIndex[i]] != i)
88 | return 6;
89 | }
90 | } else {
91 | if (g_board[i] != 0x80)
92 | return 5;
93 | }
94 | }
95 |
96 | var hashResult = SetHash();
97 | if (hashResult.hashKeyLow != g_hashKeyLow ||
98 | hashResult.hashKeyHigh != g_hashKeyHigh) {
99 | return 6;
100 | }
101 |
102 | return 0;
103 | }
104 |
105 | State.prototype.CompareTo = function (other) {
106 | for (var i = 0; i < 256; i++)
107 | if (this.board[i] != other.board[i])
108 | return 1;
109 | if (this.toMove != other.toMove)
110 | return 3;
111 | if (this.castleRights != other.castleRights)
112 | return 4;
113 | if (this.enPassentSquare != other.enPassentSquare)
114 | return 5;
115 | if (this.baseEval != other.baseEval)
116 | return 6;
117 | if (this.hashKeyLow != other.hashKeyLow ||
118 | this.hashKeyHigh != other.hashKeyHigh)
119 | return 7;
120 | if (this.inCheck != other.inCheck)
121 | return 8;
122 | return 0;
123 | }
124 |
125 | function CheckSee(fen, move, expected) {
126 | InitializeFromFen(fen);
127 | var captureMove = GetMoveFromString(move);
128 |
129 | var start = new State();
130 | if (See(captureMove) != expected) {
131 | alert("busted");
132 | }
133 | if (new State().CompareTo(start) != 0) {
134 | alert("see modified board");
135 | }
136 |
137 | // assert(position.see_sign(captureMove) == expected);
138 |
139 | /*Position flipped;
140 | flipped.flipped_copy(position);
141 |
142 | std::string flippedMove(move);
143 | flippedMove[1] = '1' + ('8' - flippedMove[1]);
144 | flippedMove[3] = '1' + ('8' - flippedMove[3]);
145 | captureMove = move_from_string(flipped, flippedMove);
146 | assert(move_is_ok(captureMove));
147 | assert(flipped.see_sign(captureMove) == expected); */
148 | }
149 |
150 | function SeeTests() {
151 | // Winning pawn capture on rook
152 | CheckSee("2r3k1/2r4p/1PNqb1p1/3p1p2/4p3/2Q1P2P/5PP1/1R4K1 w - - 0 37", "b6c7", true);
153 |
154 | // Winning rook/queen capture on pawn
155 | CheckSee("2r3k1/2P4p/2Nqb1p1/3p1p2/4p3/2Q1P2P/5PP1/1R4K1 b - - 0 37", "c8c7", true);
156 | CheckSee("2r3k1/2P4p/2Nqb1p1/3p1p2/4p3/2Q1P2P/5PP1/1R4K1 b - - 0 37", "d6c7", true);
157 |
158 | // Winning rook/queen capture on knight
159 | CheckSee("6k1/2r4p/2Nqb1p1/3p1p2/4p3/2Q1P2P/5PP1/1R4K1 b - - 0 38", "c7c6", true);
160 | CheckSee("6k1/2r4p/2Nqb1p1/3p1p2/4p3/2Q1P2P/5PP1/1R4K1 b - - 0 38", "d6c6", true);
161 | CheckSee("6k1/2r4p/2Nqb1p1/3p1p2/4p3/2Q1P2P/5PP1/2B3K1 b - - 0 38", "c7c6", true);
162 |
163 | // Losing rook/queen capture on knight (revealed rook attack)
164 | CheckSee("6k1/2r4p/2Nqb1p1/3p1p2/4p3/2Q1P2P/5PP1/2R3K1 b - - 0 38", "c7c6", false);
165 | CheckSee("6k1/2r4p/2Nqb1p1/3p1p2/4p3/2Q1P2P/5PP1/2R3K1 b - - 0 38", "d6c6", false);
166 |
167 | // Winning rook/queen capture on knight (revealed bishop attack)
168 | CheckSee("4b1k1/2rq3p/2N3p1/3p1p2/4p3/2Q1P2P/5PP1/2R3K1 b - - 0 38", "c7c6", true);
169 | CheckSee("4b1k1/2rq3p/2N3p1/3p1p2/4p3/2Q1P2P/5PP1/2R3K1 b - - 0 38", "d7c6", true);
170 |
171 | // Winning pawn capture on pawn
172 | CheckSee("2r3k1/2pq3p/3P2p1/b4p2/4p3/2R1P2P/5PP1/2R3K1 w - - 0 38", "d6c7", true);
173 |
174 | // Losing rook capture on pawn
175 | CheckSee("2r3k1/2pq3p/3P2p1/b4p2/4p3/2R1P2P/5PP1/2R3K1 w - - 0 38", "c3c7", false);
176 |
177 | // Losing queen capture on rook
178 | CheckSee("2r3k1/2p4p/3P2p1/q4p2/4p3/2R1P2P/5PP1/2R3K1 b - - 0 38", "a5c3", false);
179 |
180 | // Losing rook capture on pawn
181 | CheckSee("1br3k1/2p4p/3P2p1/q4p2/4p3/2R1P2P/5PP1/2R3K1 w - - 0 38", "c3c7", false);
182 |
183 | // Winning Q promotion (non-capture)
184 | CheckSee("4rrk1/2P4p/6p1/5p2/4p3/2R1P2P/5PP1/2R3K1 w - - 0 38", "c7c8q", true);
185 |
186 | // Losing Q promotion (non-capture)
187 | //CheckSee("r3rrk1/2P4p/6p1/5p2/4p3/2R1P2P/5PP1/2R3K1 w - - 0 38", "c7c8q", false);
188 |
189 | // Knight capturing pawn defended by pawn
190 | CheckSee("K7/8/2p5/3p4/8/4N3/8/7k w - - 0 1", "e3d5", false);
191 |
192 | // Knight capturing undefended pawn
193 | CheckSee("K7/8/8/3p4/8/4N3/8/7k w - - 0 1", "e3d5", true);
194 |
195 | // Rook capturing pawn defended by knight
196 | CheckSee("K7/4n3/8/3p4/8/3R4/3R4/7k w - - 0 1", "d3d5", false);
197 |
198 | // Rook capturing pawn defended by bishop
199 | CheckSee("K7/5b2/8/3p4/8/3R4/3R4/7k w - - 0 1", "d3d5", false);
200 |
201 | // Rook capturing knight defended by bishop
202 | CheckSee("K7/5b2/8/3n4/8/3R4/3R4/7k w - - 0 1", "d3d5", true);
203 |
204 | // Rook capturing rook defended by bishop
205 | CheckSee("K7/5b2/8/3r4/8/3R4/3R4/7k w - - 0 1", "d3d5", true);
206 | }
207 |
208 |
--------------------------------------------------------------------------------
/js/tourney.js:
--------------------------------------------------------------------------------
1 | var g_lastMove = null;
2 | var g_currentTestGame = 0;
3 | var g_currentTestWhite = false;
4 | var g_myScore = 0;
5 | var g_gameOver = false;
6 | var oldGarboChess = null;
7 |
8 | function sleep(delay) {
9 | var start = new Date().getTime();
10 | while (new Date().getTime() < start + delay);
11 | }
12 |
13 | function AddGameOver(s1, s2) {
14 | if (g_toMove == 8) {
15 | // White mated
16 | if (!g_currentTestWhite) {
17 | g_myScore += s1;
18 | } else {
19 | g_myScore += s2;
20 | }
21 | } else {
22 | // Black mated
23 | if (g_currentTestWhite) {
24 | g_myScore += s1;
25 | } else {
26 | g_myScore += s2;
27 | }
28 | }
29 | }
30 |
31 | function CheckGameOver(move) {
32 | if (g_gameOver) return;
33 |
34 | MakeMove(move);
35 | if (GenerateValidMoves().length == 0 || IsRepDraw() || g_move50 >= 20) {
36 | if (g_inCheck) {
37 | AddGameOver(1, 0);
38 | } else {
39 | if (!IsRepDraw()) {
40 | if (g_baseEval > 5000) {
41 | AddGameOver(1, 0);
42 | } else if (g_baseEval < -5000) {
43 | AddGameOver(0, 1);
44 | } else {
45 | g_myScore += 0.5;
46 | }
47 | } else {
48 | g_myScore += 0.5;
49 | }
50 | }
51 |
52 | g_gameOver = true;
53 | g_lastMove = null;
54 | if (g_currentTestWhite) {
55 | g_currentTestWhite = false;
56 | g_currentTestGame++;
57 | } else {
58 | g_currentTestWhite = true;
59 | }
60 |
61 | setTimeout("TestGames()", 0);
62 | return true;
63 | }
64 | g_gameOver = false;
65 | return false;
66 | }
67 |
68 | function FinishMoveTestGames(bestMove, value, timeTaken, ply) {
69 | if (bestMove != null) {
70 | g_lastMove = bestMove;
71 | CheckGameOver(bestMove);
72 | if (!g_gameOver) {
73 | TestGame();
74 | }
75 | }
76 | }
77 |
78 | function TestGamesCallback() {
79 | if (g_gameOver) return;
80 | Search(FinishMoveTestGames, 99, null);
81 | }
82 |
83 | function TestGame() {
84 | if (g_gameOver) return;
85 | if (g_lastMove != null) {
86 | oldGarboChess.postMessage(FormatMove(g_lastMove));
87 | }
88 | oldGarboChess.postMessage("search " + g_timeout);
89 | }
90 |
91 | function TestGames() {
92 | if (oldGarboChess == null) {
93 | oldGarboChess = new Worker("js/garbochess-old.js");
94 | oldGarboChess.onmessage = function (e) {
95 | if (e.data[0] != 'p') {
96 | if (!CheckGameOver(GetMoveFromString(e.data))) {
97 | TestGamesCallback();
98 | }
99 | }
100 | }
101 | oldGarboChess.error = function (e) {
102 | alert(e.message);
103 | }
104 | }
105 |
106 | if (g_gameOver) {
107 | var totalGames = ((g_currentTestGame * 2) + (g_currentTestWhite ? 1 : 0));
108 | var percentage = g_myScore / totalGames;
109 | var eloDifference = -400 * Math.log(1 / percentage - 1) / Math.LN10;
110 |
111 | var statusString = g_myScore + "/" + totalGames + " %:" + (Math.round(percentage * 10000) / 100) + " Elo:" + Math.round(eloDifference);
112 | var outputDiv = document.getElementById("output");
113 | outputDiv.removeChild(outputDiv.childNodes[0]);
114 | outputDiv.appendChild(document.createTextNode(statusString));
115 | }
116 |
117 | if (g_currentTestGame >= 500) {
118 | return;
119 | }
120 |
121 | g_gameOver = false;
122 | ResetGame();
123 | InitializeFromFen(g_testOpenings[g_currentTestGame]);
124 |
125 | oldGarboChess.postMessage("position " + g_testOpenings[g_currentTestGame]);
126 |
127 | if (g_currentTestWhite) {
128 | Search(FinishMoveTestGames, 99, null);
129 | } else {
130 | TestGame();
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/rungames.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Test harness
5 |
6 |
7 |
8 |
9 |
10 |
11 |
17 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | output
29 |
30 | FEN:
31 |
32 |
33 |
--------------------------------------------------------------------------------
/tests/perft-positions.js:
--------------------------------------------------------------------------------
1 | var g_perfTTable = [
2 | [ "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 ", 20, 400, 8902, 197281, 4865609, 119060324],
3 | [ "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1 ", 48, 2039, 97862, 4085603, 193690690],
4 | [ "4k3/8/8/8/8/8/8/4K2R w K - 0 1 ", 15, 66, 1197, 7059, 133987, 764643],
5 | [ "4k3/8/8/8/8/8/8/R3K3 w Q - 0 1 ", 16, 71, 1287, 7626, 145232, 846648],
6 | [ "4k2r/8/8/8/8/8/8/4K3 w k - 0 1 ", 5, 75, 459, 8290, 47635, 899442],
7 | [ "r3k3/8/8/8/8/8/8/4K3 w q - 0 1 ", 5, 80, 493, 8897, 52710, 1001523],
8 | [ "4k3/8/8/8/8/8/8/R3K2R w KQ - 0 1 ", 26, 112, 3189, 17945, 532933, 2788982],
9 | [ "r3k2r/8/8/8/8/8/8/4K3 w kq - 0 1 ", 5, 130, 782, 22180, 118882, 3517770],
10 | [ "8/8/8/8/8/8/6k1/4K2R w K - 0 1 ", 12, 38, 564, 2219, 37735, 185867],
11 | [ "8/8/8/8/8/8/1k6/R3K3 w Q - 0 1 ", 15, 65, 1018, 4573, 80619, 413018],
12 | [ "4k2r/6K1/8/8/8/8/8/8 w k - 0 1 ", 3, 32, 134, 2073, 10485, 179869],
13 | [ "r3k3/1K6/8/8/8/8/8/8 w q - 0 1 ", 4, 49, 243, 3991, 20780, 367724],
14 | [ "r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1 ", 26, 568, 13744, 314346, 7594526, 179862938],
15 | [ "r3k2r/8/8/8/8/8/8/1R2K2R w Kkq - 0 1 ", 25, 567, 14095, 328965, 8153719, 195629489],
16 | [ "r3k2r/8/8/8/8/8/8/2R1K2R w Kkq - 0 1 ", 25, 548, 13502, 312835, 7736373, 184411439],
17 | [ "r3k2r/8/8/8/8/8/8/R3K1R1 w Qkq - 0 1 ", 25, 547, 13579, 316214, 7878456, 189224276],
18 | [ "1r2k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1 ", 26, 583, 14252, 334705, 8198901, 198328929],
19 | [ "2r1k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1 ", 25, 560, 13592, 317324, 7710115, 185959088],
20 | [ "r3k1r1/8/8/8/8/8/8/R3K2R w KQq - 0 1 ", 25, 560, 13607, 320792, 7848606, 190755813],
21 | [ "4k3/8/8/8/8/8/8/4K2R b K - 0 1 ", 5, 75, 459, 8290, 47635, 899442],
22 | [ "4k3/8/8/8/8/8/8/R3K3 b Q - 0 1 ", 5, 80, 493, 8897, 52710, 1001523],
23 | [ "4k2r/8/8/8/8/8/8/4K3 b k - 0 1 ", 15, 66, 1197, 7059, 133987, 764643],
24 | [ "r3k3/8/8/8/8/8/8/4K3 b q - 0 1 ", 16, 71, 1287, 7626, 145232, 846648],
25 | [ "4k3/8/8/8/8/8/8/R3K2R b KQ - 0 1 ", 5, 130, 782, 22180, 118882, 3517770],
26 | [ "r3k2r/8/8/8/8/8/8/4K3 b kq - 0 1 ", 26, 112, 3189, 17945, 532933, 2788982],
27 | [ "8/8/8/8/8/8/6k1/4K2R b K - 0 1 ", 3, 32, 134, 2073, 10485, 179869],
28 | [ "8/8/8/8/8/8/1k6/R3K3 b Q - 0 1 ", 4, 49, 243, 3991, 20780, 367724],
29 | [ "4k2r/6K1/8/8/8/8/8/8 b k - 0 1 ", 12, 38, 564, 2219, 37735, 185867],
30 | [ "r3k3/1K6/8/8/8/8/8/8 b q - 0 1 ", 15, 65, 1018, 4573, 80619, 413018],
31 | [ "r3k2r/8/8/8/8/8/8/R3K2R b KQkq - 0 1 ", 26, 568, 13744, 314346, 7594526, 179862938],
32 | [ "r3k2r/8/8/8/8/8/8/1R2K2R b Kkq - 0 1 ", 26, 583, 14252, 334705, 8198901, 198328929],
33 | [ "r3k2r/8/8/8/8/8/8/2R1K2R b Kkq - 0 1 ", 25, 560, 13592, 317324, 7710115, 185959088],
34 | [ "r3k2r/8/8/8/8/8/8/R3K1R1 b Qkq - 0 1 ", 25, 560, 13607, 320792, 7848606, 190755813],
35 | [ "1r2k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1 ", 25, 567, 14095, 328965, 8153719, 195629489],
36 | [ "2r1k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1 ", 25, 548, 13502, 312835, 7736373, 184411439],
37 | [ "r3k1r1/8/8/8/8/8/8/R3K2R b KQq - 0 1 ", 25, 547, 13579, 316214, 7878456, 189224276],
38 | [ "8/1n4N1/2k5/8/8/5K2/1N4n1/8 w - - 0 1 ", 14, 195, 2760, 38675, 570726, 8107539],
39 | [ "8/1k6/8/5N2/8/4n3/8/2K5 w - - 0 1 ", 11, 156, 1636, 20534, 223507, 2594412],
40 | [ "8/8/4k3/3Nn3/3nN3/4K3/8/8 w - - 0 1 ", 19, 289, 4442, 73584, 1198299, 19870403],
41 | [ "K7/8/2n5/1n6/8/8/8/k6N w - - 0 1 ", 3, 51, 345, 5301, 38348, 588695],
42 | [ "k7/8/2N5/1N6/8/8/8/K6n w - - 0 1 ", 17, 54, 835, 5910, 92250, 688780],
43 | [ "8/1n4N1/2k5/8/8/5K2/1N4n1/8 b - - 0 1 ", 15, 193, 2816, 40039, 582642, 8503277],
44 | [ "8/1k6/8/5N2/8/4n3/8/2K5 b - - 0 1 ", 16, 180, 2290, 24640, 288141, 3147566],
45 | [ "8/8/3K4/3Nn3/3nN3/4k3/8/8 b - - 0 1 ", 4, 68, 1118, 16199, 281190, 4405103],
46 | [ "K7/8/2n5/1n6/8/8/8/k6N b - - 0 1 ", 17, 54, 835, 5910, 92250, 688780],
47 | [ "k7/8/2N5/1N6/8/8/8/K6n b - - 0 1 ", 3, 51, 345, 5301, 38348, 588695],
48 | [ "B6b/8/8/8/2K5/4k3/8/b6B w - - 0 1 ", 17, 278, 4607, 76778, 1320507, 22823890],
49 | [ "8/8/1B6/7b/7k/8/2B1b3/7K w - - 0 1 ", 21, 316, 5744, 93338, 1713368, 28861171],
50 | [ "k7/B7/1B6/1B6/8/8/8/K6b w - - 0 1 ", 21, 144, 3242, 32955, 787524, 7881673],
51 | [ "K7/b7/1b6/1b6/8/8/8/k6B w - - 0 1 ", 7, 143, 1416, 31787, 310862, 7382896],
52 | [ "B6b/8/8/8/2K5/5k2/8/b6B b - - 0 1 ", 6, 106, 1829, 31151, 530585, 9250746],
53 | [ "8/8/1B6/7b/7k/8/2B1b3/7K b - - 0 1 ", 17, 309, 5133, 93603, 1591064, 29027891],
54 | [ "k7/B7/1B6/1B6/8/8/8/K6b b - - 0 1 ", 7, 143, 1416, 31787, 310862, 7382896],
55 | [ "K7/b7/1b6/1b6/8/8/8/k6B b - - 0 1 ", 21, 144, 3242, 32955, 787524, 7881673],
56 | [ "7k/RR6/8/8/8/8/rr6/7K w - - 0 1 ", 19, 275, 5300, 104342, 2161211, 44956585],
57 | [ "R6r/8/8/2K5/5k2/8/8/r6R w - - 0 1 ", 36, 1027, 29215, 771461, 20506480, 525169084],
58 | [ "7k/RR6/8/8/8/8/rr6/7K b - - 0 1 ", 19, 275, 5300, 104342, 2161211, 44956585],
59 | [ "R6r/8/8/2K5/5k2/8/8/r6R b - - 0 1 ", 36, 1027, 29227, 771368, 20521342, 524966748],
60 | [ "6kq/8/8/8/8/8/8/7K w - - 0 1 ", 2, 36, 143, 3637, 14893, 391507],
61 | [ "6KQ/8/8/8/8/8/8/7k b - - 0 1 ", 2, 36, 143, 3637, 14893, 391507],
62 | [ "K7/8/8/3Q4/4q3/8/8/7k w - - 0 1 ", 6, 35, 495, 8349, 166741, 3370175],
63 | [ "6qk/8/8/8/8/8/8/7K b - - 0 1 ", 22, 43, 1015, 4167, 105749, 419369],
64 | [ "6KQ/8/8/8/8/8/8/7k b - - 0 1 ", 2, 36, 143, 3637, 14893, 391507],
65 | [ "K7/8/8/3Q4/4q3/8/8/7k b - - 0 1 ", 6, 35, 495, 8349, 166741, 3370175],
66 | [ "8/8/8/8/8/K7/P7/k7 w - - 0 1 ", 3, 7, 43, 199, 1347, 6249],
67 | [ "8/8/8/8/8/7K/7P/7k w - - 0 1 ", 3, 7, 43, 199, 1347, 6249],
68 | [ "K7/p7/k7/8/8/8/8/8 w - - 0 1 ", 1, 3, 12, 80, 342, 2343],
69 | [ "7K/7p/7k/8/8/8/8/8 w - - 0 1 ", 1, 3, 12, 80, 342, 2343],
70 | [ "8/2k1p3/3pP3/3P2K1/8/8/8/8 w - - 0 1 ", 7, 35, 210, 1091, 7028, 34834],
71 | [ "8/8/8/8/8/K7/P7/k7 b - - 0 1 ", 1, 3, 12, 80, 342, 2343],
72 | [ "8/8/8/8/8/7K/7P/7k b - - 0 1 ", 1, 3, 12, 80, 342, 2343],
73 | [ "K7/p7/k7/8/8/8/8/8 b - - 0 1 ", 3, 7, 43, 199, 1347, 6249],
74 | [ "7K/7p/7k/8/8/8/8/8 b - - 0 1 ", 3, 7, 43, 199, 1347, 6249],
75 | [ "8/2k1p3/3pP3/3P2K1/8/8/8/8 b - - 0 1 ", 5, 35, 182, 1091, 5408, 34822],
76 | [ "8/8/8/8/8/4k3/4P3/4K3 w - - 0 1 ", 2, 8, 44, 282, 1814, 11848],
77 | [ "4k3/4p3/4K3/8/8/8/8/8 b - - 0 1 ", 2, 8, 44, 282, 1814, 11848],
78 | [ "8/8/7k/7p/7P/7K/8/8 w - - 0 1 ", 3, 9, 57, 360, 1969, 10724],
79 | [ "8/8/k7/p7/P7/K7/8/8 w - - 0 1 ", 3, 9, 57, 360, 1969, 10724],
80 | [ "8/8/3k4/3p4/3P4/3K4/8/8 w - - 0 1 ", 5, 25, 180, 1294, 8296, 53138],
81 | [ "8/3k4/3p4/8/3P4/3K4/8/8 w - - 0 1 ", 8, 61, 483, 3213, 23599, 157093],
82 | [ "8/8/3k4/3p4/8/3P4/3K4/8 w - - 0 1 ", 8, 61, 411, 3213, 21637, 158065],
83 | [ "k7/8/3p4/8/3P4/8/8/7K w - - 0 1 ", 4, 15, 90, 534, 3450, 20960],
84 | [ "8/8/7k/7p/7P/7K/8/8 b - - 0 1 ", 3, 9, 57, 360, 1969, 10724],
85 | [ "8/8/k7/p7/P7/K7/8/8 b - - 0 1 ", 3, 9, 57, 360, 1969, 10724],
86 | [ "8/8/3k4/3p4/3P4/3K4/8/8 b - - 0 1 ", 5, 25, 180, 1294, 8296, 53138],
87 | [ "8/3k4/3p4/8/3P4/3K4/8/8 b - - 0 1 ", 8, 61, 411, 3213, 21637, 158065],
88 | [ "8/8/3k4/3p4/8/3P4/3K4/8 b - - 0 1 ", 8, 61, 483, 3213, 23599, 157093],
89 | [ "k7/8/3p4/8/3P4/8/8/7K b - - 0 1 ", 4, 15, 89, 537, 3309, 21104],
90 | [ "7k/3p4/8/8/3P4/8/8/K7 w - - 0 1 ", 4, 19, 117, 720, 4661, 32191],
91 | [ "7k/8/8/3p4/8/8/3P4/K7 w - - 0 1 ", 5, 19, 116, 716, 4786, 30980],
92 | [ "k7/8/8/7p/6P1/8/8/K7 w - - 0 1 ", 5, 22, 139, 877, 6112, 41874],
93 | [ "k7/8/7p/8/8/6P1/8/K7 w - - 0 1 ", 4, 16, 101, 637, 4354, 29679],
94 | [ "k7/8/8/6p1/7P/8/8/K7 w - - 0 1 ", 5, 22, 139, 877, 6112, 41874],
95 | [ "k7/8/6p1/8/8/7P/8/K7 w - - 0 1 ", 4, 16, 101, 637, 4354, 29679],
96 | [ "k7/8/8/3p4/4p3/8/8/7K w - - 0 1 ", 3, 15, 84, 573, 3013, 22886],
97 | [ "k7/8/3p4/8/8/4P3/8/7K w - - 0 1 ", 4, 16, 101, 637, 4271, 28662],
98 | [ "7k/3p4/8/8/3P4/8/8/K7 b - - 0 1 ", 5, 19, 117, 720, 5014, 32167],
99 | [ "7k/8/8/3p4/8/8/3P4/K7 b - - 0 1 ", 4, 19, 117, 712, 4658, 30749],
100 | [ "k7/8/8/7p/6P1/8/8/K7 b - - 0 1 ", 5, 22, 139, 877, 6112, 41874],
101 | [ "k7/8/7p/8/8/6P1/8/K7 b - - 0 1 ", 4, 16, 101, 637, 4354, 29679],
102 | [ "k7/8/8/6p1/7P/8/8/K7 b - - 0 1 ", 5, 22, 139, 877, 6112, 41874],
103 | [ "k7/8/6p1/8/8/7P/8/K7 b - - 0 1 ", 4, 16, 101, 637, 4354, 29679],
104 | [ "k7/8/8/3p4/4p3/8/8/7K b - - 0 1 ", 5, 15, 102, 569, 4337, 22579],
105 | [ "k7/8/3p4/8/8/4P3/8/7K b - - 0 1 ", 4, 16, 101, 637, 4271, 28662],
106 | [ "7k/8/8/p7/1P6/8/8/7K w - - 0 1 ", 5, 22, 139, 877, 6112, 41874],
107 | [ "7k/8/p7/8/8/1P6/8/7K w - - 0 1 ", 4, 16, 101, 637, 4354, 29679],
108 | [ "7k/8/8/1p6/P7/8/8/7K w - - 0 1 ", 5, 22, 139, 877, 6112, 41874],
109 | [ "7k/8/1p6/8/8/P7/8/7K w - - 0 1 ", 4, 16, 101, 637, 4354, 29679],
110 | [ "k7/7p/8/8/8/8/6P1/K7 w - - 0 1 ", 5, 25, 161, 1035, 7574, 55338],
111 | [ "k7/6p1/8/8/8/8/7P/K7 w - - 0 1 ", 5, 25, 161, 1035, 7574, 55338],
112 | [ "3k4/3pp3/8/8/8/8/3PP3/3K4 w - - 0 1 ", 7, 49, 378, 2902, 24122, 199002],
113 | [ "7k/8/8/p7/1P6/8/8/7K b - - 0 1 ", 5, 22, 139, 877, 6112, 41874],
114 | [ "7k/8/p7/8/8/1P6/8/7K b - - 0 1 ", 4, 16, 101, 637, 4354, 29679],
115 | [ "7k/8/8/1p6/P7/8/8/7K b - - 0 1 ", 5, 22, 139, 877, 6112, 41874],
116 | [ "7k/8/1p6/8/8/P7/8/7K b - - 0 1 ", 4, 16, 101, 637, 4354, 29679],
117 | [ "k7/7p/8/8/8/8/6P1/K7 b - - 0 1 ", 5, 25, 161, 1035, 7574, 55338],
118 | [ "k7/6p1/8/8/8/8/7P/K7 b - - 0 1 ", 5, 25, 161, 1035, 7574, 55338],
119 | [ "3k4/3pp3/8/8/8/8/3PP3/3K4 b - - 0 1 ", 7, 49, 378, 2902, 24122, 199002],
120 | [ "8/Pk6/8/8/8/8/6Kp/8 w - - 0 1 ", 11, 97, 887, 8048, 90606, 1030499],
121 | [ "n1n5/1Pk5/8/8/8/8/5Kp1/5N1N w - - 0 1 ", 24, 421, 7421, 124608, 2193768, 37665329],
122 | [ "8/PPPk4/8/8/8/8/4Kppp/8 w - - 0 1 ", 18, 270, 4699, 79355, 1533145, 28859283],
123 | [ "n1n5/PPPk4/8/8/8/8/4Kppp/5N1N w - - 0 1 ", 24, 496, 9483, 182838, 3605103, 71179139],
124 | [ "8/Pk6/8/8/8/8/6Kp/8 b - - 0 1 ", 11, 97, 887, 8048, 90606, 1030499],
125 | [ "n1n5/1Pk5/8/8/8/8/5Kp1/5N1N b - - 0 1 ", 24, 421, 7421, 124608, 2193768, 37665329],
126 | [ "8/PPPk4/8/8/8/8/4Kppp/8 b - - 0 1 ", 18, 270, 4699, 79355, 1533145, 28859283],
127 | [ "n1n5/PPPk4/8/8/8/8/4Kppp/5N1N b - - 0 1 ", 24, 496, 9483, 182838, 3605103, 71179139]
128 | ];
129 |
--------------------------------------------------------------------------------
/webpage.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 11.00
3 | # Visual Studio 2010
4 | Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "webpage", ".", "{609C52A8-869B-457E-BF34-35D961725ACA}"
5 | ProjectSection(WebsiteProperties) = preProject
6 | TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.0"
7 | Debug.AspNetCompiler.VirtualPath = "/webpage"
8 | Debug.AspNetCompiler.PhysicalPath = "..\webpage\"
9 | Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\webpage\"
10 | Debug.AspNetCompiler.Updateable = "true"
11 | Debug.AspNetCompiler.ForceOverwrite = "true"
12 | Debug.AspNetCompiler.FixedNames = "false"
13 | Debug.AspNetCompiler.Debug = "True"
14 | Release.AspNetCompiler.VirtualPath = "/webpage"
15 | Release.AspNetCompiler.PhysicalPath = "..\webpage\"
16 | Release.AspNetCompiler.TargetPath = "PrecompiledWeb\webpage\"
17 | Release.AspNetCompiler.Updateable = "true"
18 | Release.AspNetCompiler.ForceOverwrite = "true"
19 | Release.AspNetCompiler.FixedNames = "false"
20 | Release.AspNetCompiler.Debug = "False"
21 | VWDPort = "43072"
22 | EndProjectSection
23 | EndProject
24 | Global
25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
26 | Debug|Any CPU = Debug|Any CPU
27 | EndGlobalSection
28 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
29 | {609C52A8-869B-457E-BF34-35D961725ACA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
30 | {609C52A8-869B-457E-BF34-35D961725ACA}.Debug|Any CPU.Build.0 = Debug|Any CPU
31 | EndGlobalSection
32 | GlobalSection(SolutionProperties) = preSolution
33 | HideSolutionNode = FALSE
34 | EndGlobalSection
35 | EndGlobal
36 |
--------------------------------------------------------------------------------
/webpage.suo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/glinscott/Garbochess-JS/4ff0f9e49a0041545b645e25d3d2da7af3b36a90/webpage.suo
--------------------------------------------------------------------------------