Merge mancala.js repo

This commit is contained in:
Halit Aksoy 2024-03-24 02:54:15 +03:00
commit 58ad1561ac
28 changed files with 13745 additions and 0 deletions

2
mancala.js/.eslintignore Normal file
View File

@ -0,0 +1,2 @@
node_modules
dist

12
mancala.js/.eslintrc Normal file
View File

@ -0,0 +1,12 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
]
}

2
mancala.js/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
node_modules
dist

6
mancala.js/.prettierrc Normal file
View File

@ -0,0 +1,6 @@
{
"semi": true,
"trailingComma": "none",
"singleQuote": true,
"printWidth": 80
}

3
mancala.js/README.md Normal file
View File

@ -0,0 +1,3 @@
# mancala.js
mancala.js is a javascript library for mancala game.

View File

@ -0,0 +1,6 @@
module.exports = {
transform: {'^.+\\.ts?$': 'ts-jest'},
testEnvironment: 'node',
testRegex: '/tests/.*\\.(test|spec)?\\.(ts|tsx)$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node']
};

9294
mancala.js/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

27
mancala.js/package.json Normal file
View File

@ -0,0 +1,27 @@
{
"name": "mancala.js",
"version": "0.0.2-beta.3",
"description": "Mancala game library",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"dev": "yarn tsc -w",
"test": "yarn jest",
"lint": "eslint . --ext .ts",
"prettier-format": "prettier --config .prettierrc 'src/**/*.ts' --write",
"build": "tsc",
"prepublish": "tsc"
},
"author": "Halit Aksoy",
"license": "MIT",
"devDependencies": {
"@types/jest": "^27.4.1",
"@typescript-eslint/eslint-plugin": "^5.21.0",
"@typescript-eslint/parser": "^5.21.0",
"eslint": "^8.14.0",
"jest": "^27.5.1",
"prettier": "^2.6.2",
"ts-jest": "^27.1.4",
"typescript": "^4.6.3"
}
}

View File

@ -0,0 +1,10 @@
import { Board } from '../core/Board';
const pitCountByUser = 6;
const initialStoneCountInPit = 4;
export class CommonBoard extends Board {
constructor() {
super(pitCountByUser, initialStoneCountInPit);
}
}

View File

@ -0,0 +1,25 @@
import { MancalaGame } from '../core/MancalaGame';
import { CommonBoard } from './CommonBoard';
import { GRClearBoardAtEnd } from './game_rules/GRClearBoardAtEnd';
import { GRDoubleStoneInPit } from './game_rules/GRDoubleStoneInPit';
import { GRLastStoneInBank } from './game_rules/GRLastStoneInBank';
import { GRLastStoneInEmptyPit } from './game_rules/GRLastStoneInEmptyPit';
export class CommonMancalaGame extends MancalaGame {
constructor(id: string, player1Id: string, player2Id: string) {
super(
id,
new CommonBoard(),
player1Id,
player2Id,
player1Id,
[
new GRLastStoneInEmptyPit(),
new GRDoubleStoneInPit(),
new GRLastStoneInBank(),
new GRClearBoardAtEnd()
],
[]
);
}
}

View File

@ -0,0 +1,52 @@
import { Board } from '../../core/Board';
import { GameRule } from '../../core/GameRule';
import { GameStep } from '../../core/HistoryItem';
import { MancalaGame } from '../../core/MancalaGame';
export const GAME_STEP_BOARD_CLEARED = 'GAME_STEP_BOARD_CLEARED';
export type ClearBoardAtEndData = { pitIndexesThatHasStone: number[] };
export class GRClearBoardAtEnd implements GameRule {
onGameMoveStart(game: MancalaGame, index: number): void {}
onGameMove(game: MancalaGame, index: number): void {}
onGameMoveEnd(game: MancalaGame, index: number): void {
if (game.getPlayer1StoneCountInPits() === 0) {
const clearBoardAtEndData = {
pitIndexesThatHasStone: this.getPitIndexesThatHasStone(game.board)
};
game.board.player1Bank.stoneCount += game.getPlayer2StoneCountInPits();
game.board.clearPlayer2Pits();
this.addGameStep(game, index, clearBoardAtEndData);
} else if (game.getPlayer2StoneCountInPits() === 0) {
const clearBoardAtEndData = {
pitIndexesThatHasStone: this.getPitIndexesThatHasStone(game.board)
};
game.board.player2Bank.stoneCount += game.getPlayer1StoneCountInPits();
game.board.clearPlayer1Pits();
this.addGameStep(game, index, clearBoardAtEndData);
}
}
private getPitIndexesThatHasStone(board: Board): number[] {
let index = 0;
const indexList = [];
for (const stoneCount of board.getStoneArray()) {
if (stoneCount > 0 && board.checkPitTypeIsNormalPitByIndex(index)) {
indexList.push(index);
}
index++;
}
return indexList;
}
private addGameStep(
game: MancalaGame,
index: number,
clearBoardAtEndData: ClearBoardAtEndData
) {
game.addGameStep(
new GameStep(index, GAME_STEP_BOARD_CLEARED, clearBoardAtEndData)
);
}
}

