Skip to content

Instantly share code, notes, and snippets.

@raganwald
Last active January 1, 2019 23:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save raganwald/d55012c262c02d769054f70f55a1032e to your computer and use it in GitHub Desktop.
Save raganwald/d55012c262c02d769054f70f55a1032e to your computer and use it in GitHub Desktop.
function scoop(before, fromPit) {
const after = before.slice(0);
const seedsInHand = after[fromPit];
after[fromPit] = 0;
return [after, seedsInHand];
}
function nextPit(pit) {
return (pit + 1) % 12;
}
function distribute(before, skipPit, currentPit, seedsInHand) {
const after = before.slice(0);
if (currentPit === skipPit) {
currentPit = nextPit(currentPit);
}
++after[currentPit];
--seedsInHand;
if (seedsInHand === 0) {
return [after, currentPit];
} else if (seedsInHand > 0) {
return distribute(after, skipPit, nextPit(currentPit), seedsInHand);
}
}
function sow(before, skipPit, fromPit = skipPit) {
const [after, pitsInHand] = scoop(before, fromPit);
return distribute(after, skipPit, nextPit(fromPit), pitsInHand);
}
function relaySow(before, skipPit, currentPit = skipPit) {
let [after, lastPit] = sow(before, skipPit, currentPit);
if (after[lastPit] === 1) {
return [after, lastPit];
} else {
return relaySow(after, skipPit, lastPit);
}
}
function handleCapturesInTurn(beforeBoard, beforeScore, startPit, endPit) {
const endedOnThePlayersSide = startPit < 6 === endPit < 6;
if (endedOnThePlayersSide) {
const pitOnOpponentsSide = 11 - endPit;
const afterBoard = beforeBoard.slice(0);
const playerNumber = startPit > 5 ? 1 : 0;
const afterScore = Object.assign(
beforeScore,
{ [playerNumber]: beforeScore[playerNumber] + beforeBoard[pitOnOpponentsSide] }
);
afterBoard[pitOnOpponentsSide] = 0;
return [afterBoard, afterScore];
} else {
return [beforeBoard, beforeScore];
}
}
function handleGameEnd(beforeBoard, beforeScore, playerWhoJustMoved) {
console.log({beforeBoard, beforeScore, playerWhoJustMoved})
const otherPlayer = 1 - playerWhoJustMoved;
const otherPlayerHasMoves = atLeastOnePossibleMove(beforeBoard, otherPlayer);
if (otherPlayerHasMoves) {
return [beforeBoard, beforeScore];
} else {
const playerPits =
playerWhoJustMoved === 0 ? [0, 1, 2, 3, 4, 5] : [6, 7, 8, 9, 10, 11];
const stonesRemaining = (function countStones(board, pits, runningTotal = 0) {
if (pits.length === 0) {
return runningTotal;
} else {
const [first, ...rest] = pits;
return countStones(board, rest, runningTotal + board[first]);
}
})(beforeBoard, playerPits);
const playerWhoJustMovedScore = beforeScore[playerWhoJustMoved] + stonesRemaining;
const finalScore = {
[playerWhoJustMoved]: playerWhoJustMovedScore,
[otherPlayer]: beforeScore[otherPlayer]
};
return [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
finalScore
];
}
}
function handleCaptures(beforeBoard, beforeScore, startPit, endPit) {
const [afterCapturesInTurn, afterScoreInTurn] =
handleCapturesInTurn(beforeBoard, beforeScore, startPit, endPit);
const playerWhoJustMoved = startPit > 5 ? 1 : 0;
const [afterTurn, afterScore] = handleGameEnd(afterCapturesInTurn, afterScoreInTurn, playerWhoJustMoved);
const isOver = (afterScore[0] + afterScore[1]) === 48;
return [afterTurn, afterScore, isOver];
}
function potentialMoves(player) {
if (player === 0) {
return [0, 1, 2, 3, 4, 5];
} else {
return [6, 7, 8, 9, 10, 11];
}
}
function possible(pits, pit) {
return pits[pit] > 0;
}
function possibleMoves(pits, moves) {
if (moves.length === 0) {
return moves
} else {
const [first, ...rest] = moves;
if (possible(pits, first)) {
return [first].concat(possibleMoves(pits, rest));
} else {
return possibleMoves(pits, rest);
}
}
}
function sowAndCapture(beforePits, beforeScore, startPit) {
const [afterSowing, endPit] = relaySow(beforePits, startPit);
const [afterTurn, scoreAfter, isOver] = handleCaptures(afterSowing, beforeScore, startPit, endPit);
return [afterTurn, scoreAfter, isOver];
}
function atLeastOnePossibleMove(pits, player) {
const row =
player === 0 ? [0, 1, 2, 3, 4, 5] : [6, 7, 8, 9, 10, 11];
return (function atLeastOne(pits, moves) {
if (moves.length === 0) {
return false;
} else {
const [first, ...rest] = moves;
return pits[first] > 0 || atLeastOne(pits, rest);
}
})(pits, row);
}
function movesThatFeedTheOtherPlayer(before, moves) {
// the degenerate case
if (moves.length === 0) {
return moves;
}
const [first, ...rest] = moves;
const irrelevantScore = { 0: 0, 1: 0 };
const [after] = sowAndCapture(before, irrelevantScore, first);
const otherPlayer = first > 5 ? 0 : 1;
const otherPlayerHasStonesAfter =
atLeastOnePossibleMove(after, otherPlayer);
if (otherPlayerHasStonesAfter) {
return [first].concat(movesThatFeedTheOtherPlayer(before, rest));
} else {
return movesThatFeedTheOtherPlayer(before, rest);
}
}
function permissibleFilter(pits, moves) {
// the degenerate case
if (moves.length === 0) {
return moves;
}
const firstMove = moves[1];
const otherPlayer = firstMove > 5 ? 0 : 1;
const otherPlayerHasStones =
atLeastOnePossibleMove(pits, otherPlayer);
if (otherPlayerHasStones) {
return moves;
} else {
const permissibleMoves = movesThatFeedTheOtherPlayer(pits, moves);
if (permissibleMoves.length > 0) {
return permissibleMoves;
} else {
// slightly irrelevant, as the gamne is about to end
// no matter which move is chosen
return moves;
}
}
}
function permissibleMoves(before, player) {
const potentialMovesForPlayer = potentialMoves(player);
const possibleMovesForPlayer = possibleMoves(lateGameBoard, potentialMovesForPlayer);
const permissibleMovesForPlayer = permissibleFilter(lateGameBoard, possibleMovesForPlayer);
return permissibleMovesForPlayer;
}
function whoWonForPeopleWhoCantCountGood (score) {
return (function oneAtATime (zero, one) {
if (zero === 0 && one === 0) {
return [];
} else if (zero === 0) {
return [1];
} else if (one === 0) {
return [0];
} else {
return oneAtATime(zero - 1, one - 1);
}
})(score[0], score[1]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment