Earthcomputer

chess.js

Jul 13th, 2018 (edited)
545
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. function log(msg) {
  2.     //channel.message(msg);
  3. }
  4.  
  5. var PAWN = function(src, dst, game, simulate) {
  6.     log("Evaluating move function for pawn at " + src.x + ", " + src.y + " to " + dst.x + ", " + dst.y);
  7.     var board = game.board;
  8.     var black = getPiece(board, src).black;
  9.     var dir = black ? -1 : 1;
  10.     if (dst.y != src.y + dir) {
  11.         if (dst.x != src.x)
  12.             return false;
  13.         if (dst.y != src.y + dir * 2)
  14.             return false;
  15.         if (src.y != (black ? 6 : 1))
  16.             return false;
  17.         if (getPiece(board, add(src, 0, dir)) !== null || getPiece(board, dst) !== null)
  18.             return false;
  19.         if (!simulate)
  20.             game.enPassantSquare = dst;
  21.     } else if (dst.x == src.x) {
  22.         if (getPiece(board, add(src, 0, dir)) !== null)
  23.             return false;
  24.         if (!simulate)
  25.             game.enPassantSquare = null;
  26.     } else {
  27.         if (Math.abs(dst.x - src.x) != 1)
  28.             return false;
  29.         if (getPiece(board, dst) === null) {
  30.             var eps = add(dst, 0, -dir);
  31.             if (game.enPassantSquare === null || game.enPassantSquare.x != eps.x || game.enPassantSquare.y != eps.y)
  32.                 return false;
  33.             if (!simulate)
  34.                 setPiece(board, eps, null);
  35.         } else {
  36.             if (getPiece(board, dst).black == black)
  37.                 return false;
  38.         }
  39.         if (!simulate)
  40.             game.enPassantSquare = null;
  41.     }
  42.     if (!simulate) {
  43.         var piece = getPiece(board, src);
  44.         if (dst.y == 0 || dst.y == 7)
  45.             piece.type = game.promotePiece;
  46.         setPiece(board, dst, piece);
  47.         setPiece(board, src, null);
  48.     }
  49.     return true;
  50. };
  51.  
  52. var ROOK = function(src, dst, game, simulate) {
  53.     log("Evaluating move function for rook at " + src.x + ", " + src.y + " to " + dst.x + ", " + dst.y);
  54.     var board = game.board;
  55.     if (src.y == dst.y) {
  56.         var minX = Math.min(src.x, dst.x);
  57.         var maxX = Math.max(src.x, dst.x);
  58.         for (var x = minX + 1; x < maxX; x++) {
  59.             if (getPiece(board, {x: x, y: src.y}) !== null)
  60.                 return false;
  61.         }
  62.     } else if (src.x == dst.x) {
  63.         var minY = Math.min(src.y, dst.y);
  64.         var maxY = Math.max(src.y, dst.y);
  65.         for (var y = minY + 1; y < maxY; y++) {
  66.             if (getPiece(board, {x: src.x, y: y}) !== null)
  67.                 return false;
  68.         }
  69.     } else {
  70.         return false;
  71.     }
  72.     if (getPiece(board, dst) !== null && getPiece(board, dst).black == getPiece(board, src).black)
  73.         return false;
  74.     if (!simulate) {
  75.         setPiece(board, dst, getPiece(board, src));
  76.         setPiece(board, src, null);
  77.         if (src.x == 0) {
  78.             if (src.y == 0)
  79.                 game.castleState[0].queenside = false;
  80.             else if (src.y == 7)
  81.                 game.castleState[1].queenside = false;
  82.         } else if (src.x == 7) {
  83.             if (src.y == 0)
  84.                 game.castleState[0].kingside = false;
  85.             else if (src.y == 7)
  86.                 game.castleState[1].kingside = false;
  87.         }
  88.         game.enPassantSquare = null;
  89.     }
  90.     return true;
  91. };
  92.  
  93. var KNIGHT = function(src, dst, game, simulate) {
  94.     log("Evaluating move function for knight at " + src.x + ", " + src.y + " to " + dst.x + ", " + dst.y);
  95.     var board = game.board;
  96.     if (src.x == dst.x || src.y == dst.y)
  97.         return false;
  98.     if (Math.abs(dst.x - src.x) + Math.abs(dst.y - src.y) != 3)
  99.         return false;
  100.     if (getPiece(board, dst) !== null && getPiece(board, dst).black == getPiece(board, src).black)
  101.         return false;
  102.     if (!simulate) {
  103.         setPiece(board, dst, getPiece(board, src));
  104.         setPiece(board, src, null);
  105.         game.enPassantSquare = null;
  106.     }
  107.     return true;
  108. };
  109.  
  110. var BISHOP = function(src, dst, game, simulate) {
  111.     log("Evaluating move function for bishop at " + src.x + ", " + src.y + " to " + dst.x + ", " + dst.y);
  112.     var board = game.board;
  113.     var black = getPiece(board, src).black;
  114.     var dx = dst.x - src.x;
  115.     var dy = dst.y - src.y;
  116.     if (Math.abs(dx) != Math.abs(dy))
  117.         return false;
  118.     if (dst.x == src.x)
  119.         return false;
  120.     var dirX = dx < 0 ? -1 : 1;
  121.     var dirY = dy < 0 ? -1 : 1;
  122.     for (var x = src.x + dirX, y = src.y + dirY; x != dst.x; x += dirX, y += dirY) {
  123.         if (getPiece(board, {x: x, y: y}) !== null)
  124.             return false;
  125.     }
  126.     if (getPiece(board, dst) !== null && getPiece(board, dst).black == black)
  127.         return false;
  128.     if (!simulate) {
  129.         setPiece(board, dst, getPiece(board, src));
  130.         setPiece(board, src, null);
  131.         game.enPassantSquare = null;
  132.     }
  133.     return true;
  134. };
  135.  
  136. var QUEEN = function(src, dst, game, simulate) {
  137.     log("Evaluating move function for queen at " + src.x + ", " + src.y + " to " + dst.x + ", " + dst.y);
  138.     var board = game.board;
  139.     var black = getPiece(board, src).black;
  140.     var dx = dst.x - src.x;
  141.     var dy = dst.y - src.y;
  142.     if (Math.abs(dx) != Math.abs(dy) && dx != 0 && dy != 0)
  143.         return false;
  144.     var dirX = dx < 0 ? -1 : dx > 0 ? 1 : 0;
  145.     var dirY = dy < 0 ? -1 : dy > 0 ? 1 : 0;
  146.     for (var x = src.x + dirX, y = src.y + dirY; x != dst.x || y != dst.y; x += dirX, y += dirY) {
  147.         if (getPiece(board, {x: x, y: y}) !== null)
  148.             return false;
  149.     }
  150.     if (getPiece(board, dst) !== null && getPiece(board, dst).black == black)
  151.         return false;
  152.     if (!simulate) {
  153.         setPiece(board, dst, getPiece(board, src));
  154.         setPiece(board, src, null);
  155.         game.enPassantSquare = null;
  156.     }
  157.     return true;
  158. };
  159.  
  160. var KING = function(src, dst, game, simulate) {
  161.     log("Evaluating move function for king at " + src.x + ", " + src.y + " to " + dst.x + ", " + dst.y);
  162.     var board = game.board;
  163.     var black = getPiece(board, src).black;
  164.     var dx = dst.x - src.x;
  165.     var dy = dst.y - src.y;
  166.     if (Math.abs(dy) > 1)
  167.         return false;
  168.     if (Math.abs(dx) > 2)
  169.         return false;
  170.     if (getPiece(board, dst) !== null && getPiece(board, dst).black == black)
  171.         return false;
  172.     if (Math.abs(dx) == 2) {
  173.         if (dy != 0)
  174.             return false;
  175.         if (dx < 0) {
  176.             if (!game.castleState[black ? 1 : 0].queenside)
  177.                 return false;
  178.             if (getPiece(board, {x: 1, y: src.y}) !== null)
  179.                 return false;
  180.         } else {
  181.             if (!game.castleState[black ? 1 : 0].kingside)
  182.                 return false;
  183.         }
  184.         if (getPiece(board, dst) !== null)
  185.             return false;
  186.         if (getPiece(board, add(src, dx / 2, 0)) !== null)
  187.             return false;
  188.         if (isPieceAttacked(game, src))
  189.             return false;
  190.         var piece = getPiece(board, src);
  191.         setPiece(board, add(src, dx / 2, 0), piece);
  192.         setPiece(board, src, null);
  193.         var attacked = isPieceAttacked(game, add(src, dx / 2, 0));
  194.         setPiece(board, add(src, dx / 2, 0), null);
  195.         setPiece(board, src, piece);
  196.         if (attacked) {
  197.             return false;
  198.         }
  199.         if (!simulate) {
  200.             setPiece(board, {x: dx < 0 ? 0 : 7, y: src.y}, null);
  201.             setPiece(board, add(src, dx / 2, 0), {type: "r", black: black});
  202.         }
  203.     }
  204.     if (!simulate) {
  205.         setPiece(board, dst, getPiece(board, src));
  206.         setPiece(board, src, null);
  207.         game.castleState[black ? 1 : 0].queenside = false;
  208.         game.castleState[black ? 1 : 0].kingside = false;
  209.         game.enPassantSquare = null;
  210.     }
  211.     return true;
  212. };
  213.  
  214. var pieceMoveFuncs = {
  215.     "p": PAWN,
  216.     "r": ROOK,
  217.     "n": KNIGHT,
  218.     "b": BISHOP,
  219.     "q": QUEEN,
  220.     "k": KING
  221. };
  222.  
  223. function add(posA, x, y) {
  224.     return {x: posA.x + x, y: posA.y + y};
  225. }
  226.  
  227. function newBoard() {
  228.     var board = {tiles: []};
  229.     for (var x = 0; x < 8; x++) {
  230.         board.tiles[x] = [];
  231.         for (var y = 0; y < 8; y++) {
  232.             board.tiles[x][y] = null;
  233.         }
  234.     }
  235.     return board;
  236. }
  237. function checkBounds(pos) {
  238.     if (pos.x < 0 || pos.y < 0 || pos.x >= 8 || pos.y >= 8) {
  239.         channel.message(new Error().stack);
  240.         throw (pos.x + ", " + pos.y + " out of bounds");
  241.     }
  242. }
  243. function getPiece(board, pos) {
  244.     checkBounds(pos);
  245.     return board.tiles[pos.x][pos.y];
  246. }
  247. function setPiece(board, pos, piece) {
  248.     checkBounds(pos);
  249.     board.tiles[pos.x][pos.y] = piece;
  250. }
  251.  
  252. function newGame(userWhite, userBlack) {
  253.     var game = {};
  254.     game.userWhite = userWhite;
  255.     game.userBlack = userBlack;
  256.     game.blackToMove = false;
  257.     game.castleState = [{queenside: true, kingside: true}, {queenside: true, kingside: true}]
  258.     game.enPassantSquare = null;
  259.     game.board = newBoard();
  260.     var majors = ["r", "n", "b", "q", "k", "b", "n", "r"];
  261.     for (var x = 0; x < 8; x++) {
  262.         setPiece(game.board, {x: x, y: 0}, {type: majors[x], black: false});
  263.         setPiece(game.board, {x: x, y: 1}, {type: "p", black: false});
  264.         setPiece(game.board, {x: x, y: 6}, {type: "p", black: true});
  265.         setPiece(game.board, {x: x, y: 7}, {type: majors[x], black: true});
  266.     }
  267.     game.promotePiece = null;
  268.     game.lastMove = null;
  269.     return game;
  270. }
  271. function isPieceAttacked(game, pos) {
  272.     log("isPieceAttacked(" + pos.x + ", " + pos.y + ")");
  273.     if (getPiece(game.board, pos) === null)
  274.         return false;
  275.     var black = getPiece(game.board, pos).black;
  276.     for (x = 0; x < 8; x++) {
  277.         for (y = 0; y < 8; y++) {
  278.             var piece = getPiece(game.board, {x: x, y: y});
  279.             if (piece !== null && piece.black != black) {
  280.                 if (pieceMoveFuncs[piece.type]({x: x, y: y}, pos, game, true))
  281.                     return true;
  282.             }
  283.         }
  284.     }
  285.     return false;
  286. }
  287.  
  288. var games = storage.get("chessGames");
  289. games = JSON.parse(games);
  290. if (games === null)
  291.     games = [];
  292.  
  293. var options = storage.get("chessOptions");
  294. options = JSON.parse(options);
  295. if (options === null)
  296.     options = {};
  297. function getOptions(user) {
  298.     if (user in options)
  299.         return options[user];
  300.    
  301.     var opts = {};
  302.     opts.flip = true;
  303.     options[user] = opts;
  304.     saveOptions();
  305.     return opts;
  306. }
  307. function saveOptions() {
  308.     storage.set("chessOptions", JSON.stringify(options));
  309. }
  310.  
  311. function saveGames() {
  312.     storage.set("chessGames", JSON.stringify(games));
  313. }
  314.  
  315. function userName(user) {
  316.     var userObj = guild.getUser(user);
  317.     if (userObj.getNickname() === null)
  318.         return userObj.getName();
  319.     else
  320.         return userObj.getNickname();
  321. }
  322. function makePing(user) {
  323.     var userObj = guild.getUser(user);
  324.     if (userObj.getNickname() === null)
  325.         return "<@" + user + ">";
  326.     else
  327.         return "<@!" + user + ">";
  328. }
  329.  
  330. function parseMoveWithContext(moveStr, game) {
  331.     var match;
  332.     try {
  333.         match = parseMove(moveStr);
  334.     } catch (e) {
  335.         return {error: "Invalid syntax"};
  336.     }
  337.    
  338.     var ret = {};
  339.     if ("promotePiece" in match) {
  340.         ret.promotePiece = match.promotePiece.toLowerCase();
  341.     }
  342.     if ("queensideCastle" in match) {
  343.         if (!game.castleState[game.blackToMove ? 1 : 0].queenside) {
  344.             return {error: "Illegal move"};
  345.         }
  346.         ret.fromX = 4;
  347.         ret.fromY = game.blackToMove ? 7 : 0;
  348.         ret.toX = 2;
  349.         ret.toY = ret.fromY;
  350.     } else if ("kingsideCastle" in match) {
  351.         if (!game.castleState[game.blackToMove ? 1 : 0].kingside) {
  352.             return {error: "Illegal move"};
  353.         }
  354.         ret.fromX = 4;
  355.         ret.fromY = game.blackToMove ? 7 : 0;
  356.         ret.toX = 6;
  357.         ret.toY = ret.fromY;
  358.     } else if ("fromFile" in match && "fromRank" in match) {
  359.         ret.fromX = match.fromFile.toLowerCase().charCodeAt(0) - 97;
  360.         ret.fromY = parseInt(match.fromRank) - 1;
  361.         ret.toX = match.toFile.toLowerCase().charCodeAt(0) - 97;
  362.         ret.toY = parseInt(match.toRank) - 1;
  363.     } else {
  364.         ret.toX = match.toFile.toLowerCase().charCodeAt(0) - 97;
  365.         ret.toY = parseInt(match.toRank) - 1;
  366.         var dst = {x: ret.toX, y: ret.toY};
  367.         var fromX = ("fromFile" in match) ? match.fromFile.toLowerCase().charCodeAt(0) - 97 : -1;
  368.         var fromY = ("fromRank" in match) ? parseInt(match.fromRank) - 1 : -1;
  369.        
  370.         var foundPiece = false;
  371.         for (var x = 0; x < 8; x++) {
  372.             if (fromX == -1 || x == fromX) {
  373.                 for (var y = 0; y < 8; y++) {
  374.                     if (fromY == -1 || y == fromY) {
  375.                         var pos = {x: x, y: y};
  376.                         var piece = getPiece(game.board, pos);
  377.                         if (piece !== null && piece.black == game.blackToMove && piece.type == match.piece.toLowerCase()) {
  378.                             if (pieceMoveFuncs[match.piece.toLowerCase()](pos, dst, game, true)) {
  379.                                 if (foundPiece) {
  380.                                     return {error: "Ambiguous move"};
  381.                                 }
  382.                                 log("Found piece at " + x + ", " + y);
  383.                                 foundPiece = true;
  384.                                 ret.fromX = x;
  385.                                 ret.fromY = y;
  386.                             }
  387.                         }
  388.                     }
  389.                 }
  390.             }
  391.         }
  392.         if (!foundPiece)
  393.             return {error: "Invalid move"};
  394.     }
  395.    
  396.     var piece = getPiece(game.board, {x: ret.fromX, y: ret.fromY});
  397.     if (piece !== null && piece.type == "p" && (ret.toY == 0 || ret.toY == 7))
  398.         if (!("promotePiece" in ret))
  399.             return {error: "Don't know what to promote to"};
  400.    
  401.     return ret;
  402. }
  403.  
  404. function getGame(user) {
  405.     for (var i = 0; i < games.length; i++) {
  406.         var game = games[i];
  407.         if (game.userWhite == user || game.userBlack == user)
  408.             return game;
  409.     }
  410.     return null;
  411. }
  412.  
  413. function startGame(userA, userB) {
  414.     if (userA == userB) {
  415.         channel.message("You cannot play yourself!");
  416.         return;
  417.     }
  418.     if (getGame(userA) !== null) {
  419.         channel.message(userName(userA) + " is already in a game");
  420.         return;
  421.     }
  422.     if (getGame(userB) !== null) {
  423.         channel.message(userName(userB) + " is already in a game");
  424.         return;
  425.     }
  426.    
  427.     var game;
  428.     if (Math.random() < 0.5)
  429.         game = newGame(userA, userB);
  430.     else
  431.         game = newGame(userB, userA);
  432.     games.push(game);
  433.     saveGames();
  434.     printBoard(game, true, game.userWhite);
  435. }
  436.  
  437. function resignGame(resigner) {
  438.     var game = getGame(resigner);
  439.     if (game === null) {
  440.         channel.message("You aren't in a game");
  441.         return;
  442.     }
  443.    
  444.     var opponent;
  445.     if (resigner == game.userWhite)
  446.         opponent = game.userBlack;
  447.     else
  448.         opponent = game.userWhite;
  449.     games.splice(games.indexOf(game), 1);
  450.     saveGames();
  451.     channel.message(userName(resigner) + " resigned, " + makePing(opponent) + " wins!");
  452. }
  453.  
  454. function doMove(user, arg) {
  455.     var game = getGame(user);
  456.     if (game === null) {
  457.         channel.message("You aren't in a game");
  458.         return;
  459.     }
  460.    
  461.     var black;
  462.     var opponent;
  463.     if (user == game.userWhite) {
  464.         black = false;
  465.         opponent = game.userBlack;
  466.     } else {
  467.         black = true;
  468.         opponent = game.userWhite;
  469.     }
  470.     if (black != game.blackToMove) {
  471.         channel.message("It's not your turn");
  472.         return;
  473.     }
  474.    
  475.     var move = parseMoveWithContext(arg, game);
  476.     if ("error" in move) {
  477.         channel.message(move.error);
  478.         return;
  479.     }
  480.    
  481.     if ("promotePiece" in move) {
  482.         game.promotePiece = move.promotePiece;
  483.     } else {
  484.         game.promotePiece = null;
  485.     }
  486.    
  487.     var src = {x: move.fromX, y: move.fromY};
  488.     var dst = {x: move.toX, y: move.toY};
  489.     var piece = getPiece(game.board, src);
  490.     if (piece === null || piece.black != black) {
  491.         channel.message("Invalid piece");
  492.         return;
  493.     }
  494.     if (!pieceMoveFuncs[piece.type](src, dst, game, false)) {
  495.         channel.message("Invalid move");
  496.         return;
  497.     }
  498.    
  499.     if (getCheckedKing(game)) {
  500.         channel.message("Illegal move");
  501.         return;
  502.     }
  503.    
  504.     game.lastMove = {fromX: src.x, fromY: src.y, toX: dst.x, toY: dst.y};
  505.    
  506.     game.blackToMove = !game.blackToMove;
  507.    
  508.     var winState = detectWinState(game);
  509.    
  510.     printBoard(game, winState == 0, opponent);
  511.    
  512.     if (winState == 1) { // checkmate
  513.         channel.message("Checkmate! " + userName(user) + " wins! " + makePing(opponent) + " lost.");
  514.     } else if (winState == 2) { // stalemate
  515.         channel.message("Stalemate. Draw between " + userName(user) + " and " + makePing(opponent) + ".");
  516.     }
  517.     if (winState != 0) {
  518.         games.splice(games.indexOf(game), 1);
  519.     }
  520.    
  521.     saveGames();
  522.    
  523. }
  524.  
  525. function getCheckedKing(game) {
  526.     var kingPos;
  527.     outerLoop:
  528.     for (var x = 0; x < 8; x++) {
  529.         for (var y = 0; y < 8; y++) {
  530.             kingPos = {x: x, y: y};
  531.             var piece = getPiece(game.board, kingPos);
  532.             if (piece !== null && piece.black == game.blackToMove && piece.type == "k") {
  533.                 break outerLoop;
  534.             }
  535.         }
  536.     }
  537.     return isPieceAttacked(game, kingPos) ? kingPos : null;
  538. }
  539.  
  540. // 0 for none, 1 for checkmate, 2 for stalemate
  541. function detectWinState(game) {
  542.     for (var fromX = 0; fromX < 8; fromX++) {
  543.         for (var fromY = 0; fromY < 8; fromY++) {
  544.             var src = {x: fromX, y: fromY};
  545.             var piece = getPiece(game.board, src);
  546.             if (piece !== null && piece.black == game.blackToMove) {
  547.                 for (var toX = 0; toX < 8; toX++) {
  548.                     for (var toY = 0; toY < 8; toY++) {
  549.                         var dst = {x: toX, y: toY};
  550.                         if (pieceMoveFuncs[piece.type](src, dst, game, true)) {
  551.                             var gameCopy = JSON.parse(JSON.stringify(game));
  552.                             pieceMoveFuncs[piece.type](src, dst, gameCopy, false);
  553.                             if (!getCheckedKing(gameCopy))
  554.                                 return 0;
  555.                         }
  556.                     }
  557.                 }
  558.             }
  559.         }
  560.     }
  561.     return getCheckedKing(game) ? 1 : 2;
  562. }
  563.  
  564. function printBoard(game, includeToMoveMessage, user) {
  565.     var fen = "";
  566.     for (var y = 7; y >= 0; y--) {
  567.         var emptySquaresCount = 0;
  568.         for (var x = 0; x < 8; x++) {
  569.             var piece = getPiece(game.board, {x: x, y: y});
  570.             if (piece === null) {
  571.                 emptySquaresCount++;
  572.             } else {
  573.                 if (emptySquaresCount != 0)
  574.                     fen += emptySquaresCount;
  575.                 emptySquaresCount = 0;
  576.                 if (piece.black)
  577.                     fen += piece.type;
  578.                 else
  579.                     fen += piece.type.toUpperCase();
  580.             }
  581.         }
  582.         if (emptySquaresCount != 0)
  583.             fen += emptySquaresCount;
  584.         if (y != 0)
  585.             fen += "/";
  586.     }
  587.    
  588.     var url = "https://backscattering.de/web-boardimage/board.png";
  589.     url += "?fen=" + fen;
  590.     if (getOptions(user).flip)
  591.         url += "&orientation=" + (user == game.userBlack ? "black" : "white");
  592.     if (game.lastMove != null) {
  593.         url += "&lastMove=";
  594.         url += String.fromCharCode(97 + game.lastMove.fromX);
  595.         url += 1 + game.lastMove.fromY;
  596.         url += String.fromCharCode(97 + game.lastMove.toX);
  597.         url += 1 + game.lastMove.toY;
  598.     }
  599.     var kingPos = getCheckedKing(game);
  600.     if (kingPos != null) {
  601.         url += "&check=";
  602.         url += String.fromCharCode(97 + kingPos.x);
  603.         url += 1 + kingPos.y;
  604.     }
  605.    
  606.     channel.message(embeds.newEmbed().setImage(url).build());
  607.    
  608.     if (includeToMoveMessage) {
  609.         if (game.blackToMove)
  610.             channel.message("Black to move " + makePing(game.userBlack));
  611.         else
  612.             channel.message("White to move " + makePing(game.userWhite));
  613.     }
  614. }
  615.  
  616. function setOption(user, opt, value) {
  617.     var opts = getOptions(user);
  618.     if (opt in opts) {
  619.         var valid = true;
  620.         if (value == "true") {
  621.             opts[opt] = true;
  622.         } else if (value == "false") {
  623.             opts[opt] = false;
  624.         } else {
  625.             channel.message("Invalid option value");
  626.             valid = false;
  627.         }
  628.         if (valid) {
  629.             channel.message("Option \"" + opt + "\" set to " + value + " for " + userName(user));
  630.         }
  631.     } else {
  632.         channel.message("Invalid option name. Type $chess help for a list of accepted options.");
  633.     }
  634.     saveOptions();
  635. }
  636.  
  637. function printUsage() {
  638.     var usage = "```\n"
  639.     + "$chess start <@opponent>\n"
  640.     + "$chess [move] <theMove>\n"
  641.     + "$chess resign\n"
  642.     + "$chess board\n"
  643.     + "$chess help\n"
  644.     + "$chess option <flip> <value>\n"
  645.     + "```";
  646.     channel.message(usage);
  647. }
  648.  
  649. var arg = message.getContent();
  650. var args = arg.split(" ");
  651. if (args.length < 2) {
  652.     printUsage();
  653. } else {
  654.     arg = args[1];
  655.     if (arg == "start") {
  656.         if (args.length != 3) {
  657.             printUsage();
  658.         } else {
  659.             var regex = /^<@!?(\d+)>$/
  660.             var match = regex.exec(args[2]);
  661.             if (match === null) {
  662.                 printUsage();
  663.             } else {
  664.                 startGame(message.getUser().getID(), match[1]);
  665.             }
  666.         }
  667.     } else if (arg == "resign") {
  668.         resignGame(message.getUser().getID());
  669.     } else if (arg == "help" || arg == "?") {
  670.         printUsage();
  671.     } else if (arg == "board") {
  672.         var game = getGame(message.getUser().getID());
  673.         if (game === null) {
  674.             channel.message("You aren't in a game");
  675.         } else {
  676.             printBoard(game, true, message.getUser().getID());
  677.         }
  678.     } else if (arg == "option") {
  679.         if (args.length != 4)
  680.             printUsage();
  681.         setOption(message.getUser().getID(), args[2], args[3]);
  682.     } else {
  683.         var move;
  684.         if (arg == "move")
  685.             move = args.slice(2).join(" ");
  686.         else
  687.             move = args.slice(1).join(" ");
  688.         doMove(message.getUser().getID(), move);
  689.     }
  690. }
  691.  
  692. // ====== MOVE PARSER ====== //
  693.  
  694. function parsePeek(parser) {
  695.     return parser.input.charAt(parser.pos);
  696. }
  697.  
  698. function parsePeekN(parser, n) {
  699.     return parser.input.charAt(parser.pos + n - 1);
  700. }
  701.  
  702. function parseConsumeAny(parser) {
  703.     return parser.input.charAt(parser.pos++);
  704. }
  705.  
  706. function parseConsume(parser, expected) {
  707.     var token = parseConsumeAny(parser);
  708.     if (token != expected)
  709.         parseError();
  710.     return token;
  711. }
  712.  
  713. function parseError() {
  714.     throw 1;
  715. }
  716.  
  717. function parseMove(input) {
  718.     var parser = {input: input, pos: 0, result: {}};
  719.     try {
  720.         parseStandardMove(parser);
  721.         parseSuffix(parser);
  722.         parseConsume(parser, "");
  723.     } catch (e) {
  724.         parser.pos = 0;
  725.         parser.result = {};
  726.         parseSimpleMove(parser);
  727.         parseSuffix(parser);
  728.         parseConsume(parser, "");
  729.     }
  730.     return parser.result;
  731. }
  732.  
  733. function parseStandardMove(parser) {
  734.     var next = parsePeek(parser);
  735.     if (next == "0") {
  736.         parseCastle(parser);
  737.     } else if (["p", "P", "r", "R", "n", "N", "q", "Q", "k", "K"].indexOf(next) != -1) {
  738.         parseNormalMove(parser);
  739.     } else {
  740.         var nextNext = parsePeekN(parser, 2);
  741.         if (nextNext == "x") {
  742.             if (next == "B") {
  743.                 parseBishopMove(parser);
  744.             } else {
  745.                 parsePawnMove(parser);
  746.             }
  747.         } else {
  748.             if ((next == "b" || next == "B") && ["1", "2", "3", "4", "5", "6", "7", "8"].indexOf(nextNext) == -1) {
  749.                 parseBishopMove(parser);
  750.             } else {
  751.                 parsePawnMove(parser);
  752.             }
  753.         }
  754.     }
  755. }
  756.  
  757. function parseCastle(parser) {
  758.     parseConsume(parser, "0");
  759.     parseConsume(parser, "-");
  760.     parseConsume(parser, "0");
  761.     if (parsePeek(parser) == "-") {
  762.         parseConsume(parser, "-");
  763.         parseConsume(parser, "0");
  764.         parser.result.queensideCastle = true;
  765.     } else {
  766.         parser.result.kingsideCastle = true;
  767.     }
  768. }
  769.  
  770. function parseNormalMove(parser) {
  771.     parser.result.piece = parseConsumeAny(parser);
  772.     if (isNextRank(parser, 1)) {
  773.         parser.result.fromRank = parseRank(parser);
  774.     } else if (isNextFile(parser, 1)) {
  775.         if (isNextFile(parser, 2) || parsePeekN(parser, 2) == "x") {
  776.             parser.result.fromFile = parseFile(parser);
  777.         }
  778.     }
  779.     if (parsePeek(parser) == "x")
  780.         parseConsume(parser, "x");
  781.     parser.result.toFile = parseFile(parser);
  782.     parser.result.toRank = parseRank(parser);
  783. }
  784.  
  785. function parseBishopMove(parser) {
  786.     log("Parsing bishop move")
  787.     if (parsePeekN(parser, 2) == "x" || parsePeekN(parser, 3) == "x") {
  788.         parser.result.piece = parseConsume(parser, "B");
  789.         if (isNextFile(parser, 1))
  790.             parser.result.fromFile = parseFile(parser);
  791.         parseConsume(parser, "x");
  792.     } else {
  793.         var token = parseConsumeAny(parser);
  794.         if (token != "b" && token != "B") {
  795.             parseError();
  796.         }
  797.         parser.result.piece = token;
  798.     }
  799.     parser.result.toFile = parseFile(parser);
  800.     parser.result.toRank = parseRank(parser);
  801. }
  802.  
  803. function parsePawnMove(parser) {
  804.     parser.result.piece = "p";
  805.     if (parsePeekN(parser, 2) == "x") {
  806.         parser.result.fromFile = parseFile(parser);
  807.         parseConsume(parser, "x");
  808.     }
  809.     parser.result.toFile = parseFile(parser);
  810.     parser.result.toRank = parseRank(parser);
  811. }
  812.  
  813. function isNextFile(parser, n) {
  814.     var token = parsePeekN(parser, n);
  815.     return ["a", "b", "c", "d", "e", "f", "g", "h"].indexOf(token.toLowerCase()) != -1;
  816. }
  817.  
  818. function isNextRank(parser, n) {
  819.     var token = parsePeekN(parser, n);
  820.     return ["1", "2", "3", "4", "5", "6", "7", "8"].indexOf(token) != -1;
  821. }
  822.  
  823. function parseFile(parser) {
  824.     if (isNextFile(parser, 1)) {
  825.         return parseConsumeAny(parser);
  826.     } else {
  827.         parseError();
  828.     }
  829. }
  830.  
  831. function parseRank(parser) {
  832.     if (isNextRank(parser, 1)) {
  833.         return parseConsumeAny(parser);
  834.     } else {
  835.         parseError();
  836.     }
  837. }
  838.  
  839. function parseSimpleMove(parser) {
  840.     parser.result.fromFile = parseFile(parser);
  841.     parser.result.fromRank = parseRank(parser);
  842.     if (parsePeek(parser) == "x")
  843.         parseConsume(parser, "x");
  844.     else if (parsePeek(parser) == "-")
  845.         parseConsume(parser, "-");
  846.     else if (parsePeek(parser) == " ")
  847.         parseConsume(parser, " ");
  848.     parser.result.toFile = parseFile(parser);
  849.     parser.result.toRank = parseRank(parser);
  850. }
  851.  
  852. function parseSuffix(parser) {
  853.     if (parsePeek(parser) == "=") {
  854.         parseConsume(parser, "=");
  855.         var piece = parseConsumeAny(parser);
  856.         if (["r", "n", "b", "q"].indexOf(piece.toLowerCase()) == -1)
  857.             parseError();
  858.         parser.result.promotePiece = piece;
  859.     }
  860.    
  861.     if (parsePeek(parser) == "#") {
  862.         parseConsume(parser, "#");
  863.     } else if (parsePeek(parser) == "+") {
  864.         parseConsume(parser, "+");
  865.         if (parsePeek(parser) == "+")
  866.             parseConsume(parser, "+");
  867.     }
  868. }
Add Comment
Please, Sign In to add comment