add mancala game
This commit is contained in:
parent
d511a3d16c
commit
8c22f1f887
177
src/core/MancalaGame.ts
Normal file
177
src/core/MancalaGame.ts
Normal file
@ -0,0 +1,177 @@
|
||||
import { Board, PitType } from './Board';
|
||||
import { GameRule } from './GameRule';
|
||||
import { Player } from './Player';
|
||||
|
||||
export type GameState = 'initial' | 'playing' | 'ended';
|
||||
|
||||
export class MancalaGame {
|
||||
board: Board;
|
||||
player1: Player;
|
||||
player2: Player;
|
||||
turnPlayerId: string;
|
||||
state: GameState;
|
||||
gameRules: GameRule[];
|
||||
|
||||
constructor(
|
||||
board: Board,
|
||||
player1: Player,
|
||||
player2: Player,
|
||||
turnPlayerId: string,
|
||||
gameRules: GameRule[]
|
||||
) {
|
||||
this.board = board;
|
||||
this.player1 = player1;
|
||||
this.player2 = player2;
|
||||
this.turnPlayerId = turnPlayerId;
|
||||
this.state = 'initial';
|
||||
this.gameRules = gameRules;
|
||||
this.listenBoardMoveEvents();
|
||||
}
|
||||
|
||||
listenBoardMoveEvents() {
|
||||
this.board.onGameMoveStart = (index: number) => {
|
||||
this.gameRules.forEach((gameRule) => {
|
||||
gameRule.onGameMoveStart(this, index);
|
||||
});
|
||||
};
|
||||
this.board.onGameMove = (index: number) => {
|
||||
this.gameRules.forEach((gameRule) => {
|
||||
gameRule.onGameMove(this, index);
|
||||
});
|
||||
};
|
||||
this.board.onGameMoveEnd = (index: number) => {
|
||||
this.gameRules.forEach((gameRule) => {
|
||||
gameRule.onGameMoveEnd(this, index);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
changePlayerTurn() {
|
||||
if (this.turnPlayerId === this.player1.id) {
|
||||
this.turnPlayerId = this.player2.id;
|
||||
} else {
|
||||
this.turnPlayerId = this.player1.id;
|
||||
}
|
||||
}
|
||||
|
||||
isTurnPlayer1() {
|
||||
return this.player1.id === this.turnPlayerId;
|
||||
}
|
||||
|
||||
isTurnPlayer2() {
|
||||
return this.player2.id === this.turnPlayerId;
|
||||
}
|
||||
|
||||
getPlayerByPitType(pitType: PitType): Player {
|
||||
if (pitType === 'player1Pit' || pitType === 'player1Bank') {
|
||||
return this.player1;
|
||||
} else if (pitType === 'player2Pit' || pitType === 'player2Bank') {
|
||||
return this.player2;
|
||||
} else {
|
||||
throw new Error('Unknown pit type : ' + pitType);
|
||||
}
|
||||
}
|
||||
|
||||
getPlayerByIndex(index: number): Player {
|
||||
const pitType = this.board.getPitTypeByIndex(index);
|
||||
return this.getPlayerByPitType(pitType);
|
||||
}
|
||||
|
||||
checkIsPlayerTurnByIndex(index: number): boolean {
|
||||
const player = this.getPlayerByIndex(index);
|
||||
return this.checkIsPlayerTurn(player);
|
||||
}
|
||||
|
||||
getBoardIndexByPlayer(player: Player, pitIndex: number) {
|
||||
if (this.player1.id === player.id) {
|
||||
return this.board.player1PitStartIndex() + pitIndex;
|
||||
} else if (this.player2.id === player.id) {
|
||||
return this.board.player2PitStartIndex() + pitIndex;
|
||||
} else {
|
||||
return -1; // throwing an error might be better
|
||||
}
|
||||
}
|
||||
|
||||
public checkIsPlayerTurn(player: Player) {
|
||||
return player.id === this.turnPlayerId;
|
||||
}
|
||||
|
||||
public checkPitIndexForPlayer(player: Player, pitIndex: number) {
|
||||
const foundPlayer = this.getPlayerByIndex(
|
||||
this.getBoardIndexByPlayer(player, pitIndex)
|
||||
);
|
||||
return player.id === foundPlayer.id;
|
||||
}
|
||||
|
||||
public checkIsPitIndexBank(player: Player, pitIndex: number) {
|
||||
const pitType = this.board.getPitTypeByIndex(
|
||||
this.getBoardIndexByPlayer(player, pitIndex)
|
||||
);
|
||||
return pitType === 'player1Bank' || pitType === 'player2Bank';
|
||||
}
|
||||
|
||||
public canPlayerMove(player: Player, pitIndex: number) {
|
||||
const isPitIndexCorrect = this.checkPitIndexForPlayer(player, pitIndex);
|
||||
const isPitIndexBank = this.checkIsPitIndexBank(player, pitIndex);
|
||||
const isPlayerTurn = this.checkIsPlayerTurn(player);
|
||||
return isPitIndexCorrect && !isPitIndexBank && isPlayerTurn;
|
||||
}
|
||||
|
||||
public moveByPlayerPit(player: Player, pitIndex: number) {
|
||||
if (this.state === 'ended') return;
|
||||
if (this.state === 'initial') {
|
||||
this.state = 'playing';
|
||||
}
|
||||
if (this.canPlayerMove(player, pitIndex)) {
|
||||
this.board.move(this.getBoardIndexByPlayer(player, pitIndex));
|
||||
if (this.checkGameIsEnded()) {
|
||||
this.state = 'ended';
|
||||
}
|
||||
} else {
|
||||
const isPitIndexCorrect = this.checkPitIndexForPlayer(player, pitIndex);
|
||||
const isPlayerTurn = this.checkIsPlayerTurn(player);
|
||||
throw new Error(
|
||||
`Player cannot move reason : isPitIndexCorrect = ${isPitIndexCorrect} isPlayerTurn = ${isPlayerTurn}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
getPlayer1StoneCountInPits(): number {
|
||||
return this.getPlayerStoneCountInPits(
|
||||
this.board.player1PitStartIndex(),
|
||||
this.board.player1BankIndex()
|
||||
);
|
||||
}
|
||||
|
||||
getPlayer2StoneCountInPits(): number {
|
||||
return this.getPlayerStoneCountInPits(
|
||||
this.board.player2PitStartIndex(),
|
||||
this.board.player2BankIndex()
|
||||
);
|
||||
}
|
||||
|
||||
getPlayerStoneCountInPits(
|
||||
playerPitStartIndex: number,
|
||||
playerBankIndex: number
|
||||
): number {
|
||||
const player2Pits = this.board.pits.slice(
|
||||
playerPitStartIndex,
|
||||
playerBankIndex
|
||||
);
|
||||
return player2Pits
|
||||
.map((pit) => pit.stoneCount)
|
||||
.reduce(
|
||||
(previousStoneCount, stoneCount) => previousStoneCount + stoneCount
|
||||
);
|
||||
}
|
||||
|
||||
checkGameIsEnded(): boolean {
|
||||
if (
|
||||
this.getPlayer1StoneCountInPits() === 0 ||
|
||||
this.getPlayer2StoneCountInPits() === 0
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
109
tests/MancalaGame.test.ts
Normal file
109
tests/MancalaGame.test.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import { GRLastStoneInBank } from '../src/common/game_rules/GRLastStoneInBank';
|
||||
import { GRLastStoneInEmptyPit } from '../src/common/game_rules/GRLastStoneInEmptyPit';
|
||||
import { Board } from '../src/core/Board';
|
||||
import { MancalaGame } from '../src/core/MancalaGame';
|
||||
import { Player } from '../src/core/Player';
|
||||
|
||||
function createGame(): MancalaGame {
|
||||
const board = new Board(6, 4);
|
||||
const player1 = new Player('0', 'player1');
|
||||
const player2 = new Player('1', 'player2');
|
||||
const game = new MancalaGame(board, player1, player2, player1.id, [
|
||||
new GRLastStoneInEmptyPit(),
|
||||
new GRLastStoneInBank()
|
||||
]);
|
||||
return game;
|
||||
}
|
||||
|
||||
describe('Game Test', () => {
|
||||
test('test getPlayerByIndex', () => {
|
||||
const game = createGame();
|
||||
const player1 = new Player('0', 'player1');
|
||||
const player2 = new Player('1', 'player2');
|
||||
expect(game.getPlayerByIndex(0)).toStrictEqual(player1);
|
||||
expect(game.getPlayerByIndex(1)).toStrictEqual(player1);
|
||||
expect(game.getPlayerByIndex(2)).toStrictEqual(player1);
|
||||
expect(game.getPlayerByIndex(3)).toStrictEqual(player1);
|
||||
expect(game.getPlayerByIndex(4)).toStrictEqual(player1);
|
||||
expect(game.getPlayerByIndex(5)).toStrictEqual(player1);
|
||||
expect(game.getPlayerByIndex(6)).toStrictEqual(player1);
|
||||
expect(game.getPlayerByIndex(7)).toStrictEqual(player2);
|
||||
expect(game.getPlayerByIndex(8)).toStrictEqual(player2);
|
||||
expect(game.getPlayerByIndex(9)).toStrictEqual(player2);
|
||||
expect(game.getPlayerByIndex(10)).toStrictEqual(player2);
|
||||
expect(game.getPlayerByIndex(11)).toStrictEqual(player2);
|
||||
expect(game.getPlayerByIndex(12)).toStrictEqual(player2);
|
||||
expect(game.getPlayerByIndex(13)).toStrictEqual(player2);
|
||||
});
|
||||
test('test canPlayerMove', () => {
|
||||
const game = createGame();
|
||||
const player1 = new Player('0', 'player1');
|
||||
const player2 = new Player('1', 'player2');
|
||||
expect(game.canPlayerMove(player1, 0)).toBe(true);
|
||||
expect(game.canPlayerMove(player1, 1)).toBe(true);
|
||||
expect(game.canPlayerMove(player1, 2)).toBe(true);
|
||||
expect(game.canPlayerMove(player1, 3)).toBe(true);
|
||||
expect(game.canPlayerMove(player1, 4)).toBe(true);
|
||||
expect(game.canPlayerMove(player1, 5)).toBe(true);
|
||||
expect(game.canPlayerMove(player1, 6)).toBe(false);
|
||||
expect(game.canPlayerMove(player2, 0)).toBe(false);
|
||||
});
|
||||
|
||||
test('test moveByPlayerPit', () => {
|
||||
const game = createGame();
|
||||
const player1 = new Player('0', 'player1');
|
||||
const player2 = new Player('1', 'player2');
|
||||
expect(game.state).toBe('initial');
|
||||
expect(game.turnPlayerId).toBe(player1.id);
|
||||
|
||||
game.moveByPlayerPit(player1, 0);
|
||||
expect(game.state).toBe('playing');
|
||||
expect(game.turnPlayerId).toBe(player2.id);
|
||||
|
||||
game.moveByPlayerPit(player2, 0);
|
||||
expect(game.state).toBe('playing');
|
||||
expect(game.turnPlayerId).toBe(player1.id);
|
||||
});
|
||||
|
||||
test('test game end test', () => {
|
||||
const game = createGame();
|
||||
const player1 = new Player('0', 'player1');
|
||||
const player2 = new Player('1', 'player2');
|
||||
expect(game.state).toBe('initial');
|
||||
game.board.pits[0].stoneCount = 0;
|
||||
game.board.pits[1].stoneCount = 0;
|
||||
game.board.pits[2].stoneCount = 0;
|
||||
game.board.pits[3].stoneCount = 0;
|
||||
game.board.pits[4].stoneCount = 0;
|
||||
game.board.pits[5].stoneCount = 1;
|
||||
game.moveByPlayerPit(player1, 5);
|
||||
expect(game.state).toBe('ended');
|
||||
});
|
||||
|
||||
test('test last stone in bank', () => {
|
||||
const game = createGame();
|
||||
const player1 = new Player('0', 'player1');
|
||||
const player2 = new Player('1', 'player2');
|
||||
expect(game.state).toBe('initial');
|
||||
expect(game.turnPlayerId).toBe(player1.id);
|
||||
|
||||
game.moveByPlayerPit(player1, 3);
|
||||
expect(game.state).toBe('playing');
|
||||
expect(game.turnPlayerId).toBe(player1.id);
|
||||
});
|
||||
|
||||
test('test empty pit 1', () => {
|
||||
const game = createGame();
|
||||
const player1 = new Player('0', 'player1');
|
||||
const player2 = new Player('1', 'player2');
|
||||
expect(game.state).toBe('initial');
|
||||
game.board.pits[0].stoneCount = 1;
|
||||
game.board.pits[1].stoneCount = 0;
|
||||
game.moveByPlayerPit(player1, 0);
|
||||
expect(game.state).toBe('playing');
|
||||
expect(game.turnPlayerId).toBe(player2.id);
|
||||
expect(game.board.getStoneArray()).toStrictEqual([
|
||||
0, 0, 4, 4, 4, 4, 5, 4, 0, 4, 4, 4, 4, 0
|
||||
]);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user