mancala/src/core/MancalaGame.ts

178 lines
4.8 KiB
TypeScript
Raw Normal View History

2022-05-02 00:42:59 +03:00
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;
}
}