View File

@ -0,0 +1,44 @@
import { GameRule } from '../../core/GameRule';
import { GameStep } from '../../core/HistoryItem';
import { MancalaGame } from '../../core/MancalaGame';
export const GAME_STEP_DOUBLE_STONE_IN_PIT = 'GAME_STEP_DOUBLE_STONE_IN_PIT';
export type DoubleStoneInPitData = { pitIndex: number; bankIndex: number };
export class GRDoubleStoneInPit implements GameRule {
onGameMoveStart(game: MancalaGame, index: number): void {}
onGameMove(game: MancalaGame, index: number): void {}
onGameMoveEnd(game: MancalaGame, index: number): void {
index = game.board.getPitIndexCircularly(index);
const pit = game.board.pits[index];
const pitType = game.board.getPitTypeByIndex(index);
if (pit.stoneCount % 2 === 0) {
if (pitType === 'player1Pit' && game.isTurnPlayer2()) {
const pitIndex = index;
const bankIndex = game.board.player2BankIndex();
game.board.player2Bank.stoneCount +=
game.board.getPitCircularly(pitIndex).stoneCount;
game.board.getPitCircularly(pitIndex).stoneCount = 0;
this.addGameStep(game, index, { pitIndex, bankIndex });
} else if (pitType === 'player2Pit' && game.isTurnPlayer1()) {
const pitIndex = index;
const bankIndex = game.board.player1BankIndex();
game.board.player1Bank.stoneCount +=
game.board.getPitCircularly(pitIndex).stoneCount;
game.board.getPitCircularly(pitIndex).stoneCount = 0;
this.addGameStep(game, index, { pitIndex, bankIndex });
}
}
}
private addGameStep(
game: MancalaGame,
index: number,
doubleStoneInPitData: DoubleStoneInPitData
) {
game.addGameStep(
new GameStep(index, GAME_STEP_DOUBLE_STONE_IN_PIT, doubleStoneInPitData)
);
}
}

View File

@ -0,0 +1,22 @@
import { GameRule } from '../../core/GameRule';
import { GameStep } from '../../core/HistoryItem';
import { MancalaGame } from '../../core/MancalaGame';
export const GAME_STEP_LAST_STONE_IN_BANK = 'GAME_STEP_LAST_STONE_IN_BANK';
export class GRLastStoneInBank implements GameRule {
onGameMoveStart(game: MancalaGame, index: number): void {}
onGameMove(game: MancalaGame, index: number): void {}
onGameMoveEnd(game: MancalaGame, index: number): void {
index = game.board.getPitIndexCircularly(index);
const pitType = game.board.getPitTypeByIndex(index);
if (
(pitType === 'player1Bank' && game.isTurnPlayer1()) ||
(pitType === 'player2Bank' && game.isTurnPlayer2())
) {
game.addGameStep(new GameStep(index, GAME_STEP_LAST_STONE_IN_BANK));
} else {
game.changePlayerTurn();
}
}
}

View File

@ -0,0 +1,53 @@
import { GameRule } from '../../core/GameRule';
import { GameStep } from '../../core/HistoryItem';
import { MancalaGame } from '../../core/MancalaGame';
export const GAME_STEP_LAST_STONE_IN_EMPTY_PIT =
'GAME_STEP_LAST_STONE_IN_EMPTY_PIT';
export type LastStoneInEmptyPitData = { oppositeIndex: number };
export class GRLastStoneInEmptyPit implements GameRule {
onGameMoveStart(game: MancalaGame, index: number): void {}
onGameMove(game: MancalaGame, index: number): void {}
onGameMoveEnd(game: MancalaGame, index: number): void {
index = game.board.getPitIndexCircularly(index);
const pit = game.board.pits[index];
const pitType = game.board.getPitTypeByIndex(index);
if (pit.stoneCount === 1) {
if (pitType === 'player1Pit' && game.isTurnPlayer1()) {
const oppositeIndex = game.board.getOppositePitIndex(index);
const oppositePit = game.board.pits[oppositeIndex];
if (oppositePit.stoneCount > 0) {
const player1BankIndex =
game.board.pits[game.board.player1BankIndex()];
player1BankIndex.stoneCount += 1 + oppositePit.stoneCount;
oppositePit.stoneCount = 0;
pit.stoneCount = 0;
this.addGameStep(game, index, { oppositeIndex });
}
} else if (pitType === 'player2Pit' && game.isTurnPlayer2()) {
const oppositeIndex = game.board.getOppositePitIndex(index);
const oppositePit = game.board.pits[oppositeIndex];
if (oppositePit.stoneCount > 0) {
const player2BankIndex =
game.board.pits[game.board.player2BankIndex()];
player2BankIndex.stoneCount += 1 + oppositePit.stoneCount;
oppositePit.stoneCount = 0;
pit.stoneCount = 0;
this.addGameStep(game, index, { oppositeIndex });
}
}
}
}
private addGameStep(
game: MancalaGame,
index: number,
data: LastStoneInEmptyPitData
) {
game.addGameStep(
new GameStep(index, GAME_STEP_LAST_STONE_IN_EMPTY_PIT, data)
);
}
}

