Release: Quarkus backend, Keycloak login, game history, clock, bot, tutorial and rematch #3
@ -64,14 +64,11 @@ export function describeMove(moves: PlayedMove[], moveNumber: number): MoveDescr
|
||||
};
|
||||
}
|
||||
|
||||
// Board indexes the stones of the given pit are dropped into, in order, as Board.move sows them: a single stone goes
|
||||
// to the next pit; otherwise the first stone goes back into the played pit. Every pit and store is passed, the
|
||||
// opponent's store too. The last index is where the last stone lands. Empty for an empty pit or a store.
|
||||
// Board indexes the stones of the given pit are dropped into, in order, as Board.move sows them (see
|
||||
// Board.getSowingPath): the opponent's store is skipped. The last index is where the last stone lands. Empty for an
|
||||
// empty pit or a store.
|
||||
export function getSowingPath(stones: number[], pitIndex: number): number[] {
|
||||
const stoneCount = stones[pitIndex];
|
||||
if (!stoneCount || referenceBoard.checkPitTypeIsBankByIndex(pitIndex)) return [];
|
||||
const start = stoneCount === 1 ? pitIndex + 1 : pitIndex;
|
||||
return Array.from({ length: stoneCount }, (_, step) => (start + step) % stones.length);
|
||||
return referenceBoard.getSowingPath(pitIndex, stones[pitIndex] ?? 0);
|
||||
}
|
||||
|
||||
// The game as it was after the given move, for showing or animating that position (e.g. with PitAnimator,
|
||||
|
||||
@ -145,12 +145,12 @@ describe("getSowingPath", () => {
|
||||
expect(getSowingPath(stones, 4)).toEqual([5]);
|
||||
});
|
||||
|
||||
it("passes both stores and wraps around the board", () => {
|
||||
it("passes the player's own store, skips the opponent's and wraps around the board", () => {
|
||||
const stones = getInitialBoard();
|
||||
stones[12] = 4;
|
||||
expect(getSowingPath(stones, 12)).toEqual([12, 13, 0, 1]);
|
||||
stones[5] = 10;
|
||||
expect(getSowingPath(stones, 5)).toEqual([5, 6, 7, 8, 9, 10, 11, 12, 13, 0]);
|
||||
expect(getSowingPath(stones, 5)).toEqual([5, 6, 7, 8, 9, 10, 11, 12, 0, 1]);
|
||||
});
|
||||
|
||||
it("is empty for an empty pit or a store", () => {
|
||||
|
||||
@ -107,19 +107,34 @@ export class Board {
|
||||
`StoneNotFound => index : ${index} stoneCount = ${pit.stoneCount}`
|
||||
);
|
||||
}
|
||||
const stepCount = pit.stoneCount;
|
||||
this.pits[index].stoneCount = 0;
|
||||
const startIndex = stepCount === 1 ? index + 1 : index;
|
||||
this.moveInternal(startIndex, stepCount);
|
||||
const path = this.getSowingPath(index);
|
||||
pit.stoneCount = 0;
|
||||
this.fireOnGameMoveStart(path[0]);
|
||||
path.forEach((pathIndex) => this.onGameMoveStep(pathIndex));
|
||||
this.fireOnGameMoveEnd(path[path.length - 1]);
|
||||
}
|
||||
|
||||
private moveInternal(startIndex: number, stepCount: number) {
|
||||
this.fireOnGameMoveStart(startIndex);
|
||||
for (let i = startIndex; i < startIndex + stepCount; i++) {
|
||||
this.onGameMoveStep(i);
|
||||
// Indexes the stones of the pit are dropped into, in order. A single stone goes to the next pit; otherwise the first
|
||||
// stone goes back into the played pit. The player's own bank gets a stone, the opponent's bank is skipped. The last
|
||||
// index is where the last stone lands. Empty for a bank or an empty pit.
|
||||
public getSowingPath(
|
||||
index: number,
|
||||
stoneCount = this.pits[index].stoneCount
|
||||
): number[] {
|
||||
this.checkIndeAndMaybeThrowError(index);
|
||||
if (stoneCount <= 0 || this.checkPitTypeIsBankByIndex(index)) return [];
|
||||
const opponentBankIndex =
|
||||
this.getPitTypeByIndex(index) === 'player1Pit'
|
||||
? this.player2BankIndex()
|
||||
: this.player1BankIndex();
|
||||
const path: number[] = [];
|
||||
let current =
|
||||
stoneCount === 1 ? this.getNextPitIndexCircularly(index) : index;
|
||||
while (path.length < stoneCount) {
|
||||
if (current !== opponentBankIndex) path.push(current);
|
||||
current = this.getNextPitIndexCircularly(current);
|
||||
}
|
||||
const stopIndex = startIndex + stepCount - 1;
|
||||
this.fireOnGameMoveEnd(stopIndex);
|
||||
return path;
|
||||
}
|
||||
|
||||
public onGameMoveStep(index: number) {
|
||||
|
||||
@ -103,9 +103,33 @@ describe('Board Test', () => {
|
||||
expect(board.getStoneArray()).toStrictEqual(initialBoard);
|
||||
board.move(5);
|
||||
expect(board.getStoneArray()).toStrictEqual([
|
||||
5, 5, 5, 5, 5, 2, 1, 5, 5, 5, 5, 5, 5, 1
|
||||
5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 0
|
||||
]);
|
||||
});
|
||||
test('move skips the opponent bank for both players', () => {
|
||||
const board = new Board(6, 4);
|
||||
board.pits[12].stoneCount = 10;
|
||||
board.move(12);
|
||||
expect(board.getStoneArray()).toStrictEqual([
|
||||
5, 5, 5, 5, 5, 5, 0, 5, 5, 4, 4, 4, 1, 1
|
||||
]);
|
||||
});
|
||||
test('test getSowingPath', () => {
|
||||
const board = new Board(6, 4);
|
||||
expect(board.getSowingPath(0)).toStrictEqual([0, 1, 2, 3]);
|
||||
expect(board.getSowingPath(0, 1)).toStrictEqual([1]);
|
||||
expect(board.getSowingPath(5, 2)).toStrictEqual([5, 6]);
|
||||
expect(board.getSowingPath(5, 9)).toStrictEqual([
|
||||
5, 6, 7, 8, 9, 10, 11, 12, 0
|
||||
]);
|
||||
expect(board.getSowingPath(12, 3)).toStrictEqual([12, 13, 0]);
|
||||
expect(board.getSowingPath(11, 10)).toStrictEqual([
|
||||
11, 12, 13, 0, 1, 2, 3, 4, 5, 7
|
||||
]);
|
||||
expect(board.getSowingPath(0, 0)).toStrictEqual([]);
|
||||
expect(board.getSowingPath(6)).toStrictEqual([]);
|
||||
expect(board.getSowingPath(13)).toStrictEqual([]);
|
||||
});
|
||||
|
||||
test('test getOppositePitIndex', () => {
|
||||
const board = new Board(6, 4);
|
||||
|
||||
@ -33,7 +33,7 @@ describe('GRDoubleStoneInPit Test', () => {
|
||||
expect(game.history[game.history.length - 1].gameSteps).toStrictEqual([
|
||||
new GameStep(12, GAME_STEP_GAME_MOVE),
|
||||
new GameStep(13, GAME_STEP_GAME_MOVE),
|
||||
new GameStep(14, GAME_STEP_GAME_MOVE),
|
||||
new GameStep(0, GAME_STEP_GAME_MOVE),
|
||||
new GameStep(0, GAME_STEP_DOUBLE_STONE_IN_PIT, {
|
||||
pitIndex: 0,
|
||||
bankIndex: 13
|
||||
|
||||
Loading…
Reference in New Issue
Block a user