diff --git a/packages/server/packages/play-node-go/server/services/Game.js b/packages/server/packages/play-node-go/server/services/Game.js index 4b067b2..cd77a02 100644 --- a/packages/server/packages/play-node-go/server/services/Game.js +++ b/packages/server/packages/play-node-go/server/services/Game.js @@ -62,7 +62,8 @@ class Game { this.winner = null; this.pass = null; this.turn = this.handicap ? -1 : 1; - return this.initBoard(); + this.initBoard(); + return this.getBoardState(); } initBoard = () => { @@ -79,18 +80,57 @@ class Game { if (this.handicap < 2) return; HANDI_PLACE[this.boardSize][this.handicap].forEach(pt => { if (!pt) return; - let handi = findPointFromIdx(pt); + let handi = this.findPointFromIdx(pt); handi.stone = 1; - handi.joinGroup(); + handi.joinGroup(this); }) } getBoardState = () => { return this.boardState.reduce((boardState, point) => { - boardState[point.pos[0]][point.pos[1]] = point.legal || point.stone + boardState[`${point.pos[0]}-${point.pos[1]}`] = point.legal || point.stone; + return boardState; }, {}) } + findPointFromIdx = (arr) => { + return this.boardState.find( point => point.pos[0] === arr[0] && point.pos[1] === arr[1] ); + } + + makeMove = (move) => { + const player = move.player === 'white' ? -1 : 1; + const point = this.findPointFromIdx([move.pos.X, move.pos.Y]) + if ( !checkLegal(point, this) ) throw 'illegal move'; + clearKo(this); + clearPass(this); + resolveCaptures(point, this); + point.stone = this.turn; + point.joinGroup(this); + clearCaptures(this); + this.gameRecord.push(`${STONES_DATA[this.turn]}: ${point.pos}`) + this.turn*= -1; + return this.getBoardState(); + } + + clickBoard = (evt) => { + evt.stopPropagation(); + if (gameState.pass > 1 || gameState.winner) return editTerritory(evt); + // checks for placement and pushes to cell + let placement = [ parseInt(evt.target.closest('td').id.split('-')[0]), parseInt(evt.target.closest('td').id.split('-')[1]) ]; + let point = findPointFromIdx(placement); + //checks that this placement was marked as legal + if ( !checkLegal(point) ) return; + clearKo(); + clearPass(); + resolveCaptures(point); + point.stone = gameState.turn; + point.joinGroup(); + playSound(point); + clearCaptures(); + gameState.gameRecord.push(`${STONES_DATA[gameState.turn]}: ${point.pos}`) + gameState.turn*= -1; + } + } // index represents handicap placement for different board-sizes, eg handiPlace['9][1] = { (3, 3), (7, 7) } @@ -146,28 +186,28 @@ class Point { this.neighbors.lft = y > 1 ? [ x, y - 1 ] : null; } - checkNeighbors = () => { + checkNeighbors = (Game) => { let neighborsArr = []; for (let neighbor in this.neighbors) { let nbr = this.neighbors[neighbor]; // neighbor exists it's point is stored as { rPos, cPos} if ( nbr !== null ) { - neighborsArr.push(boardState.find(pt => pt.pos[0] === nbr[0] && pt.pos[1] === nbr[1])) + neighborsArr.push(Game.boardState.find(pt => pt.pos[0] === nbr[0] && pt.pos[1] === nbr[1])) } }; // returns array of existing neighbors to calling function return neighborsArr; } - getLiberties = () => { - let neighborsArr = this.checkNeighbors().filter(pt => pt.stone === 0); + getLiberties = (Game) => { + let neighborsArr = this.checkNeighbors(Game).filter(pt => pt.stone === 0); return neighborsArr; } - joinGroup = () => { + joinGroup = (Game) => { this.groupMembers = this.groupMembers.filter(grp => grp.stone === this.stone); this.groupMembers.push(this); - let frns = this.checkNeighbors().filter(nbr => nbr.stone === this.stone); + let frns = this.checkNeighbors(Game).filter(nbr => nbr.stone === this.stone); for (let frn of frns) { this.groupMembers.push(frn); } @@ -180,8 +220,8 @@ class Point { } } - checkCapture = () => { - let opps = this.checkNeighbors().filter(nbr => nbr.stone === gameState.turn * -1 + checkCapture = (Game) => { + let opps = this.checkNeighbors(Game).filter(nbr => nbr.stone === Game.turn * -1 && nbr.getLiberties().every(liberty => liberty === this)); for (let opp of opps) { if (opp.groupMembers.every(stone => stone.getLiberties().filter(liberty => liberty !== this).length === 0)) { @@ -221,63 +261,41 @@ class Point { } } -function findPointFromIdx(arr) { - return pointFromIdx = boardState.find( point => point.pos[0] === arr[0] && point.pos[1] === arr[1] ); -} -function clickBoard(evt) { - evt.stopPropagation(); - if (gameState.pass > 1 || gameState.winner) return editTerritory(evt); - // checks for placement and pushes to cell - let placement = [ parseInt(evt.target.closest('td').id.split('-')[0]), parseInt(evt.target.closest('td').id.split('-')[1]) ]; - let point = findPointFromIdx(placement); - //checks that this placement was marked as legal - if ( !checkLegal(point) ) return; - clearKo(); - clearPass(); - resolveCaptures(point); - point.stone = gameState.turn; - point.joinGroup(); - playSound(point); - clearCaptures(); - gameState.gameRecord.push(`${STONES_DATA[gameState.turn]}: ${point.pos}`) - gameState.turn*= -1; -} - -function clearKo() { - for (let point in boardState) { - point = boardState[point]; +function clearKo(Game) { + for (let point in Game.boardState) { + point = Game.boardState[point]; point.stone = point.stone === 'k' ? 0 : point.stone; } } -function clearPass() { - gameState.pass = 0; +function clearPass(Game) { + Game.pass = 0; } -function resolveCaptures(point) { +function resolveCaptures(point, Game) { if(!point.capturing.length) { - point.checkCapture(); + point.checkCapture(Game); } if(point.capturing.length) { point.capturing.forEach(cap => { - gameState.playerState[gameState.turn > 0 ? 'bCaptures' : 'wCaptures']++; + Game.playerState[gameState.turn > 0 ? 'bCaptures' : 'wCaptures']++; cap.stone = checkKo(point) ? 'k' : 0; cap.groupMembers = []; }) } } -function checkLegal(point) { - clearOverlay(); +function checkLegal(point, Game) { + // clearOverlay(); // first step in logic: is point occupied, or in ko if (point.stone) return false; // if point is not empty check if liberties - if (point.getLiberties().length < 1) { + if (point.getLiberties(Game).length < 1) { //if no liberties check if enemy group has liberties - if ( point.checkCapture().length ) return true; + if ( point.checkCapture(Game).length ) return true; //if neighboring point is not empty check if friendly group is alive - if (point.checkGroup()) return true; + if (point.checkGroup(Game)) return true; return false; } return true; @@ -295,9 +313,9 @@ function checkKo(point) { // currently prevents snapback // capturing point has } -function clearCaptures() { - for (let point in boardState) { - point = boardState[point]; +function clearCaptures(Game) { + for (let point in Game.boardState) { + point = Game.boardState[point]; point.capturing = []; } } diff --git a/packages/server/packages/play-node-go/server/services/gameServices.js b/packages/server/packages/play-node-go/server/services/gameServices.js index 6fc775a..341da1a 100644 --- a/packages/server/packages/play-node-go/server/services/gameServices.js +++ b/packages/server/packages/play-node-go/server/services/gameServices.js @@ -11,14 +11,9 @@ const initGame = (game) => { return gamesInProgress[game.id].initGame(); } -const placeMove = (game, move) => { - if (!gamesInProgress[game]) { - gamesInProgress[game] = storeGame(game) - } - // gamesInProgress[] +const makeMove = (game, move) => { let meta = {}; - // let newBoard = {...board}; - let board = []; + const board = gamesInProgress[game.id].makeMove(move); return {board, meta} } @@ -31,7 +26,7 @@ const getAllGames = () => { } module.exports = { - placeMove, + makeMove, getAllGames, getBoard, initGame diff --git a/packages/server/packages/play-node-go/server/test/gameServices.spec.js b/packages/server/packages/play-node-go/server/test/gameServices.spec.js index 38499e8..fe9484d 100644 --- a/packages/server/packages/play-node-go/server/test/gameServices.spec.js +++ b/packages/server/packages/play-node-go/server/test/gameServices.spec.js @@ -1,15 +1,42 @@ +const assert = require('assert'); const gameServices = require('../services/gameServices'); describe('game services', () => { - it('games services persists game data', done => { - gameServices.placeMove({id: 1}, {player: 'black', move: '3,3'}) - console.log(gameServices.getAllGames()) + it('init game returns game board', done => { + gameServices.initGame({id: 1, handicap: 4}) + assert.deepEqual(gameServices.getBoard(1), fourHandicapBoard) done(); }); - it('init game returns game board', done => { - gameServices.initGame({id: 1}) - console.log(gameServices.getBoard(1)) + it('games services places move', done => { + gameServices.initGame({id: 1, handicap: 4}) + const afterMoveOne = gameServices.makeMove({id: 1}, {player: 'white', pos: { X:6, Y:3 }}); + const afterMoveOneShould = { board:{ ...fourHandicapBoard, '6-3': -1}, meta: {} }; + assert.deepEqual(afterMoveOne, afterMoveOneShould); done(); - }) -}) \ No newline at end of file + }); + +}) + + +const fourHandicapBoard = { + '1-1': 0,'1-2': 0,'1-3': 0,'1-4': 0,'1-5': 0,'1-6': 0,'1-7': 0,'1-8': 0,'1-9': 0,'1-10': 0,'1-11': 0,'1-12': 0,'1-13': 0,'1-14': 0,'1-15': 0,'1-16': 0,'1-17': 0,'1-18': 0,'1-19': 0, + '2-1': 0,'2-2': 0,'2-3': 0,'2-4': 0,'2-5': 0,'2-6': 0,'2-7': 0,'2-8': 0,'2-9': 0,'2-10': 0,'2-11': 0,'2-12': 0,'2-13': 0,'2-14': 0,'2-15': 0,'2-16': 0,'2-17': 0,'2-18': 0,'2-19': 0, + '3-1': 0,'3-2': 0,'3-3': 0,'3-4': 0,'3-5': 0,'3-6': 0,'3-7': 0,'3-8': 0,'3-9': 0,'3-10': 0,'3-11': 0,'3-12': 0,'3-13': 0,'3-14': 0,'3-15': 0,'3-16': 0,'3-17': 0,'3-18': 0,'3-19': 0, + '4-1': 0,'4-2': 0,'4-3': 0,'4-4': 1,'4-5': 0,'4-6': 0,'4-7': 0,'4-8': 0,'4-9': 0,'4-10': 0,'4-11': 0,'4-12': 0,'4-13': 0,'4-14': 0,'4-15': 0,'4-16': 1,'4-17': 0,'4-18': 0,'4-19': 0, + '5-1': 0,'5-2': 0,'5-3': 0,'5-4': 0,'5-5': 0,'5-6': 0,'5-7': 0,'5-8': 0,'5-9': 0,'5-10': 0,'5-11': 0,'5-12': 0,'5-13': 0,'5-14': 0,'5-15': 0,'5-16': 0,'5-17': 0,'5-18': 0,'5-19': 0, + '6-1': 0,'6-2': 0,'6-3': 0,'6-4': 0,'6-5': 0,'6-6': 0,'6-7': 0,'6-8': 0,'6-9': 0,'6-10': 0,'6-11': 0,'6-12': 0,'6-13': 0,'6-14': 0,'6-15': 0,'6-16': 0,'6-17': 0,'6-18': 0,'6-19': 0, + '7-1': 0,'7-2': 0,'7-3': 0,'7-4': 0,'7-5': 0,'7-6': 0,'7-7': 0,'7-8': 0,'7-9': 0,'7-10': 0,'7-11': 0,'7-12': 0,'7-13': 0,'7-14': 0,'7-15': 0,'7-16': 0,'7-17': 0,'7-18': 0,'7-19': 0, + '8-1': 0,'8-2': 0,'8-3': 0,'8-4': 0,'8-5': 0,'8-6': 0,'8-7': 0,'8-8': 0,'8-9': 0,'8-10': 0,'8-11': 0,'8-12': 0,'8-13': 0,'8-14': 0,'8-15': 0,'8-16': 0,'8-17': 0,'8-18': 0,'8-19': 0, + '9-1': 0,'9-2': 0,'9-3': 0,'9-4': 0,'9-5': 0,'9-6': 0,'9-7': 0,'9-8': 0,'9-9': 0,'9-10': 0,'9-11': 0,'9-12': 0,'9-13': 0,'9-14': 0,'9-15': 0,'9-16': 0,'9-17': 0,'9-18': 0,'9-19': 0, + '10-1': 0,'10-2': 0,'10-3': 0,'10-4': 0,'10-5': 0,'10-6': 0,'10-7': 0,'10-8': 0,'10-9': 0,'10-10': 0,'10-11': 0,'10-12': 0,'10-13': 0,'10-14': 0,'10-15': 0,'10-16': 0,'10-17': 0,'10-18': 0,'10-19': 0, + '11-1': 0,'11-2': 0,'11-3': 0,'11-4': 0,'11-5': 0,'11-6': 0,'11-7': 0,'11-8': 0,'11-9': 0,'11-10': 0,'11-11': 0,'11-12': 0,'11-13': 0,'11-14': 0,'11-15': 0,'11-16': 0,'11-17': 0,'11-18': 0,'11-19': 0, + '12-1': 0,'12-2': 0,'12-3': 0,'12-4': 0,'12-5': 0,'12-6': 0,'12-7': 0,'12-8': 0,'12-9': 0,'12-10': 0,'12-11': 0,'12-12': 0,'12-13': 0,'12-14': 0,'12-15': 0,'12-16': 0,'12-17': 0,'12-18': 0,'12-19': 0, + '13-1': 0,'13-2': 0,'13-3': 0,'13-4': 0,'13-5': 0,'13-6': 0,'13-7': 0,'13-8': 0,'13-9': 0,'13-10': 0,'13-11': 0,'13-12': 0,'13-13': 0,'13-14': 0,'13-15': 0,'13-16': 0,'13-17': 0,'13-18': 0,'13-19': 0, + '14-1': 0,'14-2': 0,'14-3': 0,'14-4': 0,'14-5': 0,'14-6': 0,'14-7': 0,'14-8': 0,'14-9': 0,'14-10': 0,'14-11': 0,'14-12': 0,'14-13': 0,'14-14': 0,'14-15': 0,'14-16': 0,'14-17': 0,'14-18': 0,'14-19': 0, + '15-1': 0,'15-2': 0,'15-3': 0,'15-4': 0,'15-5': 0,'15-6': 0,'15-7': 0,'15-8': 0,'15-9': 0,'15-10': 0,'15-11': 0,'15-12': 0,'15-13': 0,'15-14': 0,'15-15': 0,'15-16': 0,'15-17': 0,'15-18': 0,'15-19': 0, + '16-1': 0,'16-2': 0,'16-3': 0,'16-4': 1,'16-5': 0,'16-6': 0,'16-7': 0,'16-8': 0,'16-9': 0,'16-10': 0,'16-11': 0,'16-12': 0,'16-13': 0,'16-14': 0,'16-15': 0,'16-16': 1,'16-17': 0,'16-18': 0,'16-19': 0, + '17-1': 0,'17-2': 0,'17-3': 0,'17-4': 0,'17-5': 0,'17-6': 0,'17-7': 0,'17-8': 0,'17-9': 0,'17-10': 0,'17-11': 0,'17-12': 0,'17-13': 0,'17-14': 0,'17-15': 0,'17-16': 0,'17-17': 0,'17-18': 0,'17-19': 0, + '18-1': 0,'18-2': 0,'18-3': 0,'18-4': 0,'18-5': 0,'18-6': 0,'18-7': 0,'18-8': 0,'18-9': 0,'18-10': 0,'18-11': 0,'18-12': 0,'18-13': 0,'18-14': 0,'18-15': 0,'18-16': 0,'18-17': 0,'18-18': 0,'18-19': 0, + '19-1': 0,'19-2': 0,'19-3': 0,'19-4': 0,'19-5': 0,'19-6': 0,'19-7': 0,'19-8': 0,'19-9': 0,'19-10': 0,'19-11': 0,'19-12': 0,'19-13': 0,'19-14': 0,'19-15': 0,'19-16': 0,'19-17': 0,'19-18': 0,'19-19': 0 +}; \ No newline at end of file