View File

@ -0,0 +1,216 @@
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,
pits: Pit[] | null = null
) {
this.playerPitCount = playerPitCount;
this.initialStoneCountInPits = initialStoneCountInPits;
this.pits = pits
? pits
: this.createPits(playerPitCount, initialStoneCountInPits);
}
createPits(playerPitCount: number, initialStoneCountInPits: number): Pit[] {
const totalPitCount = playerPitCount + playerPitCount + bankCount;
const pitArray = new Array<Pit>(totalPitCount);
for (let index = 0; index < this.totalPitCount(); index++) {
const pitType = this.getPitTypeByIndex(index);
if (pitType === 'player1Pit' || pitType === 'player2Pit') {
pitArray[index] = new Pit(index, initialStoneCountInPits);
} else if (pitType === 'player1Bank' || pitType === 'player2Bank') {
pitArray[index] = new Bank(index, 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 checkPitTypeIsNormalPitByIndex(index: number): boolean {
this.checkIndeAndMaybeThrowError(index);
const pitType = this.getPitTypeByIndex(index);
return pitType === 'player1Pit' || pitType === 'player2Pit';
}
public checkPitTypeIsBankByIndex(index: number): boolean {
this.checkIndeAndMaybeThrowError(index);
const pitType = this.getPitTypeByIndex(index);
return pitType === 'player1Bank' || pitType === '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;
const startIndex = stepCount === 1 ? index + 1 : index;
this.moveInternal(startIndex, stepCount);
}
private moveInternal(startIndex: number, stepCount: number) {
this.fireOnGameMoveStart(startIndex);
for (let i = startIndex; i < startIndex + stepCount; i++) {
this.onGameMoveStep(i);
}
const stopIndex = startIndex + stepCount - 1;
this.fireOnGameMoveEnd(stopIndex);
}
public onGameMoveStep(index: number) {
const pit = this.pits[this.getPitIndexCircularly(index)];
pit.increaseStone();
this.fireOnGameMove(index);
}
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)];
}
public get player1Pits(): Pit[] {
return this.pits.slice(
this.player1PitStartIndex(),
this.player1BankIndex()
);
}
get player2Pits(): Pit[] {
return this.pits.slice(
this.player2PitStartIndex(),
this.player2BankIndex()
);
}
get player1Bank(): Bank {
return this.pits[this.player1BankIndex()];
}
get player2Bank(): Bank {
return this.pits[this.player2BankIndex()];
}
public getOppositePitIndex(index: number) {
if (index > this.player1BankIndex()) {
return this.player2PitStopIndex() - index;
} else {
return this.player1BankIndex() + this.playerPitCount - index;
}
}
public clear() {
this.player1Bank.stoneCount = 0;
this.player2Bank.stoneCount = 0;
this.clearPlayer1Pits();
this.clearPlayer2Pits();
}
public clearPlayer1Pits() {
this.fillPlayer1Pits(0);
}
public clearPlayer2Pits() {
this.fillPlayer2Pits(0);
}
public fillPlayer1Pits(stoneCount: number) {
this.player1Pits.forEach((pit) => (pit.stoneCount = stoneCount));
}
public fillPlayer2Pits(stoneCount: number) {
this.player2Pits.forEach((pit) => (pit.stoneCount = stoneCount));
}
}

View File

@ -0,0 +1,7 @@
import { MancalaGame } from './MancalaGame';
export interface GameRule {
onGameMoveStart(game: MancalaGame, index: number): void;
onGameMove(game: MancalaGame, index: number): void;
onGameMoveEnd(game: MancalaGame, index: number): void;
}

View File

