var PIECES = { EMPTY: 0, wP: 1, wN: 2, wB: 3, wR: 4, wQ: 5, wK: 6, bP: 7, bN: 8, bB: 9, bR: 10, bQ: 11, bK: 12 }, BRD_SQ_NUM = 120, FILES = { FILE_A: 0, FILE_B: 1, FILE_C: 2, FILE_D: 3, FILE_E: 4, FILE_F: 5, FILE_G: 6, FILE_H: 7, FILE_NONE: 8 }, RANKS = { RANK_1: 0, RANK_2: 1, RANK_3: 2, RANK_4: 3, RANK_5: 4, RANK_6: 5, RANK_7: 6, RANK_8: 7, RANK_NONE: 8 }, COLOURS = { WHITE: 0, BLACK: 1, BOTH: 2 }, CASTLEBIT = { WKCA: 1, WQCA: 2, BKCA: 4, BQCA: 8 }, SQUARES = { A1: 21, B1: 22, C1: 23, D1: 24, E1: 25, F1: 26, G1: 27, H1: 28, A8: 91, B8: 92, C8: 93, D8: 94, E8: 95, F8: 96, G8: 97, H8: 98, NO_SQ: 99, OFFBOARD: 100 }, BOOL = { FALSE: 0, TRUE: 1 }, MAXGAMEMOVES = 2048, MAXPOSITIONMOVES = 256, MAXDEPTH = 64, INFINITE = 3e4, MATE = 29e3, PVENTRIES = 1e4, FilesBrd = Array(BRD_SQ_NUM), RanksBrd = Array(BRD_SQ_NUM), START_FEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", PceChar = ".PNBRQKpnbrqk", SideChar = "wb-", RankChar = "12345678", FileChar = "abcdefgh"; function FR2SQ(a, b) { return 21 + a + 10 * b } var SideKey, PieceBig = [BOOL.FALSE, BOOL.FALSE, BOOL.TRUE, BOOL.TRUE, BOOL.TRUE, BOOL.TRUE, BOOL.TRUE, BOOL.FALSE, BOOL.TRUE, BOOL.TRUE, BOOL.TRUE, BOOL.TRUE, BOOL.TRUE], PieceMaj = [BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.TRUE, BOOL.TRUE, BOOL.TRUE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.TRUE, BOOL.TRUE, BOOL.TRUE], PieceMin = [BOOL.FALSE, BOOL.FALSE, BOOL.TRUE, BOOL.TRUE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.TRUE, BOOL.TRUE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE], PieceVal = [0, 100, 325, 325, 550, 1e3, 5e4, 100, 325, 325, 550, 1e3, 5e4], PieceCol = [COLOURS.BOTH, COLOURS.WHITE, COLOURS.WHITE, COLOURS.WHITE, COLOURS.WHITE, COLOURS.WHITE, COLOURS.WHITE, COLOURS.BLACK, COLOURS.BLACK, COLOURS.BLACK, COLOURS.BLACK, COLOURS.BLACK, COLOURS.BLACK], PiecePawn = [BOOL.FALSE, BOOL.TRUE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.TRUE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE], PieceKnight = [BOOL.FALSE, BOOL.FALSE, BOOL.TRUE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.TRUE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE], PieceKing = [BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.TRUE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.TRUE], PieceRookQueen = [BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.TRUE, BOOL.TRUE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.TRUE, BOOL.TRUE, BOOL.FALSE], PieceBishopQueen = [BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.TRUE, BOOL.FALSE, BOOL.TRUE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.TRUE, BOOL.FALSE, BOOL.TRUE, BOOL.FALSE], PieceSlides = [BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.TRUE, BOOL.TRUE, BOOL.TRUE, BOOL.FALSE, BOOL.FALSE, BOOL.FALSE, BOOL.TRUE, BOOL.TRUE, BOOL.TRUE, BOOL.FALSE], KnDir = [-8, -19, -21, -12, 8, 19, 21, 12], RkDir = [-1, -10, 1, 10], BiDir = [-9, -11, 11, 9], KiDir = [-1, -10, 1, 10, -9, -11, 11, 9], DirNum = [0, 0, 8, 4, 4, 8, 8, 0, 8, 4, 4, 8, 8], PceDir = [0, 0, KnDir, BiDir, RkDir, KiDir, KiDir, 0, KnDir, BiDir, RkDir, KiDir, KiDir], LoopNonSlidePce = [PIECES.wN, PIECES.wK, 0, PIECES.bN, PIECES.bK, 0], LoopNonSlideIndex = [0, 3], LoopSlidePce = [PIECES.wB, PIECES.wR, PIECES.wQ, 0, PIECES.bB, PIECES.bR, PIECES.bQ, 0], LoopSlideIndex = [0, 4], PieceKeys = Array(1680), CastleKeys = Array(16), Sq120ToSq64 = Array(BRD_SQ_NUM), Sq64ToSq120 = Array(64); function RAND_32() { return Math.floor(255 * Math.random() + 1) << 23 | Math.floor(255 * Math.random() + 1) << 16 | Math.floor(255 * Math.random() + 1) << 8 | Math.floor(255 * Math.random() + 1) } var Mirror64 = [56, 57, 58, 59, 60, 61, 62, 63, 48, 49, 50, 51, 52, 53, 54, 55, 40, 41, 42, 43, 44, 45, 46, 47, 32, 33, 34, 35, 36, 37, 38, 39, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7]; function SQ64(a) { return Sq120ToSq64[a] } function SQ120(a) { return Sq64ToSq120[a] } function PCEINDEX(a, b) { return 10 * a + b } function MIRROR64(a) { return Mirror64[a] } var Kings = [PIECES.wK, PIECES.bK], CastlePerm = [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 13, 15, 15, 15, 12, 15, 15, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 7, 15, 15, 15, 3, 15, 15, 11, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15]; function FROMSQ(a) { return 127 & a } function TOSQ(a) { return 127 & a >> 7 } function CAPTURED(a) { return 15 & a >> 14 } function PROMOTED(a) { return 15 & a >> 20 } var MFLAGEP = 262144, MFLAGPS = 524288, MFLAGCA = 16777216, MFLAGCAP = 507904, MFLAGPROM = 15728640, NOMOVE = 0; function SQOFFBOARD(a) { return FilesBrd[a] == SQUARES.OFFBOARD ? BOOL.TRUE : BOOL.FALSE } function HASH_PCE(a, b) { GameBoard.posKey ^= PieceKeys[120 * a + b] } function HASH_CA() { GameBoard.posKey ^= CastleKeys[GameBoard.castlePerm] } function HASH_SIDE() { GameBoard.posKey ^= SideKey } function HASH_EP() { GameBoard.posKey ^= PieceKeys[GameBoard.enPas] } var GameController = { EngineSide: COLOURS.BOTH, PlayerSide: COLOURS.BOTH, GameOver: BOOL.FALSE }, UserMove = {}; UserMove.from = SQUARES.NO_SQ, UserMove.to = SQUARES.NO_SQ; function PrSq(a) { return FileChar[FilesBrd[a]] + RankChar[RanksBrd[a]] } function PrMove(a) { var b, c = FilesBrd[FROMSQ(a)], d = RanksBrd[FROMSQ(a)], e = FilesBrd[TOSQ(a)], f = RanksBrd[TOSQ(a)]; b = FileChar[c] + RankChar[d] + FileChar[e] + RankChar[f]; var g = PROMOTED(a); if (g != PIECES.EMPTY) { var h = "q"; PieceKnight[g] == BOOL.TRUE ? h = "n" : PieceRookQueen[g] == BOOL.TRUE && PieceBishopQueen[g] == BOOL.FALSE ? h = "r" : PieceRookQueen[g] == BOOL.FALSE && PieceBishopQueen[g] == BOOL.TRUE && (h = "b"), b += h } return b } function PrintMoveList() { var a, b, c = 1; for (console.log("MoveList:"), a = GameBoard.moveListStart[GameBoard.ply]; a < GameBoard.moveListStart[GameBoard.ply + 1]; ++a) b = GameBoard.moveList[a], console.log("IMove:" + c + ":(" + a + "):" + PrMove(b) + " Score:" + GameBoard.moveScores[a]), c++; console.log("End MoveList") } function ParseMove(a, b) { GenerateMoves(); var c = NOMOVE, d = PIECES.EMPTY, e = BOOL.FALSE; for (index = GameBoard.moveListStart[GameBoard.ply]; index < GameBoard.moveListStart[GameBoard.ply + 1]; ++index) if (c = GameBoard.moveList[index], FROMSQ(c) == a && TOSQ(c) == b) { if (d = PROMOTED(c), d != PIECES.EMPTY) { if (d == PIECES.wQ && GameBoard.side == COLOURS.WHITE || d == PIECES.bQ && GameBoard.side == COLOURS.BLACK) { e = BOOL.TRUE; break } continue } e = BOOL.TRUE; break } return e == BOOL.FALSE ? NOMOVE : MakeMove(c) == BOOL.FALSE ? NOMOVE : (TakeMove(), c) } function PCEINDEX(a, b) { return 10 * a + b } var GameBoard = { pieces: Array(BRD_SQ_NUM), side: COLOURS.WHITE, fiftyMove: 0, hisPly: 0, history: [], ply: 0, enPas: 0, castlePerm: 0, material: [, , ], pceNum: Array(13), pList: Array(140), posKey: 0, moveList: Array(MAXDEPTH * MAXPOSITIONMOVES), moveScores: Array(MAXDEPTH * MAXPOSITIONMOVES), moveListStart: Array(MAXDEPTH), PvTable: [], PvArray: Array(MAXDEPTH), searchHistory: Array(14 * BRD_SQ_NUM), searchKillers: Array(3 * MAXDEPTH) }; function CheckBoard() { var a, b, c, d, e = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], f = [0, 0]; for (b = PIECES.wP; b <= PIECES.bK; ++b) for (c = 0; c < GameBoard.pceNum[b]; ++c) if (d = GameBoard.pList[PCEINDEX(b, c)], GameBoard.pieces[d] != b) return console.log("Error Pce Lists"), BOOL.FALSE; for (a = 0; 64 > a; ++a) d = SQ120(a), b = GameBoard.pieces[d], e[b]++, f[PieceCol[b]] += PieceVal[b]; for (b = PIECES.wP; b <= PIECES.bK; ++b) if (e[b] != GameBoard.pceNum[b]) return console.log("Error t_pceNum"), BOOL.FALSE; return f[COLOURS.WHITE] != GameBoard.material[COLOURS.WHITE] || f[COLOURS.BLACK] != GameBoard.material[COLOURS.BLACK] ? (console.log("Error t_material"), BOOL.FALSE) : GameBoard.side != COLOURS.WHITE && GameBoard.side != COLOURS.BLACK ? (console.log("Error GameBoard.side"), BOOL.FALSE) : GeneratePosKey() == GameBoard.posKey ? BOOL.TRUE : (console.log("Error GameBoard.posKey"), BOOL.FALSE) } function PrintBoard() { var a, b, c, d; for (console.log("\nGame Board:\n"), c = RANKS.RANK_8; c >= RANKS.RANK_1; c--) { var e = RankChar[c] + " "; for (b = FILES.FILE_A; b <= FILES.FILE_H; b++) a = FR2SQ(b, c), d = GameBoard.pieces[a], e += " " + PceChar[d] + " "; console.log(e) } console.log(""); var e = " "; for (b = FILES.FILE_A; b <= FILES.FILE_H; b++) e += " " + FileChar[b] + " "; console.log(e), console.log("side:" + SideChar[GameBoard.side]), console.log("enPas:" + GameBoard.enPas), e = "", GameBoard.castlePerm & CASTLEBIT.WKCA && (e += "K"), GameBoard.castlePerm & CASTLEBIT.WQCA && (e += "Q"), GameBoard.castlePerm & CASTLEBIT.BKCA && (e += "k"), GameBoard.castlePerm & CASTLEBIT.BQCA && (e += "q"), console.log("castle:" + e), console.log("key:" + GameBoard.posKey.toString(16)) } function GeneratePosKey() { var a = 0, b = 0, c = PIECES.EMPTY; for (a = 0; a < BRD_SQ_NUM; ++a) c = GameBoard.pieces[a], c != PIECES.EMPTY && c != SQUARES.OFFBOARD && (b ^= PieceKeys[120 * c + a]); return GameBoard.side == COLOURS.WHITE && (b ^= SideKey), GameBoard.enPas != SQUARES.NO_SQ && (b ^= PieceKeys[GameBoard.enPas]), b ^= CastleKeys[GameBoard.castlePerm], b } function PrintPieceLists() { var a, b; for (a = PIECES.wP; a <= PIECES.bK; ++a) for (b = 0; b < GameBoard.pceNum[a]; ++b) console.log("Piece " + PceChar[a] + " on " + PrSq(GameBoard.pList[PCEINDEX(a, b)])) } function UpdateListsMaterial() { var a, b, c, d; for (c = 0; c < 1680; ++c) GameBoard.pList[c] = PIECES.EMPTY; for (c = 0; 2 > c; ++c) GameBoard.material[c] = 0; for (c = 0; 13 > c; ++c) GameBoard.pceNum[c] = 0; for (c = 0; 64 > c; ++c) b = SQ120(c), a = GameBoard.pieces[b], a != PIECES.EMPTY && (d = PieceCol[a], GameBoard.material[d] += PieceVal[a], GameBoard.pList[PCEINDEX(a, GameBoard.pceNum[a])] = b, GameBoard.pceNum[a]++) } function ResetBoard() { var a = 0; for (a = 0; a < BRD_SQ_NUM; ++a) GameBoard.pieces[a] = SQUARES.OFFBOARD; for (a = 0; 64 > a; ++a) GameBoard.pieces[SQ120(a)] = PIECES.EMPTY; GameBoard.side = COLOURS.BOTH, GameBoard.enPas = SQUARES.NO_SQ, GameBoard.fiftyMove = 0, GameBoard.ply = 0, GameBoard.hisPly = 0, GameBoard.castlePerm = 0, GameBoard.posKey = 0, GameBoard.moveListStart[GameBoard.ply] = 0 } function ParseFen(a) { ResetBoard(); for (var b = RANKS.RANK_8, c = FILES.FILE_A, d = 0, e = 0, f = 0, g = 0, h = 0; b >= RANKS.RANK_1 && h < a.length;) { switch (e = 1, a[h]) { case "p": d = PIECES.bP; break; case "r": d = PIECES.bR; break; case "n": d = PIECES.bN; break; case "b": d = PIECES.bB; break; case "k": d = PIECES.bK; break; case "q": d = PIECES.bQ; break; case "P": d = PIECES.wP; break; case "R": d = PIECES.wR; break; case "N": d = PIECES.wN; break; case "B": d = PIECES.wB; break; case "K": d = PIECES.wK; break; case "Q": d = PIECES.wQ; break; case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": d = PIECES.EMPTY, e = a[h].charCodeAt() - 48; break; case "/": case " ": b--, c = FILES.FILE_A, h++; continue; default: return void console.log("FEN error"); } for (f = 0; f < e; f++) g = FR2SQ(c, b), GameBoard.pieces[g] = d, c++; h++ } for (GameBoard.side = "w" == a[h] ? COLOURS.WHITE : COLOURS.BLACK, h += 2, f = 0; 4 > f && !(" " == a[h]); f++) { switch (a[h]) { case "K": GameBoard.castlePerm |= CASTLEBIT.WKCA; break; case "Q": GameBoard.castlePerm |= CASTLEBIT.WQCA; break; case "k": GameBoard.castlePerm |= CASTLEBIT.BKCA; break; case "q": GameBoard.castlePerm |= CASTLEBIT.BQCA; break; default: } h++ } h++, "-" != a[h] && (c = a[h].charCodeAt() - 97, b = a[h + 1].charCodeAt() - 49, console.log("fen[fenCnt]:" + a[h] + " File:" + c + " Rank:" + b), GameBoard.enPas = FR2SQ(c, b)), GameBoard.posKey = GeneratePosKey(), UpdateListsMaterial() } function PrintSqAttacked() { var a, b, c, d; for (console.log("\nAttacked:\n"), c = RANKS.RANK_8; c >= RANKS.RANK_1; c--) { var e = c + 1 + " "; for (b = FILES.FILE_A; b <= FILES.FILE_H; b++) a = FR2SQ(b, c), d = SqAttacked(a, 1 ^ GameBoard.side) == BOOL.TRUE ? "X" : "-", e += " " + d + " "; console.log(e) } console.log("") } function SqAttacked(a, b) { var c, d, e; if (b == COLOURS.WHITE) { if (GameBoard.pieces[a - 11] == PIECES.wP || GameBoard.pieces[a - 9] == PIECES.wP) return BOOL.TRUE; } else if (GameBoard.pieces[a + 11] == PIECES.bP || GameBoard.pieces[a + 9] == PIECES.bP) return BOOL.TRUE; for (e = 0; 8 > e; e++) if (c = GameBoard.pieces[a + KnDir[e]], c != SQUARES.OFFBOARD && PieceCol[c] == b && PieceKnight[c] == BOOL.TRUE) return BOOL.TRUE; for (e = 0; 4 > e; ++e) for (dir = RkDir[e], d = a + dir, c = GameBoard.pieces[d]; c != SQUARES.OFFBOARD;) { if (c != PIECES.EMPTY) { if (PieceRookQueen[c] == BOOL.TRUE && PieceCol[c] == b) return BOOL.TRUE; break } d += dir, c = GameBoard.pieces[d] } for (e = 0; 4 > e; ++e) for (dir = BiDir[e], d = a + dir, c = GameBoard.pieces[d]; c != SQUARES.OFFBOARD;) { if (c != PIECES.EMPTY) { if (PieceBishopQueen[c] == BOOL.TRUE && PieceCol[c] == b) return BOOL.TRUE; break } d += dir, c = GameBoard.pieces[d] } for (e = 0; 8 > e; e++) if (c = GameBoard.pieces[a + KiDir[e]], c != SQUARES.OFFBOARD && PieceCol[c] == b && PieceKing[c] == BOOL.TRUE) return BOOL.TRUE; return BOOL.FALSE } var MvvLvaValue = [0, 100, 200, 300, 400, 500, 600, 100, 200, 300, 400, 500, 600], MvvLvaScores = Array(196); function InitMvvLva() { var a, b; for (a = PIECES.wP; a <= PIECES.bK; ++a) for (b = PIECES.wP; b <= PIECES.bK; ++b) MvvLvaScores[14 * b + a] = MvvLvaValue[b] + 6 - MvvLvaValue[a] / 100 } function MoveExists(a) { GenerateMoves(); var b, c = NOMOVE; for (b = GameBoard.moveListStart[GameBoard.ply]; b < GameBoard.moveListStart[GameBoard.ply + 1]; ++b) if ((c = GameBoard.moveList[b], MakeMove(c) != BOOL.FALSE) && (TakeMove(), a == c)) return BOOL.TRUE; return BOOL.FALSE } function MOVE(a, b, c, d, e) { return a | b << 7 | c << 14 | d << 20 | e } function AddCaptureMove(a) { GameBoard.moveList[GameBoard.moveListStart[GameBoard.ply + 1]] = a, GameBoard.moveScores[GameBoard.moveListStart[GameBoard.ply + 1]++] = MvvLvaScores[14 * CAPTURED(a) + GameBoard.pieces[FROMSQ(a)]] + 1e6 } function AddQuietMove(a) { GameBoard.moveList[GameBoard.moveListStart[GameBoard.ply + 1]] = a, GameBoard.moveScores[GameBoard.moveListStart[GameBoard.ply + 1]] = 0, GameBoard.moveScores[GameBoard.moveListStart[GameBoard.ply + 1]] = a == GameBoard.searchKillers[GameBoard.ply] ? 9e5 : a == GameBoard.searchKillers[GameBoard.ply + MAXDEPTH] ? 8e5 : GameBoard.searchHistory[GameBoard.pieces[FROMSQ(a)] * BRD_SQ_NUM + TOSQ(a)], GameBoard.moveListStart[GameBoard.ply + 1]++ } function AddEnPassantMove(a) { GameBoard.moveList[GameBoard.moveListStart[GameBoard.ply + 1]] = a, GameBoard.moveScores[GameBoard.moveListStart[GameBoard.ply + 1]++] = 1000105 } function AddWhitePawnCaptureMove(a, b, c) { RanksBrd[a] == RANKS.RANK_7 ? (AddCaptureMove(MOVE(a, b, c, PIECES.wQ, 0)), AddCaptureMove(MOVE(a, b, c, PIECES.wR, 0)), AddCaptureMove(MOVE(a, b, c, PIECES.wB, 0)), AddCaptureMove(MOVE(a, b, c, PIECES.wN, 0))) : AddCaptureMove(MOVE(a, b, c, PIECES.EMPTY, 0)) } function AddBlackPawnCaptureMove(a, b, c) { RanksBrd[a] == RANKS.RANK_2 ? (AddCaptureMove(MOVE(a, b, c, PIECES.bQ, 0)), AddCaptureMove(MOVE(a, b, c, PIECES.bR, 0)), AddCaptureMove(MOVE(a, b, c, PIECES.bB, 0)), AddCaptureMove(MOVE(a, b, c, PIECES.bN, 0))) : AddCaptureMove(MOVE(a, b, c, PIECES.EMPTY, 0)) } function AddWhitePawnQuietMove(a, b) { RanksBrd[a] == RANKS.RANK_7 ? (AddQuietMove(MOVE(a, b, PIECES.EMPTY, PIECES.wQ, 0)), AddQuietMove(MOVE(a, b, PIECES.EMPTY, PIECES.wR, 0)), AddQuietMove(MOVE(a, b, PIECES.EMPTY, PIECES.wB, 0)), AddQuietMove(MOVE(a, b, PIECES.EMPTY, PIECES.wN, 0))) : AddQuietMove(MOVE(a, b, PIECES.EMPTY, PIECES.EMPTY, 0)) } function AddBlackPawnQuietMove(a, b) { RanksBrd[a] == RANKS.RANK_2 ? (AddQuietMove(MOVE(a, b, PIECES.EMPTY, PIECES.bQ, 0)), AddQuietMove(MOVE(a, b, PIECES.EMPTY, PIECES.bR, 0)), AddQuietMove(MOVE(a, b, PIECES.EMPTY, PIECES.bB, 0)), AddQuietMove(MOVE(a, b, PIECES.EMPTY, PIECES.bN, 0))) : AddQuietMove(MOVE(a, b, PIECES.EMPTY, PIECES.EMPTY, 0)) } function GenerateMoves() { GameBoard.moveListStart[GameBoard.ply + 1] = GameBoard.moveListStart[GameBoard.ply]; var a, b, c, d, e, f, g; if (GameBoard.side == COLOURS.WHITE) { for (a = PIECES.wP, b = 0; b < GameBoard.pceNum[a]; ++b) c = GameBoard.pList[PCEINDEX(a, b)], GameBoard.pieces[c + 10] == PIECES.EMPTY && (AddWhitePawnQuietMove(c, c + 10), RanksBrd[c] == RANKS.RANK_2 && GameBoard.pieces[c + 20] == PIECES.EMPTY && AddQuietMove(MOVE(c, c + 20, PIECES.EMPTY, PIECES.EMPTY, MFLAGPS))), SQOFFBOARD(c + 9) == BOOL.FALSE && PieceCol[GameBoard.pieces[c + 9]] == COLOURS.BLACK && AddWhitePawnCaptureMove(c, c + 9, GameBoard.pieces[c + 9]), SQOFFBOARD(c + 11) == BOOL.FALSE && PieceCol[GameBoard.pieces[c + 11]] == COLOURS.BLACK && AddWhitePawnCaptureMove(c, c + 11, GameBoard.pieces[c + 11]), GameBoard.enPas != SQUARES.NO_SQ && (c + 9 == GameBoard.enPas && AddEnPassantMove(MOVE(c, c + 9, PIECES.EMPTY, PIECES.EMPTY, MFLAGEP)), c + 11 == GameBoard.enPas && AddEnPassantMove(MOVE(c, c + 11, PIECES.EMPTY, PIECES.EMPTY, MFLAGEP))); GameBoard.castlePerm & CASTLEBIT.WKCA && GameBoard.pieces[SQUARES.F1] == PIECES.EMPTY && GameBoard.pieces[SQUARES.G1] == PIECES.EMPTY && SqAttacked(SQUARES.F1, COLOURS.BLACK) == BOOL.FALSE && SqAttacked(SQUARES.E1, COLOURS.BLACK) == BOOL.FALSE && AddQuietMove(MOVE(SQUARES.E1, SQUARES.G1, PIECES.EMPTY, PIECES.EMPTY, MFLAGCA)), GameBoard.castlePerm & CASTLEBIT.WQCA && GameBoard.pieces[SQUARES.D1] == PIECES.EMPTY && GameBoard.pieces[SQUARES.C1] == PIECES.EMPTY && GameBoard.pieces[SQUARES.B1] == PIECES.EMPTY && SqAttacked(SQUARES.D1, COLOURS.BLACK) == BOOL.FALSE && SqAttacked(SQUARES.E1, COLOURS.BLACK) == BOOL.FALSE && AddQuietMove(MOVE(SQUARES.E1, SQUARES.C1, PIECES.EMPTY, PIECES.EMPTY, MFLAGCA)) } else { for (a = PIECES.bP, b = 0; b < GameBoard.pceNum[a]; ++b) c = GameBoard.pList[PCEINDEX(a, b)], GameBoard.pieces[c - 10] == PIECES.EMPTY && (AddBlackPawnQuietMove(c, c - 10), RanksBrd[c] == RANKS.RANK_7 && GameBoard.pieces[c - 20] == PIECES.EMPTY && AddQuietMove(MOVE(c, c - 20, PIECES.EMPTY, PIECES.EMPTY, MFLAGPS))), SQOFFBOARD(c - 9) == BOOL.FALSE && PieceCol[GameBoard.pieces[c - 9]] == COLOURS.WHITE && AddBlackPawnCaptureMove(c, c - 9, GameBoard.pieces[c - 9]), SQOFFBOARD(c - 11) == BOOL.FALSE && PieceCol[GameBoard.pieces[c - 11]] == COLOURS.WHITE && AddBlackPawnCaptureMove(c, c - 11, GameBoard.pieces[c - 11]), GameBoard.enPas != SQUARES.NO_SQ && (c - 9 == GameBoard.enPas && AddEnPassantMove(MOVE(c, c - 9, PIECES.EMPTY, PIECES.EMPTY, MFLAGEP)), c - 11 == GameBoard.enPas && AddEnPassantMove(MOVE(c, c - 11, PIECES.EMPTY, PIECES.EMPTY, MFLAGEP))); GameBoard.castlePerm & CASTLEBIT.BKCA && GameBoard.pieces[SQUARES.F8] == PIECES.EMPTY && GameBoard.pieces[SQUARES.G8] == PIECES.EMPTY && SqAttacked(SQUARES.F8, COLOURS.WHITE) == BOOL.FALSE && SqAttacked(SQUARES.E8, COLOURS.WHITE) == BOOL.FALSE && AddQuietMove(MOVE(SQUARES.E8, SQUARES.G8, PIECES.EMPTY, PIECES.EMPTY, MFLAGCA)), GameBoard.castlePerm & CASTLEBIT.BQCA && GameBoard.pieces[SQUARES.D8] == PIECES.EMPTY && GameBoard.pieces[SQUARES.C8] == PIECES.EMPTY && GameBoard.pieces[SQUARES.B8] == PIECES.EMPTY && SqAttacked(SQUARES.D8, COLOURS.WHITE) == BOOL.FALSE && SqAttacked(SQUARES.E8, COLOURS.WHITE) == BOOL.FALSE && AddQuietMove(MOVE(SQUARES.E8, SQUARES.C8, PIECES.EMPTY, PIECES.EMPTY, MFLAGCA)) } for (d = LoopNonSlideIndex[GameBoard.side], e = LoopNonSlidePce[d++]; 0 != e;) { for (b = 0; b < GameBoard.pceNum[e]; ++b) for (c = GameBoard.pList[PCEINDEX(e, b)], index = 0; index < DirNum[e]; index++)(g = PceDir[e][index], f = c + g, SQOFFBOARD(f) != BOOL.TRUE) && (GameBoard.pieces[f] == PIECES.EMPTY ? AddQuietMove(MOVE(c, f, PIECES.EMPTY, PIECES.EMPTY, 0)) : PieceCol[GameBoard.pieces[f]] != GameBoard.side && AddCaptureMove(MOVE(c, f, GameBoard.pieces[f], PIECES.EMPTY, 0))); e = LoopNonSlidePce[d++] } for (d = LoopSlideIndex[GameBoard.side], e = LoopSlidePce[d++]; 0 != e;) { for (b = 0; b < GameBoard.pceNum[e]; ++b) for (c = GameBoard.pList[PCEINDEX(e, b)], index = 0; index < DirNum[e]; index++) for (g = PceDir[e][index], f = c + g; SQOFFBOARD(f) == BOOL.FALSE;) { if (GameBoard.pieces[f] != PIECES.EMPTY) { PieceCol[GameBoard.pieces[f]] != GameBoard.side && AddCaptureMove(MOVE(c, f, GameBoard.pieces[f], PIECES.EMPTY, 0)); break } AddQuietMove(MOVE(c, f, PIECES.EMPTY, PIECES.EMPTY, 0)), f += g } e = LoopSlidePce[d++] } } function GenerateCaptures() { GameBoard.moveListStart[GameBoard.ply + 1] = GameBoard.moveListStart[GameBoard.ply]; var a, b, c, d, e, f, g; if (GameBoard.side == COLOURS.WHITE) for (a = PIECES.wP, b = 0; b < GameBoard.pceNum[a]; ++b) c = GameBoard.pList[PCEINDEX(a, b)], SQOFFBOARD(c + 9) == BOOL.FALSE && PieceCol[GameBoard.pieces[c + 9]] == COLOURS.BLACK && AddWhitePawnCaptureMove(c, c + 9, GameBoard.pieces[c + 9]), SQOFFBOARD(c + 11) == BOOL.FALSE && PieceCol[GameBoard.pieces[c + 11]] == COLOURS.BLACK && AddWhitePawnCaptureMove(c, c + 11, GameBoard.pieces[c + 11]), GameBoard.enPas != SQUARES.NO_SQ && (c + 9 == GameBoard.enPas && AddEnPassantMove(MOVE(c, c + 9, PIECES.EMPTY, PIECES.EMPTY, MFLAGEP)), c + 11 == GameBoard.enPas && AddEnPassantMove(MOVE(c, c + 11, PIECES.EMPTY, PIECES.EMPTY, MFLAGEP))); else for (a = PIECES.bP, b = 0; b < GameBoard.pceNum[a]; ++b) c = GameBoard.pList[PCEINDEX(a, b)], SQOFFBOARD(c - 9) == BOOL.FALSE && PieceCol[GameBoard.pieces[c - 9]] == COLOURS.WHITE && AddBlackPawnCaptureMove(c, c - 9, GameBoard.pieces[c - 9]), SQOFFBOARD(c - 11) == BOOL.FALSE && PieceCol[GameBoard.pieces[c - 11]] == COLOURS.WHITE && AddBlackPawnCaptureMove(c, c - 11, GameBoard.pieces[c - 11]), GameBoard.enPas != SQUARES.NO_SQ && (c - 9 == GameBoard.enPas && AddEnPassantMove(MOVE(c, c - 9, PIECES.EMPTY, PIECES.EMPTY, MFLAGEP)), c - 11 == GameBoard.enPas && AddEnPassantMove(MOVE(c, c - 11, PIECES.EMPTY, PIECES.EMPTY, MFLAGEP))); for (d = LoopNonSlideIndex[GameBoard.side], e = LoopNonSlidePce[d++]; 0 != e;) { for (b = 0; b < GameBoard.pceNum[e]; ++b) for (c = GameBoard.pList[PCEINDEX(e, b)], index = 0; index < DirNum[e]; index++)(g = PceDir[e][index], f = c + g, SQOFFBOARD(f) != BOOL.TRUE) && GameBoard.pieces[f] != PIECES.EMPTY && PieceCol[GameBoard.pieces[f]] != GameBoard.side && AddCaptureMove(MOVE(c, f, GameBoard.pieces[f], PIECES.EMPTY, 0)); e = LoopNonSlidePce[d++] } for (d = LoopSlideIndex[GameBoard.side], e = LoopSlidePce[d++]; 0 != e;) { for (b = 0; b < GameBoard.pceNum[e]; ++b) for (c = GameBoard.pList[PCEINDEX(e, b)], index = 0; index < DirNum[e]; index++) for (g = PceDir[e][index], f = c + g; SQOFFBOARD(f) == BOOL.FALSE;) { if (GameBoard.pieces[f] != PIECES.EMPTY) { PieceCol[GameBoard.pieces[f]] != GameBoard.side && AddCaptureMove(MOVE(c, f, GameBoard.pieces[f], PIECES.EMPTY, 0)); break } f += g } e = LoopSlidePce[d++] } } function ClearPiece(a) { var b, c = GameBoard.pieces[a], d = PieceCol[c], e = -1; for (HASH_PCE(c, a), GameBoard.pieces[a] = PIECES.EMPTY, GameBoard.material[d] -= PieceVal[c], b = 0; b < GameBoard.pceNum[c]; ++b) if (GameBoard.pList[PCEINDEX(c, b)] == a) { e = b; break } GameBoard.pceNum[c]--, GameBoard.pList[PCEINDEX(c, e)] = GameBoard.pList[PCEINDEX(c, GameBoard.pceNum[c])] } function AddPiece(a, b) { var c = PieceCol[b]; HASH_PCE(b, a), GameBoard.pieces[a] = b, GameBoard.material[c] += PieceVal[b], GameBoard.pList[PCEINDEX(b, GameBoard.pceNum[b])] = a, GameBoard.pceNum[b]++ } function MovePiece(a, b) { var c = 0, d = GameBoard.pieces[a]; for (HASH_PCE(d, a), GameBoard.pieces[a] = PIECES.EMPTY, HASH_PCE(d, b), GameBoard.pieces[b] = d, c = 0; c < GameBoard.pceNum[d]; ++c) if (GameBoard.pList[PCEINDEX(d, c)] == a) { GameBoard.pList[PCEINDEX(d, c)] = b; break } } function MakeMove(a) { var b = FROMSQ(a), c = TOSQ(a), d = GameBoard.side; if (GameBoard.history[GameBoard.hisPly].posKey = GameBoard.posKey, 0 != (a & MFLAGEP)) d == COLOURS.WHITE ? ClearPiece(c - 10) : ClearPiece(c + 10); else if (0 != (a & MFLAGCA)) switch (c) { case SQUARES.C1: MovePiece(SQUARES.A1, SQUARES.D1); break; case SQUARES.C8: MovePiece(SQUARES.A8, SQUARES.D8); break; case SQUARES.G1: MovePiece(SQUARES.H1, SQUARES.F1); break; case SQUARES.G8: MovePiece(SQUARES.H8, SQUARES.F8); break; default: } GameBoard.enPas != SQUARES.NO_SQ && HASH_EP(), HASH_CA(), GameBoard.history[GameBoard.hisPly].move = a, GameBoard.history[GameBoard.hisPly].fiftyMove = GameBoard.fiftyMove, GameBoard.history[GameBoard.hisPly].enPas = GameBoard.enPas, GameBoard.history[GameBoard.hisPly].castlePerm = GameBoard.castlePerm, GameBoard.castlePerm &= CastlePerm[b], GameBoard.castlePerm &= CastlePerm[c], GameBoard.enPas = SQUARES.NO_SQ, HASH_CA(); var e = CAPTURED(a); GameBoard.fiftyMove++, e != PIECES.EMPTY && (ClearPiece(c), GameBoard.fiftyMove = 0), GameBoard.hisPly++, GameBoard.ply++, PiecePawn[GameBoard.pieces[b]] == BOOL.TRUE && (GameBoard.fiftyMove = 0, 0 != (a & MFLAGPS) && (GameBoard.enPas = d == COLOURS.WHITE ? b + 10 : b - 10, HASH_EP())), MovePiece(b, c); var f = PROMOTED(a); return f != PIECES.EMPTY && (ClearPiece(c), AddPiece(c, f)), GameBoard.side ^= 1, HASH_SIDE(), SqAttacked(GameBoard.pList[PCEINDEX(Kings[d], 0)], GameBoard.side) ? (TakeMove(), BOOL.FALSE) : BOOL.TRUE } function TakeMove() { GameBoard.hisPly--, GameBoard.ply--; var a = GameBoard.history[GameBoard.hisPly].move, b = FROMSQ(a), c = TOSQ(a); if (GameBoard.enPas != SQUARES.NO_SQ && HASH_EP(), HASH_CA(), GameBoard.castlePerm = GameBoard.history[GameBoard.hisPly].castlePerm, GameBoard.fiftyMove = GameBoard.history[GameBoard.hisPly].fiftyMove, GameBoard.enPas = GameBoard.history[GameBoard.hisPly].enPas, GameBoard.enPas != SQUARES.NO_SQ && HASH_EP(), HASH_CA(), GameBoard.side ^= 1, HASH_SIDE(), 0 != (MFLAGEP & a)) GameBoard.side == COLOURS.WHITE ? AddPiece(c - 10, PIECES.bP) : AddPiece(c + 10, PIECES.wP); else if (0 != (MFLAGCA & a)) switch (c) { case SQUARES.C1: MovePiece(SQUARES.D1, SQUARES.A1); break; case SQUARES.C8: MovePiece(SQUARES.D8, SQUARES.A8); break; case SQUARES.G1: MovePiece(SQUARES.F1, SQUARES.H1); break; case SQUARES.G8: MovePiece(SQUARES.F8, SQUARES.H8); break; default: } MovePiece(c, b); var d = CAPTURED(a); d != PIECES.EMPTY && AddPiece(c, d), PROMOTED(a) != PIECES.EMPTY && (ClearPiece(b), AddPiece(b, PieceCol[PROMOTED(a)] == COLOURS.WHITE ? PIECES.wP : PIECES.bP)) } var PawnTable = [0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 0, -10, -10, 0, 10, 10, 5, 0, 0, 5, 5, 0, 0, 5, 0, 0, 10, 20, 20, 10, 0, 0, 5, 5, 5, 10, 10, 5, 5, 5, 10, 10, 10, 20, 20, 10, 10, 10, 20, 20, 20, 30, 30, 20, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0], KnightTable = [0, -10, 0, 0, 0, 0, -10, 0, 0, 0, 0, 5, 5, 0, 0, 0, 0, 0, 10, 10, 10, 10, 0, 0, 0, 0, 10, 20, 20, 10, 5, 0, 5, 10, 15, 20, 20, 15, 10, 5, 5, 10, 10, 20, 20, 10, 10, 5, 0, 0, 5, 10, 10, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], BishopTable = [0, 0, -10, 0, 0, -10, 0, 0, 0, 0, 0, 10, 10, 0, 0, 0, 0, 0, 10, 15, 15, 10, 0, 0, 0, 10, 15, 20, 20, 15, 10, 0, 0, 10, 15, 20, 20, 15, 10, 0, 0, 0, 10, 15, 15, 10, 0, 0, 0, 0, 0, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], RookTable = [0, 0, 5, 10, 10, 5, 0, 0, 0, 0, 5, 10, 10, 5, 0, 0, 0, 0, 5, 10, 10, 5, 0, 0, 0, 0, 5, 10, 10, 5, 0, 0, 0, 0, 5, 10, 10, 5, 0, 0, 0, 0, 5, 10, 10, 5, 0, 0, 25, 25, 25, 25, 25, 25, 25, 25, 0, 0, 5, 10, 10, 5, 0, 0], BishopPair = 40; function EvalPosition() { var a, b, c, d = GameBoard.material[COLOURS.WHITE] - GameBoard.material[COLOURS.BLACK]; for (a = PIECES.wP, c = 0; c < GameBoard.pceNum[a]; ++c) b = GameBoard.pList[PCEINDEX(a, c)], d += PawnTable[SQ64(b)]; for (a = PIECES.bP, c = 0; c < GameBoard.pceNum[a]; ++c) b = GameBoard.pList[PCEINDEX(a, c)], d -= PawnTable[MIRROR64(SQ64(b))]; for (a = PIECES.wN, c = 0; c < GameBoard.pceNum[a]; ++c) b = GameBoard.pList[PCEINDEX(a, c)], d += KnightTable[SQ64(b)]; for (a = PIECES.bN, c = 0; c < GameBoard.pceNum[a]; ++c) b = GameBoard.pList[PCEINDEX(a, c)], d -= KnightTable[MIRROR64(SQ64(b))]; for (a = PIECES.wB, c = 0; c < GameBoard.pceNum[a]; ++c) b = GameBoard.pList[PCEINDEX(a, c)], d += BishopTable[SQ64(b)]; for (a = PIECES.bB, c = 0; c < GameBoard.pceNum[a]; ++c) b = GameBoard.pList[PCEINDEX(a, c)], d -= BishopTable[MIRROR64(SQ64(b))]; for (a = PIECES.wR, c = 0; c < GameBoard.pceNum[a]; ++c) b = GameBoard.pList[PCEINDEX(a, c)], d += RookTable[SQ64(b)]; for (a = PIECES.bR, c = 0; c < GameBoard.pceNum[a]; ++c) b = GameBoard.pList[PCEINDEX(a, c)], d -= RookTable[MIRROR64(SQ64(b))]; for (a = PIECES.wQ, c = 0; c < GameBoard.pceNum[a]; ++c) b = GameBoard.pList[PCEINDEX(a, c)], d += RookTable[SQ64(b)]; for (a = PIECES.bQ, c = 0; c < GameBoard.pceNum[a]; ++c) b = GameBoard.pList[PCEINDEX(a, c)], d -= RookTable[MIRROR64(SQ64(b))]; return 2 <= GameBoard.pceNum[PIECES.wB] && (d += BishopPair), 2 <= GameBoard.pceNum[PIECES.bB] && (d -= BishopPair), GameBoard.side == COLOURS.WHITE ? d : -d } function GetPvLine(a) { for (var b = ProbePvTable(), c = 0; b != NOMOVE && c < a && MoveExists(b) == BOOL.TRUE;) { MakeMove(b), GameBoard.PvArray[c++] = b; b = ProbePvTable() } for (; 0 < GameBoard.ply;) TakeMove(); return c } function ProbePvTable() { var a = GameBoard.posKey % PVENTRIES; return GameBoard.PvTable[a].posKey == GameBoard.posKey ? GameBoard.PvTable[a].move : NOMOVE } function StorePvMove(a) { var b = GameBoard.posKey % PVENTRIES; GameBoard.PvTable[b].posKey = GameBoard.posKey, GameBoard.PvTable[b].move = a } var SearchController = {}; SearchController.nodes, SearchController.fh, SearchController.fhf, SearchController.depth, SearchController.time, SearchController.start, SearchController.stop, SearchController.best, SearchController.thinking; function PickNextMove(a) { var b = 0, c = -1, d = a; for (b = a; b < GameBoard.moveListStart[GameBoard.ply + 1]; ++b) GameBoard.moveScores[b] > c && (c = GameBoard.moveScores[b], d = b); if (d != a) { var e = 0; e = GameBoard.moveScores[a], GameBoard.moveScores[a] = GameBoard.moveScores[d], GameBoard.moveScores[d] = e, e = GameBoard.moveList[a], GameBoard.moveList[a] = GameBoard.moveList[d], GameBoard.moveList[d] = e } } function ClearPvTable() { for (index = 0; index < PVENTRIES; index++) GameBoard.PvTable[index].move = NOMOVE, GameBoard.PvTable[index].posKey = 0 } function CheckUp() { $.now() - SearchController.start > SearchController.time && (SearchController.stop = BOOL.TRUE) } function IsRepetition() { var a = 0; for (a = GameBoard.hisPly - GameBoard.fiftyMove; a < GameBoard.hisPly - 1; ++a) if (GameBoard.posKey == GameBoard.history[a].posKey) return BOOL.TRUE; return BOOL.FALSE } function Quiescence(a, b) { if (0 == (2047 & SearchController.nodes) && CheckUp(), SearchController.nodes++, (IsRepetition() || 100 <= GameBoard.fiftyMove) && 0 != GameBoard.ply) return 0; if (GameBoard.ply > MAXDEPTH - 1) return EvalPosition(); var c = EvalPosition(); if (c >= b) return b; c > a && (a = c), GenerateCaptures(); var d = 0, e = 0, f = a, g = NOMOVE, h = NOMOVE; for (d = GameBoard.moveListStart[GameBoard.ply]; d < GameBoard.moveListStart[GameBoard.ply + 1]; ++d) if (PickNextMove(d), h = GameBoard.moveList[d], MakeMove(h) != BOOL.FALSE) { if (e++, c = -Quiescence(-b, -a), TakeMove(), SearchController.stop == BOOL.TRUE) return 0; if (c > a) { if (c >= b) return 1 == e && SearchController.fhf++, SearchController.fh++, b; a = c, g = h } } return a != f && StorePvMove(g), a } function AlphaBeta(a, b, c) { if (0 >= c) return Quiescence(a, b); if (0 == (2047 & SearchController.nodes) && CheckUp(), SearchController.nodes++, (IsRepetition() || 100 <= GameBoard.fiftyMove) && 0 != GameBoard.ply) return 0; if (GameBoard.ply > MAXDEPTH - 1) return EvalPosition(); var d = SqAttacked(GameBoard.pList[PCEINDEX(Kings[GameBoard.side], 0)], 1 ^ GameBoard.side); d == BOOL.TRUE && c++; var e = -INFINITE; GenerateMoves(); var f = 0, g = 0, h = a, i = NOMOVE, j = NOMOVE, k = ProbePvTable(); if (k != NOMOVE) for (f = GameBoard.moveListStart[GameBoard.ply]; f < GameBoard.moveListStart[GameBoard.ply + 1]; ++f) if (GameBoard.moveList[f] == k) { GameBoard.moveScores[f] = 2e6; break } for (f = GameBoard.moveListStart[GameBoard.ply]; f < GameBoard.moveListStart[GameBoard.ply + 1]; ++f) if (PickNextMove(f), j = GameBoard.moveList[f], MakeMove(j) != BOOL.FALSE) { if (g++, e = -AlphaBeta(-b, -a, c - 1), TakeMove(), SearchController.stop == BOOL.TRUE) return 0; if (e > a) { if (e >= b) return 1 == g && SearchController.fhf++, SearchController.fh++, 0 == (j & MFLAGCAP) && (GameBoard.searchKillers[MAXDEPTH + GameBoard.ply] = GameBoard.searchKillers[GameBoard.ply], GameBoard.searchKillers[GameBoard.ply] = j), b; 0 == (j & MFLAGCAP) && (GameBoard.searchHistory[GameBoard.pieces[FROMSQ(j)] * BRD_SQ_NUM + TOSQ(j)] += c * c), a = e, i = j } } return 0 == g ? d == BOOL.TRUE ? -MATE + GameBoard.ply : 0 : (a != h && StorePvMove(i), a) } function ClearForSearch() { var a = 0; for (a = 0; a < 14 * BRD_SQ_NUM; ++a) GameBoard.searchHistory[a] = 0; for (a = 0; a < 3 * MAXDEPTH; ++a) GameBoard.searchKillers[a] = 0; ClearPvTable(), GameBoard.ply = 0, SearchController.nodes = 0, SearchController.fh = 0, SearchController.fhf = 0, SearchController.start = $.now(), SearchController.stop = BOOL.FALSE } function SearchPosition() { var a, b, d, e = NOMOVE, f = -INFINITE, g = -INFINITE, h = 0; for (ClearForSearch(), h = 1; h <= SearchController.depth && (g = AlphaBeta(-INFINITE, INFINITE, h), SearchController.stop != BOOL.TRUE); ++h) { for (f = g, e = ProbePvTable(), a = "D:" + h + " Best:" + PrMove(e) + " Score:" + f + " nodes:" + SearchController.nodes, b = GetPvLine(h), a += " Pv:", d = 0; d < b; ++d) a += " " + PrMove(GameBoard.PvArray[d]); 1 != h && (a += " Ordering:" + (100 * (SearchController.fhf / SearchController.fh)).toFixed(2) + "%"), console.log(a) } SearchController.best = e, SearchController.thinking = BOOL.FALSE, UpdateDOMStats(f, h) } function UpdateDOMStats(a, b) { var c = "Score: " + (a / 100).toFixed(2); Math.abs(a) > MATE - MAXDEPTH && (c = "Score: Mate In " + (MATE - Math.abs(a) - 1) + " moves"), $("#OrderingOut").text("Ordering: " + (100 * (SearchController.fhf / SearchController.fh)).toFixed(2) + "%"), $("#DepthOut").text("Depth: " + b), $("#ScoreOut").text(c), $("#NodesOut").text("Nodes: " + SearchController.nodes), $("#TimeOut").text("Time: " + (($.now() - SearchController.start) / 1e3).toFixed(1) + "s"), $("#BestOut").text("BestMove: " + PrMove(SearchController.best)) } var perft_leafNodes; function Perft(a) { if (0 == a) return void perft_leafNodes++; GenerateMoves(); var b, c; for (b = GameBoard.moveListStart[GameBoard.ply]; b < GameBoard.moveListStart[GameBoard.ply + 1]; ++b)(c = GameBoard.moveList[b], MakeMove(c) != BOOL.FALSE) && (Perft(a - 1), TakeMove()) } function PerftTest(a) { PrintBoard(), console.log("Starting Test To Depth:" + a), perft_leafNodes = 0; var b, c, d = 0; for (b = GameBoard.moveListStart[GameBoard.ply]; b < GameBoard.moveListStart[GameBoard.ply + 1]; ++b) if (c = GameBoard.moveList[b], MakeMove(c) != BOOL.FALSE) { d++; var e = perft_leafNodes; Perft(a - 1), TakeMove(); var f = perft_leafNodes - e; console.log("move:" + d + " " + PrMove(c) + " " + f) } console.log("Test Complete : " + perft_leafNodes + " leaf nodes visited") } $("#SetFen").click(function () { var a = $("#fenIn").val(); NewGame(a) }), $("#TakeButton").click(function () { 0 < GameBoard.hisPly && (TakeMove(), GameBoard.ply = 0, SetInitialBoardPieces()) }), $("#NewGameButton").click(function () { NewGame(START_FEN) }); function NewGame(a) { ParseFen(a), PrintBoard(), SetInitialBoardPieces(), CheckAndSet() } function ClearAllPieces() { $(".Piece").remove() } function SetInitialBoardPieces() { var a, b, c; for (ClearAllPieces(), a = 0; 64 > a; ++a) b = SQ120(a), c = GameBoard.pieces[b], c >= PIECES.wP && c <= PIECES.bK && AddGUIPiece(b, c) } function DeSelectSq(a) { $(".Square").each(function () { PieceIsOnSq(a, $(this).position().top, $(this).position().left) == BOOL.TRUE && $(this).removeClass("SqSelected") }) } function SetSqSelected(a) { $(".Square").each(function () { PieceIsOnSq(a, $(this).position().top, $(this).position().left) == BOOL.TRUE && $(this).addClass("SqSelected") }) } function ClickedSquare(a, b) { console.log("ClickedSquare() at " + a + "," + b); var c = $("#Board").position(), d = Math.floor(c.left), e = Math.floor(c.top); a = Math.floor(a), b = Math.floor(b); var f = Math.floor((a - d) / 60), g = 7 - Math.floor((b - e) / 60), h = FR2SQ(f, g); return console.log("Clicked sq:" + PrSq(h)), SetSqSelected(h), h } $(document).on("click", ".Piece", function (a) { console.log("Piece Click"), UserMove.from == SQUARES.NO_SQ ? UserMove.from = ClickedSquare(a.pageX, a.pageY) : UserMove.to = ClickedSquare(a.pageX, a.pageY), MakeUserMove() }), $(document).on("click", ".Square", function (a) { console.log("Square Click"), UserMove.from != SQUARES.NO_SQ && (UserMove.to = ClickedSquare(a.pageX, a.pageY), MakeUserMove()) }); function MakeUserMove() { if (UserMove.from != SQUARES.NO_SQ && UserMove.to != SQUARES.NO_SQ) { console.log("User Move:" + PrSq(UserMove.from) + PrSq(UserMove.to)); var a = ParseMove(UserMove.from, UserMove.to); a != NOMOVE && (MakeMove(a), PrintBoard(), MoveGUIPiece(a), CheckAndSet(), PreSearch()), DeSelectSq(UserMove.from), DeSelectSq(UserMove.to), UserMove.from = SQUARES.NO_SQ, UserMove.to = SQUARES.NO_SQ } } function PieceIsOnSq(a, b, c) { return RanksBrd[a] == 7 - Math.round(b / 60) && FilesBrd[a] == Math.round(c / 60) ? BOOL.TRUE : BOOL.FALSE } function RemoveGUIPiece(a) { $(".Piece").each(function () { PieceIsOnSq(a, $(this).position().top, $(this).position().left) == BOOL.TRUE && $(this).remove() }) } function AddGUIPiece(a, b) { var c = FilesBrd[a], d = RanksBrd[a], e = "images/" + SideChar[PieceCol[b]] + PceChar[b].toUpperCase() + ".png"; $("#Board").append("") } function MoveGUIPiece(a) { var b = FROMSQ(a), c = TOSQ(a); if (a & MFLAGEP) { var d; d = GameBoard.side == COLOURS.BLACK ? c - 10 : c + 10, RemoveGUIPiece(d) } else CAPTURED(a) && RemoveGUIPiece(c); var e = FilesBrd[c], f = RanksBrd[c]; $(".Piece").each(function () { PieceIsOnSq(b, $(this).position().top, $(this).position().left) == BOOL.TRUE && ($(this).removeClass(), $(this).addClass("Piece " + ("rank" + (f + 1)) + " " + ("file" + (e + 1)))) }); a & MFLAGCA ? c === SQUARES.G1 ? (RemoveGUIPiece(SQUARES.H1), AddGUIPiece(SQUARES.F1, PIECES.wR)) : c === SQUARES.C1 ? (RemoveGUIPiece(SQUARES.A1), AddGUIPiece(SQUARES.D1, PIECES.wR)) : c === SQUARES.G8 ? (RemoveGUIPiece(SQUARES.H8), AddGUIPiece(SQUARES.F8, PIECES.bR)) : c === SQUARES.C8 ? (RemoveGUIPiece(SQUARES.A8), AddGUIPiece(SQUARES.D8, PIECES.bR)) : void 0 : PROMOTED(a) && (RemoveGUIPiece(c), AddGUIPiece(c, PROMOTED(a))) } function DrawMaterial() { return 0 != GameBoard.pceNum[PIECES.wP] || 0 != GameBoard.pceNum[PIECES.bP] ? BOOL.FALSE : 0 != GameBoard.pceNum[PIECES.wQ] || 0 != GameBoard.pceNum[PIECES.bQ] || 0 != GameBoard.pceNum[PIECES.wR] || 0 != GameBoard.pceNum[PIECES.bR] ? BOOL.FALSE : 1 < GameBoard.pceNum[PIECES.wB] || 1 < GameBoard.pceNum[PIECES.bB] ? BOOL.FALSE : 1 < GameBoard.pceNum[PIECES.wN] || 1 < GameBoard.pceNum[PIECES.bN] ? BOOL.FALSE : 0 != GameBoard.pceNum[PIECES.wN] && 0 != GameBoard.pceNum[PIECES.wB] ? BOOL.FALSE : 0 != GameBoard.pceNum[PIECES.bN] && 0 != GameBoard.pceNum[PIECES.bB] ? BOOL.FALSE : BOOL.TRUE } function ThreeFoldRep() { var a = 0, b = 0; for (a = 0; a < GameBoard.hisPly; ++a) GameBoard.history[a].posKey == GameBoard.posKey && b++; return b } function CheckResult() { if (100 <= GameBoard.fiftyMove) return $("#GameStatus").text("GAME DRAWN {fifty move rule}"), BOOL.TRUE; if (2 <= ThreeFoldRep()) return $("#GameStatus").text("GAME DRAWN {3-fold repetition}"), BOOL.TRUE; if (DrawMaterial() == BOOL.TRUE) return $("#GameStatus").text("GAME DRAWN {insufficient material to mate}"), BOOL.TRUE; GenerateMoves(); var a = 0, b = 0; for (a = GameBoard.moveListStart[GameBoard.ply]; a < GameBoard.moveListStart[GameBoard.ply + 1]; ++a) if (MakeMove(GameBoard.moveList[a]) != BOOL.FALSE) { b++, TakeMove(); break } if (0 != b) return BOOL.FALSE; var c = SqAttacked(GameBoard.pList[PCEINDEX(Kings[GameBoard.side], 0)], 1 ^ GameBoard.side); return c == BOOL.TRUE ? GameBoard.side == COLOURS.WHITE ? ($("#GameStatus").text("GAME OVER {black mates}"), BOOL.TRUE) : ($("#GameStatus").text("GAME OVER {white mates}"), BOOL.TRUE) : ($("#GameStatus").text("GAME DRAWN {stalemate}"), BOOL.TRUE) } function CheckAndSet() { CheckResult() == BOOL.TRUE ? GameController.GameOver = BOOL.TRUE : (GameController.GameOver = BOOL.FALSE, $("#GameStatus").text("")) } function PreSearch() { GameController.GameOver == BOOL.FALSE && (SearchController.thinking = BOOL.TRUE, setTimeout(function () { StartSearch() }, 200)) } $("#SearchButton").click(function () { GameController.PlayerSide = 1 ^ GameController.side, PreSearch() }); function StartSearch() { SearchController.depth = MAXDEPTH; var a = $.now(), b = $("#ThinkTimeChoice").val(); SearchController.time = 1e3 * parseInt(b), SearchPosition(), MakeMove(SearchController.best), MoveGUIPiece(SearchController.best), CheckAndSet() } $(function () { init(), console.log("©Jansher_Aquib"), NewGame(START_FEN) }); function InitFilesRanksBrd() { var a = 0, b = FILES.FILE_A, c = RANKS.RANK_1, d = SQUARES.A1; for (a = 0; a < BRD_SQ_NUM; ++a) FilesBrd[a] = SQUARES.OFFBOARD, RanksBrd[a] = SQUARES.OFFBOARD; for (c = RANKS.RANK_1; c <= RANKS.RANK_8; ++c) for (b = FILES.FILE_A; b <= FILES.FILE_H; ++b) d = FR2SQ(b, c), FilesBrd[d] = b, RanksBrd[d] = c } function InitHashKeys() { var a = 0; for (a = 0; a < 1680; ++a) PieceKeys[a] = RAND_32(); for (SideKey = RAND_32(), a = 0; 16 > a; ++a) CastleKeys[a] = RAND_32() } function InitSq120To64() { var a = 0, b = FILES.FILE_A, c = RANKS.RANK_1, d = SQUARES.A1, e = 0; for (a = 0; a < BRD_SQ_NUM; ++a) Sq120ToSq64[a] = 65; for (a = 0; 64 > a; ++a) Sq64ToSq120[a] = 120; for (c = RANKS.RANK_1; c <= RANKS.RANK_8; ++c) for (b = FILES.FILE_A; b <= FILES.FILE_H; ++b) d = FR2SQ(b, c), Sq64ToSq120[e] = d, Sq120ToSq64[d] = e, e++ } function InitBoardVars() { var a = 0; for (a = 0; a < MAXGAMEMOVES; ++a) GameBoard.history.push({ move: NOMOVE, castlePerm: 0, enPas: 0, fiftyMove: 0, posKey: 0 }); for (a = 0; a < PVENTRIES; ++a) GameBoard.PvTable.push({ move: NOMOVE, posKey: 0 }) } function InitBoardSquares() { var a, b, c, d, e = 0, f = 0, g = 0, h = 0; for (g = RANKS.RANK_8; g >= RANKS.RANK_1; g--) for (e = 1 ^ f, f ^= 1, a = "rank" + (g + 1), h = FILES.FILE_A; h <= FILES.FILE_H; h++) b = "file" + (h + 1), d = 0 == e ? "Light" : "Dark", c = "
", e ^= 1, $("#Board").append(c) } function InitBoardSquares() { var a, b, c, d, e, f, g = 1; for (d = RANKS.RANK_8; d >= RANKS.RANK_1; d--) for (g ^= 1, a = "rank" + (d + 1), e = FILES.FILE_A; e <= FILES.FILE_H; e++) b = "file" + (e + 1), f = 0 == g ? "Light" : "Dark", g ^= 1, c = "
", $("#Board").append(c) } function init() { console.log("גם זה יעבור"), InitFilesRanksBrd(), InitHashKeys(), InitSq120To64(), InitBoardVars(), InitMvvLva(), InitBoardSquares() }