diff --git a/src/core/Board.ts b/src/core/Board.ts new file mode 100644 index 0000000..c700cb4 --- /dev/null +++ b/src/core/Board.ts @@ -0,0 +1,143 @@ +import { Bank, Pit } from './Pit'; + +const bankCount = 2; + +export type PitType = + | 'player1Pit' + | 'player1Bank' + | 'player2Pit' + | 'player2Bank'; + +export class Board { + pits: Pit[]; + playerPitCount: number; + initialStoneCountInPits: number; + + onGameMoveStart: (index: number) => void = () => {}; + onGameMove: (index: number) => void = () => {}; + onGameMoveEnd: (index: number) => void = () => {}; + + constructor(playerPitCount: number, initialStoneCountInPits: number) { + this.playerPitCount = playerPitCount; + this.initialStoneCountInPits = initialStoneCountInPits; + this.pits = this.createPits(playerPitCount, initialStoneCountInPits); + } + + createPits(playerPitCount: number, initialStoneCountInPits: number): Pit[] { + const totalPitCount = playerPitCount + playerPitCount + bankCount; + const pitArray = new Array(totalPitCount); + for (let index = 0; index < this.totalPitCount(); index++) { + const pitType = this.getPitTypeByIndex(index); + if (pitType === 'player1Pit' || pitType === 'player2Pit') { + pitArray[index] = new Pit(initialStoneCountInPits); + } else if (pitType === 'player1Bank' || pitType === 'player2Bank') { + pitArray[index] = new Bank(0); + } + } + return pitArray; + } + + public player1PitStartIndex = () => 0; + public player1PitStopIndex = () => + this.player1PitStartIndex() + this.playerPitCount - 1; + public player1BankIndex = () => this.player1PitStopIndex() + 1; + public player2PitStartIndex = () => this.player1BankIndex() + 1; + public player2PitStopIndex = () => + this.player2PitStartIndex() + this.playerPitCount - 1; + public player2BankIndex = () => this.player2PitStopIndex() + 1; + public totalPitCount = () => this.player2BankIndex() + 1; + + public checkIndex(index: number): boolean { + return ( + index >= this.player1PitStartIndex() && index <= this.player2BankIndex() + ); + } + + checkIndeAndMaybeThrowError(index: number): void { + if (!this.checkIndex(index)) { + throw new Error( + `IndexOutOfRange => index : ${index} [start, stop] = [${this.player1PitStartIndex()}, ${this.player2BankIndex()}]` + ); + } + } + + public getPitTypeByIndex(index: number): PitType { + this.checkIndeAndMaybeThrowError(index); + if ( + index >= this.player1PitStartIndex() && + index <= this.player1PitStopIndex() + ) { + return 'player1Pit'; + } else if (index === this.player1BankIndex()) { + return 'player1Bank'; + } else if (index <= this.player2PitStopIndex()) { + return 'player2Pit'; + } else { + return 'player2Bank'; + } + } + + public move(index: number) { + this.checkIndeAndMaybeThrowError(index); + const pitType = this.getPitTypeByIndex(index); + if (pitType === 'player1Bank' || pitType === 'player2Bank') { + throw new Error(`InCorrectPit => index : ${index} pitType = ${pitType}`); + } + const pit = this.pits[index]; + if (pit.stoneCount <= 0) { + throw new Error( + `StoneNotFound => index : ${index} stoneCount = ${pit.stoneCount}` + ); + } + const stepCount = pit.stoneCount; + this.pits[index].stoneCount = 0; + this.fireOnGameMoveStart(index); + if (stepCount === 1) { + this.fireOnGameMove(index); + this.getNextPitCircularly(index).increaseStone(); + const nextPitIndex = this.getNextPitIndexCircularly(index); + this.fireOnGameMove(nextPitIndex); + this.fireOnGameMoveEnd(nextPitIndex); + } else { + for (let i = 0; i < stepCount; i++) { + const pit = this.getPitCircularly(index + i); + pit.increaseStone(); + this.fireOnGameMove(index + i); + } + this.fireOnGameMoveEnd(index + stepCount - 1); + } + } + + public getNextPitCircularly(currentPitIndex: number) { + return this.pits[this.getNextPitIndexCircularly(currentPitIndex)]; + } + + public getNextPitIndexCircularly(currentPitIndex: number) { + this.checkIndeAndMaybeThrowError(currentPitIndex); + return this.getPitIndexCircularly(currentPitIndex + 1); + } + + public getPitCircularly(currentPitIndex: number) { + return this.pits[this.getPitIndexCircularly(currentPitIndex)]; + } + + public getPitIndexCircularly(currentPitIndex: number) { + return currentPitIndex % this.totalPitCount(); + } + + fireOnGameMoveStart(index: number): void { + this.onGameMoveStart(index); + } + + fireOnGameMove(index: number): void { + this.onGameMove(index); + } + + fireOnGameMoveEnd(index: number): void { + this.onGameMoveEnd(index); + } + + public getStoneArray(): number[] { + return [...this.pits.map((pit) => pit.stoneCount)]; + } +} diff --git a/tests/Board.test.ts b/tests/Board.test.ts new file mode 100644 index 0000000..739ccf7 --- /dev/null +++ b/tests/Board.test.ts @@ -0,0 +1,92 @@ +import { Board } from '../src/core/Board'; + +describe('Board Test', () => { + test('test getPitTypeByIndex', () => { + const board = new Board(6, 4); + expect(board.getPitTypeByIndex(0)).toBe('player1Pit'); + expect(board.getPitTypeByIndex(1)).toBe('player1Pit'); + expect(board.getPitTypeByIndex(2)).toBe('player1Pit'); + expect(board.getPitTypeByIndex(3)).toBe('player1Pit'); + expect(board.getPitTypeByIndex(4)).toBe('player1Pit'); + expect(board.getPitTypeByIndex(5)).toBe('player1Pit'); + expect(board.getPitTypeByIndex(6)).toBe('player1Bank'); + expect(board.getPitTypeByIndex(7)).toBe('player2Pit'); + expect(board.getPitTypeByIndex(8)).toBe('player2Pit'); + expect(board.getPitTypeByIndex(9)).toBe('player2Pit'); + expect(board.getPitTypeByIndex(10)).toBe('player2Pit'); + expect(board.getPitTypeByIndex(11)).toBe('player2Pit'); + expect(board.getPitTypeByIndex(12)).toBe('player2Pit'); + expect(board.getPitTypeByIndex(13)).toBe('player2Bank'); + expect(() => board.getPitTypeByIndex(-1)).toThrowError(); + expect(() => board.getPitTypeByIndex(14)).toThrowError(); + }); + test('test checkIndex', () => { + const board = new Board(6, 4); + expect(board.checkIndex(0)).toBe(true); + expect(board.checkIndex(1)).toBe(true); + expect(board.checkIndex(2)).toBe(true); + expect(board.checkIndex(4)).toBe(true); + expect(board.checkIndex(5)).toBe(true); + expect(board.checkIndex(6)).toBe(true); + expect(board.checkIndex(7)).toBe(true); + expect(board.checkIndex(8)).toBe(true); + expect(board.checkIndex(9)).toBe(true); + expect(board.checkIndex(10)).toBe(true); + expect(board.checkIndex(11)).toBe(true); + expect(board.checkIndex(12)).toBe(true); + expect(board.checkIndex(13)).toBe(true); + expect(board.checkIndex(14)).toBe(false); + expect(board.checkIndex(-1)).toBe(false); + }); + test('test getPitIndexCircularly', () => { + const board = new Board(6, 4); + expect(board.getPitIndexCircularly(0)).toBe(0); + expect(board.getPitIndexCircularly(1)).toBe(1); + expect(board.getPitIndexCircularly(2)).toBe(2); + expect(board.getPitIndexCircularly(3)).toBe(3); + expect(board.getPitIndexCircularly(4)).toBe(4); + expect(board.getPitIndexCircularly(5)).toBe(5); + expect(board.getPitIndexCircularly(6)).toBe(6); + expect(board.getPitIndexCircularly(7)).toBe(7); + expect(board.getPitIndexCircularly(8)).toBe(8); + expect(board.getPitIndexCircularly(9)).toBe(9); + expect(board.getPitIndexCircularly(10)).toBe(10); + expect(board.getPitIndexCircularly(11)).toBe(11); + expect(board.getPitIndexCircularly(12)).toBe(12); + expect(board.getPitIndexCircularly(13)).toBe(13); + expect(board.getPitIndexCircularly(14)).toBe(0); + expect(board.getPitIndexCircularly(15)).toBe(1); + expect(board.getPitIndexCircularly(16)).toBe(2); + }); + test('test move', () => { + const board = new Board(6, 4); + const initialBoard = [4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0]; + expect(board.getStoneArray()).toStrictEqual(initialBoard); + board.move(0); + expect(board.getStoneArray()).toStrictEqual([ + 1, 5, 5, 5, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0 + ]); + board.move(0); + expect(board.getStoneArray()).toStrictEqual([ + 0, 6, 5, 5, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0 + ]); + board.move(1); + expect(board.getStoneArray()).toStrictEqual([ + 0, 1, 6, 6, 5, 5, 1, 4, 4, 4, 4, 4, 4, 0 + ]); + board.move(5); + expect(board.getStoneArray()).toStrictEqual([ + 0, 1, 6, 6, 5, 1, 2, 5, 5, 5, 4, 4, 4, 0 + ]); + }); + test('test move 2', () => { + const board = new Board(6, 4); + board.pits[5].stoneCount = 15; + const initialBoard = [4, 4, 4, 4, 4, 15, 0, 4, 4, 4, 4, 4, 4, 0]; + 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 + ]); + }); +});