@ -0,0 +1,34 @@
export class HistoryItem {
boardSnapshot: number[];
gameSteps: GameStep[];
constructor(boardSnapshot: number[], gameSteps: GameStep[]) {
this.boardSnapshot = boardSnapshot;
this.gameSteps = gameSteps;
}
}
export class MoveHistoryItem extends HistoryItem {
playerId: string;
moveIndex: number;
constructor(
playerId: string,
moveIndex: number,
boardSnapshot: number[],
gameSteps: GameStep[]
) {
super(boardSnapshot, gameSteps);
this.playerId = playerId;
this.moveIndex = moveIndex;
}
}
export class GameStep {
index: number;
type: string;
data: any | null;
constructor(index: number, type: string, data: any = null) {
this.index = index;
this.type = type;
this.data = data;
}
}

View File

@ -0,0 +1,239 @@
import { Board, PitType } from './Board';
import { GameRule } from './GameRule';
import { HistoryItem, MoveHistoryItem, GameStep } from './HistoryItem';
export type GameState = 'initial' | 'playing' | 'ended';
export const GAME_STEP_GAME_MOVE = 'GAME_STEP_GAME_MOVE';
export class MancalaGame {
id: string;
board: Board;
player1Id: string;
player2Id: string;
turnPlayerId: string;
state: GameState;
gameRules: GameRule[];
history: HistoryItem[];
currentHistoryItem: HistoryItem | null = null;
constructor(
id: string,
board: Board,
player1Id: string,
player2Id: string,
turnPlayerId: string,
gameRules: GameRule[],
history: HistoryItem[],
state: GameState = 'initial'
) {
this.id = id;
this.board = board;
this.player1Id = player1Id;
this.player2Id = player2Id;
this.turnPlayerId = turnPlayerId;
this.state = state;
this.gameRules = gameRules;
this.history = history;
this.listenBoardMoveEvents();
}
listenBoardMoveEvents() {
this.board.onGameMoveStart = (index: number) => {
this.gameRules.forEach((gameRule) => {
gameRule.onGameMoveStart(this, index);
});
};
this.board.onGameMove = (index: number) => {
this.addGameStep(new GameStep(index, GAME_STEP_GAME_MOVE));
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.player1Id) {
this.turnPlayerId = this.player2Id;
} else {
this.turnPlayerId = this.player1Id;
}
}
isTurnPlayer1() {
return this.player1Id === this.turnPlayerId;
}
isTurnPlayer2() {
return this.player2Id === this.turnPlayerId;
}
getPlayerIdByPitType(pitType: PitType): string {
if (pitType === 'player1Pit' || pitType === 'player1Bank') {
return this.player1Id;
} else if (pitType === 'player2Pit' || pitType === 'player2Bank') {
return this.player2Id;
} else {
throw new Error('Unknown pit type : ' + pitType);
}
}
getPlayerIdByIndex(index: number): string {
const pitType = this.board.getPitTypeByIndex(index);
return this.getPlayerIdByPitType(pitType);
}
checkIsPlayerTurnByIndex(index: number): boolean {
const playerId = this.getPlayerIdByIndex(index);
return this.checkIsPlayerTurn(playerId);
}
getBoardIndexByPlayerId(playerId: string, pitIndex: number) {
if (this.player1Id === playerId) {
return this.board.player1PitStartIndex() + pitIndex;
} else if (this.player2Id === playerId) {
return this.board.player2PitStartIndex() + pitIndex;
} else {
return -1; // throwing an error might be better
}
}
public checkIsPlayerTurn(playerId: string) {
return playerId === this.turnPlayerId;
}
public checkPitIndexForPlayerId(playerId: string, pitIndex: number) {
const foundPlayerId = this.getPlayerIdByIndex(
this.getBoardIndexByPlayerId(playerId, pitIndex)
);
return playerId === foundPlayerId;
}
public checkIsPitIndexBank(playerId: string, pitIndex: number) {
const pitType = this.board.getPitTypeByIndex(
this.getBoardIndexByPlayerId(playerId, pitIndex)
);
return pitType === 'player1Bank' || pitType === 'player2Bank';
}
public canPlayerMove(playerId: string, pitIndex: number) {
const isPitIndexCorrect = this.checkPitIndexForPlayerId(playerId, pitIndex);
const isPitIndexBank = this.checkIsPitIndexBank(playerId, pitIndex);
const isPlayerTurn = this.checkIsPlayerTurn(playerId);
return isPitIndexCorrect && !isPitIndexBank && isPlayerTurn;
}
public moveByPlayerPit(playerId: string, pitIndex: number) {
if (this.state === 'ended') return;
if (this.state === 'initial') {
this.state = 'playing';
}
if (this.canPlayerMove(playerId, pitIndex)) {
const moveIndex = this.getBoardIndexByPlayerId(playerId, pitIndex);
this.currentHistoryItem = new MoveHistoryItem(
playerId,
moveIndex,
[],
[]
);
this.board.move(moveIndex);
this.currentHistoryItem.boardSnapshot = this.board.getStoneArray();
this.history.push(this.currentHistoryItem);
if (this.checkGameIsEnded()) {
this.state = 'ended';
}
} else {
const isPitIndexCorrect = this.checkPitIndexForPlayerId(
playerId,
pitIndex
);
const isPlayerTurn = this.checkIsPlayerTurn(playerId);
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
);
}
public checkGameIsEnded(): boolean {
if (
this.getPlayer1StoneCountInPits() === 0 ||
this.getPlayer2StoneCountInPits() === 0
) {
return true;
}
return false;
}
public getWonPlayerId(): string | undefined {
const player1StoneCount = this.board.player1Bank.stoneCount;
const player2StoneCount = this.board.player2Bank.stoneCount;
if (this.checkGameIsEnded()) {
if (player1StoneCount === player2StoneCount) {
return undefined;
} else if (player1StoneCount > player2StoneCount) {
return this.player1Id;
} else {
return this.player2Id;
}
}
}
public static createFromMancalaGame(mancalaGame: MancalaGame): MancalaGame {
return new MancalaGame(
mancalaGame.id,
new Board(
mancalaGame.board.playerPitCount,
mancalaGame.board.initialStoneCountInPits,
mancalaGame.board.pits
),
mancalaGame.player1Id,
mancalaGame.player2Id,
mancalaGame.turnPlayerId,
mancalaGame.gameRules,
mancalaGame.history,
mancalaGame.state
);
}
public getCurrentHistoryItem(): HistoryItem | null {
return this.currentHistoryItem;
}
public addGameStep(gameStep: GameStep) {
this.getCurrentHistoryItem()?.gameSteps.push(gameStep);
}
}

