Compare commits
3 commits
master
...
game_recor
Author | SHA1 | Date | |
---|---|---|---|
|
0da9d26bf2 | ||
|
6742d54aff | ||
|
e95315053c |
7 changed files with 102 additions and 46 deletions
|
@ -36,7 +36,7 @@ Adjacent points without stones are very important to the state of a point as wel
|
||||||
![Image of Game logic](public/game-logic.png)
|
![Image of Game logic](public/game-logic.png)
|
||||||
|
|
||||||
### Game Records
|
### Game Records
|
||||||
For the moment, game records are modeled as a list where each move has the type `{player: <string: player color>, pos: {x: <integer>, y: <integer>}}`.
|
Game records are modeled as a list where each move has the type `{player: <string: player color>, pos: {x: <integer>, y: <integer>}, number: <integer>}`.
|
||||||
|
|
||||||
The database `move` table contains additional information `number`, `game_record`, and `prior_move` in addition to a foreign key for a `game` row. `number` represents the move number in a game record (for now this corresponds to list position), `game_record` is a boolean representing whether the move was 'official' or is an alternate move used in study, and `prior_move` is a reference to another `move` row allowing for the construction of a tree model for the game (see [Expanding this representation](#expanding-this-representation), below.)
|
The database `move` table contains additional information `number`, `game_record`, and `prior_move` in addition to a foreign key for a `game` row. `number` represents the move number in a game record (for now this corresponds to list position), `game_record` is a boolean representing whether the move was 'official' or is an alternate move used in study, and `prior_move` is a reference to another `move` row allowing for the construction of a tree model for the game (see [Expanding this representation](#expanding-this-representation), below.)
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ There is a backend service that processes this list of moves into a current boar
|
||||||
This is a customary representation of game records printed in Go literature. A frontend service processes the list of moves and plots each move onto a `<canvas>` element drawn to resemble the grid lines on a board, with moves that are placed at prior points plotted on an additional `<canvas>` element below.
|
This is a customary representation of game records printed in Go literature. A frontend service processes the list of moves and plots each move onto a `<canvas>` element drawn to resemble the grid lines on a board, with moves that are placed at prior points plotted on an additional `<canvas>` element below.
|
||||||
|
|
||||||
#### Expanding this representation
|
#### Expanding this representation
|
||||||
This representation will be expanded when support for alternate game paths is added. Alternate game paths will allow users to study completed games by playing out the consequences of moves not in the completed game. This feature will require a tree structure for the game record where moves on the main line are referred with a `main` property on each move node and alternate moves with any number of alternate references.
|
The list representation is expanded for alternate game paths with the addition of sub-lists in places where paths diverge. Alternate game paths allow users to study completed games by playing out the consequences of moves not in the completed game. Each move with a diverging path contains lists for each path, with the official path being the first.
|
||||||
|
|
||||||
### Caching multiple in-progress games
|
### Caching multiple in-progress games
|
||||||
![Image of Game module in context](public/game-module.png)
|
![Image of Game module in context](public/game-module.png)
|
||||||
|
|
|
@ -48,6 +48,9 @@ const Point = (props) => {
|
||||||
};
|
};
|
||||||
return dispatch(action);
|
return dispatch(action);
|
||||||
}
|
}
|
||||||
|
const priorMove = meta.gameRecord.length
|
||||||
|
? meta.gameRecord[meta.gameRecord.length - 1].id
|
||||||
|
: null;
|
||||||
const action = {
|
const action = {
|
||||||
type: "SOCKET",
|
type: "SOCKET",
|
||||||
message: "MAKE_MOVE",
|
message: "MAKE_MOVE",
|
||||||
|
@ -56,7 +59,7 @@ const Point = (props) => {
|
||||||
game,
|
game,
|
||||||
room: game.room,
|
room: game.room,
|
||||||
board: {},
|
board: {},
|
||||||
move: { player: turn, pos: { x: posX, y: posY } },
|
move: { player: turn, pos: { x: posX, y: posY }, priorMove },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
dispatch(action);
|
dispatch(action);
|
||||||
|
|
|
@ -3,12 +3,22 @@ const knex = require("../db");
|
||||||
const findGameRecord = async (gameId) => {
|
const findGameRecord = async (gameId) => {
|
||||||
return await knex("move")
|
return await knex("move")
|
||||||
.where({ game: gameId, game_record: true })
|
.where({ game: gameId, game_record: true })
|
||||||
.select("player", "point_x", "point_y", "number", "prior_move", "placement")
|
.select(
|
||||||
|
"player",
|
||||||
|
"point_x",
|
||||||
|
"point_y",
|
||||||
|
"number",
|
||||||
|
"prior_move",
|
||||||
|
"placement",
|
||||||
|
"id"
|
||||||
|
)
|
||||||
.orderBy("number")
|
.orderBy("number")
|
||||||
.then((record) =>
|
.then((record) =>
|
||||||
record.map(({ player, point_x, point_y }) => ({
|
record.map(({ player, point_x, point_y, id, prior_move }) => ({
|
||||||
player,
|
player,
|
||||||
pos: { x: point_x, y: point_y },
|
pos: { x: point_x, y: point_y },
|
||||||
|
id,
|
||||||
|
prior: prior_move,
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
// .then(res => res)
|
// .then(res => res)
|
||||||
|
@ -32,11 +42,10 @@ const addMove = async ({ gameId, player, x, y, gameRecord, priorMove }) => {
|
||||||
game_record: gameRecord,
|
game_record: gameRecord,
|
||||||
prior_move: priorMove,
|
prior_move: priorMove,
|
||||||
})
|
})
|
||||||
.then((res) => res);
|
.then((res) => res[0]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
result = e;
|
result = e;
|
||||||
} finally {
|
} finally {
|
||||||
console.log(result);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -184,7 +184,8 @@ const Game = ({ gameData = {}, gameRecord = [] } = {}) => {
|
||||||
if (gameRecord.length) {
|
if (gameRecord.length) {
|
||||||
// play through all the moves
|
// play through all the moves
|
||||||
const game = gameRecord.reduce(
|
const game = gameRecord.reduce(
|
||||||
(game, move) => game.makeMove(move),
|
(game, move) =>
|
||||||
|
move.length ? game.makeMove(move[0]) : game.makeMove(move),
|
||||||
Game({ gameData }).initGame()
|
Game({ gameData }).initGame()
|
||||||
);
|
);
|
||||||
// ? why is this being wrapped?
|
// ? why is this being wrapped?
|
||||||
|
@ -262,7 +263,7 @@ const Game = ({ gameData = {}, gameRecord = [] } = {}) => {
|
||||||
return { ...this, success: false };
|
return { ...this, success: false };
|
||||||
},
|
},
|
||||||
|
|
||||||
makeMove: function ({ player, pos: { x, y } }) {
|
makeMove: function ({ player, pos: { x, y }, id, prior }) {
|
||||||
if (this.pass > 1) {
|
if (this.pass > 1) {
|
||||||
return { ...this, success: false };
|
return { ...this, success: false };
|
||||||
}
|
}
|
||||||
|
@ -286,7 +287,7 @@ const Game = ({ gameData = {}, gameRecord = [] } = {}) => {
|
||||||
const point = game.boardState[`${x}-${y}`];
|
const point = game.boardState[`${x}-${y}`];
|
||||||
game.pass = 0;
|
game.pass = 0;
|
||||||
// allows for recording of prior move on game record
|
// allows for recording of prior move on game record
|
||||||
game.addToRecord(game.move);
|
game.addToRecord({ player, pos: { x, y }, id, prior });
|
||||||
if (game.kos.length) helper.clearKo.call(game);
|
if (game.kos.length) helper.clearKo.call(game);
|
||||||
point.makeMove(game);
|
point.makeMove(game);
|
||||||
game.turn *= -1;
|
game.turn *= -1;
|
||||||
|
|
|
@ -7,22 +7,28 @@ const GameService = ({ moveQueries, gameQueries }) => {
|
||||||
};
|
};
|
||||||
const gamesInProgress = {};
|
const gamesInProgress = {};
|
||||||
|
|
||||||
const storeMove = (gameId) => async ({ player, pos: { x, y } }) => {
|
const storeMove = (gameId) => async ({
|
||||||
let move = { player, pos: { x, y } };
|
player,
|
||||||
|
pos: { x, y },
|
||||||
|
priorMove,
|
||||||
|
}) => {
|
||||||
|
let move = { player, pos: { x, y }, priorMove };
|
||||||
try {
|
try {
|
||||||
if (moveQueries) {
|
if (moveQueries) {
|
||||||
const { id } = await moveQueries.addMove({
|
const moveDbResult = await moveQueries.addMove({
|
||||||
gameId,
|
gameId,
|
||||||
player,
|
player,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
gameRecord: true,
|
gameRecord: true,
|
||||||
priorMove: null,
|
priorMove,
|
||||||
});
|
});
|
||||||
move.id = id;
|
move.id = moveDbResult.id;
|
||||||
|
move.prior = moveDbResult.prior_move;
|
||||||
move.success = true;
|
move.success = true;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
console.log("Exception ------------");
|
||||||
console.log(e);
|
console.log(e);
|
||||||
move.success = false;
|
move.success = false;
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -55,23 +61,14 @@ const GameService = ({ moveQueries, gameQueries }) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gamesInProgress[id] = await gamesInProgress[id].checkMove(move);
|
gamesInProgress[id] = await gamesInProgress[id].checkMove(move);
|
||||||
gamesInProgress[id] = gamesInProgress[id].makeMove(move);
|
|
||||||
if (gamesInProgress[id].success === false)
|
if (gamesInProgress[id].success === false)
|
||||||
return { message: "illegal move" };
|
return { message: "illegal move" };
|
||||||
try {
|
try {
|
||||||
if (moveQueries) {
|
if (moveQueries) {
|
||||||
const priorMove = gamesInProgress[id].gameRecord.length;
|
// todo change prior move
|
||||||
const moveInsert = {
|
move = await storeMove(id)(move);
|
||||||
gameId: id,
|
|
||||||
player: move.player,
|
|
||||||
x: move.pos.x,
|
|
||||||
y: move.pos.y,
|
|
||||||
gameRecord: true,
|
|
||||||
priorMove,
|
|
||||||
};
|
|
||||||
let moveDbResult;
|
|
||||||
moveDbResult = await moveQueries.addMove(moveInsert);
|
|
||||||
}
|
}
|
||||||
|
gamesInProgress[id] = gamesInProgress[id].makeMove(move);
|
||||||
} catch {
|
} catch {
|
||||||
gamesInProgress[id].returnToMove(-1);
|
gamesInProgress[id].returnToMove(-1);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -608,14 +608,34 @@ describe("capture logic: snapback, ko and playing in eyes", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Game history functionality", () => {
|
describe("Game history functionality", () => {
|
||||||
const firstMove = { player: "black", pos: { x: 4, y: 4 } };
|
const firstMove = {
|
||||||
const secondMove = { player: "white", pos: { x: 16, y: 16 } };
|
player: "black",
|
||||||
const thirdMove = { player: "black", pos: { x: 16, y: 4 } };
|
pos: { x: 4, y: 4 },
|
||||||
const fourthMove = { player: "white", pos: { x: 4, y: 16 } };
|
id: 1,
|
||||||
const fifthMove = { player: "black", pos: { x: 10, y: 4 } };
|
prior: null,
|
||||||
const sixthMove = { player: "white", pos: { x: 4, y: 10 } };
|
};
|
||||||
const seventhMove = { player: "black", pos: { x: 10, y: 16 } };
|
const secondMove = {
|
||||||
const eighthMove = { player: "white", pos: { x: 16, y: 10 } };
|
player: "white",
|
||||||
|
pos: { x: 16, y: 16 },
|
||||||
|
id: 2,
|
||||||
|
prior: 1,
|
||||||
|
};
|
||||||
|
const thirdMove = { player: "black", pos: { x: 16, y: 4 }, id: 3, prior: 2 };
|
||||||
|
const fourthMove = { player: "white", pos: { x: 4, y: 16 }, id: 4, prior: 3 };
|
||||||
|
const fifthMove = { player: "black", pos: { x: 10, y: 4 }, id: 5, prior: 4 };
|
||||||
|
const sixthMove = { player: "white", pos: { x: 4, y: 10 }, id: 6, prior: 5 };
|
||||||
|
const seventhMove = {
|
||||||
|
player: "black",
|
||||||
|
pos: { x: 10, y: 16 },
|
||||||
|
id: 7,
|
||||||
|
prior: 6,
|
||||||
|
};
|
||||||
|
const eighthMove = {
|
||||||
|
player: "white",
|
||||||
|
pos: { x: 16, y: 10 },
|
||||||
|
id: 8,
|
||||||
|
prior: 7,
|
||||||
|
};
|
||||||
|
|
||||||
it("makeMove creates gameRecord item", (done) => {
|
it("makeMove creates gameRecord item", (done) => {
|
||||||
Game().initGame().makeMove(firstMove).gameRecord[0].should.eql(firstMove);
|
Game().initGame().makeMove(firstMove).gameRecord[0].should.eql(firstMove);
|
||||||
|
@ -728,11 +748,11 @@ describe("Game end logic", () => {
|
||||||
it("two nonconsecutive passes continue game", (done) => {
|
it("two nonconsecutive passes continue game", (done) => {
|
||||||
Game()
|
Game()
|
||||||
.initGame()
|
.initGame()
|
||||||
.makeMove({ player: "black", pos: { x: 4, y: 4 } })
|
.makeMove({ player: "black", pos: { x: 4, y: 4 }, id: 1, prior: null })
|
||||||
.makeMove({ player: "white", pos: { x: 4, y: 5 } })
|
.makeMove({ player: "white", pos: { x: 4, y: 5 }, id: 2, prior: 1 })
|
||||||
.makeMove({ player: "black", pos: { x: 5, y: 3 } })
|
.makeMove({ player: "black", pos: { x: 5, y: 3 }, id: 3, prior: 2 })
|
||||||
.submitPass("white")
|
.submitPass("white")
|
||||||
.makeMove({ player: "black", pos: { x: 16, y: 16 } })
|
.makeMove({ player: "black", pos: { x: 16, y: 16 }, id: 4, prior: 3 })
|
||||||
.submitPass("white")
|
.submitPass("white")
|
||||||
.getMeta()
|
.getMeta()
|
||||||
.should.eql({
|
.should.eql({
|
||||||
|
@ -740,12 +760,38 @@ describe("Game end logic", () => {
|
||||||
pass: 1,
|
pass: 1,
|
||||||
turn: 1,
|
turn: 1,
|
||||||
gameRecord: [
|
gameRecord: [
|
||||||
{ player: "black", pos: { x: 4, y: 4 } },
|
{
|
||||||
{ player: "white", pos: { x: 4, y: 5 } },
|
player: "black",
|
||||||
{ player: "black", pos: { x: 5, y: 3 } },
|
pos: { x: 4, y: 4 },
|
||||||
{ player: "white", pos: { x: null, y: null } },
|
id: 1,
|
||||||
{ player: "black", pos: { x: 16, y: 16 } },
|
prior: null,
|
||||||
{ player: "white", pos: { x: null, y: null } },
|
},
|
||||||
|
{
|
||||||
|
player: "white",
|
||||||
|
pos: { x: 4, y: 5 },
|
||||||
|
id: 2,
|
||||||
|
prior: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
player: "black",
|
||||||
|
pos: { x: 5, y: 3 },
|
||||||
|
id: 3,
|
||||||
|
prior: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
player: "white",
|
||||||
|
pos: { x: null, y: null },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
player: "black",
|
||||||
|
pos: { x: 16, y: 16 },
|
||||||
|
id: 4,
|
||||||
|
prior: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
player: "white",
|
||||||
|
pos: { x: null, y: null },
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
done();
|
done();
|
||||||
|
|
|
@ -20,7 +20,7 @@ describe("game services", () => {
|
||||||
|
|
||||||
it("games services places move", async () => {
|
it("games services places move", async () => {
|
||||||
gameServices.initGame({ id: 1, handicap: 4 });
|
gameServices.initGame({ id: 1, handicap: 4 });
|
||||||
const move = { player: "white", pos: { x: 6, y: 3 } };
|
const move = { player: "white", pos: { x: 6, y: 3 }, id: 1, prior: null };
|
||||||
const afterMove = await gameServices.makeMove({ id: 1, move });
|
const afterMove = await gameServices.makeMove({ id: 1, move });
|
||||||
const afterMoveShould = {
|
const afterMoveShould = {
|
||||||
board: { ...fourHandicapBoard, "6-3": -1 },
|
board: { ...fourHandicapBoard, "6-3": -1 },
|
||||||
|
|
Loading…
Reference in a new issue