UNPKG

jungle-board-service

Version:
609 lines (608 loc) 23.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getAllMoves = exports.makeMove = exports.getWinner = exports.getPlayersPieces = exports.getPiecePossibleMoves = exports.getRatPossibleMoves = exports.getCatPossibleMoves = exports.getDogPossibleMoves = exports.getWolfPossibleMoves = exports.getLeopardPossibleMoves = exports.getTigerPossibleMoves = exports.getLionPossibleMoves = exports.getElephantPossibleMoves = exports.getSwimPossibleMoves = exports.getFlyAnimalPossibleMoves = exports.getLandAnimalPossibleMoves = exports.canSwimAnimalMove = exports.canFlyAnimalMove = exports.canLandAnimalMove = exports.canMoveHelper = exports.isRatOnWay = exports.isOwnTrap = exports.isOwnChessPiece = exports.noChessPiece = exports.isOpponent = exports.isOwnDen = exports.isLand = exports.isBDen = exports.isWDen = exports.isInBTrap = exports.isInWTrap = exports.isInRiver = exports.isOutBoard = exports.getOpponentTurn = exports.getPieceKind = exports.getAnimalLevel = exports.getInitialBoard = exports.getEmptyBoard = exports.PieceName = exports.Animal = exports.Structure = exports.PlayerSymbol = exports.RiverPos = exports.WhiteDen = exports.BlackDen = exports.WhiteTraps = exports.BlackTraps = exports.COLS = exports.ROWS = void 0; const dequal_1 = require("dequal"); const klona_1 = require("klona"); exports.ROWS = 9; exports.COLS = 7; exports.BlackTraps = [ { row: 8, col: 2 }, { row: 7, col: 3 }, { row: 8, col: 4 }, ]; exports.WhiteTraps = [ { row: 0, col: 2 }, { row: 1, col: 3 }, { row: 0, col: 4 }, ]; exports.BlackDen = { row: 8, col: 3 }; exports.WhiteDen = { row: 0, col: 3 }; exports.RiverPos = [ { row: 3, col: 1 }, { row: 3, col: 2 }, { row: 3, col: 4 }, { row: 3, col: 5 }, { row: 4, col: 1 }, { row: 4, col: 2 }, { row: 4, col: 4 }, { row: 4, col: 5 }, { row: 5, col: 1 }, { row: 5, col: 2 }, { row: 5, col: 4 }, { row: 5, col: 5 }, ]; exports.PlayerSymbol = { B: 'B', W: 'W', }; exports.Structure = { Den: 'Den', Trap: 'Trap', }; exports.Animal = { Elephant: 'Elephant', Lion: 'Lion', Tiger: 'Tiger', Leopard: 'Leopard', Wolf: 'Wolf', Dog: 'Dog', Cat: 'Cat', Rat: 'Rat', }; exports.PieceName = { L: 'L', R: 'R', BDen: 'BDen', BTrap: 'BTrap', BElephant: 'BElephant', BLion: 'BLion', BTiger: 'BTiger', BLeopard: 'BLeopard', BWolf: 'BWolf', BDog: 'BDog', BCat: 'BCat', BRat: 'BRat', WDen: 'WDen', WTrap: 'WTrap', WElephant: 'WElephant', WLion: 'WLion', WTiger: 'WTiger', WLeopard: 'WLeopard', WWolf: 'WWolf', WDog: 'WDog', WCat: 'WCat', WRat: 'WRat', }; const { L, R } = exports.PieceName; const { BDen, BTrap, BElephant, BLion, BTiger, BLeopard, BWolf, BDog, BCat, BRat } = exports.PieceName; const { WDen, WTrap, WElephant, WLion, WTiger, WLeopard, WWolf, WDog, WCat, WRat } = exports.PieceName; function getEmptyBoard() { return [ [L, L, WTrap, WDen, WTrap, L, L], [L, L, L, WTrap, L, L, L], [L, L, L, L, L, L, L], [L, R, R, L, R, R, L], [L, R, R, L, R, R, L], [L, R, R, L, R, R, L], [L, L, L, L, L, L, L], [L, L, L, BTrap, L, L, L], [L, L, BTrap, BDen, BTrap, L, L], ]; } exports.getEmptyBoard = getEmptyBoard; function getInitialBoard() { return [ [WLion, L, WTrap, WDen, WTrap, L, WTiger], [L, WDog, L, WTrap, L, WCat, L], [WRat, L, WLeopard, L, WWolf, L, WElephant], [L, R, R, L, R, R, L], [L, R, R, L, R, R, L], [L, R, R, L, R, R, L], [BElephant, L, BWolf, L, BLeopard, L, BRat], [L, BCat, L, BTrap, L, BDog, L], [BTiger, L, BTrap, BDen, BTrap, L, BLion], ]; } exports.getInitialBoard = getInitialBoard; function getAnimalLevel(animal) { switch (animal) { case exports.Animal.Elephant: return 7; case exports.Animal.Lion: return 6; case exports.Animal.Tiger: return 5; case exports.Animal.Leopard: return 4; case exports.Animal.Dog: return 3; case exports.Animal.Wolf: return 2; case exports.Animal.Cat: return 1; case exports.Animal.Rat: return 0; default: return -1; } } exports.getAnimalLevel = getAnimalLevel; function getPieceKind(piece) { switch (piece) { case exports.PieceName.BElephant: case exports.PieceName.WElephant: return exports.Animal.Elephant; case exports.PieceName.BLion: case exports.PieceName.WLion: return exports.Animal.Lion; case exports.PieceName.BTiger: case exports.PieceName.WTiger: return exports.Animal.Tiger; case exports.PieceName.BLeopard: case exports.PieceName.WLeopard: return exports.Animal.Leopard; case exports.PieceName.BWolf: case exports.PieceName.WWolf: return exports.Animal.Wolf; case exports.PieceName.BDog: case exports.PieceName.WDog: return exports.Animal.Dog; case exports.PieceName.BCat: case exports.PieceName.WCat: return exports.Animal.Cat; case exports.PieceName.BRat: case exports.PieceName.WRat: return exports.Animal.Rat; default: return ''; } } exports.getPieceKind = getPieceKind; function getOpponentTurn(playerTurn) { return playerTurn === exports.PlayerSymbol.B ? exports.PlayerSymbol.W : playerTurn; } exports.getOpponentTurn = getOpponentTurn; function isOutBoard({ row, col }) { return row < 0 || row >= exports.ROWS || col < 0 || col >= exports.COLS; } exports.isOutBoard = isOutBoard; function isInRiver(delta) { return !!exports.RiverPos.find((pos) => dequal_1.dequal(pos, delta)); } exports.isInRiver = isInRiver; function isInWTrap(delta) { return !!exports.WhiteTraps.find((pos) => dequal_1.dequal(pos, delta)); } exports.isInWTrap = isInWTrap; function isInBTrap(delta) { return !!exports.BlackTraps.find((pos) => dequal_1.dequal(pos, delta)); } exports.isInBTrap = isInBTrap; function isWDen(delta) { return dequal_1.dequal(exports.WhiteDen, delta); } exports.isWDen = isWDen; function isBDen(delta) { return dequal_1.dequal(exports.BlackDen, delta); } exports.isBDen = isBDen; function isLand(delta) { return !isInRiver(delta) && !isInWTrap(delta) && !isInBTrap(delta) && !isWDen(delta) && !isBDen(delta); } exports.isLand = isLand; function isOwnDen(playerTurn, delta) { if (playerTurn === exports.PlayerSymbol.B) return dequal_1.dequal(delta, exports.BlackDen); if (playerTurn === exports.PlayerSymbol.W) return dequal_1.dequal(delta, exports.WhiteDen); return false; } exports.isOwnDen = isOwnDen; function isOpponent(board, delta) { const piece = board[delta.row][delta.col]; return piece.charAt(0) === exports.PlayerSymbol.W; } exports.isOpponent = isOpponent; /** * Return true if the position has no chess piece */ function noChessPiece(board, delta) { const { L, R, BTrap, WTrap, BDen, WDen } = exports.PieceName; const piece = board[delta.row][delta.col]; return [L, R, BTrap, WTrap, BDen, WDen].includes(piece); } exports.noChessPiece = noChessPiece; /** * Return true if the position has player's own chess piece */ function isOwnChessPiece(board, playerTurn, delta) { if (noChessPiece(board, delta)) return false; const { row, col } = delta; const piece = board[row][col]; const isWhiteChessPiece = playerTurn === exports.PlayerSymbol.W && piece.charAt(0) === exports.PlayerSymbol.W; const isBlackChessPiece = playerTurn === exports.PlayerSymbol.B && piece.charAt(0) === exports.PlayerSymbol.B; return isWhiteChessPiece || isBlackChessPiece; } exports.isOwnChessPiece = isOwnChessPiece; function isOwnTrap(playerTurn, delta) { if (playerTurn === exports.PlayerSymbol.B) { return !!exports.BlackTraps.find((trap) => dequal_1.dequal(trap, delta)); } if (playerTurn === exports.PlayerSymbol.W) { return !!exports.WhiteTraps.find((trap) => dequal_1.dequal(trap, delta)); } return false; } exports.isOwnTrap = isOwnTrap; /** * Return true if there's a rat in the river when lion or tiger wants to fly through */ function isRatOnWay(board, deltaFrom, deltaTo) { // horizontal line if (deltaFrom.row === deltaTo.row) { let riverCol1; let riverCol2; // player's chess is being on left side of the river if (deltaFrom.col < deltaTo.col) { riverCol1 = board[deltaFrom.row][deltaFrom.col + 1]; riverCol2 = board[deltaFrom.row][deltaFrom.col + 2]; } else { // right side riverCol1 = board[deltaFrom.row][deltaFrom.col - 1]; riverCol2 = board[deltaFrom.row][deltaFrom.col - 2]; } return riverCol1.substring(1) === exports.Animal.Rat || riverCol2.substring(1) === exports.Animal.Rat; } // vertical line let riverRow1; let riverRow2; let riverRow3; // player's chess is being on top of the river if (deltaFrom.row < deltaTo.row) { riverRow1 = board[deltaFrom.row + 1][deltaFrom.col]; riverRow2 = board[deltaFrom.row + 2][deltaFrom.col]; riverRow3 = board[deltaFrom.row + 3][deltaFrom.col]; } else { riverRow1 = board[deltaFrom.row - 1][deltaFrom.col]; riverRow2 = board[deltaFrom.row - 2][deltaFrom.col]; riverRow3 = board[deltaFrom.row - 3][deltaFrom.col]; } return (riverRow1.substring(1) === exports.Animal.Rat || riverRow2.substring(1) === exports.Animal.Rat || riverRow3.substring(1) === exports.Animal.Rat); } exports.isRatOnWay = isRatOnWay; /** * Return true if can move (for final compare) */ function canMoveHelper(board, playerTurn, deltaFrom, deltaTo) { // can move if there are no chess pieces on next move if (noChessPiece(board, deltaTo)) return true; // cannot move if there are player's own chess pieces on next move if (isOwnChessPiece(board, playerTurn, deltaTo)) return false; /** * there are two cases that player's chess can move * 1. opponent's chess is on player's own trap * 2. higher animal level can beat lower animal level * 3. elephant and rat is special case (rat beats elephant) */ // opponent's chess is on player's own trap if (isOwnTrap(playerTurn, deltaTo)) return true; const playerPiece = board[deltaFrom.row][deltaFrom.col]; const opponentPiece = board[deltaTo.row][deltaTo.col]; const playerPieceLevel = getAnimalLevel(playerPiece.substring(1)); const opponentPieceLevel = getAnimalLevel(opponentPiece.substring(1)); const elephantLevel = getAnimalLevel(exports.Animal.Elephant); const ratLevel = getAnimalLevel(exports.Animal.Rat); // higher animal level can beat lower animal level if (playerPieceLevel >= opponentPieceLevel) { // special case if (playerPieceLevel === elephantLevel && opponentPieceLevel === ratLevel) { return false; } return true; } else { // playerPieceLevel < opponentPieceLevel if (playerPieceLevel === ratLevel && opponentPieceLevel === elephantLevel) { return !isInRiver(deltaFrom); // but the rat is in river cannot beat the elephant } return false; } } exports.canMoveHelper = canMoveHelper; function canLandAnimalMove(board, playerTurn, deltaFrom, deltaTo) { if (isOutBoard(deltaTo) || isInRiver(deltaTo) || isOwnDen(playerTurn, deltaTo) || dequal_1.dequal(deltaFrom, deltaTo)) { return false; } return canMoveHelper(board, playerTurn, deltaFrom, deltaTo); } exports.canLandAnimalMove = canLandAnimalMove; function canFlyAnimalMove(board, playerTurn, deltaFrom, deltaTo) { if (isOutBoard(deltaTo) || isInRiver(deltaTo) || isOwnDen(playerTurn, deltaTo) || dequal_1.dequal(deltaFrom, deltaTo)) { return false; } return canMoveHelper(board, playerTurn, deltaFrom, deltaTo); } exports.canFlyAnimalMove = canFlyAnimalMove; function canSwimAnimalMove(board, playerTurn, deltaFrom, deltaTo) { if (isOutBoard(deltaTo) || isOwnDen(playerTurn, deltaTo) || dequal_1.dequal(deltaFrom, deltaTo)) { return false; } return canMoveHelper(board, playerTurn, deltaFrom, deltaTo); } exports.canSwimAnimalMove = canSwimAnimalMove; function getLandAnimalPossibleMoves(board, playerTurn, deltaFrom) { const possibleMoves = []; const moveUp = { row: deltaFrom.row - 1, col: deltaFrom.col }; if (canLandAnimalMove(board, playerTurn, deltaFrom, moveUp)) { possibleMoves.push(moveUp); } const moveDown = { row: deltaFrom.row + 1, col: deltaFrom.col }; if (canLandAnimalMove(board, playerTurn, deltaFrom, moveDown)) { possibleMoves.push(moveDown); } const moveLeft = { row: deltaFrom.row, col: deltaFrom.col - 1 }; if (canLandAnimalMove(board, playerTurn, deltaFrom, moveLeft)) { possibleMoves.push(moveLeft); } const moveRight = { row: deltaFrom.row, col: deltaFrom.col + 1 }; if (canLandAnimalMove(board, playerTurn, deltaFrom, moveRight)) { possibleMoves.push(moveRight); } return possibleMoves; } exports.getLandAnimalPossibleMoves = getLandAnimalPossibleMoves; function getFlyAnimalPossibleMoves(board, playerTurn, deltaFrom) { const possibleMoves = []; const moveUp = { row: deltaFrom.row - 1, col: deltaFrom.col }; if (isInRiver(moveUp)) { // a rat is not on the way, can fly through if (!isRatOnWay(board, deltaFrom, moveUp)) { moveUp.row = moveUp.row - 3; if (canFlyAnimalMove(board, playerTurn, deltaFrom, moveUp)) { possibleMoves.push(moveUp); } } } else { if (canFlyAnimalMove(board, playerTurn, deltaFrom, moveUp)) { possibleMoves.push(moveUp); } } const moveDown = { row: deltaFrom.row + 1, col: deltaFrom.col }; if (isInRiver(moveDown)) { if (!isRatOnWay(board, deltaFrom, moveDown)) { moveDown.row = moveDown.row + 3; if (canFlyAnimalMove(board, playerTurn, deltaFrom, moveDown)) { possibleMoves.push(moveDown); } } } else { if (canFlyAnimalMove(board, playerTurn, deltaFrom, moveDown)) { possibleMoves.push(moveDown); } } const moveLeft = { row: deltaFrom.row, col: deltaFrom.col - 1 }; if (isInRiver(moveLeft)) { if (!isRatOnWay(board, deltaFrom, moveLeft)) { moveLeft.col = moveLeft.col - 2; if (canFlyAnimalMove(board, playerTurn, deltaFrom, moveLeft)) { possibleMoves.push(moveLeft); } } } else { if (canFlyAnimalMove(board, playerTurn, deltaFrom, moveLeft)) { possibleMoves.push(moveLeft); } } const moveRight = { row: deltaFrom.row, col: deltaFrom.col + 1 }; if (isInRiver(moveRight)) { if (!isRatOnWay(board, deltaFrom, moveRight)) { moveRight.col = moveRight.col + 2; if (canFlyAnimalMove(board, playerTurn, deltaFrom, moveRight)) { possibleMoves.push(moveRight); } } } else { if (canFlyAnimalMove(board, playerTurn, deltaFrom, moveRight)) { possibleMoves.push(moveRight); } } return possibleMoves; } exports.getFlyAnimalPossibleMoves = getFlyAnimalPossibleMoves; function getSwimPossibleMoves(board, playerTurn, deltaFrom) { const possibleMoves = []; const moveUp = { row: deltaFrom.row - 1, col: deltaFrom.col }; if (canSwimAnimalMove(board, playerTurn, deltaFrom, moveUp)) { possibleMoves.push(moveUp); } const moveDown = { row: deltaFrom.row + 1, col: deltaFrom.col }; if (canSwimAnimalMove(board, playerTurn, deltaFrom, moveDown)) { possibleMoves.push(moveDown); } const moveLeft = { row: deltaFrom.row, col: deltaFrom.col - 1 }; if (canSwimAnimalMove(board, playerTurn, deltaFrom, moveLeft)) { possibleMoves.push(moveLeft); } const moveRight = { row: deltaFrom.row, col: deltaFrom.col + 1 }; if (canSwimAnimalMove(board, playerTurn, deltaFrom, moveRight)) { possibleMoves.push(moveRight); } return possibleMoves; } exports.getSwimPossibleMoves = getSwimPossibleMoves; function getElephantPossibleMoves(board, playerTurn, deltaFrom) { return getLandAnimalPossibleMoves(board, playerTurn, deltaFrom); } exports.getElephantPossibleMoves = getElephantPossibleMoves; function getLionPossibleMoves(board, playerTurn, deltaFrom) { return getFlyAnimalPossibleMoves(board, playerTurn, deltaFrom); } exports.getLionPossibleMoves = getLionPossibleMoves; function getTigerPossibleMoves(board, playerTurn, deltaFrom) { return getFlyAnimalPossibleMoves(board, playerTurn, deltaFrom); } exports.getTigerPossibleMoves = getTigerPossibleMoves; function getLeopardPossibleMoves(board, playerTurn, deltaFrom) { return getLandAnimalPossibleMoves(board, playerTurn, deltaFrom); } exports.getLeopardPossibleMoves = getLeopardPossibleMoves; function getWolfPossibleMoves(board, playerTurn, deltaFrom) { return getLandAnimalPossibleMoves(board, playerTurn, deltaFrom); } exports.getWolfPossibleMoves = getWolfPossibleMoves; function getDogPossibleMoves(board, playerTurn, deltaFrom) { return getLandAnimalPossibleMoves(board, playerTurn, deltaFrom); } exports.getDogPossibleMoves = getDogPossibleMoves; function getCatPossibleMoves(board, playerTurn, deltaFrom) { return getLandAnimalPossibleMoves(board, playerTurn, deltaFrom); } exports.getCatPossibleMoves = getCatPossibleMoves; function getRatPossibleMoves(board, playerTurn, deltaFrom) { return getSwimPossibleMoves(board, playerTurn, deltaFrom); } exports.getRatPossibleMoves = getRatPossibleMoves; function getPiecePossibleMoves(board, playerTurn, deltaFrom) { if (!board) return []; const piece = board[deltaFrom.row][deltaFrom.col]; switch (piece.substring(1)) { case exports.Animal.Elephant: return getElephantPossibleMoves(board, playerTurn, deltaFrom); case exports.Animal.Lion: return getLionPossibleMoves(board, playerTurn, deltaFrom); case exports.Animal.Tiger: return getTigerPossibleMoves(board, playerTurn, deltaFrom); case exports.Animal.Leopard: return getLeopardPossibleMoves(board, playerTurn, deltaFrom); case exports.Animal.Wolf: return getWolfPossibleMoves(board, playerTurn, deltaFrom); case exports.Animal.Dog: return getDogPossibleMoves(board, playerTurn, deltaFrom); case exports.Animal.Cat: return getCatPossibleMoves(board, playerTurn, deltaFrom); case exports.Animal.Rat: return getRatPossibleMoves(board, playerTurn, deltaFrom); default: return []; } } exports.getPiecePossibleMoves = getPiecePossibleMoves; exports.getPlayersPieces = (board) => { const wPieces = []; const bPieces = []; for (let row = 0; row < exports.ROWS; row++) { for (let col = 0; col < exports.COLS; col++) { const piece = board[row][col]; const animal = getPieceKind(piece); if (animal && piece.includes(exports.PlayerSymbol.B)) { bPieces.push(animal); } else if (animal && piece.includes(exports.PlayerSymbol.W)) { wPieces.push(animal); } } } return { B: bPieces, W: wPieces, }; }; function getWinner(board) { // W win if (board[exports.BlackDen.row][exports.BlackDen.col] !== exports.PieceName.BDen) { return exports.PlayerSymbol.W; } // B win if (board[exports.WhiteDen.row][exports.WhiteDen.col] !== exports.PieceName.WDen) { return exports.PlayerSymbol.B; } // const { B: bPieces, W: wPieces } = getPlayersPieces(board) // const bPiecesLevel = bPieces.map((piece) => getAnimalLevel(piece)) // const bPieceMax = Math.max(...bPiecesLevel) // const bPieceMin = Math.min(...bPiecesLevel) // const wPiecesLevel = wPieces.map((piece) => getAnimalLevel(piece)) // const wPieceMax = Math.max(...wPiecesLevel) // const wPieceMin = Math.min(...wPiecesLevel) // const elephantLevel = getAnimalLevel(Animal.Elephant) // const ratLevel = getAnimalLevel(Animal.Rat) // if (bPieceMax > wPieceMax) { // // black elephant (highest level) is alive and white rat is dead => black cannot be beated // if (bPieceMax === elephantLevel) { // if (wPieceMin !== ratLevel) { // return PlayerSymbol.B // } // } else { // return PlayerSymbol.B // } // // white elephant (highest level) is alive and black rat is dead => white cannot be beated // } else if (bPieceMax < wPieceMax) { // if (wPieceMax === elephantLevel) { // if (bPieceMin !== ratLevel) { // return PlayerSymbol.W // } // } else { // return PlayerSymbol.W // } // } return ''; } exports.getWinner = getWinner; function makeMove(board, deltaFrom, deltaTo) { const prevBoard = klona_1.klona(board); const nextBoard = klona_1.klona(board); const pieceFrom = board[deltaFrom.row][deltaFrom.col]; const isRiver = isInRiver(deltaFrom); const isWTrap = isInWTrap(deltaFrom); const isBTrap = isInBTrap(deltaFrom); const pieceReplaceFrom = isBTrap ? exports.PieceName.BTrap : isWTrap ? exports.PieceName.WTrap : isRiver ? exports.PieceName.R : exports.PieceName.L; nextBoard[deltaFrom.row][deltaFrom.col] = pieceReplaceFrom; nextBoard[deltaTo.row][deltaTo.col] = pieceFrom; const winner = getWinner(nextBoard); return { prevBoard, nextBoard, winner, }; } exports.makeMove = makeMove; /** * moves: { * BLion: [{ row: 0, col: 1 }, { row: 5, col: 7 }], * BCat: [{ row: 1, col: 1 }, { row: 1, col: 2 }], * } */ function getAllMoves(board, playerTurn) { const allMoves = {}; for (let row = 0; row < exports.ROWS; row++) { for (let col = 0; col < exports.COLS; col++) { const piece = board[row][col]; if (piece === exports.PieceName.L || piece === exports.PieceName.R || piece.substring(1) === exports.Structure.Den || piece.substring(1) === exports.Structure.Trap || piece.charAt(0) !== playerTurn) { continue; } const deltaFrom = { row, col }; const possibleMoves = getPiecePossibleMoves(board, playerTurn, deltaFrom); if ((possibleMoves === null || possibleMoves === void 0 ? void 0 : possibleMoves.length) > 0) { allMoves[piece] = possibleMoves; } } } return allMoves; } exports.getAllMoves = getAllMoves;