add determineLife to Game.endGame, refactor joinEmptyPoints to avoid removing empty points from adjoining groups' liberties

This commit is contained in:
sorrelbri 2020-06-01 17:12:28 -07:00
parent 1b3dbd870a
commit d1f3459516
2 changed files with 58 additions and 262 deletions

View file

@ -186,7 +186,7 @@ const Game = ({ gameData = {}, gameRecord = [] } = {}) => {
komi: gameData.komi || 6.5, // komi depends on handicap stones + player rank komi: gameData.komi || 6.5, // komi depends on handicap stones + player rank
handicap: gameData.handicap || 0, handicap: gameData.handicap || 0,
boardSize: gameData.boardSize || 19, boardSize: gameData.boardSize || 19,
groups: {}, groups: {}, // key is Symbol(position): {points: Set(), liberties: Set()}
boardState: {}, boardState: {},
kos: [], kos: [],
gameRecord: gameRecord, gameRecord: gameRecord,
@ -299,7 +299,7 @@ const Game = ({ gameData = {}, gameRecord = [] } = {}) => {
// form groups for empty points // form groups for empty points
const joinEmptyPoints = (point) => { const joinEmptyPoints = (point) => {
if (point.stone) return point; if (point.stone) return point;
point.joinGroup({ point, Game: this }); point.joinEmptyPoints({ point, Game: this });
return point; return point;
}; };
// for each point determine territory // for each point determine territory
@ -314,11 +314,19 @@ const Game = ({ gameData = {}, gameRecord = [] } = {}) => {
stones.reduce((acc, { stone }) => acc + stone, 0) || "d"; stones.reduce((acc, { stone }) => acc + stone, 0) || "d";
return point; return point;
}; };
// for each non-empty point group determine life
const determineLife = (point) => {
if (!point.stone || point.territory) return point;
const liberties = Array.from(this.groups[point.group].liberties);
point.territory =
liberties.reduce((acc, { territory }) => acc + territory, 0) ||
point.stone;
return point;
};
let boardState = pipeMap(joinEmptyPoints)(this.boardState); let boardState = pipeMap(joinEmptyPoints)(this.boardState);
boardState = pipeMap(determineTerritory)(boardState); boardState = pipeMap(determineTerritory)(boardState);
boardState = pipeMap(determineLife)(boardState);
this.boardState = boardState; this.boardState = boardState;
// for each non-empty point group determine life
// submit board state to users // submit board state to users
// if boardState is approved calculate winner // if boardState is approved calculate winner
// submit end game board state and data for study // submit end game board state and data for study
@ -389,6 +397,30 @@ const Point = ({ x, y, boardSize = 19 }) => {
return Game; return Game;
}, },
joinEmptyPoints: function ({ point, Game }) {
if (point.group !== this.group || !point.group) {
if (!point.group) {
const { game, group } = Game.initGroup(point);
this.group = group;
Game = game;
}
Game.groups[point.group].stones.add(this);
if (this.group !== point.group) {
this.group = point.group;
}
getNeighbors({ point: this, Game }).forEach((neighbor) => {
if (
neighbor.stone === this.stone &&
// this check prevents infinite call chains
neighbor.group !== this.group
) {
Game = neighbor.joinEmptyPoints({ point: this, Game });
}
});
return Game;
}
},
setLiberties: function (Game) { setLiberties: function (Game) {
const neighbors = getNeighbors({ point: this, Game }); const neighbors = getNeighbors({ point: this, Game });
const liberties = Game.groups[this.group].liberties; const liberties = Game.groups[this.group].liberties;
@ -494,260 +526,3 @@ module.exports = {
Game, Game,
Point, Point,
}; };
const honinboGameRecord = [
{ player: "black", pos: { x: 4, y: 16 } },
{ player: "white", pos: { x: 3, y: 4 } },
{ player: "black", pos: { x: 16, y: 4 } },
{ player: "white", pos: { x: 17, y: 16 } },
{ player: "black", pos: { x: 15, y: 17 } },
{ player: "white", pos: { x: 5, y: 3 } },
{ player: "black", pos: { x: 17, y: 14 } },
{ player: "white", pos: { x: 16, y: 15 } },
{ player: "black", pos: { x: 16, y: 14 } },
{ player: "white", pos: { x: 14, y: 15 } },
{ player: "black", pos: { x: 15, y: 14 } },
{ player: "white", pos: { x: 14, y: 17 } },
{ player: "black", pos: { x: 14, y: 18 } },
{ player: "white", pos: { x: 13, y: 17 } },
{ player: "black", pos: { x: 16, y: 17 } },
{ player: "white", pos: { x: 15, y: 15 } },
{ player: "black", pos: { x: 13, y: 18 } },
{ player: "white", pos: { x: 11, y: 17 } },
{ player: "black", pos: { x: 17, y: 17 } },
{ player: "white", pos: { x: 18, y: 16 } },
{ player: "black", pos: { x: 12, y: 17 } },
{ player: "white", pos: { x: 12, y: 16 } },
{ player: "black", pos: { x: 12, y: 18 } },
{ player: "white", pos: { x: 16, y: 10 } },
{ player: "black", pos: { x: 17, y: 7 } },
{ player: "white", pos: { x: 13, y: 13 } },
{ player: "black", pos: { x: 15, y: 11 } },
{ player: "white", pos: { x: 11, y: 16 } },
{ player: "black", pos: { x: 15, y: 10 } },
{ player: "white", pos: { x: 16, y: 8 } },
{ player: "black", pos: { x: 17, y: 8 } },
{ player: "white", pos: { x: 15, y: 9 } },
{ player: "black", pos: { x: 17, y: 9 } },
{ player: "white", pos: { x: 16, y: 9 } },
{ player: "black", pos: { x: 13, y: 10 } },
{ player: "white", pos: { x: 16, y: 7 } },
{ player: "black", pos: { x: 16, y: 6 } },
{ player: "white", pos: { x: 15, y: 6 } },
{ player: "black", pos: { x: 16, y: 5 } },
{ player: "white", pos: { x: 13, y: 7 } },
{ player: "black", pos: { x: 11, y: 10 } },
{ player: "white", pos: { x: 13, y: 4 } },
{ player: "black", pos: { x: 3, y: 9 } },
{ player: "white", pos: { x: 10, y: 8 } },
{ player: "black", pos: { x: 9, y: 10 } },
{ player: "white", pos: { x: 7, y: 16 } },
{ player: "black", pos: { x: 4, y: 14 } },
{ player: "white", pos: { x: 8, y: 9 } },
{ player: "black", pos: { x: 8, y: 14 } },
{ player: "white", pos: { x: 8, y: 10 } },
{ player: "black", pos: { x: 9, y: 11 } },
{ player: "white", pos: { x: 8, y: 11 } },
{ player: "black", pos: { x: 10, y: 14 } },
{ player: "white", pos: { x: 9, y: 15 } },
{ player: "black", pos: { x: 12, y: 14 } },
{ player: "white", pos: { x: 11, y: 13 } },
{ player: "black", pos: { x: 12, y: 13 } },
{ player: "white", pos: { x: 12, y: 12 } },
{ player: "black", pos: { x: 13, y: 14 } },
{ player: "white", pos: { x: 14, y: 14 } },
{ player: "black", pos: { x: 11, y: 14 } },
{ player: "white", pos: { x: 14, y: 13 } },
{ player: "black", pos: { x: 14, y: 9 } },
{ player: "white", pos: { x: 12, y: 9 } },
{ player: "black", pos: { x: 12, y: 10 } },
{ player: "white", pos: { x: 16, y: 12 } },
{ player: "black", pos: { x: 16, y: 11 } },
{ player: "white", pos: { x: 17, y: 12 } },
{ player: "black", pos: { x: 18, y: 11 } },
{ player: "white", pos: { x: 18, y: 12 } },
{ player: "black", pos: { x: 15, y: 12 } },
{ player: "white", pos: { x: 15, y: 13 } },
{ player: "black", pos: { x: 17, y: 11 } },
{ player: "white", pos: { x: 15, y: 3 } },
{ player: "black", pos: { x: 16, y: 3 } },
{ player: "white", pos: { x: 16, y: 2 } },
{ player: "black", pos: { x: 17, y: 2 } },
{ player: "white", pos: { x: 3, y: 7 } },
{ player: "black", pos: { x: 5, y: 9 } },
{ player: "white", pos: { x: 8, y: 4 } },
{ player: "black", pos: { x: 5, y: 11 } },
{ player: "white", pos: { x: 4, y: 17 } },
{ player: "black", pos: { x: 3, y: 17 } },
{ player: "white", pos: { x: 5, y: 17 } },
{ player: "black", pos: { x: 3, y: 18 } },
{ player: "white", pos: { x: 5, y: 16 } },
{ player: "black", pos: { x: 4, y: 15 } },
{ player: "white", pos: { x: 15, y: 2 } },
{ player: "black", pos: { x: 4, y: 3 } },
{ player: "white", pos: { x: 4, y: 4 } },
{ player: "black", pos: { x: 10, y: 4 } },
{ player: "white", pos: { x: 8, y: 3 } },
{ player: "black", pos: { x: 12, y: 7 } },
{ player: "white", pos: { x: 13, y: 6 } },
{ player: "black", pos: { x: 5, y: 2 } },
{ player: "white", pos: { x: 4, y: 2 } },
{ player: "black", pos: { x: 10, y: 7 } },
{ player: "white", pos: { x: 9, y: 7 } },
{ player: "black", pos: { x: 10, y: 6 } },
{ player: "white", pos: { x: 12, y: 8 } },
{ player: "black", pos: { x: 9, y: 8 } },
{ player: "white", pos: { x: 10, y: 9 } },
{ player: "black", pos: { x: 8, y: 5 } },
{ player: "white", pos: { x: 9, y: 9 } },
{ player: "black", pos: { x: 6, y: 3 } },
{ player: "white", pos: { x: 5, y: 4 } },
{ player: "black", pos: { x: 7, y: 5 } },
{ player: "white", pos: { x: 6, y: 4 } },
{ player: "black", pos: { x: 12, y: 4 } },
{ player: "white", pos: { x: 11, y: 3 } },
{ player: "black", pos: { x: 12, y: 3 } },
{ player: "white", pos: { x: 10, y: 3 } },
{ player: "black", pos: { x: 14, y: 4 } },
{ player: "white", pos: { x: 15, y: 4 } },
{ player: "black", pos: { x: 13, y: 5 } },
{ player: "white", pos: { x: 15, y: 5 } },
{ player: "black", pos: { x: 9, y: 4 } },
{ player: "white", pos: { x: 9, y: 3 } },
{ player: "black", pos: { x: 7, y: 7 } },
{ player: "white", pos: { x: 12, y: 6 } },
{ player: "black", pos: { x: 11, y: 4 } },
{ player: "white", pos: { x: 8, y: 8 } },
{ player: "black", pos: { x: 6, y: 8 } },
{ player: "white", pos: { x: 2, y: 8 } },
{ player: "black", pos: { x: 2, y: 9 } },
{ player: "white", pos: { x: 9, y: 12 } },
{ player: "black", pos: { x: 10, y: 12 } },
{ player: "white", pos: { x: 10, y: 10 } },
{ player: "black", pos: { x: 10, y: 11 } },
{ player: "white", pos: { x: 9, y: 13 } },
{ player: "black", pos: { x: 9, y: 14 } },
{ player: "white", pos: { x: 10, y: 13 } },
{ player: "black", pos: { x: 11, y: 11 } },
{ player: "white", pos: { x: 7, y: 13 } },
{ player: "black", pos: { x: 7, y: 14 } },
{ player: "white", pos: { x: 6, y: 13 } },
{ player: "black", pos: { x: 6, y: 14 } },
{ player: "white", pos: { x: 5, y: 13 } },
{ player: "black", pos: { x: 8, y: 13 } },
{ player: "white", pos: { x: 8, y: 12 } },
{ player: "black", pos: { x: 6, y: 11 } },
{ player: "white", pos: { x: 7, y: 11 } },
{ player: "black", pos: { x: 6, y: 15 } },
{ player: "white", pos: { x: 14, y: 8 } },
{ player: "black", pos: { x: 19, y: 12 } },
{ player: "white", pos: { x: 19, y: 13 } },
{ player: "black", pos: { x: 18, y: 13 } },
{ player: "white", pos: { x: 19, y: 11 } },
{ player: "black", pos: { x: 19, y: 14 } },
{ player: "white", pos: { x: 16, y: 13 } },
{ player: "black", pos: { x: 19, y: 12 } },
{ player: "white", pos: { x: 13, y: 15 } },
{ player: "black", pos: { x: 11, y: 18 } },
{ player: "white", pos: { x: 10, y: 18 } },
{ player: "black", pos: { x: 19, y: 10 } },
{ player: "white", pos: { x: 8, y: 18 } },
{ player: "black", pos: { x: 5, y: 19 } },
{ player: "white", pos: { x: 13, y: 9 } },
{ player: "black", pos: { x: 14, y: 10 } },
{ player: "white", pos: { x: 18, y: 18 } },
{ player: "black", pos: { x: 18, y: 17 } },
{ player: "white", pos: { x: 19, y: 17 } },
{ player: "black", pos: { x: 17, y: 18 } },
{ player: "white", pos: { x: 8, y: 15 } },
{ player: "black", pos: { x: 4, y: 13 } },
{ player: "white", pos: { x: 17, y: 1 } },
{ player: "black", pos: { x: 18, y: 2 } },
{ player: "white", pos: { x: 1, y: 9 } },
{ player: "black", pos: { x: 1, y: 10 } },
{ player: "white", pos: { x: 1, y: 8 } },
{ player: "black", pos: { x: 2, y: 10 } },
{ player: "white", pos: { x: 9, y: 6 } },
{ player: "black", pos: { x: 9, y: 5 } },
{ player: "white", pos: { x: 6, y: 6 } },
{ player: "black", pos: { x: 8, y: 7 } },
{ player: "white", pos: { x: 5, y: 7 } },
{ player: "black", pos: { x: 6, y: 18 } },
{ player: "white", pos: { x: 7, y: 18 } },
{ player: "black", pos: { x: 10, y: 19 } },
{ player: "white", pos: { x: 9, y: 19 } },
{ player: "black", pos: { x: 9, y: 17 } },
{ player: "white", pos: { x: 11, y: 19 } },
{ player: "black", pos: { x: 19, y: 18 } },
{ player: "white", pos: { x: 4, y: 19 } },
{ player: "black", pos: { x: 4, y: 18 } },
{ player: "white", pos: { x: 5, y: 18 } },
{ player: "black", pos: { x: 3, y: 19 } },
{ player: "white", pos: { x: 6, y: 17 } },
{ player: "black", pos: { x: 19, y: 16 } },
{ player: "white", pos: { x: 11, y: 2 } },
{ player: "black", pos: { x: 12, y: 2 } },
{ player: "white", pos: { x: 14, y: 5 } },
{ player: "black", pos: { x: 13, y: 3 } },
{ player: "white", pos: { x: 6, y: 10 } },
{ player: "black", pos: { x: 3, y: 8 } },
{ player: "white", pos: { x: 2, y: 7 } },
{ player: "black", pos: { x: 5, y: 10 } },
{ player: "white", pos: { x: 18, y: 15 } },
{ player: "black", pos: { x: 19, y: 15 } },
{ player: "white", pos: { x: 6, y: 19 } },
{ player: "black", pos: { x: 12, y: 19 } },
{ player: "white", pos: { x: 18, y: 1 } },
{ player: "black", pos: { x: 10, y: 19 } },
{ player: "white", pos: { x: 9, y: 18 } },
{ player: "black", pos: { x: 10, y: 15 } },
{ player: "white", pos: { x: 10, y: 16 } },
{ player: "black", pos: { x: 19, y: 2 } },
{ player: "white", pos: { x: 18, y: 14 } },
{ player: "black", pos: { x: 17, y: 13 } },
{ player: "white", pos: { x: 6, y: 9 } },
{ player: "black", pos: { x: 7, y: 9 } },
{ player: "white", pos: { x: 7, y: 10 } },
{ player: "black", pos: { x: 5, y: 8 } },
{ player: "white", pos: { x: 7, y: 8 } },
{ player: "black", pos: { x: 6, y: 7 } },
{ player: "white", pos: { x: 4, y: 7 } },
{ player: "black", pos: { x: 6, y: 5 } },
{ player: "white", pos: { x: 5, y: 5 } },
{ player: "black", pos: { x: 7, y: 4 } },
{ player: "white", pos: { x: 7, y: 3 } },
{ player: "black", pos: { x: 11, y: 7 } },
{ player: "white", pos: { x: 4, y: 8 } },
{ player: "black", pos: { x: 4, y: 9 } },
{ player: "white", pos: { x: 5, y: 12 } },
{ player: "black", pos: { x: 4, y: 12 } },
{ player: "white", pos: { x: 17, y: 10 } },
{ player: "black", pos: { x: 18, y: 10 } },
{ player: "white", pos: { x: 15, y: 16 } },
{ player: "black", pos: { x: 12, y: 5 } },
{ player: "white", pos: { x: 11, y: 6 } },
{ player: "black", pos: { x: 14, y: 2 } },
{ player: "white", pos: { x: 14, y: 3 } },
{ player: "black", pos: { x: 11, y: 1 } },
{ player: "white", pos: { x: 10, y: 1 } },
{ player: "black", pos: { x: 12, y: 1 } },
{ player: "white", pos: { x: 17, y: 15 } },
{ player: "black", pos: { x: 19, y: 13 } },
{ player: "white", pos: { x: 17, y: 3 } },
{ player: "black", pos: { x: 18, y: 3 } },
{ player: "white", pos: { x: 11, y: 8 } },
{ player: "black", pos: { x: 10, y: 5 } },
{ player: "white", pos: { x: 14, y: 1 } },
{ player: "black", pos: { x: 13, y: 1 } },
{ player: "white", pos: { x: 13, y: 12 } },
{ player: "black", pos: { x: 15, y: 1 } },
{ player: "white", pos: { x: 16, y: 1 } },
{ player: "black", pos: { x: 14, y: 1 } },
{ player: "white", pos: { x: 4, y: 19 } },
{ player: "black", pos: { x: 13, y: 4 } },
{ player: "white", pos: { x: 5, y: 19 } },
{ player: "black", pos: { x: 11, y: 19 } },
];
const honinboGame = Game({ gameRecord: honinboGameRecord })
.submitPass("white")
.submitPass("black");

View file

@ -1022,12 +1022,33 @@ describe("Game end logic", () => {
.submitPass("white") .submitPass("white")
.submitPass("black"); .submitPass("black");
it("consecutive passes return board state with empty points in groups", (done) => { it("end game returns board state with empty points in groups", (done) => {
honinboGame.boardState["1-1"].group.should.not.eql(null); honinboGame.boardState["1-1"].group.should.not.eql(null);
const group = honinboGame.boardState["19-5"].group; const group = honinboGame.boardState["19-5"].group;
// console.log(honinboGame.groups[group]);
honinboGame.boardState["17-4"].group.should.eql(group); honinboGame.boardState["17-4"].group.should.eql(group);
done(); done();
}); });
const isWhite = (x) => x < 0;
const isBlack = (x) => x > 0;
const territories = [
["4-2", isWhite],
["4-3", isWhite],
["5-2", isWhite],
["6-3", isWhite],
["17-3", isBlack],
["9-17", isWhite],
["18-18", isBlack],
["19-18", isBlack],
];
territories.forEach(([point, territoryPredicate]) => {
it(`end game return board state with ${point} marked with appropriate territory`, (done) => {
territoryPredicate(honinboGame.boardState[point].territory).should.eql(
true
);
done();
});
});
}); });
const initialMeta = { const initialMeta = {