View File

@ -0,0 +1,31 @@
export class Pit {
index: number;
stoneCount: number;
constructor(index: number, stoneCount = 0) {
this.index = index;
this.stoneCount = stoneCount;
}
get isBank(): boolean {
return false;
}
public increaseStone(count = 1) {
this.stoneCount += count;
}
public decreaseStone(count = 1) {
this.stoneCount -= count;
}
}
export class Bank extends Pit {
constructor(index: number, stoneCount = 0) {
super(index, stoneCount);
}
override get isBank(): boolean {
return true;
}
}

11
mancala.js/src/index.ts Normal file
View File

@ -0,0 +1,11 @@
export * from './core/MancalaGame';
export * from './core/Board';
export * from './core/Pit';
export * from './core/GameRule';
export * from './core/HistoryItem';
export * from './common/CommonBoard';
export * from './common/CommonMancalaGame';
export * from './common/game_rules/GRLastStoneInBank';
export * from './common/game_rules/GRLastStoneInEmptyPit';
export * from './common/game_rules/GRDoubleStoneInPit';
export * from './common/game_rules/GRClearBoardAtEnd';

View File

@ -0,0 +1,125 @@
import { Board } from '../src/core/Board';
describe('Board Test', () => {
test('test pit index', () => {
const board = new Board(6, 4);
expect(board.pits[0].index).toBe(0);
expect(board.pits[1].index).toBe(1);
expect(board.pits[2].index).toBe(2);
expect(board.pits[3].index).toBe(3);
expect(board.pits[4].index).toBe(4);
expect(board.pits[5].index).toBe(5);
expect(board.pits[6].index).toBe(6);
expect(board.pits[7].index).toBe(7);
expect(board.pits[8].index).toBe(8);
expect(board.pits[9].index).toBe(9);
expect(board.pits[10].index).toBe(10);
expect(board.pits[11].index).toBe(11);
expect(board.pits[12].index).toBe(12);
expect(board.pits[13].index).toBe(13);
});
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
]);
});
test('test getOppositePitIndex', () => {
const board = new Board(6, 4);
expect(board.getOppositePitIndex(0)).toBe(12);
expect(board.getOppositePitIndex(1)).toBe(11);
expect(board.getOppositePitIndex(2)).toBe(10);
expect(board.getOppositePitIndex(3)).toBe(9);
expect(board.getOppositePitIndex(4)).toBe(8);
expect(board.getOppositePitIndex(5)).toBe(7);
expect(board.getOppositePitIndex(7)).toBe(5);
expect(board.getOppositePitIndex(8)).toBe(4);
expect(board.getOppositePitIndex(9)).toBe(3);
expect(board.getOppositePitIndex(10)).toBe(2);
expect(board.getOppositePitIndex(11)).toBe(1);
expect(board.getOppositePitIndex(12)).toBe(0);
});
});

