diff --git a/packages/server/services/Game.js b/packages/server/services/Game.js index fcf679e..94a4c97 100644 --- a/packages/server/services/Game.js +++ b/packages/server/services/Game.js @@ -1,77 +1,68 @@ // index corresponds to difference in player rank const KOMI_REC = { - '9': [ - 5.5, 2.5, -0.5, -3.5, -6.5, -9.5, 12.5, 15.5, 18.5, 21.5 - ], - '13': [ - 5.5, 0.5, -5.5, 0.5, -5.5, 0.5, -5.5, 0.5, -5.5, 0.5 - ], - '19': [ - 7.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 - ] -} - -const HANDI_REC = { - '9': [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ], - '13': [ - 0, 0, 0, 2, 2, 3, 3, 4, 4, 5 - ], - '19': [ - 0, 0, 2, 3, 4, 5, 6, 7, 8, 9 - ] -} - -// index represents handicap placement for different board-sizes, eg handiPlace['9][1] = { (3, 3), (7, 7) } -// last array in each property also used for hoshi rendering -const HANDI_PLACE = { - 9 : [ - 0, 0, - [ '7-3', '3-7' ], // 2 - [ '7-7', '7-3', '3-7' ], - [ '3-3', '7-7', '3-7', '7-3' ] - ], - 13 : [ - 0, 0, - [ '4-10', '10-4' ], // 2 - [ '10-10', '4-10', '10-4' ], - [ '4-4', '10-10', '4-10', '10-4' ], - [ '7-7', '4-4', '10-10', '4-10', '10-4' ], - [ '7-4', '4-7', '4-4', '10-10', '4-10', '10-4' ], - [ '7-7', '7-4', '4-7', '4-4', '10-10', '4-10', '10-4' ], - [ '10-7', '7-4', '7-10', '4-7', '4-4', '10-10', '4-10', '10-4' ], - [ '7-7', '10-7', '7-4', '7-10', '4-7', '4-4', '10-10', '4-10', '10-4' ], - ], - 19 : [ - 0, 0, - [ '4-16', '16-4' ], // 2 - [ '16-16', '4-16', '16-4' ], - [ '4-4', '16-16', '4-16', '16-4' ], - [ '10-10', '4-4', '16-16', '4-16', '16-4' ], - [ '10-4', '4-10', '4-4', '16-16', '4-16', '16-4' ], - [ '10-10', '10-4', '4-10', '4-4', '16-16', '4-16', '16-4' ], - [ '16-10', '10-4', '10-16', '4-10', '4-4', '16-16', '4-16', '16-4' ], - [ '10-10', '16-10', '10-4', '10-16', '4-10', '4-4', '16-16', '4-16', '16-4' ], - ] + "9": [5.5, 2.5, -0.5, -3.5, -6.5, -9.5, 12.5, 15.5, 18.5, 21.5], + "13": [5.5, 0.5, -5.5, 0.5, -5.5, 0.5, -5.5, 0.5, -5.5, 0.5], + "19": [7.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5], }; -const getSingleItemFromSet = set => { +const HANDI_REC = { + "9": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "13": [0, 0, 0, 2, 2, 3, 3, 4, 4, 5], + "19": [0, 0, 2, 3, 4, 5, 6, 7, 8, 9], +}; + +// index represents handicap placement for different board-sizes, eg handiPlace['9][1] = { (3, 3), (7, 7) } +// last array in each property also used for hoshi rendering +const HANDI_PLACE = { + 9: [ + 0, + 0, + ["7-3", "3-7"], // 2 + ["7-7", "7-3", "3-7"], + ["3-3", "7-7", "3-7", "7-3"], + ], + 13: [ + 0, + 0, + ["4-10", "10-4"], // 2 + ["10-10", "4-10", "10-4"], + ["4-4", "10-10", "4-10", "10-4"], + ["7-7", "4-4", "10-10", "4-10", "10-4"], + ["7-4", "4-7", "4-4", "10-10", "4-10", "10-4"], + ["7-7", "7-4", "4-7", "4-4", "10-10", "4-10", "10-4"], + ["10-7", "7-4", "7-10", "4-7", "4-4", "10-10", "4-10", "10-4"], + ["7-7", "10-7", "7-4", "7-10", "4-7", "4-4", "10-10", "4-10", "10-4"], + ], + 19: [ + 0, + 0, + ["4-16", "16-4"], // 2 + ["16-16", "4-16", "16-4"], + ["4-4", "16-16", "4-16", "16-4"], + ["10-10", "4-4", "16-16", "4-16", "16-4"], + ["10-4", "4-10", "4-4", "16-16", "4-16", "16-4"], + ["10-10", "10-4", "4-10", "4-4", "16-16", "4-16", "16-4"], + ["16-10", "10-4", "10-16", "4-10", "4-4", "16-16", "4-16", "16-4"], + ["10-10", "16-10", "10-4", "10-16", "4-10", "4-4", "16-16", "4-16", "16-4"], + ], +}; + +const getSingleItemFromSet = (set) => { let entry; for (entry of set.entries()) { } return entry[0]; -} +}; -const pipeMap = (...funcs) => obj => { +const pipeMap = (...funcs) => (obj) => { const arr = Object.entries(obj).reduce((acc, [key, value], i, arr) => { - funcs.forEach(func => value = func(value, i, arr)); + funcs.forEach((func) => (value = func(value, i, arr))); return [...acc, [key, value]]; - },[]); + }, []); return arr.reduce((acc, [key, value]) => { - return { ...acc, [key]: value } + return { ...acc, [key]: value }; }, {}); -} +}; const checkLegal = ({ point, Game }) => { // if stone (includes ko) return false @@ -79,20 +70,24 @@ const checkLegal = ({ point, Game }) => { point.legal = false; return point; } - const neighbors = getNeighbors({Game, point}); + const neighbors = getNeighbors({ Game, point }); - const isEmpty = point => point.stone === 0 && point.legal === true; + const isEmpty = (point) => point.stone === 0 && point.legal === true; const isEmptyAdjacent = neighbors.filter(isEmpty); // if empty point adjacent return true if (!isEmptyAdjacent.length) { - // if group has liberties return true - const isTurnStone = neighbor => neighbor.stone === Game.turn; - const getGroupLiberties = point => Array.from(Game.groups[point.group].liberties); - const isNotSamePoint = liberty => liberty.pos.x !== point.pos.x && liberty.pos.y !== point.pos.y; - const isInGroupWithLiberties = neighbor => getGroupLiberties(neighbor).filter(isNotSamePoint).length; - const isInLiveGroup = neighbors.filter(isTurnStone).filter(isInGroupWithLiberties).length; + const isTurnStone = (neighbor) => neighbor.stone === Game.turn; + const getGroupLiberties = (point) => + Array.from(Game.groups[point.group].liberties); + const isNotSamePoint = (liberty) => + liberty.pos.x !== point.pos.x && liberty.pos.y !== point.pos.y; + const isInGroupWithLiberties = (neighbor) => + getGroupLiberties(neighbor).filter(isNotSamePoint).length; + const isInLiveGroup = neighbors + .filter(isTurnStone) + .filter(isInGroupWithLiberties).length; if (isInLiveGroup) { point.legal = true; @@ -110,126 +105,137 @@ const checkLegal = ({ point, Game }) => { } point.legal = true; return point; -} +}; const getBoardState = (Game) => { - const getLegal = point => checkLegal({ point, Game }) + const getLegal = (point) => checkLegal({ point, Game }); const boardState = pipeMap(getLegal)(Game.boardState); - Game.kos.forEach(ko => { + Game.kos.forEach((ko) => { boardState[ko].legal = false; }); return boardState; -} +}; const getLegalMoves = (Game) => { - const mapLegal = point => point.legal ? 'l' : point.stone; + const mapLegal = (point) => (point.legal ? "l" : point.stone); const legalMoves = pipeMap(mapLegal)(Game.boardState); - Game.kos.forEach(ko => { - legalMoves[ko] = 'k'; + Game.kos.forEach((ko) => { + legalMoves[ko] = "k"; }); return legalMoves; -} +}; const getNeighbors = ({ Game, point }) => { - let { top = null, btm = null, lft = null, rgt = null} = point.neighbors; + let { top = null, btm = null, lft = null, rgt = null } = point.neighbors; const { boardState, boardSize } = Game; // boardState[0] = [ '1-1', Point({x:1, y:1, boardSize}) ] if (top) top = boardState[top]; if (btm) btm = boardState[btm]; if (lft) lft = boardState[lft]; if (rgt) rgt = boardState[rgt]; - return [ top, btm, lft, rgt ].filter(value => value); -} + return [top, btm, lft, rgt].filter((value) => value); +}; const initBoard = (game) => { const boardState = {}; const { boardSize, handicap } = game; for (let i = 0; i < Math.pow(boardSize, 2); i++) { const point = Point({ - x: Math.floor(i / boardSize) + 1, - y: i % boardSize + 1, - boardSize + x: Math.floor(i / boardSize) + 1, + y: (i % boardSize) + 1, + boardSize, }); boardState[`${point.pos.x}-${point.pos.y}`] = point; } if (handicap) { - HANDI_PLACE[boardSize][handicap].forEach(pt => { - boardState[pt].makeMove({...game, boardState}); + HANDI_PLACE[boardSize][handicap].forEach((pt) => { + boardState[pt].makeMove({ ...game, boardState }); }); game.turn *= -1; } return boardState; -} +}; // returns Game object -const Game = ({gameData = {}, gameRecord = []} = {}) => { +const Game = ({ gameData = {}, gameRecord = [] } = {}) => { const helper = { - clearKo: function() { - this.kos.forEach(ko => { - this.boardState[ko] = { ...this.boardState[ko], legal: true, ko: false }; - }) + clearKo: function () { + this.kos.forEach((ko) => { + this.boardState[ko] = { + ...this.boardState[ko], + legal: true, + ko: false, + }; + }); this.kos = []; }, - } + }; if (gameRecord.length) { // play through all the moves - return gameRecord.reduce((game, move) => game.makeMove(move), Game({gameData}).initGame()) + return gameRecord.reduce( + (game, move) => game.makeMove(move), + Game({ gameData }).initGame() + ); } return { - winner: gameData.winner ||null, - turn: gameData.turn || 0, // turn logic depends on handicap stones - pass: gameData.pass || 0, // -1 represents state in which resignation has been submitted, not confirmed - komi: gameData.komi || 6.5, // komi depends on handicap stones + player rank - handicap: gameData.handicap || 0, - boardSize: gameData.boardSize || 19, - groups: {}, - boardState: {}, - kos: [], - gameRecord: gameRecord, - playerState: gameData.playerState || { + winner: gameData.winner || null, + turn: gameData.turn || 0, // turn logic depends on handicap stones + pass: gameData.pass || 0, // -1 represents state in which resignation has been submitted, not confirmed + komi: gameData.komi || 6.5, // komi depends on handicap stones + player rank + handicap: gameData.handicap || 0, + boardSize: gameData.boardSize || 19, + groups: {}, + boardState: {}, + kos: [], + gameRecord: gameRecord, + playerState: gameData.playerState || { bCaptures: 0, wCaptures: 0, bScore: 0, - wScore: 0 + wScore: 0, }, - initGame: function() { - this.winner = null; - this.pass = 0; - this.turn = 1; + initGame: function () { + this.winner = null; + this.pass = 0; + this.turn = 1; this.boardState = initBoard(this); this.boardState = getBoardState(this); - this.legalMoves = getLegalMoves(this) + this.legalMoves = getLegalMoves(this); return this; }, - addToRecord: function(moveObject) { + addToRecord: function (moveObject) { this.gameRecord.push(moveObject); }, - getMeta: function() { - // cannot be chained + getMeta: function () { + // cannot be chained // does not affect game object return { - winner: this.winner, - turn: this.turn, - pass: this.pass, - playerState: this.playerState, - gameRecord: this.gameRecord, - boardSize: this.boardSize, - handicap: this.handicap, - komi: this.komi - } + winner: this.winner, + turn: this.turn, + pass: this.pass, + playerState: this.playerState, + gameRecord: this.gameRecord, + boardSize: this.boardSize, + handicap: this.handicap, + komi: this.komi, + }; }, - makeMove: function({ player, pos: {x, y}}) { + makeMove: function ({ player, pos: { x, y } }) { + if (this.pass > 1) { + return { ...this, success: false }; + } let game = this; let success = false; const point = game.boardState[`${x}-${y}`]; - const isTurn = ( game.turn === 1 && player === 'black' ) - || ( game.turn === -1 && player === 'white' ); + const isTurn = + (game.turn === 1 && player === "black") || + (game.turn === -1 && player === "white"); if (isTurn) { if (point.legal) { game.pass = 0; @@ -241,33 +247,33 @@ const Game = ({gameData = {}, gameRecord = []} = {}) => { } } game.boardState = getBoardState(game); - return {...game, legalMoves: getLegalMoves(game), success }; + return { ...game, legalMoves: getLegalMoves(game), success }; }, - initGroup: function(point) { + initGroup: function (point) { const group = Symbol(`${point.pos.x}-${point.pos.y}`); - this.groups[group] = { stones: new Set(), liberties: new Set()}; + this.groups[group] = { stones: new Set(), liberties: new Set() }; return { game: this, group }; }, - returnToMove: function(lastMove) { + returnToMove: function (lastMove) { const { komi, handicap, boardSize } = this; if (lastMove === 0) { return Game({ - gameData: { komi, handicap, boardSize } + gameData: { komi, handicap, boardSize }, }).initGame(); } const length = this.gameRecord.length; const index = lastMove < 0 ? length + lastMove : lastMove; if (lastMove >= length && lastMove > 0) return this; - return Game({ - gameData: { komi, handicap, boardSize }, - gameRecord: [...this.gameRecord.slice(0, index)] + return Game({ + gameData: { komi, handicap, boardSize }, + gameRecord: [...this.gameRecord.slice(0, index)], }); }, - submitPass: function(player) { - if (player !== 'black' && player !== 'white') { + submitPass: function (player) { + if (player !== "black" && player !== "white") { return { ...this, success: false }; } if (this.pass > 0) { @@ -276,47 +282,56 @@ const Game = ({gameData = {}, gameRecord = []} = {}) => { this.pass = 1; this.addToRecord({ player, pos: { x: null, y: null } }); if (this.kos.length) helper.clearKo.call(this); - this.turn = player === 'black' ? -1 : 1; + this.turn = player === "black" ? -1 : 1; this.boardState = getBoardState(this); - return {...this, legalMoves: getLegalMoves(this), success: true }; + return { ...this, legalMoves: getLegalMoves(this), success: true }; }, - submitResign: function(player) { - if (player === 'black') this.winner = -1; - if (player === 'white') this.winner = 1; + submitResign: function (player) { + if (player === "black") this.winner = -1; + if (player === "white") this.winner = 1; this.turn = 0; return this; }, - endGame: function() { + endGame: function () { // TODO manage territory counting + // form groups for empty points + // for each empty point group determine territory + // for each non-empty point group determine life + // submit board state to users + // if boardState is approved calculate winner + // submit end game board state and data for study + // (study module should run client side and only ) + // if toggleGroups(key) toggle that group + // submit board state to winner this.turn = 0; return this; - } - } + }, + }; }; -const Point = ({x, y, boardSize = 19}) => { +const Point = ({ x, y, boardSize = 19 }) => { let point = { - pos: {x, y}, - key: `${x}-${y}`, - stone: 0, // can be 1, -1, 0, - ko: false, - legal: true, - territory: 0, - capturing: { - '1': new Set(), - '-1': new Set() + pos: { x, y }, + key: `${x}-${y}`, + stone: 0, // can be 1, -1, 0, + ko: false, + legal: true, + territory: 0, + capturing: { + "1": new Set(), + "-1": new Set(), }, - group: null, + group: null, neighbors: { - top: x > 1 ? `${ x - 1 }-${ y }` : null, - btm: x < boardSize ? `${ x + 1 }-${ y }` : null, - rgt: y < boardSize ? `${ x }-${ y + 1 }` : null, - lft: y > 1 ? `${ x }-${ y - 1 }` : null + top: x > 1 ? `${x - 1}-${y}` : null, + btm: x < boardSize ? `${x + 1}-${y}` : null, + rgt: y < boardSize ? `${x}-${y + 1}` : null, + lft: y > 1 ? `${x}-${y - 1}` : null, }, - makeMove: function(Game) { + makeMove: function (Game) { this.stone = Game.turn; this.legal = false; if (this.capturing[this.stone].size) { @@ -325,8 +340,8 @@ const Point = ({x, y, boardSize = 19}) => { Game = this.joinGroup({ point: this, Game }); return this.checkCaptures(Game); }, - - joinGroup: function({ point, Game }) { + + joinGroup: function ({ point, Game }) { if (point.group !== this.group || !point.group) { // if point has no group set current group to new Symbol in game object if (!point.group) { @@ -334,42 +349,43 @@ const Point = ({x, y, boardSize = 19}) => { this.group = group; Game = game; } - + // add current point to global group and override current group Game.groups[point.group].stones.add(this); if (this.group !== point.group) { this.group = point.group; } Game = this.setLiberties(Game); - getNeighbors({ point:this, Game }).forEach(neighbor => { - if ( neighbor.stone === this.stone + getNeighbors({ point: this, Game }).forEach((neighbor) => { + if ( + neighbor.stone === this.stone && // this check prevents infinite call chains - && neighbor.group !== this.group + neighbor.group !== this.group ) { Game = neighbor.joinGroup({ point: this, Game }); } - }) + }); } return Game; }, - setLiberties: function(Game) { + setLiberties: function (Game) { const neighbors = getNeighbors({ point: this, Game }); const liberties = Game.groups[this.group].liberties; // if point is occupied remove it from liberties set of point group, else add it - neighbors.forEach(neighbor => { + neighbors.forEach((neighbor) => { if (neighbor.stone !== 0) { liberties.delete(neighbor); Game.groups[neighbor.group].liberties.delete(this); - } + } if (neighbor.stone === 0) { - liberties.add(neighbor) + liberties.add(neighbor); } }); return Game; }, - checkCaptures: function(game) { + checkCaptures: function (game) { // if this stone has one liberty const liberties = game.groups[this.group].liberties; if (liberties.size === 1) { @@ -377,13 +393,18 @@ const Point = ({x, y, boardSize = 19}) => { lastLiberty.capturing[this.stone * -1].add(this.group); } if (liberties.size > 1) { - liberties.forEach(liberty => liberty.capturing[this.stone * -1 ].delete(this.group)) + liberties.forEach((liberty) => + liberty.capturing[this.stone * -1].delete(this.group) + ); } // if neighbors have one liberty - const neighbors = getNeighbors({point: this, Game: game}).filter(neighbor => neighbor.stone === -1 * this.stone) - neighbors.forEach( neighbor => { - const liberties = game.groups[neighbor.group] && game.groups[neighbor.group].liberties; + const neighbors = getNeighbors({ point: this, Game: game }).filter( + (neighbor) => neighbor.stone === -1 * this.stone + ); + neighbors.forEach((neighbor) => { + const liberties = + game.groups[neighbor.group] && game.groups[neighbor.group].liberties; if (liberties && liberties.size === 1) { const lastLiberty = getSingleItemFromSet(liberties); lastLiberty.capturing[neighbor.stone * -1].add(neighbor.group); @@ -392,32 +413,34 @@ const Point = ({x, y, boardSize = 19}) => { return game; }, - makeCaptures: function(game) { + makeCaptures: function (game) { // for each group for (let [captureGroup, _] of this.capturing[this.stone].entries()) { - const capturesSet = game.groups[captureGroup].stones; for (let [capture, _] of capturesSet.entries()) { game = capture.removeStone(game); if (capturesSet.size === 1) { - const neighbors = getNeighbors({ point: this, Game: game }) - const liberties = neighbors.filter(neighbor => neighbor.stone === 0); - const groupStones = neighbors.filter(neighbor => neighbor.stone === this.stone); + const neighbors = getNeighbors({ point: this, Game: game }); + const liberties = neighbors.filter( + (neighbor) => neighbor.stone === 0 + ); + const groupStones = neighbors.filter( + (neighbor) => neighbor.stone === this.stone + ); if (liberties.length === 1 && groupStones.length === 0) { capture.ko = true; - game.kos.push(capture.key) + game.kos.push(capture.key); } } } - } // points with stones cannot be played to capture - this.capturing = { '1': new Set(), '-1': new Set() } - return {...game, boardState: { ...game.boardState, [this.key]: this } }; + this.capturing = { "1": new Set(), "-1": new Set() }; + return { ...game, boardState: { ...game.boardState, [this.key]: this } }; }, - removeStone: function(game) { - if (this.stone = 0) { + removeStone: function (game) { + if ((this.stone = 0)) { return game; } // reset point @@ -425,19 +448,21 @@ const Point = ({x, y, boardSize = 19}) => { this.group = null; this.capturing[game.turn] = new Set(); // add captures - const player = game.turn > 0 ? 'b' : 'w'; + const player = game.turn > 0 ? "b" : "w"; game.playerState[`${player}Captures`] += 1; // add as liberty to neighbors - const neighbors = getNeighbors({ point: this, Game: game }).filter(neighbor => neighbor.stone !== 0 && neighbor.group); - neighbors.forEach(neighbor => { + const neighbors = getNeighbors({ point: this, Game: game }).filter( + (neighbor) => neighbor.stone !== 0 && neighbor.group + ); + neighbors.forEach((neighbor) => { game.groups[neighbor.group].liberties.add(this); - neighbor.checkCaptures(game) - }) + neighbor.checkCaptures(game); + }); - return {...game, boardState: {...game.boardState, [this.key]: this}}; - } - } + return { ...game, boardState: { ...game.boardState, [this.key]: this } }; + }, + }; for (let [key, value] of Object.entries(point.neighbors)) { if (value) continue; delete point.neighbors[key]; @@ -447,12 +472,12 @@ const Point = ({x, y, boardSize = 19}) => { module.exports = { Game, - Point -} + Point, +}; // Game().initGame() // .makeMove({ player: 'black', pos: { x: 1, y: 1 } }) // 1 2 3 // .makeMove({ player: 'white', pos: { x: 1, y: 2 } }) // 1 1 -1 1 // .makeMove({ player: 'black', pos: { x: 2, y: 2 } }) // 2 -1 1 // .makeMove({ player: 'white', pos: { x: 2, y: 1 } }) -// .makeMove({ player: 'black', pos: { x: 1, y: 3 } }) \ No newline at end of file +// .makeMove({ player: 'black', pos: { x: 1, y: 3 } }) diff --git a/packages/server/services/gameServices.js b/packages/server/services/gameServices.js index ce66f56..bbdd9e4 100644 --- a/packages/server/services/gameServices.js +++ b/packages/server/services/gameServices.js @@ -1,63 +1,65 @@ -const Game = require('./Game').Game; -const moveQueries = require('../data/queries/move'); +const Game = require("./Game").Game; -const gamesInProgress = { } - -const storeGame = (game) => { - gamesInProgress[game.id] = Game(game); - return gamesInProgress[game.id]; -} - -const initGame = ({id, gameRecord = [], ...gameData}) => { - if (gamesInProgress[id]) return getDataForUI(id); - if (gameRecord.length) { - console.log('here') - gamesInProgress[id] = Game({ gameData, gameRecord }) - } - else { - gamesInProgress[id] = Game({gameData}).initGame(); - } - return getDataForUI(id) -} - -const makeMove = async ({id, move}) => { - if (!gamesInProgress[id]) storeGame({id}).initGame(); - gamesInProgress[id] = gamesInProgress[id].makeMove(move) - if (gamesInProgress[id].success === false) return { message: 'illegal move' }; - const priorMove = gamesInProgress[id].gameRecord.length; - const moveInsert = { gameId: id, player: move.player, x: move.pos.x, y: move.pos.y, gameRecord: true, priorMove }; - let moveDbResult; - try { - moveDbResult = await moveQueries.addMove(moveInsert); - } - catch { - gamesInProgress[id].returnToMove(-1); - } finally { - return getDataForUI(id); - } -} - -const getDataForUI = (id) => { - return { - board: gamesInProgress[id].legalMoves, - ...gamesInProgress[id].getMeta() +const GameService = (moveQueries) => { + const storeGame = (game) => { + gamesInProgress[game.id] = Game(game); + return gamesInProgress[game.id]; }; -} + const gamesInProgress = {}; -const dropGame = (id) => { - return { message: - `${delete gamesInProgress[id]}` - } -} + return { + initGame({ id, gameRecord = [], ...gameData }) { + if (gamesInProgress[id]) return this.getDataForUI(id); + if (gameRecord.length) { + console.log("here"); + gamesInProgress[id] = Game({ gameData, gameRecord }); + } else { + gamesInProgress[id] = Game({ gameData }).initGame(); + } + return this.getDataForUI(id); + }, -const getAllGames = () => { - return gamesInProgress; -} + async makeMove({ id, move }) { + if (!gamesInProgress[id]) storeGame({ id }).initGame(); + gamesInProgress[id] = gamesInProgress[id].makeMove(move); + if (gamesInProgress[id].success === false) + return { message: "illegal move" }; + try { + if (moveQueries) { + const priorMove = gamesInProgress[id].gameRecord.length; + const moveInsert = { + gameId: id, + player: move.player, + x: move.pos.x, + y: move.pos.y, + gameRecord: true, + priorMove, + }; + let moveDbResult; + moveDbResult = await moveQueries.addMove(moveInsert); + } + } catch { + gamesInProgress[id].returnToMove(-1); + } finally { + return this.getDataForUI(id); + } + }, -module.exports = { - makeMove, - getAllGames, - getDataForUI, - initGame, - dropGame -} + getDataForUI: (id) => { + return { + board: gamesInProgress[id].legalMoves, + ...gamesInProgress[id].getMeta(), + }; + }, + + dropGame: (id) => { + return { message: `${delete gamesInProgress[id]}` }; + }, + + getAllGames: () => { + return gamesInProgress; + }, + }; +}; + +module.exports = GameService; diff --git a/packages/server/socket.js b/packages/server/socket.js index 3cdd51e..a48ba89 100644 --- a/packages/server/socket.js +++ b/packages/server/socket.js @@ -1,57 +1,63 @@ // TODO const someSocketLogic = require('./middleware/socketssockets/...'); -const socketIO = require('socket.io'); +const socketIO = require("socket.io"); const io = socketIO({ cookie: false }); // const gameQueries = require('./data/queries/game'); -const moveQueries = require('./data/queries/move'); -const gameServices = require('./services/gameServices'); +const moveQueries = require("./data/queries/move"); +const gameServices = require("./services/gameServices")(moveQueries); -io.on('connection', async socket=> { - socket.emit('connected', {message: 'socket connected'}); - socket.on('connect_room', async data => { +io.on("connection", async (socket) => { + socket.emit("connected", { message: "socket connected" }); + socket.on("connect_room", async (data) => { if (data.user && data.user.email) { delete data.user.email; } const room = data.room; const roomIo = io.of(room); - roomIo.on('connection', async socket => { - socket.emit('connected') - socket.emit('new_user', data); - socket.on('connect_game', data => { + roomIo.on("connection", async (socket) => { + socket.emit("connected"); + socket.emit("new_user", data); + socket.on("connect_game", (data) => { const game = `game-${data.game.id}`; socket.join(game, async () => { - // ! temp + // ! temp const gameRecord = await moveQueries.findGameRecord(data.game.id); - console.log(gameRecord) - await gameServices.initGame({id: data.game.id, gameRecord}) + console.log("gameRecord from db"); + console.log(gameRecord); + await gameServices.initGame({ id: data.game.id, gameRecord }); // ! end-temp - const { board, ...meta } = await gameServices.getDataForUI(data.game.id); - io.of(room).to(game).emit('game_connected', { board, meta }); + const { board, ...meta } = await gameServices.getDataForUI( + data.game.id + ); + io.of(room).to(game).emit("game_connected", { board, meta }); }); }); - socket.on('make_move', async data => { + socket.on("make_move", async (data) => { const { user, move, board, game, room } = data; const gameNsp = `game-${data.game.id}`; - - try { - const { board, message, ...meta } = await gameServices.makeMove({ id: 1, move }); - const socketAction = message ? 'error' : 'update_board'; - socket.join(gameNsp, () => { - io.of(room).to(gameNsp).emit(socketAction, { board, meta, message }) - }); - } - catch (e) { - console.log(e) - socket.join(gameNsp, () => { - io.of(room).to(gameNsp).emit('error', e) - }); + try { + const { board, message, ...meta } = await gameServices.makeMove({ + id: 1, + move, + }); + const socketAction = message ? "error" : "update_board"; + socket.join(gameNsp, () => { + io.of(room) + .to(gameNsp) + .emit(socketAction, { board, meta, message }); + }); + } catch (e) { + console.log(e); + socket.join(gameNsp, () => { + io.of(room).to(gameNsp).emit("error", e); + }); } - }) + }); }); - }) -}) + }); +}); module.exports = { - io -} + io, +}; diff --git a/packages/server/test/Game.spec.js b/packages/server/test/Game.spec.js index 6149bb3..aa0c685 100644 --- a/packages/server/test/Game.spec.js +++ b/packages/server/test/Game.spec.js @@ -1,555 +1,779 @@ -const chai = require('chai'); +const chai = require("chai"); const should = chai.should(); -const { Game, Point } = require('../services/Game'); +const { Game, Point } = require("../services/Game"); -describe('Game', () => { - it('smoke test Game()', done => { - (typeof Game()) - .should.eql('object'); +describe("Game", () => { + it("smoke test Game()", (done) => { + (typeof Game()).should.eql("object"); done(); }); - it('smoke test Point()', done => { - (typeof Point({x: 1, y: 1})) - .should.eql('object'); + it("smoke test Point()", (done) => { + (typeof Point({ x: 1, y: 1 })).should.eql("object"); done(); }); - it('smoke test initGame()', done => { - (typeof Game().initGame()) - .should.eql('object'); + it("smoke test initGame()", (done) => { + (typeof Game().initGame()).should.eql("object"); done(); }); - it('Get meta returns proper data for games with no record', done => { - Game().getMeta() - .should.eql(initialMeta); + it("Get meta returns proper data for games with no record", (done) => { + Game().getMeta().should.eql(initialMeta); // Game().initGame().getMeta() // .should.eql({ ...initialMeta, turn: 1 }); done(); }); }); -describe('Game().initGame() returns legalMoves', () => { - it('initGame() returns default 19x19', done => { - Game().initGame() - .legalMoves.should.eql(emptyBoard); - done(); - }); - - it('initGame() with 2 handicap returns legalMoves with stones', done => { - Game({gameData: { handicap: 2 }}).initGame() - .legalMoves.should.eql({...emptyBoard, '4-16': 1, '16-4': 1}); +describe("Game().initGame() returns legalMoves", () => { + it("initGame() returns default 19x19", (done) => { + Game().initGame().legalMoves.should.eql(emptyBoard); done(); }); - it('handicap stone has proper liberties', done => { - const game = Game({gameData: { handicap: 2 }}).initGame(); - const group = game.boardState['4-16'].group + it("initGame() with 2 handicap returns legalMoves with stones", (done) => { + Game({ gameData: { handicap: 2 } }) + .initGame() + .legalMoves.should.eql({ ...emptyBoard, "4-16": 1, "16-4": 1 }); + done(); + }); + + it("handicap stone has proper liberties", (done) => { + const game = Game({ gameData: { handicap: 2 } }).initGame(); + const group = game.boardState["4-16"].group; game.groups[group].liberties.size.should.eql(4); done(); }); - it('initGame( 19x19 ) with all levels of handicap returns legalMoves with stones', done => { - Game({gameData: { boardSize: 19, handicap: 2 }}).initGame() - .legalMoves.should.eql({...emptyBoard, '4-16': 1, '16-4': 1 }); - Game({gameData: { boardSize: 19, handicap: 3 }}).initGame() - .legalMoves.should.eql({...emptyBoard, '16-16': 1, '4-16': 1, '16-4': 1 }); - Game({gameData: { boardSize: 19, handicap: 4 }}).initGame() - .legalMoves.should.eql({...emptyBoard, '4-4': 1, '16-16': 1, '4-16': 1, '16-4': 1 }); - Game({gameData: { boardSize: 19, handicap: 5 }}).initGame() - .legalMoves.should.eql({...emptyBoard, '10-10': 1, '4-4': 1, '16-16': 1, '4-16': 1, '16-4': 1 }); - Game({gameData: { boardSize: 19, handicap: 6 }}).initGame() - .legalMoves.should.eql({...emptyBoard, '10-4': 1, '4-10': 1, '4-4': 1, '16-16': 1, '4-16': 1, '16-4': 1 }); - Game({gameData: { boardSize: 19, handicap: 7 }}).initGame() - .legalMoves.should.eql({...emptyBoard, '10-10': 1, '10-4': 1, '4-10': 1, '4-4': 1, '16-16': 1, '4-16': 1, '16-4': 1 }); - Game({gameData: { boardSize: 19, handicap: 8 }}).initGame() - .legalMoves.should.eql({...emptyBoard, '16-10': 1, '10-4': 1, '10-16': 1, '4-10': 1, '4-4': 1, '16-16': 1, '4-16': 1, '16-4': 1 }); - Game({gameData: { boardSize: 19, handicap: 9 }}).initGame() - .legalMoves.should.eql({...emptyBoard, '10-10': 1, '16-10': 1, '10-4': 1, '10-16': 1, '4-10': 1, '4-4': 1, '16-16': 1, '4-16': 1, '16-4': 1 }); + it("initGame( 19x19 ) with all levels of handicap returns legalMoves with stones", (done) => { + Game({ gameData: { boardSize: 19, handicap: 2 } }) + .initGame() + .legalMoves.should.eql({ ...emptyBoard, "4-16": 1, "16-4": 1 }); + Game({ gameData: { boardSize: 19, handicap: 3 } }) + .initGame() + .legalMoves.should.eql({ + ...emptyBoard, + "16-16": 1, + "4-16": 1, + "16-4": 1, + }); + Game({ gameData: { boardSize: 19, handicap: 4 } }) + .initGame() + .legalMoves.should.eql({ + ...emptyBoard, + "4-4": 1, + "16-16": 1, + "4-16": 1, + "16-4": 1, + }); + Game({ gameData: { boardSize: 19, handicap: 5 } }) + .initGame() + .legalMoves.should.eql({ + ...emptyBoard, + "10-10": 1, + "4-4": 1, + "16-16": 1, + "4-16": 1, + "16-4": 1, + }); + Game({ gameData: { boardSize: 19, handicap: 6 } }) + .initGame() + .legalMoves.should.eql({ + ...emptyBoard, + "10-4": 1, + "4-10": 1, + "4-4": 1, + "16-16": 1, + "4-16": 1, + "16-4": 1, + }); + Game({ gameData: { boardSize: 19, handicap: 7 } }) + .initGame() + .legalMoves.should.eql({ + ...emptyBoard, + "10-10": 1, + "10-4": 1, + "4-10": 1, + "4-4": 1, + "16-16": 1, + "4-16": 1, + "16-4": 1, + }); + Game({ gameData: { boardSize: 19, handicap: 8 } }) + .initGame() + .legalMoves.should.eql({ + ...emptyBoard, + "16-10": 1, + "10-4": 1, + "10-16": 1, + "4-10": 1, + "4-4": 1, + "16-16": 1, + "4-16": 1, + "16-4": 1, + }); + Game({ gameData: { boardSize: 19, handicap: 9 } }) + .initGame() + .legalMoves.should.eql({ + ...emptyBoard, + "10-10": 1, + "16-10": 1, + "10-4": 1, + "10-16": 1, + "4-10": 1, + "4-4": 1, + "16-16": 1, + "4-16": 1, + "16-4": 1, + }); done(); - }) + }); - it('initGame( 13x13) returns legalMoves', done => { - Game({gameData: { boardSize: 13 }}).initGame() + it("initGame( 13x13) returns legalMoves", (done) => { + Game({ gameData: { boardSize: 13 } }) + .initGame() .legalMoves.should.eql(emptyBoard13); done(); }); - - it('initGame( 13x13 ) with all levels of handicap returns legalMoves with stones', done => { - Game({gameData: { boardSize: 13, handicap: 2 }}).initGame() - .legalMoves.should.eql({...emptyBoard13, '4-10': 1, '10-4': 1 }); - Game({gameData: { boardSize: 13, handicap: 3 }}).initGame() - .legalMoves.should.eql({...emptyBoard13, '10-10': 1, '4-10': 1, '10-4': 1 }); - Game({gameData: { boardSize: 13, handicap: 4 }}).initGame() - .legalMoves.should.eql({...emptyBoard13, '4-4': 1, '10-10': 1, '4-10': 1, '10-4': 1 }); - Game({gameData: { boardSize: 13, handicap: 5 }}).initGame() - .legalMoves.should.eql({...emptyBoard13, '7-7': 1, '4-4': 1, '10-10': 1, '4-10': 1, '10-4': 1 }); - Game({gameData: { boardSize: 13, handicap: 6 }}).initGame() - .legalMoves.should.eql({...emptyBoard13, '7-4': 1, '4-7': 1, '4-4': 1, '10-10': 1, '4-10': 1, '10-4': 1 }); - Game({gameData: { boardSize: 13, handicap: 7 }}).initGame() - .legalMoves.should.eql({...emptyBoard13, '7-7': 1, '7-4': 1, '4-7': 1, '4-4': 1, '10-10': 1, '4-10': 1, '10-4': 1 }); - Game({gameData: { boardSize: 13, handicap: 8 }}).initGame() - .legalMoves.should.eql({...emptyBoard13, '10-7': 1, '7-4': 1, '7-10': 1, '4-7': 1, '4-4': 1, '10-10': 1, '4-10': 1, '10-4': 1 }); - Game({gameData: { boardSize: 13, handicap: 9 }}).initGame() - .legalMoves.should.eql({...emptyBoard13, '7-7': 1, '10-7': 1, '7-4': 1, '7-10': 1, '4-7': 1, '4-4': 1, '10-10': 1, '4-10': 1, '10-4': 1 }); + + it("initGame( 13x13 ) with all levels of handicap returns legalMoves with stones", (done) => { + Game({ gameData: { boardSize: 13, handicap: 2 } }) + .initGame() + .legalMoves.should.eql({ ...emptyBoard13, "4-10": 1, "10-4": 1 }); + Game({ gameData: { boardSize: 13, handicap: 3 } }) + .initGame() + .legalMoves.should.eql({ + ...emptyBoard13, + "10-10": 1, + "4-10": 1, + "10-4": 1, + }); + Game({ gameData: { boardSize: 13, handicap: 4 } }) + .initGame() + .legalMoves.should.eql({ + ...emptyBoard13, + "4-4": 1, + "10-10": 1, + "4-10": 1, + "10-4": 1, + }); + Game({ gameData: { boardSize: 13, handicap: 5 } }) + .initGame() + .legalMoves.should.eql({ + ...emptyBoard13, + "7-7": 1, + "4-4": 1, + "10-10": 1, + "4-10": 1, + "10-4": 1, + }); + Game({ gameData: { boardSize: 13, handicap: 6 } }) + .initGame() + .legalMoves.should.eql({ + ...emptyBoard13, + "7-4": 1, + "4-7": 1, + "4-4": 1, + "10-10": 1, + "4-10": 1, + "10-4": 1, + }); + Game({ gameData: { boardSize: 13, handicap: 7 } }) + .initGame() + .legalMoves.should.eql({ + ...emptyBoard13, + "7-7": 1, + "7-4": 1, + "4-7": 1, + "4-4": 1, + "10-10": 1, + "4-10": 1, + "10-4": 1, + }); + Game({ gameData: { boardSize: 13, handicap: 8 } }) + .initGame() + .legalMoves.should.eql({ + ...emptyBoard13, + "10-7": 1, + "7-4": 1, + "7-10": 1, + "4-7": 1, + "4-4": 1, + "10-10": 1, + "4-10": 1, + "10-4": 1, + }); + Game({ gameData: { boardSize: 13, handicap: 9 } }) + .initGame() + .legalMoves.should.eql({ + ...emptyBoard13, + "7-7": 1, + "10-7": 1, + "7-4": 1, + "7-10": 1, + "4-7": 1, + "4-4": 1, + "10-10": 1, + "4-10": 1, + "10-4": 1, + }); done(); }); - it('initGame( 9x9 ) returns legalMoves', done => { - Game({gameData: { boardSize: 9 }}).initGame() + it("initGame( 9x9 ) returns legalMoves", (done) => { + Game({ gameData: { boardSize: 9 } }) + .initGame() .legalMoves.should.eql(emptyBoard9); done(); }); - it('initGame( 9x9 ) with all levels of handicap returns legalMoves with stones', done => { - Game({gameData: { boardSize: 9, handicap: 2 }}).initGame() - .legalMoves.should.eql({...emptyBoard9, '3-7': 1, '7-3': 1 }); - Game({gameData: { boardSize: 9, handicap: 3 }}).initGame() - .legalMoves.should.eql({...emptyBoard9, '7-7': 1, '3-7': 1, '7-3': 1 }); - Game({gameData: { boardSize: 9, handicap: 4 }}).initGame() - .legalMoves.should.eql({...emptyBoard9, '3-3': 1, '7-7': 1, '3-7': 1, '7-3': 1 }); + it("initGame( 9x9 ) with all levels of handicap returns legalMoves with stones", (done) => { + Game({ gameData: { boardSize: 9, handicap: 2 } }) + .initGame() + .legalMoves.should.eql({ ...emptyBoard9, "3-7": 1, "7-3": 1 }); + Game({ gameData: { boardSize: 9, handicap: 3 } }) + .initGame() + .legalMoves.should.eql({ ...emptyBoard9, "7-7": 1, "3-7": 1, "7-3": 1 }); + Game({ gameData: { boardSize: 9, handicap: 4 } }) + .initGame() + .legalMoves.should.eql({ + ...emptyBoard9, + "3-3": 1, + "7-7": 1, + "3-7": 1, + "7-3": 1, + }); done(); }); }); -describe('Game.makeMove({ player: str, pos: { x: int, y: int } })', () => { - it('makeMove returns game object with proper board', done => { - Game().initGame().makeMove({ player: 'black', pos: { x: 4, y: 4 } }) - .legalMoves.should.eql({ ...emptyBoard, '4-4': 1 }); - Game({ gameData: { handicap: 2 } }).initGame().makeMove({ player: 'white', pos: { x: 4, y: 4 } }) - .legalMoves.should.eql({ ...emptyBoard, '4-16': 1, '16-4': 1, '4-4': -1 }); +describe("Game.makeMove({ player: str, pos: { x: int, y: int } })", () => { + it("makeMove returns game object with proper board", (done) => { + Game() + .initGame() + .makeMove({ player: "black", pos: { x: 4, y: 4 } }) + .legalMoves.should.eql({ ...emptyBoard, "4-4": 1 }); + Game({ gameData: { handicap: 2 } }) + .initGame() + .makeMove({ player: "white", pos: { x: 4, y: 4 } }) + .legalMoves.should.eql({ + ...emptyBoard, + "4-16": 1, + "16-4": 1, + "4-4": -1, + }); done(); }); - - it('makeMove returns success: false with move out of turn', done => { - Game().initGame().makeMove({ player: 'white', pos: { x: 4, y: 4 } }) + + it("makeMove returns success: false with move out of turn", (done) => { + Game() + .initGame() + .makeMove({ player: "white", pos: { x: 4, y: 4 } }) .success.should.eql(false); - Game({ gameData: { handicap: 2 } }).initGame().makeMove({ player: 'black', pos: { x: 4, y: 4 } }) + Game({ gameData: { handicap: 2 } }) + .initGame() + .makeMove({ player: "black", pos: { x: 4, y: 4 } }) .success.should.eql(false); done(); }); - it('makeMove returns success: false when move is at occupied point', done => { - Game({ gameData: { handicap: 2 } }).initGame().makeMove({ player: 'white', pos: { x: 4, y: 16 } }) + it("makeMove returns success: false when move is at occupied point", (done) => { + Game({ gameData: { handicap: 2 } }) + .initGame() + .makeMove({ player: "white", pos: { x: 4, y: 16 } }) .success.should.eql(false); done(); }); - - it('makeMove next to adjacent stone of the same color joins stones as a group', done => { - const game = Game({ gameData: { handicap: 2 } }).initGame() // 4 3 4 - .makeMove({ player: 'white', pos: { x: 4, y: 4 } }) // 14 1 4 -1 -1 - .makeMove({ player: 'black', pos: { x: 4, y: 15 }}) // 15 1 5 -1 - .makeMove({ player: 'white', pos: { x: 3, y: 4 } }) // 16 1h - .makeMove({ player: 'black', pos: { x: 4, y: 14 }}) - .makeMove({ player: 'white', pos: { x: 4, y: 5 }}) - - const blackGroupKey = game.boardState['4-14'].group; + + it("makeMove next to adjacent stone of the same color joins stones as a group", (done) => { + const game = Game({ gameData: { handicap: 2 } }) + .initGame() // 4 3 4 + .makeMove({ player: "white", pos: { x: 4, y: 4 } }) // 14 1 4 -1 -1 + .makeMove({ player: "black", pos: { x: 4, y: 15 } }) // 15 1 5 -1 + .makeMove({ player: "white", pos: { x: 3, y: 4 } }) // 16 1h + .makeMove({ player: "black", pos: { x: 4, y: 14 } }) + .makeMove({ player: "white", pos: { x: 4, y: 5 } }); + + const blackGroupKey = game.boardState["4-14"].group; const blackGroup = game.groups[blackGroupKey].stones; - blackGroup.has(game.boardState['4-14']).should.eql(true); - blackGroup.has(game.boardState['4-15']).should.eql(true); - blackGroup.has(game.boardState['4-16']).should.eql(true); - const whiteGroupKey = game.boardState['4-4'].group; + blackGroup.has(game.boardState["4-14"]).should.eql(true); + blackGroup.has(game.boardState["4-15"]).should.eql(true); + blackGroup.has(game.boardState["4-16"]).should.eql(true); + const whiteGroupKey = game.boardState["4-4"].group; const whiteGroup = game.groups[whiteGroupKey].stones; - whiteGroup.has(game.boardState['4-4']).should.eql(true); - whiteGroup.has(game.boardState['3-4']).should.eql(true); - whiteGroup.has(game.boardState['4-5']).should.eql(true); + whiteGroup.has(game.boardState["4-4"]).should.eql(true); + whiteGroup.has(game.boardState["3-4"]).should.eql(true); + whiteGroup.has(game.boardState["4-5"]).should.eql(true); done(); }); - - const noGroupGame = Game({ gameData: { handicap: 2 } }).initGame() // 3 4 - .makeMove({ player: 'white', pos: { x: 4, y: 15 } }) // 14 1 - .makeMove({ player: 'black', pos: { x: 4, y: 14 }}) // 15 1 -1 no groups - .makeMove({ player: 'white', pos: { x: 3, y: 16 } }) // 16 -1 1h - .makeMove({ player: 'black', pos: { x: 3, y: 15 }}); - it('makeMove next to adjacent stone of different color does not join stones as a group', done => { - const hoshiGroupKey = noGroupGame.boardState['4-16'].group; + const noGroupGame = Game({ gameData: { handicap: 2 } }) + .initGame() // 3 4 + .makeMove({ player: "white", pos: { x: 4, y: 15 } }) // 14 1 + .makeMove({ player: "black", pos: { x: 4, y: 14 } }) // 15 1 -1 no groups + .makeMove({ player: "white", pos: { x: 3, y: 16 } }) // 16 -1 1h + .makeMove({ player: "black", pos: { x: 3, y: 15 } }); + + it("makeMove next to adjacent stone of different color does not join stones as a group", (done) => { + const hoshiGroupKey = noGroupGame.boardState["4-16"].group; const hoshiGroup = noGroupGame.groups[hoshiGroupKey].stones; - hoshiGroup.has(noGroupGame.boardState['4-16']).should.eql(true); - hoshiGroup.has(noGroupGame.boardState['4-15']).should.eql(false); - hoshiGroup.has(noGroupGame.boardState['3-14']).should.eql(false); - hoshiGroup.has(noGroupGame.boardState['3-15']).should.eql(false); + hoshiGroup.has(noGroupGame.boardState["4-16"]).should.eql(true); + hoshiGroup.has(noGroupGame.boardState["4-15"]).should.eql(false); + hoshiGroup.has(noGroupGame.boardState["3-14"]).should.eql(false); + hoshiGroup.has(noGroupGame.boardState["3-15"]).should.eql(false); done(); - }) + }); - it('makeMove next to adjacent stone of different color should yield proper liberties', done => { - const hoshiGroup = noGroupGame.boardState['4-16'].group; + it("makeMove next to adjacent stone of different color should yield proper liberties", (done) => { + const hoshiGroup = noGroupGame.boardState["4-16"].group; const hoshiGroupLiberties = noGroupGame.groups[hoshiGroup].liberties; hoshiGroupLiberties.size.should.eql(2); - const fourFifteen = noGroupGame.boardState['4-15'].group; + const fourFifteen = noGroupGame.boardState["4-15"].group; const fourFifteenLiberties = noGroupGame.groups[fourFifteen].liberties; fourFifteenLiberties.size.should.eql(1); done(); - }) + }); - it('makeMove returns success: false when move is made in point with no liberties', done => { - const point = Game({ gameData: { handicap: 2 } }).initGame() // 15 16 17 - .makeMove({ player: 'white', pos: { x: 4, y: 4 } }).makeMove({ player: 'black', pos: { x: 6, y: 16 } }) // 4 1 - .makeMove({ player: 'white', pos: { x: 16, y: 16 }}).makeMove({ player: 'black', pos: { x: 5, y: 15 } }) // 5 1 x 1 - .makeMove({ player: 'white', pos: { x: 16, y: 10 }}).makeMove({ player: 'black', pos: { x: 5, y: 17 } }) // 6 1 - .makeMove({ player: 'white', pos: { x: 5, y: 16 }}) - point.success.should.eql(false); + it("makeMove returns success: false when move is made in point with no liberties", (done) => { + const point = Game({ gameData: { handicap: 2 } }) + .initGame() // 15 16 17 + .makeMove({ player: "white", pos: { x: 4, y: 4 } }) + .makeMove({ player: "black", pos: { x: 6, y: 16 } }) // 4 1 + .makeMove({ player: "white", pos: { x: 16, y: 16 } }) + .makeMove({ player: "black", pos: { x: 5, y: 15 } }) // 5 1 x 1 + .makeMove({ player: "white", pos: { x: 16, y: 10 } }) + .makeMove({ player: "black", pos: { x: 5, y: 17 } }) // 6 1 + .makeMove({ player: "white", pos: { x: 5, y: 16 } }); + point.success.should.eql(false); done(); }); }); -describe('makeMove group join and basic capture logic', () => { - const joinGame = Game().initGame() - .makeMove({ player: 'black', pos: { x: 4, y: 17 } }) // 3 4 5 - .makeMove({ player: 'white', pos: { x: 3, y: 16 } }) // 15 -1 - .makeMove({ player: 'black', pos: { x: 5, y: 16 } }) // 16 -1 1 1 - .makeMove({ player: 'white', pos: { x: 4, y: 15 } }) // 17 1 - .makeMove({ player: 'black', pos: { x: 4, y: 16 } }); - - it('gain liberties from group smoke test', done => { +describe("makeMove group join and basic capture logic", () => { + const joinGame = Game() + .initGame() + .makeMove({ player: "black", pos: { x: 4, y: 17 } }) // 3 4 5 + .makeMove({ player: "white", pos: { x: 3, y: 16 } }) // 15 -1 + .makeMove({ player: "black", pos: { x: 5, y: 16 } }) // 16 -1 1 1 + .makeMove({ player: "white", pos: { x: 4, y: 15 } }) // 17 1 + .makeMove({ player: "black", pos: { x: 4, y: 16 } }); + + it("gain liberties from group smoke test", (done) => { joinGame.success.should.eql(true); done(); }); - it('stones in group have same group property', done => { - joinGame.boardState['4-16'].group.should.eql(joinGame.boardState['5-16'].group); - joinGame.boardState['4-16'].group.should.eql(joinGame.boardState['4-17'].group); - joinGame.boardState['4-17'].group.should.eql(joinGame.boardState['4-16'].group); - joinGame.boardState['4-17'].group.should.eql(joinGame.boardState['5-16'].group); - joinGame.boardState['5-16'].group.should.eql(joinGame.boardState['4-17'].group); - joinGame.boardState['5-16'].group.should.eql(joinGame.boardState['4-16'].group); + it("stones in group have same group property", (done) => { + joinGame.boardState["4-16"].group.should.eql( + joinGame.boardState["5-16"].group + ); + joinGame.boardState["4-16"].group.should.eql( + joinGame.boardState["4-17"].group + ); + joinGame.boardState["4-17"].group.should.eql( + joinGame.boardState["4-16"].group + ); + joinGame.boardState["4-17"].group.should.eql( + joinGame.boardState["5-16"].group + ); + joinGame.boardState["5-16"].group.should.eql( + joinGame.boardState["4-17"].group + ); + joinGame.boardState["5-16"].group.should.eql( + joinGame.boardState["4-16"].group + ); done(); - }) + }); - it('stones in group should have proper liberties', done => { - const group = joinGame.boardState['4-16'].group; - joinGame.groups[group] - .liberties.size.should.eql(5); + it("stones in group should have proper liberties", (done) => { + const group = joinGame.boardState["4-16"].group; + joinGame.groups[group].liberties.size.should.eql(5); done(); - }) + }); - it('group with only remaining liberty at point to be played returns success: false', done => { - Game({ gameData: { handicap: 2 } }).initGame() - .makeMove({ player: 'white', pos: { x: 4, y: 15 } }) // 3 4 5 6 - .makeMove({ player: 'black', pos: { x: 4, y: 4 } }) // 15 -1 -1 - .makeMove({ player: 'white', pos: { x: 5, y: 15 } }) // 16 -1 1h 0 -1 - .makeMove({ player: 'black', pos: { x: 16, y: 16 } }) // 17 -1 -1 - .makeMove({ player: 'white', pos: { x: 3, y: 16 } }) - .makeMove({ player: 'black', pos: { x: 4, y: 10 } }) - .makeMove({ player: 'white', pos: { x: 6, y: 16 } }) - .makeMove({ player: 'black', pos: { x: 10, y: 4 } }) - .makeMove({ player: 'white', pos: { x: 4, y: 17 } }) - .makeMove({ player: 'black', pos: { x: 10, y: 16 } }) - .makeMove({ player: 'white', pos: { x: 5, y: 17 } }) - .makeMove({ player: 'black', pos: { x: 5, y: 16 } }) + it("group with only remaining liberty at point to be played returns success: false", (done) => { + Game({ gameData: { handicap: 2 } }) + .initGame() + .makeMove({ player: "white", pos: { x: 4, y: 15 } }) // 3 4 5 6 + .makeMove({ player: "black", pos: { x: 4, y: 4 } }) // 15 -1 -1 + .makeMove({ player: "white", pos: { x: 5, y: 15 } }) // 16 -1 1h 0 -1 + .makeMove({ player: "black", pos: { x: 16, y: 16 } }) // 17 -1 -1 + .makeMove({ player: "white", pos: { x: 3, y: 16 } }) + .makeMove({ player: "black", pos: { x: 4, y: 10 } }) + .makeMove({ player: "white", pos: { x: 6, y: 16 } }) + .makeMove({ player: "black", pos: { x: 10, y: 4 } }) + .makeMove({ player: "white", pos: { x: 4, y: 17 } }) + .makeMove({ player: "black", pos: { x: 10, y: 16 } }) + .makeMove({ player: "white", pos: { x: 5, y: 17 } }) + .makeMove({ player: "black", pos: { x: 5, y: 16 } }) .success.should.eql(false); done(); - }) + }); - const captureGame = () => Game({ gameData: { handicap: 2 } }).initGame() - .makeMove({ player: 'white', pos: { x: 4, y: 15 } }) // 3 4 5 - .makeMove({ player: 'black', pos: { x: 4, y: 4 } }) // 15 -1 - .makeMove({ player: 'white', pos: { x: 3, y: 16 } }) // 16 -1 0 -1 - .makeMove({ player: 'black', pos: { x: 4, y: 10 } }) // 17 -1 - .makeMove({ player: 'white', pos: { x: 5, y: 16 } }) // 4,16 captured - .makeMove({ player: 'black', pos: { x: 10, y: 4 } }) - - it('makeMove capture smoke test', done => { - captureGame().makeMove({ player: 'white', pos: { x: 4, y: 17 } }) + const captureGame = () => + Game({ gameData: { handicap: 2 } }) + .initGame() + .makeMove({ player: "white", pos: { x: 4, y: 15 } }) // 3 4 5 + .makeMove({ player: "black", pos: { x: 4, y: 4 } }) // 15 -1 + .makeMove({ player: "white", pos: { x: 3, y: 16 } }) // 16 -1 0 -1 + .makeMove({ player: "black", pos: { x: 4, y: 10 } }) // 17 -1 + .makeMove({ player: "white", pos: { x: 5, y: 16 } }) // 4,16 captured + .makeMove({ player: "black", pos: { x: 10, y: 4 } }); + + it("makeMove capture smoke test", (done) => { + captureGame() + .makeMove({ player: "white", pos: { x: 4, y: 17 } }) .success.should.eql(true); done(); }); - it('makeMove assesses captures', done => { - captureGame().boardState['4-17'].capturing[-1].size.should.eql(1); - done(); - }) - - it('makeMove capture removes captured stone', done => { - captureGame().makeMove({ player: 'white', pos: { x: 4, y: 17 } }) - .boardState['4-16'].stone.should.eql(0); + it("makeMove assesses captures", (done) => { + captureGame().boardState["4-17"].capturing[-1].size.should.eql(1); done(); }); - - it('makeMove capture increases capturing players captures', done => { - captureGame().makeMove({ player: 'white', pos: { x: 4, y: 17 } }) + + it("makeMove capture removes captured stone", (done) => { + captureGame() + .makeMove({ player: "white", pos: { x: 4, y: 17 } }) + .boardState["4-16"].stone.should.eql(0); + done(); + }); + + it("makeMove capture increases capturing players captures", (done) => { + captureGame() + .makeMove({ player: "white", pos: { x: 4, y: 17 } }) .playerState.wCaptures.should.eql(1); done(); }); - const traceGame = () => Game().initGame() - .makeMove({ player: 'black', pos: { x: 1, y: 1 } }) // 1 2 3 - .makeMove({ player: 'white', pos: { x: 1, y: 2 } }) // 1 1 -1 1 - .makeMove({ player: 'black', pos: { x: 2, y: 2 } }) // 2 -1 1 - .makeMove({ player: 'white', pos: { x: 2, y: 1 } }) + const traceGame = () => + Game() + .initGame() + .makeMove({ player: "black", pos: { x: 1, y: 1 } }) // 1 2 3 + .makeMove({ player: "white", pos: { x: 1, y: 2 } }) // 1 1 -1 1 + .makeMove({ player: "black", pos: { x: 2, y: 2 } }) // 2 -1 1 + .makeMove({ player: "white", pos: { x: 2, y: 1 } }); - it('point at captured stone becomes liberty', done => { + it("point at captured stone becomes liberty", (done) => { const game = traceGame(); - const group = game.boardState['1-2'].group; - game.groups[group].liberties.has(game.boardState['1-1']).should.eql(true) + const group = game.boardState["1-2"].group; + game.groups[group].liberties.has(game.boardState["1-1"]).should.eql(true); done(); - }) + }); - it('capture does not leave trace filled liberty', done => { + it("capture does not leave trace filled liberty", (done) => { traceGame() - .makeMove({ player: 'black', pos: { x: 1, y: 3 } }) - .boardState['1-2'].stone.should.eql(-1); + .makeMove({ player: "black", pos: { x: 1, y: 3 } }) + .boardState["1-2"].stone.should.eql(-1); done(); - }) + }); - const multiCaptureGame = () => Game().initGame() - .makeMove({ player: 'black', pos: { x: 4, y: 17 } }) - .makeMove({ player: 'white', pos: { x: 3, y: 16 } }) - .makeMove({ player: 'black', pos: { x: 5, y: 16 } }) - .makeMove({ player: 'white', pos: { x: 4, y: 15 } }) - .makeMove({ player: 'black', pos: { x: 4, y: 16 } }) - .makeMove({ player: 'black', pos: { x: 4, y: 10 } }) // 3 4 5 6 - .makeMove({ player: 'white', pos: { x: 3, y: 17 } }) // 15 -1 -1 - .makeMove({ player: 'black', pos: { x: 10, y: 4 } }) // 16 -1 1 1 -1 - .makeMove({ player: 'white', pos: { x: 5, y: 15 } }) // 17 -1 1 -1 - .makeMove({ player: 'black', pos: { x: 10, y: 8 } }) // 18 -1 - .makeMove({ player: 'white', pos: { x: 4, y: 18} }) - .makeMove({ player: 'black', pos: { x: 3, y: 6 } }) - .makeMove({ player: 'white', pos: { x: 5, y: 17} }) - .makeMove({ player: 'black', pos: { x: 6, y: 3 } }); - - it('smoke test multi stone group capture', done => { - multiCaptureGame().makeMove({ player: 'white', pos: { x: 6, y: 16} }) + const multiCaptureGame = () => + Game() + .initGame() + .makeMove({ player: "black", pos: { x: 4, y: 17 } }) + .makeMove({ player: "white", pos: { x: 3, y: 16 } }) + .makeMove({ player: "black", pos: { x: 5, y: 16 } }) + .makeMove({ player: "white", pos: { x: 4, y: 15 } }) + .makeMove({ player: "black", pos: { x: 4, y: 16 } }) + .makeMove({ player: "black", pos: { x: 4, y: 10 } }) // 3 4 5 6 + .makeMove({ player: "white", pos: { x: 3, y: 17 } }) // 15 -1 -1 + .makeMove({ player: "black", pos: { x: 10, y: 4 } }) // 16 -1 1 1 -1 + .makeMove({ player: "white", pos: { x: 5, y: 15 } }) // 17 -1 1 -1 + .makeMove({ player: "black", pos: { x: 10, y: 8 } }) // 18 -1 + .makeMove({ player: "white", pos: { x: 4, y: 18 } }) + .makeMove({ player: "black", pos: { x: 3, y: 6 } }) + .makeMove({ player: "white", pos: { x: 5, y: 17 } }) + .makeMove({ player: "black", pos: { x: 6, y: 3 } }); + + it("smoke test multi stone group capture", (done) => { + multiCaptureGame() + .makeMove({ player: "white", pos: { x: 6, y: 16 } }) .success.should.eql(true); done(); }); - - it('multi stone group full group is in capturing', done => { - const game = multiCaptureGame() - const group = game.boardState['4-16'].group; - game.boardState['6-16'].capturing[-1].has(group).should.eql(true); + + it("multi stone group full group is in capturing", (done) => { + const game = multiCaptureGame(); + const group = game.boardState["4-16"].group; + game.boardState["6-16"].capturing[-1].has(group).should.eql(true); done(); }); - - it('multi stone group capture all points are 0', done => { + + it("multi stone group capture all points are 0", (done) => { const game = multiCaptureGame(); - game.makeMove({ player: 'white', pos: { x: 6, y: 16} }); - game.boardState['5-16'].stone.should.eql(0) - game.boardState['4-16'].stone.should.eql(0) - game.boardState['4-17'].stone.should.eql(0) + game.makeMove({ player: "white", pos: { x: 6, y: 16 } }); + game.boardState["5-16"].stone.should.eql(0); + game.boardState["4-16"].stone.should.eql(0); + game.boardState["4-17"].stone.should.eql(0); done(); }); - - it('multi stone group capture scores points properly', done => { + + it("multi stone group capture scores points properly", (done) => { const game = multiCaptureGame(); - game.makeMove({ player: 'white', pos: { x: 6, y: 16} }); + game.makeMove({ player: "white", pos: { x: 6, y: 16 } }); game.playerState.wCaptures.should.eql(3); done(); - }) + }); }); -describe('capture logic: snapback, ko and playing in eyes', () => { - it('playing in an eye formed by capture yields success: true', done => { - Game().initGame() - .makeMove({ player: 'black', pos: { x: 4, y: 4 } }) // 3 4 5 - .makeMove({ player: 'white', pos: { x: 5, y: 4 } }) // 4 1 - .makeMove({ player: 'black', pos: { x: 5, y: 5 } }) // 5 1 -1 1 - .makeMove({ player: 'white', pos: { x: 16, y: 16 } }) // 6 1 - .makeMove({ player: 'black', pos: { x: 5, y: 3 } }) // (9) at {5, 4} - .makeMove({ player: 'white', pos: { x: 16, y: 4 } }) - .makeMove({ player: 'black', pos: { x: 6, y: 4 } }) - .makeMove({ player: 'white', pos: { x: 4, y: 16 } }) - .makeMove({ player: 'black', pos: { x: 5, y: 4 } }) - .success.should.eql(true); - done(); - }); - - const snapbackGame = () => Game().initGame() - .makeMove({ player: 'black', pos: { x: 4, y: 4 } }) // 3 4 5 6 7 - .makeMove({ player: 'white', pos: { x: 5, y: 4 } }) // 4 1 1 -1 - .makeMove({ player: 'black', pos: { x: 5, y: 6 } }) // 5 1 -1 -1 1 -1 - .makeMove({ player: 'white', pos: { x: 5, y: 7 } }) // 6 1 1 -1 - .makeMove({ player: 'black', pos: { x: 4, y: 5 } }) // (13) at {5,6} - .makeMove({ player: 'white', pos: { x: 4, y: 6 } }) - .makeMove({ player: 'black', pos: { x: 5, y: 3 } }) - .makeMove({ player: 'white', pos: { x: 6, y: 6 } }) - .makeMove({ player: 'black', pos: { x: 6, y: 5 } }) - .makeMove({ player: 'white', pos: { x: 16, y: 16 } }) - .makeMove({ player: 'black', pos: { x: 6, y: 4 } }) - .makeMove({ player: 'white', pos: { x: 5, y: 5 } }) - .makeMove({ player: 'black', pos: { x: 5, y: 6 } }); - - it('snapback functions properly', done => { - snapbackGame() +describe("capture logic: snapback, ko and playing in eyes", () => { + it("playing in an eye formed by capture yields success: true", (done) => { + Game() + .initGame() + .makeMove({ player: "black", pos: { x: 4, y: 4 } }) // 3 4 5 + .makeMove({ player: "white", pos: { x: 5, y: 4 } }) // 4 1 + .makeMove({ player: "black", pos: { x: 5, y: 5 } }) // 5 1 -1 1 + .makeMove({ player: "white", pos: { x: 16, y: 16 } }) // 6 1 + .makeMove({ player: "black", pos: { x: 5, y: 3 } }) // (9) at {5, 4} + .makeMove({ player: "white", pos: { x: 16, y: 4 } }) + .makeMove({ player: "black", pos: { x: 6, y: 4 } }) + .makeMove({ player: "white", pos: { x: 4, y: 16 } }) + .makeMove({ player: "black", pos: { x: 5, y: 4 } }) .success.should.eql(true); done(); }); - const koGame = () => Game().initGame() - .makeMove({ player: 'black', pos: { x: 4, y: 4 } }) // 3 4 5 6 - .makeMove({ player: 'white', pos: { x: 4, y: 5 } }) // 4 1 -1 - .makeMove({ player: 'black', pos: { x: 5, y: 3 } }) // 5 1 -1 1 -1 - .makeMove({ player: 'white', pos: { x: 5, y: 6 } }) // 6 1 -1 - .makeMove({ player: 'black', pos: { x: 6, y: 4 } }) - .makeMove({ player: 'white', pos: { x: 6, y: 5 } }) - .makeMove({ player: 'black', pos: { x: 5, y: 5 } }) - .makeMove({ player: 'white', pos: { x: 5, y: 4 } }) + const snapbackGame = () => + Game() + .initGame() + .makeMove({ player: "black", pos: { x: 4, y: 4 } }) // 3 4 5 6 7 + .makeMove({ player: "white", pos: { x: 5, y: 4 } }) // 4 1 1 -1 + .makeMove({ player: "black", pos: { x: 5, y: 6 } }) // 5 1 -1 -1 1 -1 + .makeMove({ player: "white", pos: { x: 5, y: 7 } }) // 6 1 1 -1 + .makeMove({ player: "black", pos: { x: 4, y: 5 } }) // (13) at {5,6} + .makeMove({ player: "white", pos: { x: 4, y: 6 } }) + .makeMove({ player: "black", pos: { x: 5, y: 3 } }) + .makeMove({ player: "white", pos: { x: 6, y: 6 } }) + .makeMove({ player: "black", pos: { x: 6, y: 5 } }) + .makeMove({ player: "white", pos: { x: 16, y: 16 } }) + .makeMove({ player: "black", pos: { x: 6, y: 4 } }) + .makeMove({ player: "white", pos: { x: 5, y: 5 } }) + .makeMove({ player: "black", pos: { x: 5, y: 6 } }); - it('ko recognized properly on Point', done => { + it("snapback functions properly", (done) => { + snapbackGame().success.should.eql(true); + done(); + }); + + const koGame = () => + Game() + .initGame() + .makeMove({ player: "black", pos: { x: 4, y: 4 } }) // 3 4 5 6 + .makeMove({ player: "white", pos: { x: 4, y: 5 } }) // 4 1 -1 + .makeMove({ player: "black", pos: { x: 5, y: 3 } }) // 5 1 -1 1 -1 + .makeMove({ player: "white", pos: { x: 5, y: 6 } }) // 6 1 -1 + .makeMove({ player: "black", pos: { x: 6, y: 4 } }) + .makeMove({ player: "white", pos: { x: 6, y: 5 } }) + .makeMove({ player: "black", pos: { x: 5, y: 5 } }) + .makeMove({ player: "white", pos: { x: 5, y: 4 } }); + + it("ko recognized properly on Point", (done) => { + koGame().boardState["5-5"].ko.should.eql(true); + done(); + }); + + it("ko marked on Game object", (done) => { + koGame().kos.should.eql(["5-5"]); + done(); + }); + + it("ko marked in legalMoves", (done) => { + koGame().legalMoves["5-5"].should.eql("k"); + done(); + }); + + it("ko cleared on Point after move", (done) => { koGame() - .boardState['5-5'].ko.should.eql(true); - done(); - }) - - it('ko marked on Game object', done => { - koGame().kos.should.eql(['5-5']); + .makeMove({ player: "black", pos: { x: 16, y: 16 } }) + .makeMove({ player: "white", pos: { x: 4, y: 16 } }) + .boardState["5-5"].ko.should.eql(false); done(); }); - it('ko marked in legalMoves', done => { - koGame().legalMoves['5-5'].should.eql('k'); - done(); - }) - - it('ko cleared on Point after move', done => { - koGame().makeMove({ player: 'black', pos: { x: 16, y: 16 } }) - .makeMove({ player: 'white', pos: { x: 4, y: 16 } }) - .boardState['5-5'].ko.should.eql(false); + it("ko cleared on Game after move", (done) => { + koGame() + .makeMove({ player: "black", pos: { x: 16, y: 16 } }) + .makeMove({ player: "white", pos: { x: 4, y: 16 } }) + .kos.should.eql([]); done(); }); - - it('ko cleared on Game after move', done => { - koGame().makeMove({ player: 'black', pos: { x: 16, y: 16 } }) - .makeMove({ player: 'white', pos: { x: 4, y: 16 } }) - .kos.should.eql([]) - done(); - }); - - it('ko cleared on legalMoves after move', done => { - koGame().makeMove({ player: 'black', pos: { x: 16, y: 16 } }) - .makeMove({ player: 'white', pos: { x: 4, y: 16 } }) - .legalMoves['5-5'].should.eql('l'); + + it("ko cleared on legalMoves after move", (done) => { + koGame() + .makeMove({ player: "black", pos: { x: 16, y: 16 } }) + .makeMove({ player: "white", pos: { x: 4, y: 16 } }) + .legalMoves["5-5"].should.eql("l"); done(); }); }); -describe('Game history functionality', () => { - const firstMove = { player: 'black', pos: { x: 4, y: 4 }}; - const secondMove = { player: 'white', pos: { x: 16, y: 16 }}; - const thirdMove = { player: 'black', pos: { x: 16, y: 4 } }; - const fourthMove = { player: 'white', pos: { x: 4, y: 16 }}; - const fifthMove = { player: 'black', pos: { x: 10, y: 4 } }; - const sixthMove = { player: 'white', pos: { x: 4, y: 10 }}; - const seventhMove = { player: 'black', pos: { x: 10, y: 16 } }; - const eighthMove = { player: 'white', pos: { x: 16, y: 10 }}; +describe("Game history functionality", () => { + const firstMove = { player: "black", pos: { x: 4, y: 4 } }; + const secondMove = { player: "white", pos: { x: 16, y: 16 } }; + const thirdMove = { player: "black", pos: { x: 16, y: 4 } }; + const fourthMove = { player: "white", pos: { x: 4, y: 16 } }; + const fifthMove = { player: "black", pos: { x: 10, y: 4 } }; + const sixthMove = { player: "white", pos: { x: 4, y: 10 } }; + const seventhMove = { player: "black", pos: { x: 10, y: 16 } }; + const eighthMove = { player: "white", pos: { x: 16, y: 10 } }; - it('makeMove creates gameRecord item', done => { - Game().initGame() - .makeMove(firstMove).gameRecord[0].should.eql(firstMove); + it("makeMove creates gameRecord item", (done) => { + Game().initGame().makeMove(firstMove).gameRecord[0].should.eql(firstMove); done(); }); - - it('makeMove holds history', done => { - const game = Game().initGame() - .makeMove(firstMove).makeMove(secondMove); + + it("makeMove holds history", (done) => { + const game = Game().initGame().makeMove(firstMove).makeMove(secondMove); game.gameRecord[0].should.eql(firstMove); - game.gameRecord[1].should.eql(secondMove) + game.gameRecord[1].should.eql(secondMove); done(); }); - const rewoundGame = () => Game().initGame() - .makeMove(firstMove) - .makeMove(secondMove) - .makeMove(thirdMove) - .returnToMove(-1); + const rewoundGame = () => + Game() + .initGame() + .makeMove(firstMove) + .makeMove(secondMove) + .makeMove(thirdMove) + .returnToMove(-1); - it('Game.returnToMove returns new Game with gameRecord', done => { - rewoundGame() - .gameRecord.should.eql([ firstMove, secondMove ]) - done(); - }); - - it('Game.returnToMove returns new Game with new board state', done => { - rewoundGame() - .boardState['16-4'].stone.should.eql(0); - rewoundGame() - .boardState['4-4'].stone.should.eql(1); - rewoundGame() - .boardState['16-16'].stone.should.eql(-1); + it("Game.returnToMove returns new Game with gameRecord", (done) => { + rewoundGame().gameRecord.should.eql([firstMove, secondMove]); done(); }); - const resetGame = () => [ - firstMove, secondMove, thirdMove, fourthMove, fifthMove, sixthMove, seventhMove, eighthMove - ].reduce((game, move) => game.makeMove(move), Game().initGame()); - - it('Game.returnToMove(0) returns to init board state', done => { - const erasedGame = resetGame() - .returnToMove(0) - erasedGame.gameRecord.should.eql([]) - erasedGame.boardState['4-4'].stone.should.eql(0) + it("Game.returnToMove returns new Game with new board state", (done) => { + rewoundGame().boardState["16-4"].stone.should.eql(0); + rewoundGame().boardState["4-4"].stone.should.eql(1); + rewoundGame().boardState["16-16"].stone.should.eql(-1); done(); }); - it('Game.returnToMove(5) returns to state after 5th move', done => { - const fifthMoveGame = resetGame() - .returnToMove(5); - fifthMoveGame.gameRecord.should.eql([firstMove, secondMove, thirdMove, fourthMove, fifthMove]); - fifthMoveGame.boardState['10-4'].stone.should.eql(1) - fifthMoveGame.boardState['4-10'].stone.should.eql(0) + const resetGame = () => + [ + firstMove, + secondMove, + thirdMove, + fourthMove, + fifthMove, + sixthMove, + seventhMove, + eighthMove, + ].reduce((game, move) => game.makeMove(move), Game().initGame()); + + it("Game.returnToMove(0) returns to init board state", (done) => { + const erasedGame = resetGame().returnToMove(0); + erasedGame.gameRecord.should.eql([]); + erasedGame.boardState["4-4"].stone.should.eql(0); + done(); + }); + + it("Game.returnToMove(5) returns to state after 5th move", (done) => { + const fifthMoveGame = resetGame().returnToMove(5); + fifthMoveGame.gameRecord.should.eql([ + firstMove, + secondMove, + thirdMove, + fourthMove, + fifthMove, + ]); + fifthMoveGame.boardState["10-4"].stone.should.eql(1); + fifthMoveGame.boardState["4-10"].stone.should.eql(0); done(); }); }); -describe('Game end logic', () => { - it('resignation results in game end', done => { - Game().initGame().submitResign('black') - .getMeta().should.eql({...initialMeta, winner: -1}); +describe("Game end logic", () => { + it("resignation results in game end", (done) => { + Game() + .initGame() + .submitResign("black") + .getMeta() + .should.eql({ ...initialMeta, winner: -1 }); done(); }); - it('pass changes game turn', done => { - Game().initGame().submitPass('black') - .getMeta().turn.should.eql(-1); + it("pass changes game turn", (done) => { + Game().initGame().submitPass("black").getMeta().turn.should.eql(-1); done(); }); - it('pass adds null move to gameRecord', done => { - Game().initGame().submitPass('black').getMeta() - .gameRecord.should.eql([ { player: 'black', pos: { x: null, y: null } } ]) + it("pass adds null move to gameRecord", (done) => { + Game() + .initGame() + .submitPass("black") + .getMeta() + .gameRecord.should.eql([{ player: "black", pos: { x: null, y: null } }]); done(); }); - it('move after makes normal change in game state', done => { - Game().initGame() - .makeMove({ player: 'black', pos: { x: 4, y: 4 } }) - .makeMove({ player: 'white', pos: { x: 4, y: 5 } }) - .makeMove({ player: 'black', pos: { x: 5, y: 3 } }) - .submitPass('white') - .makeMove({ player: 'black', pos: { x: 16, y: 16 } }) - .legalMoves.should.eql({...emptyBoard, '4-4': 1, '4-5': -1, '5-3': 1, '16-16': 1}) + it("move after makes normal change in game state", (done) => { + Game() + .initGame() + .makeMove({ player: "black", pos: { x: 4, y: 4 } }) + .makeMove({ player: "white", pos: { x: 4, y: 5 } }) + .makeMove({ player: "black", pos: { x: 5, y: 3 } }) + .submitPass("white") + .makeMove({ player: "black", pos: { x: 16, y: 16 } }) + .legalMoves.should.eql({ + ...emptyBoard, + "4-4": 1, + "4-5": -1, + "5-3": 1, + "16-16": 1, + }); done(); }); - it('two nonconsecutive passes continue game', done => { - Game().initGame() - .makeMove({ player: 'black', pos: { x: 4, y: 4 } }) - .makeMove({ player: 'white', pos: { x: 4, y: 5 } }) - .makeMove({ player: 'black', pos: { x: 5, y: 3 } }) - .submitPass('white') - .makeMove({ player: 'black', pos: { x: 16, y: 16 } }) - .submitPass('white') - .getMeta().should.eql({...initialMeta, pass: 1, turn: 1, gameRecord: [ { player: 'black', pos: { x: 4, y: 4 } }, { player: 'white', pos: { x: 4, y: 5 } }, { player: 'black', pos: { x: 5, y: 3 } }, { player: 'white', pos: { x: null, y: null } }, { player: 'black', pos: { x: 16, y: 16 } }, { player: 'white', pos: { x: null, y: null } } ]}) + it("two nonconsecutive passes continue game", (done) => { + Game() + .initGame() + .makeMove({ player: "black", pos: { x: 4, y: 4 } }) + .makeMove({ player: "white", pos: { x: 4, y: 5 } }) + .makeMove({ player: "black", pos: { x: 5, y: 3 } }) + .submitPass("white") + .makeMove({ player: "black", pos: { x: 16, y: 16 } }) + .submitPass("white") + .getMeta() + .should.eql({ + ...initialMeta, + pass: 1, + turn: 1, + gameRecord: [ + { player: "black", pos: { x: 4, y: 4 } }, + { player: "white", pos: { x: 4, y: 5 } }, + { player: "black", pos: { x: 5, y: 3 } }, + { player: "white", pos: { x: null, y: null } }, + { player: "black", pos: { x: 16, y: 16 } }, + { player: "white", pos: { x: null, y: null } }, + ], + }); done(); }); - it('consecutive passes end game', done => { - Game().initGame() - .makeMove({ player: 'black', pos: { x: 4, y: 4 } }) - .makeMove({ player: 'white', pos: { x: 4, y: 5 } }) - .makeMove({ player: 'black', pos: { x: 5, y: 3 } }) - .submitPass('white') - .submitPass('black') + it("consecutive passes end game", (done) => { + Game() + .initGame() + .makeMove({ player: "black", pos: { x: 4, y: 4 } }) + .makeMove({ player: "white", pos: { x: 4, y: 5 } }) + .makeMove({ player: "black", pos: { x: 5, y: 3 } }) + .submitPass("white") + .submitPass("black") .turn.should.eql(0); done(); - }) -}) + }); + it("consecutive passes return board state with null stone groups", (done) => { + Game({ gameRecord: [] }).initGame(); + done(); + }); +}); const initialMeta = { - winner: null, - turn: 0, + winner: null, + turn: 0, pass: 0, komi: 6.5, handicap: 0, @@ -558,57 +782,627 @@ const initialMeta = { bCaptures: 0, wCaptures: 0, bScore: 0, - wScore: 0 - }, - gameRecord: [] -} + wScore: 0, + }, + gameRecord: [], +}; const emptyBoard9 = { - '1-1': 'l','1-2': 'l','1-3': 'l','1-4': 'l','1-5': 'l','1-6': 'l','1-7': 'l','1-8': 'l','1-9': 'l', - '2-1': 'l','2-2': 'l','2-3': 'l','2-4': 'l','2-5': 'l','2-6': 'l','2-7': 'l','2-8': 'l','2-9': 'l', - '3-1': 'l','3-2': 'l','3-3': 'l','3-4': 'l','3-5': 'l','3-6': 'l','3-7': 'l','3-8': 'l','3-9': 'l', - '4-1': 'l','4-2': 'l','4-3': 'l','4-4': 'l','4-5': 'l','4-6': 'l','4-7': 'l','4-8': 'l','4-9': 'l', - '5-1': 'l','5-2': 'l','5-3': 'l','5-4': 'l','5-5': 'l','5-6': 'l','5-7': 'l','5-8': 'l','5-9': 'l', - '6-1': 'l','6-2': 'l','6-3': 'l','6-4': 'l','6-5': 'l','6-6': 'l','6-7': 'l','6-8': 'l','6-9': 'l', - '7-1': 'l','7-2': 'l','7-3': 'l','7-4': 'l','7-5': 'l','7-6': 'l','7-7': 'l','7-8': 'l','7-9': 'l', - '8-1': 'l','8-2': 'l','8-3': 'l','8-4': 'l','8-5': 'l','8-6': 'l','8-7': 'l','8-8': 'l','8-9': 'l', - '9-1': 'l','9-2': 'l','9-3': 'l','9-4': 'l','9-5': 'l','9-6': 'l','9-7': 'l','9-8': 'l','9-9': 'l' -} + "1-1": "l", + "1-2": "l", + "1-3": "l", + "1-4": "l", + "1-5": "l", + "1-6": "l", + "1-7": "l", + "1-8": "l", + "1-9": "l", + "2-1": "l", + "2-2": "l", + "2-3": "l", + "2-4": "l", + "2-5": "l", + "2-6": "l", + "2-7": "l", + "2-8": "l", + "2-9": "l", + "3-1": "l", + "3-2": "l", + "3-3": "l", + "3-4": "l", + "3-5": "l", + "3-6": "l", + "3-7": "l", + "3-8": "l", + "3-9": "l", + "4-1": "l", + "4-2": "l", + "4-3": "l", + "4-4": "l", + "4-5": "l", + "4-6": "l", + "4-7": "l", + "4-8": "l", + "4-9": "l", + "5-1": "l", + "5-2": "l", + "5-3": "l", + "5-4": "l", + "5-5": "l", + "5-6": "l", + "5-7": "l", + "5-8": "l", + "5-9": "l", + "6-1": "l", + "6-2": "l", + "6-3": "l", + "6-4": "l", + "6-5": "l", + "6-6": "l", + "6-7": "l", + "6-8": "l", + "6-9": "l", + "7-1": "l", + "7-2": "l", + "7-3": "l", + "7-4": "l", + "7-5": "l", + "7-6": "l", + "7-7": "l", + "7-8": "l", + "7-9": "l", + "8-1": "l", + "8-2": "l", + "8-3": "l", + "8-4": "l", + "8-5": "l", + "8-6": "l", + "8-7": "l", + "8-8": "l", + "8-9": "l", + "9-1": "l", + "9-2": "l", + "9-3": "l", + "9-4": "l", + "9-5": "l", + "9-6": "l", + "9-7": "l", + "9-8": "l", + "9-9": "l", +}; const emptyBoard13 = { - '1-1': 'l','1-2': 'l','1-3': 'l','1-4': 'l','1-5': 'l','1-6': 'l','1-7': 'l','1-8': 'l','1-9': 'l','1-10': 'l','1-11': 'l','1-12': 'l','1-13': 'l', - '2-1': 'l','2-2': 'l','2-3': 'l','2-4': 'l','2-5': 'l','2-6': 'l','2-7': 'l','2-8': 'l','2-9': 'l','2-10': 'l','2-11': 'l','2-12': 'l','2-13': 'l', - '3-1': 'l','3-2': 'l','3-3': 'l','3-4': 'l','3-5': 'l','3-6': 'l','3-7': 'l','3-8': 'l','3-9': 'l','3-10': 'l','3-11': 'l','3-12': 'l','3-13': 'l', - '4-1': 'l','4-2': 'l','4-3': 'l','4-4': 'l','4-5': 'l','4-6': 'l','4-7': 'l','4-8': 'l','4-9': 'l','4-10': 'l','4-11': 'l','4-12': 'l','4-13': 'l', - '5-1': 'l','5-2': 'l','5-3': 'l','5-4': 'l','5-5': 'l','5-6': 'l','5-7': 'l','5-8': 'l','5-9': 'l','5-10': 'l','5-11': 'l','5-12': 'l','5-13': 'l', - '6-1': 'l','6-2': 'l','6-3': 'l','6-4': 'l','6-5': 'l','6-6': 'l','6-7': 'l','6-8': 'l','6-9': 'l','6-10': 'l','6-11': 'l','6-12': 'l','6-13': 'l', - '7-1': 'l','7-2': 'l','7-3': 'l','7-4': 'l','7-5': 'l','7-6': 'l','7-7': 'l','7-8': 'l','7-9': 'l','7-10': 'l','7-11': 'l','7-12': 'l','7-13': 'l', - '8-1': 'l','8-2': 'l','8-3': 'l','8-4': 'l','8-5': 'l','8-6': 'l','8-7': 'l','8-8': 'l','8-9': 'l','8-10': 'l','8-11': 'l','8-12': 'l','8-13': 'l', - '9-1': 'l','9-2': 'l','9-3': 'l','9-4': 'l','9-5': 'l','9-6': 'l','9-7': 'l','9-8': 'l','9-9': 'l','9-10': 'l','9-11': 'l','9-12': 'l','9-13': 'l', - '10-1': 'l','10-2': 'l','10-3': 'l','10-4': 'l','10-5': 'l','10-6': 'l','10-7': 'l','10-8': 'l','10-9': 'l','10-10': 'l','10-11': 'l','10-12': 'l','10-13': 'l', - '11-1': 'l','11-2': 'l','11-3': 'l','11-4': 'l','11-5': 'l','11-6': 'l','11-7': 'l','11-8': 'l','11-9': 'l','11-10': 'l','11-11': 'l','11-12': 'l','11-13': 'l', - '12-1': 'l','12-2': 'l','12-3': 'l','12-4': 'l','12-5': 'l','12-6': 'l','12-7': 'l','12-8': 'l','12-9': 'l','12-10': 'l','12-11': 'l','12-12': 'l','12-13': 'l', - '13-1': 'l','13-2': 'l','13-3': 'l','13-4': 'l','13-5': 'l','13-6': 'l','13-7': 'l','13-8': 'l','13-9': 'l','13-10': 'l','13-11': 'l','13-12': 'l','13-13': 'l' -} + "1-1": "l", + "1-2": "l", + "1-3": "l", + "1-4": "l", + "1-5": "l", + "1-6": "l", + "1-7": "l", + "1-8": "l", + "1-9": "l", + "1-10": "l", + "1-11": "l", + "1-12": "l", + "1-13": "l", + "2-1": "l", + "2-2": "l", + "2-3": "l", + "2-4": "l", + "2-5": "l", + "2-6": "l", + "2-7": "l", + "2-8": "l", + "2-9": "l", + "2-10": "l", + "2-11": "l", + "2-12": "l", + "2-13": "l", + "3-1": "l", + "3-2": "l", + "3-3": "l", + "3-4": "l", + "3-5": "l", + "3-6": "l", + "3-7": "l", + "3-8": "l", + "3-9": "l", + "3-10": "l", + "3-11": "l", + "3-12": "l", + "3-13": "l", + "4-1": "l", + "4-2": "l", + "4-3": "l", + "4-4": "l", + "4-5": "l", + "4-6": "l", + "4-7": "l", + "4-8": "l", + "4-9": "l", + "4-10": "l", + "4-11": "l", + "4-12": "l", + "4-13": "l", + "5-1": "l", + "5-2": "l", + "5-3": "l", + "5-4": "l", + "5-5": "l", + "5-6": "l", + "5-7": "l", + "5-8": "l", + "5-9": "l", + "5-10": "l", + "5-11": "l", + "5-12": "l", + "5-13": "l", + "6-1": "l", + "6-2": "l", + "6-3": "l", + "6-4": "l", + "6-5": "l", + "6-6": "l", + "6-7": "l", + "6-8": "l", + "6-9": "l", + "6-10": "l", + "6-11": "l", + "6-12": "l", + "6-13": "l", + "7-1": "l", + "7-2": "l", + "7-3": "l", + "7-4": "l", + "7-5": "l", + "7-6": "l", + "7-7": "l", + "7-8": "l", + "7-9": "l", + "7-10": "l", + "7-11": "l", + "7-12": "l", + "7-13": "l", + "8-1": "l", + "8-2": "l", + "8-3": "l", + "8-4": "l", + "8-5": "l", + "8-6": "l", + "8-7": "l", + "8-8": "l", + "8-9": "l", + "8-10": "l", + "8-11": "l", + "8-12": "l", + "8-13": "l", + "9-1": "l", + "9-2": "l", + "9-3": "l", + "9-4": "l", + "9-5": "l", + "9-6": "l", + "9-7": "l", + "9-8": "l", + "9-9": "l", + "9-10": "l", + "9-11": "l", + "9-12": "l", + "9-13": "l", + "10-1": "l", + "10-2": "l", + "10-3": "l", + "10-4": "l", + "10-5": "l", + "10-6": "l", + "10-7": "l", + "10-8": "l", + "10-9": "l", + "10-10": "l", + "10-11": "l", + "10-12": "l", + "10-13": "l", + "11-1": "l", + "11-2": "l", + "11-3": "l", + "11-4": "l", + "11-5": "l", + "11-6": "l", + "11-7": "l", + "11-8": "l", + "11-9": "l", + "11-10": "l", + "11-11": "l", + "11-12": "l", + "11-13": "l", + "12-1": "l", + "12-2": "l", + "12-3": "l", + "12-4": "l", + "12-5": "l", + "12-6": "l", + "12-7": "l", + "12-8": "l", + "12-9": "l", + "12-10": "l", + "12-11": "l", + "12-12": "l", + "12-13": "l", + "13-1": "l", + "13-2": "l", + "13-3": "l", + "13-4": "l", + "13-5": "l", + "13-6": "l", + "13-7": "l", + "13-8": "l", + "13-9": "l", + "13-10": "l", + "13-11": "l", + "13-12": "l", + "13-13": "l", +}; const emptyBoard = { - '1-1': 'l','1-2': 'l','1-3': 'l','1-4': 'l','1-5': 'l','1-6': 'l','1-7': 'l','1-8': 'l','1-9': 'l','1-10': 'l','1-11': 'l','1-12': 'l','1-13': 'l','1-14': 'l','1-15': 'l','1-16': 'l','1-17': 'l','1-18': 'l','1-19': 'l', - '2-1': 'l','2-2': 'l','2-3': 'l','2-4': 'l','2-5': 'l','2-6': 'l','2-7': 'l','2-8': 'l','2-9': 'l','2-10': 'l','2-11': 'l','2-12': 'l','2-13': 'l','2-14': 'l','2-15': 'l','2-16': 'l','2-17': 'l','2-18': 'l','2-19': 'l', - '3-1': 'l','3-2': 'l','3-3': 'l','3-4': 'l','3-5': 'l','3-6': 'l','3-7': 'l','3-8': 'l','3-9': 'l','3-10': 'l','3-11': 'l','3-12': 'l','3-13': 'l','3-14': 'l','3-15': 'l','3-16': 'l','3-17': 'l','3-18': 'l','3-19': 'l', - '4-1': 'l','4-2': 'l','4-3': 'l','4-4': 'l','4-5': 'l','4-6': 'l','4-7': 'l','4-8': 'l','4-9': 'l','4-10': 'l','4-11': 'l','4-12': 'l','4-13': 'l','4-14': 'l','4-15': 'l','4-16': 'l','4-17': 'l','4-18': 'l','4-19': 'l', - '5-1': 'l','5-2': 'l','5-3': 'l','5-4': 'l','5-5': 'l','5-6': 'l','5-7': 'l','5-8': 'l','5-9': 'l','5-10': 'l','5-11': 'l','5-12': 'l','5-13': 'l','5-14': 'l','5-15': 'l','5-16': 'l','5-17': 'l','5-18': 'l','5-19': 'l', - '6-1': 'l','6-2': 'l','6-3': 'l','6-4': 'l','6-5': 'l','6-6': 'l','6-7': 'l','6-8': 'l','6-9': 'l','6-10': 'l','6-11': 'l','6-12': 'l','6-13': 'l','6-14': 'l','6-15': 'l','6-16': 'l','6-17': 'l','6-18': 'l','6-19': 'l', - '7-1': 'l','7-2': 'l','7-3': 'l','7-4': 'l','7-5': 'l','7-6': 'l','7-7': 'l','7-8': 'l','7-9': 'l','7-10': 'l','7-11': 'l','7-12': 'l','7-13': 'l','7-14': 'l','7-15': 'l','7-16': 'l','7-17': 'l','7-18': 'l','7-19': 'l', - '8-1': 'l','8-2': 'l','8-3': 'l','8-4': 'l','8-5': 'l','8-6': 'l','8-7': 'l','8-8': 'l','8-9': 'l','8-10': 'l','8-11': 'l','8-12': 'l','8-13': 'l','8-14': 'l','8-15': 'l','8-16': 'l','8-17': 'l','8-18': 'l','8-19': 'l', - '9-1': 'l','9-2': 'l','9-3': 'l','9-4': 'l','9-5': 'l','9-6': 'l','9-7': 'l','9-8': 'l','9-9': 'l','9-10': 'l','9-11': 'l','9-12': 'l','9-13': 'l','9-14': 'l','9-15': 'l','9-16': 'l','9-17': 'l','9-18': 'l','9-19': 'l', - '10-1': 'l','10-2': 'l','10-3': 'l','10-4': 'l','10-5': 'l','10-6': 'l','10-7': 'l','10-8': 'l','10-9': 'l','10-10': 'l','10-11': 'l','10-12': 'l','10-13': 'l','10-14': 'l','10-15': 'l','10-16': 'l','10-17': 'l','10-18': 'l','10-19': 'l', - '11-1': 'l','11-2': 'l','11-3': 'l','11-4': 'l','11-5': 'l','11-6': 'l','11-7': 'l','11-8': 'l','11-9': 'l','11-10': 'l','11-11': 'l','11-12': 'l','11-13': 'l','11-14': 'l','11-15': 'l','11-16': 'l','11-17': 'l','11-18': 'l','11-19': 'l', - '12-1': 'l','12-2': 'l','12-3': 'l','12-4': 'l','12-5': 'l','12-6': 'l','12-7': 'l','12-8': 'l','12-9': 'l','12-10': 'l','12-11': 'l','12-12': 'l','12-13': 'l','12-14': 'l','12-15': 'l','12-16': 'l','12-17': 'l','12-18': 'l','12-19': 'l', - '13-1': 'l','13-2': 'l','13-3': 'l','13-4': 'l','13-5': 'l','13-6': 'l','13-7': 'l','13-8': 'l','13-9': 'l','13-10': 'l','13-11': 'l','13-12': 'l','13-13': 'l','13-14': 'l','13-15': 'l','13-16': 'l','13-17': 'l','13-18': 'l','13-19': 'l', - '14-1': 'l','14-2': 'l','14-3': 'l','14-4': 'l','14-5': 'l','14-6': 'l','14-7': 'l','14-8': 'l','14-9': 'l','14-10': 'l','14-11': 'l','14-12': 'l','14-13': 'l','14-14': 'l','14-15': 'l','14-16': 'l','14-17': 'l','14-18': 'l','14-19': 'l', - '15-1': 'l','15-2': 'l','15-3': 'l','15-4': 'l','15-5': 'l','15-6': 'l','15-7': 'l','15-8': 'l','15-9': 'l','15-10': 'l','15-11': 'l','15-12': 'l','15-13': 'l','15-14': 'l','15-15': 'l','15-16': 'l','15-17': 'l','15-18': 'l','15-19': 'l', - '16-1': 'l','16-2': 'l','16-3': 'l','16-4': 'l','16-5': 'l','16-6': 'l','16-7': 'l','16-8': 'l','16-9': 'l','16-10': 'l','16-11': 'l','16-12': 'l','16-13': 'l','16-14': 'l','16-15': 'l','16-16': 'l','16-17': 'l','16-18': 'l','16-19': 'l', - '17-1': 'l','17-2': 'l','17-3': 'l','17-4': 'l','17-5': 'l','17-6': 'l','17-7': 'l','17-8': 'l','17-9': 'l','17-10': 'l','17-11': 'l','17-12': 'l','17-13': 'l','17-14': 'l','17-15': 'l','17-16': 'l','17-17': 'l','17-18': 'l','17-19': 'l', - '18-1': 'l','18-2': 'l','18-3': 'l','18-4': 'l','18-5': 'l','18-6': 'l','18-7': 'l','18-8': 'l','18-9': 'l','18-10': 'l','18-11': 'l','18-12': 'l','18-13': 'l','18-14': 'l','18-15': 'l','18-16': 'l','18-17': 'l','18-18': 'l','18-19': 'l', - '19-1': 'l','19-2': 'l','19-3': 'l','19-4': 'l','19-5': 'l','19-6': 'l','19-7': 'l','19-8': 'l','19-9': 'l','19-10': 'l','19-11': 'l','19-12': 'l','19-13': 'l','19-14': 'l','19-15': 'l','19-16': 'l','19-17': 'l','19-18': 'l','19-19': 'l' + "1-1": "l", + "1-2": "l", + "1-3": "l", + "1-4": "l", + "1-5": "l", + "1-6": "l", + "1-7": "l", + "1-8": "l", + "1-9": "l", + "1-10": "l", + "1-11": "l", + "1-12": "l", + "1-13": "l", + "1-14": "l", + "1-15": "l", + "1-16": "l", + "1-17": "l", + "1-18": "l", + "1-19": "l", + "2-1": "l", + "2-2": "l", + "2-3": "l", + "2-4": "l", + "2-5": "l", + "2-6": "l", + "2-7": "l", + "2-8": "l", + "2-9": "l", + "2-10": "l", + "2-11": "l", + "2-12": "l", + "2-13": "l", + "2-14": "l", + "2-15": "l", + "2-16": "l", + "2-17": "l", + "2-18": "l", + "2-19": "l", + "3-1": "l", + "3-2": "l", + "3-3": "l", + "3-4": "l", + "3-5": "l", + "3-6": "l", + "3-7": "l", + "3-8": "l", + "3-9": "l", + "3-10": "l", + "3-11": "l", + "3-12": "l", + "3-13": "l", + "3-14": "l", + "3-15": "l", + "3-16": "l", + "3-17": "l", + "3-18": "l", + "3-19": "l", + "4-1": "l", + "4-2": "l", + "4-3": "l", + "4-4": "l", + "4-5": "l", + "4-6": "l", + "4-7": "l", + "4-8": "l", + "4-9": "l", + "4-10": "l", + "4-11": "l", + "4-12": "l", + "4-13": "l", + "4-14": "l", + "4-15": "l", + "4-16": "l", + "4-17": "l", + "4-18": "l", + "4-19": "l", + "5-1": "l", + "5-2": "l", + "5-3": "l", + "5-4": "l", + "5-5": "l", + "5-6": "l", + "5-7": "l", + "5-8": "l", + "5-9": "l", + "5-10": "l", + "5-11": "l", + "5-12": "l", + "5-13": "l", + "5-14": "l", + "5-15": "l", + "5-16": "l", + "5-17": "l", + "5-18": "l", + "5-19": "l", + "6-1": "l", + "6-2": "l", + "6-3": "l", + "6-4": "l", + "6-5": "l", + "6-6": "l", + "6-7": "l", + "6-8": "l", + "6-9": "l", + "6-10": "l", + "6-11": "l", + "6-12": "l", + "6-13": "l", + "6-14": "l", + "6-15": "l", + "6-16": "l", + "6-17": "l", + "6-18": "l", + "6-19": "l", + "7-1": "l", + "7-2": "l", + "7-3": "l", + "7-4": "l", + "7-5": "l", + "7-6": "l", + "7-7": "l", + "7-8": "l", + "7-9": "l", + "7-10": "l", + "7-11": "l", + "7-12": "l", + "7-13": "l", + "7-14": "l", + "7-15": "l", + "7-16": "l", + "7-17": "l", + "7-18": "l", + "7-19": "l", + "8-1": "l", + "8-2": "l", + "8-3": "l", + "8-4": "l", + "8-5": "l", + "8-6": "l", + "8-7": "l", + "8-8": "l", + "8-9": "l", + "8-10": "l", + "8-11": "l", + "8-12": "l", + "8-13": "l", + "8-14": "l", + "8-15": "l", + "8-16": "l", + "8-17": "l", + "8-18": "l", + "8-19": "l", + "9-1": "l", + "9-2": "l", + "9-3": "l", + "9-4": "l", + "9-5": "l", + "9-6": "l", + "9-7": "l", + "9-8": "l", + "9-9": "l", + "9-10": "l", + "9-11": "l", + "9-12": "l", + "9-13": "l", + "9-14": "l", + "9-15": "l", + "9-16": "l", + "9-17": "l", + "9-18": "l", + "9-19": "l", + "10-1": "l", + "10-2": "l", + "10-3": "l", + "10-4": "l", + "10-5": "l", + "10-6": "l", + "10-7": "l", + "10-8": "l", + "10-9": "l", + "10-10": "l", + "10-11": "l", + "10-12": "l", + "10-13": "l", + "10-14": "l", + "10-15": "l", + "10-16": "l", + "10-17": "l", + "10-18": "l", + "10-19": "l", + "11-1": "l", + "11-2": "l", + "11-3": "l", + "11-4": "l", + "11-5": "l", + "11-6": "l", + "11-7": "l", + "11-8": "l", + "11-9": "l", + "11-10": "l", + "11-11": "l", + "11-12": "l", + "11-13": "l", + "11-14": "l", + "11-15": "l", + "11-16": "l", + "11-17": "l", + "11-18": "l", + "11-19": "l", + "12-1": "l", + "12-2": "l", + "12-3": "l", + "12-4": "l", + "12-5": "l", + "12-6": "l", + "12-7": "l", + "12-8": "l", + "12-9": "l", + "12-10": "l", + "12-11": "l", + "12-12": "l", + "12-13": "l", + "12-14": "l", + "12-15": "l", + "12-16": "l", + "12-17": "l", + "12-18": "l", + "12-19": "l", + "13-1": "l", + "13-2": "l", + "13-3": "l", + "13-4": "l", + "13-5": "l", + "13-6": "l", + "13-7": "l", + "13-8": "l", + "13-9": "l", + "13-10": "l", + "13-11": "l", + "13-12": "l", + "13-13": "l", + "13-14": "l", + "13-15": "l", + "13-16": "l", + "13-17": "l", + "13-18": "l", + "13-19": "l", + "14-1": "l", + "14-2": "l", + "14-3": "l", + "14-4": "l", + "14-5": "l", + "14-6": "l", + "14-7": "l", + "14-8": "l", + "14-9": "l", + "14-10": "l", + "14-11": "l", + "14-12": "l", + "14-13": "l", + "14-14": "l", + "14-15": "l", + "14-16": "l", + "14-17": "l", + "14-18": "l", + "14-19": "l", + "15-1": "l", + "15-2": "l", + "15-3": "l", + "15-4": "l", + "15-5": "l", + "15-6": "l", + "15-7": "l", + "15-8": "l", + "15-9": "l", + "15-10": "l", + "15-11": "l", + "15-12": "l", + "15-13": "l", + "15-14": "l", + "15-15": "l", + "15-16": "l", + "15-17": "l", + "15-18": "l", + "15-19": "l", + "16-1": "l", + "16-2": "l", + "16-3": "l", + "16-4": "l", + "16-5": "l", + "16-6": "l", + "16-7": "l", + "16-8": "l", + "16-9": "l", + "16-10": "l", + "16-11": "l", + "16-12": "l", + "16-13": "l", + "16-14": "l", + "16-15": "l", + "16-16": "l", + "16-17": "l", + "16-18": "l", + "16-19": "l", + "17-1": "l", + "17-2": "l", + "17-3": "l", + "17-4": "l", + "17-5": "l", + "17-6": "l", + "17-7": "l", + "17-8": "l", + "17-9": "l", + "17-10": "l", + "17-11": "l", + "17-12": "l", + "17-13": "l", + "17-14": "l", + "17-15": "l", + "17-16": "l", + "17-17": "l", + "17-18": "l", + "17-19": "l", + "18-1": "l", + "18-2": "l", + "18-3": "l", + "18-4": "l", + "18-5": "l", + "18-6": "l", + "18-7": "l", + "18-8": "l", + "18-9": "l", + "18-10": "l", + "18-11": "l", + "18-12": "l", + "18-13": "l", + "18-14": "l", + "18-15": "l", + "18-16": "l", + "18-17": "l", + "18-18": "l", + "18-19": "l", + "19-1": "l", + "19-2": "l", + "19-3": "l", + "19-4": "l", + "19-5": "l", + "19-6": "l", + "19-7": "l", + "19-8": "l", + "19-9": "l", + "19-10": "l", + "19-11": "l", + "19-12": "l", + "19-13": "l", + "19-14": "l", + "19-15": "l", + "19-16": "l", + "19-17": "l", + "19-18": "l", + "19-19": "l", }; diff --git a/packages/server/test/gameServices.spec.js b/packages/server/test/gameServices.spec.js index 6df6f7b..3eb44e4 100644 --- a/packages/server/test/gameServices.spec.js +++ b/packages/server/test/gameServices.spec.js @@ -1,72 +1,423 @@ -const chai = require('chai'); +const chai = require("chai"); const should = chai.should(); -const gameServices = require('../services/gameServices'); +const gameServices = require("../services/gameServices")(); -describe.skip('game services', () => { - afterEach(() => gameServices.dropGame(1)) +describe("game services", () => { + afterEach(() => gameServices.dropGame(1)); - it('init game returns game board', done => { - gameServices.initGame({ id: 1, handicap: 4 }) - .board.should.eql(fourHandicapBoard) + it("init game returns game board", (done) => { + gameServices + .initGame({ id: 1, handicap: 4 }) + .board.should.eql(fourHandicapBoard); done(); }); - it('init game returns game metadata', done => { - const { board, ...game } = gameServices.initGame({ id: 1, handicap: 4 }) - game.should.eql({...initialMeta, handicap: 4, turn: -1}); - done(); - }) - - it('games services places move', done => { - gameServices.initGame({ id: 1, handicap: 4 }) - const move = { player: 'white', pos: { x: 6, y: 3 } } - const afterMove = gameServices.makeMove({ id: 1, move }); - const afterMoveShould = { board: { ...fourHandicapBoard, '6-3': -1}, ...initialMeta, handicap: 4, turn: 1, gameRecord: [ move ] }; - afterMove.should.eql(afterMoveShould); + it("init game returns game metadata", (done) => { + const { board, ...game } = gameServices.initGame({ id: 1, handicap: 4 }); + game.should.eql({ ...initialMeta, handicap: 4, turn: -1 }); done(); }); - - it('illegal move returns error message', done => { + + it("games services places move", async () => { gameServices.initGame({ id: 1, handicap: 4 }); - gameServices.makeMove({ id: 1, move: { player: 'white', pos: { x:4, y:4 } } }) - .message.should.equal('illegal move'); - done(); + const move = { player: "white", pos: { x: 6, y: 3 } }; + const afterMove = await gameServices.makeMove({ id: 1, move }); + const afterMoveShould = { + board: { ...fourHandicapBoard, "6-3": -1 }, + ...initialMeta, + handicap: 4, + turn: 1, + gameRecord: [move], + }; + afterMove.should.eql(afterMoveShould); }); - it('game services places move next to stone', done => { - gameServices.initGame({ id: 1, handicap:4 }); - gameServices.makeMove({ id: 1, move: { player: 'white', pos: { x: 4, y: 3 } } }) - .board.should.eql({ ...fourHandicapBoard, '4-3': -1 }); - done(); - }) -}) + it("illegal move returns error message", async () => { + gameServices.initGame({ id: 1, handicap: 4 }); + const afterMove = await gameServices.makeMove({ + id: 1, + move: { player: "white", pos: { x: 4, y: 4 } }, + }); + afterMove.message.should.equal("illegal move"); + }); + it("game services places move next to stone", async () => { + gameServices.initGame({ id: 1, handicap: 4 }); + const afterMove = await gameServices.makeMove({ + id: 1, + move: { player: "white", pos: { x: 4, y: 3 } }, + }); + afterMove.board.should.eql({ ...fourHandicapBoard, "4-3": -1 }); + }); +}); const fourHandicapBoard = { - '1-1': 'l','1-2': 'l','1-3': 'l','1-4': 'l','1-5': 'l','1-6': 'l','1-7': 'l','1-8': 'l','1-9': 'l','1-10': 'l','1-11': 'l','1-12': 'l','1-13': 'l','1-14': 'l','1-15': 'l','1-16': 'l','1-17': 'l','1-18': 'l','1-19': 'l', - '2-1': 'l','2-2': 'l','2-3': 'l','2-4': 'l','2-5': 'l','2-6': 'l','2-7': 'l','2-8': 'l','2-9': 'l','2-10': 'l','2-11': 'l','2-12': 'l','2-13': 'l','2-14': 'l','2-15': 'l','2-16': 'l','2-17': 'l','2-18': 'l','2-19': 'l', - '3-1': 'l','3-2': 'l','3-3': 'l','3-4': 'l','3-5': 'l','3-6': 'l','3-7': 'l','3-8': 'l','3-9': 'l','3-10': 'l','3-11': 'l','3-12': 'l','3-13': 'l','3-14': 'l','3-15': 'l','3-16': 'l','3-17': 'l','3-18': 'l','3-19': 'l', - '4-1': 'l','4-2': 'l','4-3': 'l','4-4': 1,'4-5': 'l','4-6': 'l','4-7': 'l','4-8': 'l','4-9': 'l','4-10': 'l','4-11': 'l','4-12': 'l','4-13': 'l','4-14': 'l','4-15': 'l','4-16': 1,'4-17': 'l','4-18': 'l','4-19': 'l', - '5-1': 'l','5-2': 'l','5-3': 'l','5-4': 'l','5-5': 'l','5-6': 'l','5-7': 'l','5-8': 'l','5-9': 'l','5-10': 'l','5-11': 'l','5-12': 'l','5-13': 'l','5-14': 'l','5-15': 'l','5-16': 'l','5-17': 'l','5-18': 'l','5-19': 'l', - '6-1': 'l','6-2': 'l','6-3': 'l','6-4': 'l','6-5': 'l','6-6': 'l','6-7': 'l','6-8': 'l','6-9': 'l','6-10': 'l','6-11': 'l','6-12': 'l','6-13': 'l','6-14': 'l','6-15': 'l','6-16': 'l','6-17': 'l','6-18': 'l','6-19': 'l', - '7-1': 'l','7-2': 'l','7-3': 'l','7-4': 'l','7-5': 'l','7-6': 'l','7-7': 'l','7-8': 'l','7-9': 'l','7-10': 'l','7-11': 'l','7-12': 'l','7-13': 'l','7-14': 'l','7-15': 'l','7-16': 'l','7-17': 'l','7-18': 'l','7-19': 'l', - '8-1': 'l','8-2': 'l','8-3': 'l','8-4': 'l','8-5': 'l','8-6': 'l','8-7': 'l','8-8': 'l','8-9': 'l','8-10': 'l','8-11': 'l','8-12': 'l','8-13': 'l','8-14': 'l','8-15': 'l','8-16': 'l','8-17': 'l','8-18': 'l','8-19': 'l', - '9-1': 'l','9-2': 'l','9-3': 'l','9-4': 'l','9-5': 'l','9-6': 'l','9-7': 'l','9-8': 'l','9-9': 'l','9-10': 'l','9-11': 'l','9-12': 'l','9-13': 'l','9-14': 'l','9-15': 'l','9-16': 'l','9-17': 'l','9-18': 'l','9-19': 'l', - '10-1': 'l','10-2': 'l','10-3': 'l','10-4': 'l','10-5': 'l','10-6': 'l','10-7': 'l','10-8': 'l','10-9': 'l','10-10': 'l','10-11': 'l','10-12': 'l','10-13': 'l','10-14': 'l','10-15': 'l','10-16': 'l','10-17': 'l','10-18': 'l','10-19': 'l', - '11-1': 'l','11-2': 'l','11-3': 'l','11-4': 'l','11-5': 'l','11-6': 'l','11-7': 'l','11-8': 'l','11-9': 'l','11-10': 'l','11-11': 'l','11-12': 'l','11-13': 'l','11-14': 'l','11-15': 'l','11-16': 'l','11-17': 'l','11-18': 'l','11-19': 'l', - '12-1': 'l','12-2': 'l','12-3': 'l','12-4': 'l','12-5': 'l','12-6': 'l','12-7': 'l','12-8': 'l','12-9': 'l','12-10': 'l','12-11': 'l','12-12': 'l','12-13': 'l','12-14': 'l','12-15': 'l','12-16': 'l','12-17': 'l','12-18': 'l','12-19': 'l', - '13-1': 'l','13-2': 'l','13-3': 'l','13-4': 'l','13-5': 'l','13-6': 'l','13-7': 'l','13-8': 'l','13-9': 'l','13-10': 'l','13-11': 'l','13-12': 'l','13-13': 'l','13-14': 'l','13-15': 'l','13-16': 'l','13-17': 'l','13-18': 'l','13-19': 'l', - '14-1': 'l','14-2': 'l','14-3': 'l','14-4': 'l','14-5': 'l','14-6': 'l','14-7': 'l','14-8': 'l','14-9': 'l','14-10': 'l','14-11': 'l','14-12': 'l','14-13': 'l','14-14': 'l','14-15': 'l','14-16': 'l','14-17': 'l','14-18': 'l','14-19': 'l', - '15-1': 'l','15-2': 'l','15-3': 'l','15-4': 'l','15-5': 'l','15-6': 'l','15-7': 'l','15-8': 'l','15-9': 'l','15-10': 'l','15-11': 'l','15-12': 'l','15-13': 'l','15-14': 'l','15-15': 'l','15-16': 'l','15-17': 'l','15-18': 'l','15-19': 'l', - '16-1': 'l','16-2': 'l','16-3': 'l','16-4': 1,'16-5': 'l','16-6': 'l','16-7': 'l','16-8': 'l','16-9': 'l','16-10': 'l','16-11': 'l','16-12': 'l','16-13': 'l','16-14': 'l','16-15': 'l','16-16': 1,'16-17': 'l','16-18': 'l','16-19': 'l', - '17-1': 'l','17-2': 'l','17-3': 'l','17-4': 'l','17-5': 'l','17-6': 'l','17-7': 'l','17-8': 'l','17-9': 'l','17-10': 'l','17-11': 'l','17-12': 'l','17-13': 'l','17-14': 'l','17-15': 'l','17-16': 'l','17-17': 'l','17-18': 'l','17-19': 'l', - '18-1': 'l','18-2': 'l','18-3': 'l','18-4': 'l','18-5': 'l','18-6': 'l','18-7': 'l','18-8': 'l','18-9': 'l','18-10': 'l','18-11': 'l','18-12': 'l','18-13': 'l','18-14': 'l','18-15': 'l','18-16': 'l','18-17': 'l','18-18': 'l','18-19': 'l', - '19-1': 'l','19-2': 'l','19-3': 'l','19-4': 'l','19-5': 'l','19-6': 'l','19-7': 'l','19-8': 'l','19-9': 'l','19-10': 'l','19-11': 'l','19-12': 'l','19-13': 'l','19-14': 'l','19-15': 'l','19-16': 'l','19-17': 'l','19-18': 'l','19-19': 'l' + "1-1": "l", + "1-2": "l", + "1-3": "l", + "1-4": "l", + "1-5": "l", + "1-6": "l", + "1-7": "l", + "1-8": "l", + "1-9": "l", + "1-10": "l", + "1-11": "l", + "1-12": "l", + "1-13": "l", + "1-14": "l", + "1-15": "l", + "1-16": "l", + "1-17": "l", + "1-18": "l", + "1-19": "l", + "2-1": "l", + "2-2": "l", + "2-3": "l", + "2-4": "l", + "2-5": "l", + "2-6": "l", + "2-7": "l", + "2-8": "l", + "2-9": "l", + "2-10": "l", + "2-11": "l", + "2-12": "l", + "2-13": "l", + "2-14": "l", + "2-15": "l", + "2-16": "l", + "2-17": "l", + "2-18": "l", + "2-19": "l", + "3-1": "l", + "3-2": "l", + "3-3": "l", + "3-4": "l", + "3-5": "l", + "3-6": "l", + "3-7": "l", + "3-8": "l", + "3-9": "l", + "3-10": "l", + "3-11": "l", + "3-12": "l", + "3-13": "l", + "3-14": "l", + "3-15": "l", + "3-16": "l", + "3-17": "l", + "3-18": "l", + "3-19": "l", + "4-1": "l", + "4-2": "l", + "4-3": "l", + "4-4": 1, + "4-5": "l", + "4-6": "l", + "4-7": "l", + "4-8": "l", + "4-9": "l", + "4-10": "l", + "4-11": "l", + "4-12": "l", + "4-13": "l", + "4-14": "l", + "4-15": "l", + "4-16": 1, + "4-17": "l", + "4-18": "l", + "4-19": "l", + "5-1": "l", + "5-2": "l", + "5-3": "l", + "5-4": "l", + "5-5": "l", + "5-6": "l", + "5-7": "l", + "5-8": "l", + "5-9": "l", + "5-10": "l", + "5-11": "l", + "5-12": "l", + "5-13": "l", + "5-14": "l", + "5-15": "l", + "5-16": "l", + "5-17": "l", + "5-18": "l", + "5-19": "l", + "6-1": "l", + "6-2": "l", + "6-3": "l", + "6-4": "l", + "6-5": "l", + "6-6": "l", + "6-7": "l", + "6-8": "l", + "6-9": "l", + "6-10": "l", + "6-11": "l", + "6-12": "l", + "6-13": "l", + "6-14": "l", + "6-15": "l", + "6-16": "l", + "6-17": "l", + "6-18": "l", + "6-19": "l", + "7-1": "l", + "7-2": "l", + "7-3": "l", + "7-4": "l", + "7-5": "l", + "7-6": "l", + "7-7": "l", + "7-8": "l", + "7-9": "l", + "7-10": "l", + "7-11": "l", + "7-12": "l", + "7-13": "l", + "7-14": "l", + "7-15": "l", + "7-16": "l", + "7-17": "l", + "7-18": "l", + "7-19": "l", + "8-1": "l", + "8-2": "l", + "8-3": "l", + "8-4": "l", + "8-5": "l", + "8-6": "l", + "8-7": "l", + "8-8": "l", + "8-9": "l", + "8-10": "l", + "8-11": "l", + "8-12": "l", + "8-13": "l", + "8-14": "l", + "8-15": "l", + "8-16": "l", + "8-17": "l", + "8-18": "l", + "8-19": "l", + "9-1": "l", + "9-2": "l", + "9-3": "l", + "9-4": "l", + "9-5": "l", + "9-6": "l", + "9-7": "l", + "9-8": "l", + "9-9": "l", + "9-10": "l", + "9-11": "l", + "9-12": "l", + "9-13": "l", + "9-14": "l", + "9-15": "l", + "9-16": "l", + "9-17": "l", + "9-18": "l", + "9-19": "l", + "10-1": "l", + "10-2": "l", + "10-3": "l", + "10-4": "l", + "10-5": "l", + "10-6": "l", + "10-7": "l", + "10-8": "l", + "10-9": "l", + "10-10": "l", + "10-11": "l", + "10-12": "l", + "10-13": "l", + "10-14": "l", + "10-15": "l", + "10-16": "l", + "10-17": "l", + "10-18": "l", + "10-19": "l", + "11-1": "l", + "11-2": "l", + "11-3": "l", + "11-4": "l", + "11-5": "l", + "11-6": "l", + "11-7": "l", + "11-8": "l", + "11-9": "l", + "11-10": "l", + "11-11": "l", + "11-12": "l", + "11-13": "l", + "11-14": "l", + "11-15": "l", + "11-16": "l", + "11-17": "l", + "11-18": "l", + "11-19": "l", + "12-1": "l", + "12-2": "l", + "12-3": "l", + "12-4": "l", + "12-5": "l", + "12-6": "l", + "12-7": "l", + "12-8": "l", + "12-9": "l", + "12-10": "l", + "12-11": "l", + "12-12": "l", + "12-13": "l", + "12-14": "l", + "12-15": "l", + "12-16": "l", + "12-17": "l", + "12-18": "l", + "12-19": "l", + "13-1": "l", + "13-2": "l", + "13-3": "l", + "13-4": "l", + "13-5": "l", + "13-6": "l", + "13-7": "l", + "13-8": "l", + "13-9": "l", + "13-10": "l", + "13-11": "l", + "13-12": "l", + "13-13": "l", + "13-14": "l", + "13-15": "l", + "13-16": "l", + "13-17": "l", + "13-18": "l", + "13-19": "l", + "14-1": "l", + "14-2": "l", + "14-3": "l", + "14-4": "l", + "14-5": "l", + "14-6": "l", + "14-7": "l", + "14-8": "l", + "14-9": "l", + "14-10": "l", + "14-11": "l", + "14-12": "l", + "14-13": "l", + "14-14": "l", + "14-15": "l", + "14-16": "l", + "14-17": "l", + "14-18": "l", + "14-19": "l", + "15-1": "l", + "15-2": "l", + "15-3": "l", + "15-4": "l", + "15-5": "l", + "15-6": "l", + "15-7": "l", + "15-8": "l", + "15-9": "l", + "15-10": "l", + "15-11": "l", + "15-12": "l", + "15-13": "l", + "15-14": "l", + "15-15": "l", + "15-16": "l", + "15-17": "l", + "15-18": "l", + "15-19": "l", + "16-1": "l", + "16-2": "l", + "16-3": "l", + "16-4": 1, + "16-5": "l", + "16-6": "l", + "16-7": "l", + "16-8": "l", + "16-9": "l", + "16-10": "l", + "16-11": "l", + "16-12": "l", + "16-13": "l", + "16-14": "l", + "16-15": "l", + "16-16": 1, + "16-17": "l", + "16-18": "l", + "16-19": "l", + "17-1": "l", + "17-2": "l", + "17-3": "l", + "17-4": "l", + "17-5": "l", + "17-6": "l", + "17-7": "l", + "17-8": "l", + "17-9": "l", + "17-10": "l", + "17-11": "l", + "17-12": "l", + "17-13": "l", + "17-14": "l", + "17-15": "l", + "17-16": "l", + "17-17": "l", + "17-18": "l", + "17-19": "l", + "18-1": "l", + "18-2": "l", + "18-3": "l", + "18-4": "l", + "18-5": "l", + "18-6": "l", + "18-7": "l", + "18-8": "l", + "18-9": "l", + "18-10": "l", + "18-11": "l", + "18-12": "l", + "18-13": "l", + "18-14": "l", + "18-15": "l", + "18-16": "l", + "18-17": "l", + "18-18": "l", + "18-19": "l", + "19-1": "l", + "19-2": "l", + "19-3": "l", + "19-4": "l", + "19-5": "l", + "19-6": "l", + "19-7": "l", + "19-8": "l", + "19-9": "l", + "19-10": "l", + "19-11": "l", + "19-12": "l", + "19-13": "l", + "19-14": "l", + "19-15": "l", + "19-16": "l", + "19-17": "l", + "19-18": "l", + "19-19": "l", }; const initialMeta = { - winner: null, - turn: 0, + winner: null, + turn: 0, pass: 0, komi: 6.5, handicap: 0, @@ -75,7 +426,7 @@ const initialMeta = { bCaptures: 0, wCaptures: 0, bScore: 0, - wScore: 0 - }, - gameRecord: [] -} \ No newline at end of file + wScore: 0, + }, + gameRecord: [], +};