diff --git a/packages/server/services/Game.v2.js b/packages/server/services/Game.v2.js index 9fbb717..70ba96d 100644 --- a/packages/server/services/Game.v2.js +++ b/packages/server/services/Game.v2.js @@ -1,12 +1,3 @@ -/*----- constants -----*/ -const STONES_DATA = { - '-1': 'white', - '0': 'none', - '1': 'black', - 'k': 'ko' -} - - // index corresponds to difference in player rank const KOMI_REC = { '9': [ @@ -77,11 +68,21 @@ const pipeMap = (...funcs) => obj => { } const checkLegal = ({ point, Game }) => { - return point.stone || 'l'; + // if stone (includes ko) return false + let legal = false; + if (point.stone) { + return { ...point, legal }; + } + // if liberties return true + // if group has liberties return true + // if move would capture opposing group + // set capturing object and return true + point.legal = !point.stone ? true : false; + return point; } const getBoardState = (Game) => { - const getLegal = point => checkLegal({ point, Game }); + const getLegal = point => checkLegal({ point, Game }).legal ? 'l' : point.stone; return pipeMap(getLegal)(Game.boardState); } @@ -163,7 +164,7 @@ const Game = ({gameData = {}, gameRecord = []} = {}) => ({ initGroup: function(point) { const newSymbol = Symbol(`${point.pos.x}-${point.pos.y}`); - this.groups[newSymbol] = new Set(); + this.groups[newSymbol] = { stones: new Set(), liberties: new Set()}; return newSymbol; } }); @@ -195,17 +196,26 @@ const Point = ({x, y, boardSize = 19}) => ({ point.group = game.initGroup(point); } - // add current point to global group - game.groups[point.group].add(this); + // add current point to global group and override current group + game.groups[point.group].stones.add(this); this.group = point.group; + this.setLiberties(game); for (let neighbor of Object.values(this.neighbors)) { - if (neighbor.stone === this.stone - && neighbor.group !== this.group) { - neighbor.joinGroup({ point: this, game }); - } + if ( neighbor.stone === this.stone + // this check prevents infinite call chains + && neighbor.group !== this.group + ) { + neighbor.joinGroup({ point: this, game }); } + } } + }, + setLiberties: function(game) { + const neighbors = Object.values(this.neighbors); + const liberties = game.groups[this.group].liberties; + // if point is occupied remove it from liberties set of point group, else add it + neighbors.forEach( pt => pt ? liberties.delete(pt) : liberties.add(pt) ); } }); diff --git a/packages/server/test/Game.v2.spec.js b/packages/server/test/Game.v2.spec.js index 47ea6c1..6b99665 100644 --- a/packages/server/test/Game.v2.spec.js +++ b/packages/server/test/Game.v2.spec.js @@ -158,14 +158,16 @@ describe('Game.makeMove({ player: str, pos: { x: int, y: int } })', () => { .makeMove({ player: 'black', pos: { x: 4, y: 14 }}) .makeMove({ player: 'white', pos: { x: 4, y: 5 }}) - const blackGroup = game.boardState['4-14'].group; - game.groups[blackGroup].has(game.boardState['4-14']).should.eql(true); - game.groups[blackGroup].has(game.boardState['4-15']).should.eql(true); - game.groups[blackGroup].has(game.boardState['4-16']).should.eql(true); - const whiteGroup = game.boardState['4-4'].group; - game.groups[whiteGroup].has(game.boardState['4-4']).should.eql(true); - game.groups[whiteGroup].has(game.boardState['3-4']).should.eql(true); - game.groups[whiteGroup].has(game.boardState['4-5']).should.eql(true); + 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; + 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); done(); }); @@ -176,23 +178,24 @@ describe('Game.makeMove({ player: str, pos: { x: int, y: int } })', () => { .makeMove({ player: 'white', pos: { x: 3, y: 16 } }) // 16 -1 1h .makeMove({ player: 'black', pos: { x: 3, y: 15 }}) - const hoshiGroup = game.boardState['4-16'].group; - game.groups[hoshiGroup].has(game.boardState['4-16']).should.eql(true); - game.groups[hoshiGroup].has(game.boardState['4-15']).should.eql(false); - game.groups[hoshiGroup].has(game.boardState['3-14']).should.eql(false); - game.groups[hoshiGroup].has(game.boardState['3-15']).should.eql(false); + const hoshiGroupKey = game.boardState['4-16'].group; + const hoshiGroup = game.groups[hoshiGroupKey].stones; + hoshiGroup.has(game.boardState['4-16']).should.eql(true); + hoshiGroup.has(game.boardState['4-15']).should.eql(false); + hoshiGroup.has(game.boardState['3-14']).should.eql(false); + hoshiGroup.has(game.boardState['3-15']).should.eql(false); done(); }) - // it('makeMove returns success: false when move is made in point with no liberties', done => { - // Game({ gameData: { handicap: 2 } }).initGame() - // .makeMove({ player: 'white', pos: { x: 4, y: 4 } }).makeMove({ player: 'black', pos: { x: 6, y: 16 } }) - // .makeMove({ player: 'white', pos: { x: 16, y: 16 }}).makeMove({ player: 'black', pos: { x: 5, y: 15 } }) - // .makeMove({ player: 'white', pos: { x: 16, y: 10 }}).makeMove({ player: 'black', pos: { x: 5, y: 17 } }) - // .makeMove({ player: 'white', pos: { x: 5, y: 16 }}) - // .success.should.eql(false); - // done(); - // }) + it('makeMove returns success: false when move is made in point with no liberties', done => { + Game({ gameData: { handicap: 2 } }).initGame() + .makeMove({ player: 'white', pos: { x: 4, y: 4 } }).makeMove({ player: 'black', pos: { x: 6, y: 16 } }) + .makeMove({ player: 'white', pos: { x: 16, y: 16 }}).makeMove({ player: 'black', pos: { x: 5, y: 15 } }) + .makeMove({ player: 'white', pos: { x: 16, y: 10 }}).makeMove({ player: 'black', pos: { x: 5, y: 17 } }) + .makeMove({ player: 'white', pos: { x: 5, y: 16 }}) + .success.should.eql(false); + done(); + }) }); const initialMeta = {