View File

@ -0,0 +1,176 @@
import { GameStep, MoveHistoryItem } from '../src/core/HistoryItem';
import { GAME_STEP_GAME_MOVE, MancalaGame } from '../src/core/MancalaGame';
import { createGame } from './TestUtil';
describe('Game Test', () => {
test('test getPlayerIdByIndex', () => {
const game = createGame();
const player1Id = '0';
const player2Id = '1';
expect(game.getPlayerIdByIndex(0)).toStrictEqual(player1Id);
expect(game.getPlayerIdByIndex(1)).toStrictEqual(player1Id);
expect(game.getPlayerIdByIndex(2)).toStrictEqual(player1Id);
expect(game.getPlayerIdByIndex(3)).toStrictEqual(player1Id);
expect(game.getPlayerIdByIndex(4)).toStrictEqual(player1Id);
expect(game.getPlayerIdByIndex(5)).toStrictEqual(player1Id);
expect(game.getPlayerIdByIndex(6)).toStrictEqual(player1Id);
expect(game.getPlayerIdByIndex(7)).toStrictEqual(player2Id);
expect(game.getPlayerIdByIndex(8)).toStrictEqual(player2Id);
expect(game.getPlayerIdByIndex(9)).toStrictEqual(player2Id);
expect(game.getPlayerIdByIndex(10)).toStrictEqual(player2Id);
expect(game.getPlayerIdByIndex(11)).toStrictEqual(player2Id);
expect(game.getPlayerIdByIndex(12)).toStrictEqual(player2Id);
expect(game.getPlayerIdByIndex(13)).toStrictEqual(player2Id);
});
test('test canPlayerMove', () => {
const game = createGame();
const player1Id = '0';
const player2Id = '1';
expect(game.canPlayerMove(player1Id, 0)).toBe(true);
expect(game.canPlayerMove(player1Id, 1)).toBe(true);
expect(game.canPlayerMove(player1Id, 2)).toBe(true);
expect(game.canPlayerMove(player1Id, 3)).toBe(true);
expect(game.canPlayerMove(player1Id, 4)).toBe(true);
expect(game.canPlayerMove(player1Id, 5)).toBe(true);
expect(game.canPlayerMove(player1Id, 6)).toBe(false);
expect(game.canPlayerMove(player2Id, 0)).toBe(false);
});
test('test moveByPlayerPit', () => {
const game = createGame();
const player1Id = '0';
const player2Id = '1';
expect(game.state).toBe('initial');
expect(game.turnPlayerId).toBe(player1Id);
game.moveByPlayerPit(player1Id, 0);
expect(game.state).toBe('playing');
expect(game.turnPlayerId).toBe(player2Id);
game.moveByPlayerPit(player2Id, 0);
expect(game.state).toBe('playing');
expect(game.turnPlayerId).toBe(player1Id);
});
test('test game end test', () => {
const game = createGame();
const player1Id = '0';
const player2Id = '1';
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(player1Id, 5);
expect(game.state).toBe('ended');
});
test('test last stone in bank', () => {
const game = createGame();
const player1Id = '0';
const player2Id = '1';
expect(game.state).toBe('initial');
expect(game.turnPlayerId).toBe(player1Id);
game.moveByPlayerPit(player1Id, 3);
expect(game.state).toBe('playing');
expect(game.turnPlayerId).toBe(player1Id);
});
test('test empty pit 1', () => {
const game = createGame();
const player1Id = '0';
const player2Id = '1';
expect(game.state).toBe('initial');
game.board.pits[0].stoneCount = 1;
game.board.pits[1].stoneCount = 0;
game.moveByPlayerPit(player1Id, 0);
expect(game.state).toBe('playing');
expect(game.turnPlayerId).toBe(player2Id);
expect(game.board.getStoneArray()).toStrictEqual([
0, 0, 4, 4, 4, 4, 5, 4, 4, 4, 4, 0, 4, 0
]);
});
test('test game history', () => {
const game = createGame();
const player1Id = '0';
const player2Id = '1';
game.moveByPlayerPit(player1Id, 0);
game.moveByPlayerPit(player2Id, 0);
game.moveByPlayerPit(player1Id, 1);
game.moveByPlayerPit(player2Id, 1);
const historyItem1 = new MoveHistoryItem(
player1Id,
0,
[1, 5, 5, 5, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0],
[
new GameStep(0, GAME_STEP_GAME_MOVE),
new GameStep(1, GAME_STEP_GAME_MOVE),
new GameStep(2, GAME_STEP_GAME_MOVE),
new GameStep(3, GAME_STEP_GAME_MOVE)
]
);
const historyItem2 = new MoveHistoryItem(
player2Id,
7,
[1, 5, 5, 5, 4, 4, 0, 1, 5, 5, 5, 4, 4, 0],
[
new GameStep(7, GAME_STEP_GAME_MOVE),
new GameStep(8, GAME_STEP_GAME_MOVE),
new GameStep(9, GAME_STEP_GAME_MOVE),
new GameStep(10, GAME_STEP_GAME_MOVE)
]
);
const historyItem3 = new MoveHistoryItem(
player1Id,
1,
[1, 1, 6, 6, 5, 5, 0, 1, 5, 5, 5, 4, 4, 0],
[
new GameStep(1, GAME_STEP_GAME_MOVE),
new GameStep(2, GAME_STEP_GAME_MOVE),
new GameStep(3, GAME_STEP_GAME_MOVE),
new GameStep(4, GAME_STEP_GAME_MOVE),
new GameStep(5, GAME_STEP_GAME_MOVE)
]
);
const historyItem4 = new MoveHistoryItem(
player2Id,
8,
[1, 1, 6, 6, 5, 5, 0, 1, 1, 6, 6, 5, 5, 0],
[
new GameStep(8, GAME_STEP_GAME_MOVE),
new GameStep(9, GAME_STEP_GAME_MOVE),
new GameStep(10, GAME_STEP_GAME_MOVE),
new GameStep(11, GAME_STEP_GAME_MOVE),
new GameStep(12, GAME_STEP_GAME_MOVE)
]
);
expect(game.history[0]).toStrictEqual(historyItem1);
expect(game.history[1]).toStrictEqual(historyItem2);
expect(game.history[2]).toStrictEqual(historyItem3);
expect(game.history[3]).toStrictEqual(historyItem4);
});
test('test getWonPlayerId', () => {
const game = createGame();
const player1Id = '0';
const player2Id = '1';
game.board.clear();
game.board.pits[game.board.player1BankIndex()].stoneCount = 1;
game.board.pits[game.board.player2BankIndex()].stoneCount = 0;
expect(game.getWonPlayerId()).toBe(player1Id);
game.board.pits[game.board.player1BankIndex()].stoneCount = 0;
game.board.pits[game.board.player2BankIndex()].stoneCount = 1;
expect(game.getWonPlayerId()).toBe(player2Id);
game.board.pits[game.board.player1BankIndex()].stoneCount = 0;
game.board.pits[game.board.player2BankIndex()].stoneCount = 0;
expect(game.getWonPlayerId()).toBe(undefined);
});
});

