├── 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 |
55 | Analysis: Off 56 |
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 --------------------------------------------------------------------------------