The source code of the Ayòayò implementation
June 14, 2020
This is the complete source code of the Ayòayò implementation.
function Ayoayo() {
this.board = [
[4, 4, 4, 4, 4, 4],
[4, 4, 4, 4, 4, 4],
];
this.captured = [0, 0];
this.nextPlayer = 0;
this.isGameOver = false;
this.winner = null;
this.permissibleMoves = [0, 1, 2, 3, 4, 5];
}
Ayoayo.TOTAL_NUM_SEEDS = 48;
Ayoayo.NUM_CELLS_PER_ROW = 6;
// Plays the next turn for the current player
Ayoayo.prototype.play = function play(cell) {
if (!this.permissibleMoves.includes(cell)) {
throw new Error('Not permitted to play this cell');
}
// Relay-sow. Update board and increment captures.
let captured;
[this.board, captured] = Ayoayo.relaySow(this.board, this.nextPlayer, cell);
this.captured[0] += captured[0];
this.captured[1] += captured[1];
// Toggle to next player
this.nextPlayer = Ayoayo.togglePlayer(this.nextPlayer);
this.permissibleMoves = Ayoayo.getPermissibleMoves(
this.board,
this.nextPlayer,
);
// No point proceeding if the next player has no more moves,
// or if someone has more than half of the seeds
const shouldEndGame =
this.permissibleMoves.length == 0 ||
this.captured.some((count) => count > Ayoayo.TOTAL_NUM_SEEDS / 2);
// Capture remaining seeds if the opponent is out of moves
const shouldCaptureRemainingSeeds = this.permissibleMoves.length == 0;
if (shouldCaptureRemainingSeeds) {
let numRemainingSeeds = 0;
this.board[this.nextPlayer] = this.board[this.nextPlayer].map((cell) => {
numRemainingSeeds += cell;
return 0;
});
this.captured[this.nextPlayer] += numRemainingSeeds;
}
if (shouldEndGame) {
this.permissibleMoves = [];
this.isGameOver = true;
this.winner = Ayoayo.getWinner(this.captured);
}
};
// Relay-sows the seeds starting from cell and returns
// the updated board and number of captured seeds.
Ayoayo.relaySow = function relaySow(board, player, cell) {
const captured = [0, 0];
// Pickup seeds
let numSeedsInHand = board[player][cell];
board[player][cell] = 0;
// Move to next cell position
const nextPosition = this.next(player, cell);
let [nextPositionRow, nextPositionCell] = nextPosition;
// Terminate when all seeds have been dropped and
// no continuing pickup was done
while (numSeedsInHand > 0) {
// Drop one seed in next cell
board[nextPositionRow][nextPositionCell]++;
numSeedsInHand--;
// If the cell has four seeds, capture. If this is the last seed in hand,
// give to the current player. If not, give to the owner of the row.
if (board[nextPositionRow][nextPositionCell] == 4) {
const capturer = numSeedsInHand == 0 ? player : nextPositionRow;
captured[capturer] += 4;
board[nextPositionRow][nextPositionCell] = 0;
}
// Relay. If this is the last seed in hand and the cell was not originally empty,
// pickup the seeds in the cell.
if (numSeedsInHand == 0 && board[nextPositionRow][nextPositionCell] > 1) {
numSeedsInHand = board[nextPositionRow][nextPositionCell];
board[nextPositionRow][nextPositionCell] = 0;
}
// Move to next position
const nextPosition = Ayoayo.next(nextPositionRow, nextPositionCell);
[nextPositionRow, nextPositionCell] = nextPosition;
}
return [board, captured];
};
Ayoayo.togglePlayer = function togglePlayer(player) {
return (player + 1) % 2;
};
// Returns a list of all possible cells the next player can play.
// A player may play only cells with at least one seed.
// If the other player has no seeds, the current player must "feed" them, if possible.
Ayoayo.getPermissibleMoves = function getPermissibleMoves(board, player) {
const otherPlayer = Ayoayo.togglePlayer(player);
const nonEmptyCellIndexes = board[player]
.map((_, index) => index)
.filter((cellIndex) => board[player][cellIndex] > 0);
// If the other player has seeds, permit all non-empty cells
const otherPlayerHasSeeds = board[otherPlayer].some((cell) => cell > 0);
if (otherPlayerHasSeeds) {
return nonEmptyCellIndexes;
}
// Other player has no seeds, permit only non-empty cells that feed
return nonEmptyCellIndexes.filter((cellIndex) => {
const boardCopy = board.map((row) => row.slice());
const [boardIfCellPlayed] = Ayoayo.relaySow(boardCopy, player, cellIndex);
return boardIfCellPlayed[otherPlayer].some((cell) => cell > 0);
});
};
// Returns the winning player, or -1, if draw.
Ayoayo.getWinner = function getWinner(captured) {
if (captured[0] == captured[1]) return -1;
if (captured[0] > captured[1]) return 0;
return 1;
};
// Returns the next position moving counter-clockwise from the given row and cell
Ayoayo.next = function next(row, cell) {
if (row == 0) return cell == 0 ? [1, 0] : [0, cell - 1];
return cell == Ayoayo.NUM_CELLS_PER_ROW - 1
? [0, Ayoayo.NUM_CELLS_PER_ROW - 1]
: [1, cell + 1];
};