View File

@ -0,0 +1,27 @@
import { GRClearBoardAtEnd } from '../src/common/game_rules/GRClearBoardAtEnd';
import { GRLastStoneInBank } from '../src/common/game_rules/GRLastStoneInBank';
import { GRLastStoneInEmptyPit } from '../src/common/game_rules/GRLastStoneInEmptyPit';
import { GRDoubleStoneInPit } from '../src/common/game_rules/GRDoubleStoneInPit';
import { Board } from '../src/core/Board';
import { MancalaGame } from '../src/core/MancalaGame';
export function createGame(): MancalaGame {
const board = new Board(6, 4);
const player1Id = '0';
const player2Id = '1';
const game = new MancalaGame(
'0',
board,
player1Id,
player2Id,
player1Id,
[
new GRLastStoneInEmptyPit(),
new GRDoubleStoneInPit(),
new GRLastStoneInBank(),
new GRClearBoardAtEnd()
],
[]
);
return game;
}

View File

@ -0,0 +1,46 @@
import {
GAME_STEP_LAST_STONE_IN_BANK,
GRLastStoneInBank
} from '../../src/common/game_rules/GRLastStoneInBank';
import {
GAME_STEP_LAST_STONE_IN_EMPTY_PIT,
GRLastStoneInEmptyPit
} from '../../src/common/game_rules/GRLastStoneInEmptyPit';
import {
GAME_STEP_BOARD_CLEARED,
GRClearBoardAtEnd
} from '../../src/common/game_rules/GRClearBoardAtEnd';
import { Board } from '../../src/core/Board';
import { GAME_STEP_GAME_MOVE, MancalaGame } from '../../src/core/MancalaGame';
import { GameStep } from '../../src/core/HistoryItem';
import { createGame } from '../TestUtil';
describe('GRClearBoardAtEnd Test', () => {
test('test GRClearBoardAtEnd 1', () => {
const game = createGame();
game.board.fillPlayer1Pits(0);
game.board.fillPlayer2Pits(0);
game.board.player1Pits[5].stoneCount = 1;
game.board.player2Pits[5].stoneCount = 1;
game.moveByPlayerPit('0', 5);
expect(game.board.player1Bank.stoneCount).toBe(2);
expect(game.board.player2Bank.stoneCount).toBe(0);
expect(
game.board.player1Pits
.map((pit) => pit.stoneCount)
.reduce((sum, stoneCount) => sum + stoneCount, 0)
).toBe(0);
expect(
game.board.player2Pits
.map((pit) => pit.stoneCount)
.reduce((sum, stoneCount) => sum + stoneCount, 0)
).toBe(0);
expect(game.history[0].gameSteps).toStrictEqual([
new GameStep(6, GAME_STEP_GAME_MOVE),
new GameStep(6, GAME_STEP_LAST_STONE_IN_BANK),
new GameStep(6, GAME_STEP_BOARD_CLEARED, {
pitIndexesThatHasStone: [12]
})
]);
});
});

View File

@ -0,0 +1,43 @@
import { GameStep } from '../../src/core/HistoryItem';
import { GAME_STEP_GAME_MOVE } from '../../src/core/MancalaGame';
import { createGame } from '../TestUtil';
import { GAME_STEP_DOUBLE_STONE_IN_PIT } from '../../src/common/game_rules/GRDoubleStoneInPit';
describe('GRDoubleStoneInPit Test', () => {
test('test GRDoubleStoneInPit 1', () => {
const game = createGame();
const board = game.board;
const initialBoard = [3, 4, 4, 4, 4, 3, 0, 3, 4, 4, 4, 4, 3, 0];
game.board.pits[5].stoneCount = 3;
game.board.pits[7].stoneCount = 3;
game.board.pits[12].stoneCount = 3;
game.board.pits[0].stoneCount = 3;
expect(board.getStoneArray()).toStrictEqual(initialBoard);
game.moveByPlayerPit('0', 5);
expect(board.getStoneArray()).toStrictEqual([
3, 4, 4, 4, 4, 1, 5, 0, 4, 4, 4, 4, 3, 0
]);
expect(game.history[0].gameSteps).toStrictEqual([
new GameStep(5, GAME_STEP_GAME_MOVE),
new GameStep(6, GAME_STEP_GAME_MOVE),
new GameStep(7, GAME_STEP_GAME_MOVE),
new GameStep(7, GAME_STEP_DOUBLE_STONE_IN_PIT, {
pitIndex: 7,
bankIndex: 6
})
]);
game.moveByPlayerPit('1', 5);
expect(board.getStoneArray()).toStrictEqual([
0, 4, 4, 4, 4, 1, 5, 0, 4, 4, 4, 4, 1, 5
]);
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_DOUBLE_STONE_IN_PIT, {
pitIndex: 0,
bankIndex: 13
})
]);
});
});

View File

@ -0,0 +1,27 @@
import { GAME_STEP_GAME_MOVE } from '../../src/core/MancalaGame';
import { GameStep } from '../../src/core/HistoryItem';
import { createGame } from '../TestUtil';
import { GAME_STEP_LAST_STONE_IN_EMPTY_PIT } from '../../src/common/game_rules/GRLastStoneInEmptyPit';
describe('GRClearBoardAtEnd Test', () => {
test('test GRClearBoardAtEnd 1', () => {
const game = createGame();
const board = game.board;
const initialBoard = [4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0];
expect(board.getStoneArray()).toStrictEqual(initialBoard);
game.board.player1Pits[0].stoneCount = 1;
game.board.player1Pits[1].stoneCount = 0;
expect(board.getStoneArray()).toStrictEqual([
1, 0, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0
]);
game.moveByPlayerPit('0', 0);
expect(board.getStoneArray()).toStrictEqual([
0, 0, 4, 4, 4, 4, 5, 4, 4, 4, 4, 0, 4, 0
]);
expect(game.history[0].gameSteps).toStrictEqual([
new GameStep(1, GAME_STEP_GAME_MOVE),
new GameStep(1, GAME_STEP_LAST_STONE_IN_EMPTY_PIT, { oppositeIndex: 11 })
]);
});
});

110
mancala.js/tsconfig.json Normal file
View File

@ -0,0 +1,110 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Projects */
// "incremental": true, /* Enable incremental compilation */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "resolveJsonModule": true, /* Enable importing .json files */
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
"outDir": "dist", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
//"sourceRoot": "src", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
// "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
// "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
"declaration": true
},
"include": [
"src"
],
"exclude": [
"node_modules",
"**/__tests__/*",
"dist"
]
}

3095
mancala.js/yarn.lock Normal file

File diff suppressed because it is too large Load Diff