Last active
January 1, 2019 23:09
-
-
Save raganwald/d55012c262c02d769054f70f55a1032e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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