// Cloned by Teacher World samples on 10 Dec 2017 from Mind "Checkers Mind" by Adrien HARNAY
// Please leave this clone trail here.
// =================================================================================================
// Checkers Mind
// Adrien HARNAY
// =================================================================================================
// World gives us a copy of the board as an argument
// If return illegal move, world ignores it and keps waiting for us to give it a move
// If return invalid nothing, we LOSE
function Mind() {
const MIND_COLOR = WHITE_PIECE;
// utils random
function randomFloatAtoB ( A, B )
{
return ( A + ( Math.random() * (B-A) ) );
}
function randomIntAtoB ( A, B )
{
return ( Math.round ( randomFloatAtoB ( A, B ) ) );
}
// utils coordinate functions
function translate (pos)
{
return (pos - (MAXPOS / 2));
}
function isCorrectPos(pos) {
return pos >= 0 && pos <= 7;
}
function getXFromI(i) {
return i % gridsize;
}
function getZFromI(i) {
return Math.floor(i / gridsize);
}
function getIFromXZ(x, z) {
return z * gridsize + x;
}
// world move functions
function replaceEatenCell(playerColor, i, board) {
board[i] = (board[i] === BLACK_CELL ? BLACK_PIECE : BLACK_CELL);
}
function getEatenCellI(startI, endI) {
var startX = getXFromI(startI);
var startZ = getZFromI(startI);
var endX = getXFromI(endI);
var endZ = getZFromI(endI);
var zDiff = endZ - startZ;
var xDiff = endX - startX;
if (!(zDiff === 2 || zDiff == -2)) {
return -1;
}
return getIFromXZ(endX - xDiff / 2, endZ - zDiff / 2);
}
function canMove(playerColor, startI, endI, board) {
var piece = board[startI];
var destination = board[endI];
var enemyColor = playerColor === WHITE_PIECE ? BLACK_PIECE : WHITE_PIECE;
var startX = getXFromI(startI);
var startZ = getZFromI(startI);
var endX = getXFromI(endI);
var endZ = getZFromI(endI);
if (destination !== BLACK_CELL
|| !isCorrectPos(endX) || !isCorrectPos(endZ)
|| (piece === BLACK_PIECE && (playerColor !== BLACK_PIECE || startI >= endI))
|| (piece === WHITE_PIECE && (playerColor !== WHITE_PIECE || startI <= endI))) {
return false;
}
var zDiff1 = piece === BLACK_PIECE ? 1 : -1;
var zDiff2 = piece === BLACK_PIECE ? 2 : -2;
if (!(endZ === startZ + zDiff1 && (endX === startX + 1 || endX === startX - 1))
&& !(endZ === startZ + zDiff2 && (endX === startX + 2 || endX === startX - 2))) {
return false;
}
if (endZ === startZ + zDiff2
&& ((endX === startX + 2 && board[getIFromXZ(startX + 1, startZ + zDiff1)] !== enemyColor)
|| (endX === startX - 2 && board[getIFromXZ(startX - 1, startZ + zDiff1)] !== enemyColor))) {
return false;
}
return true;
}
function move(playerColor, startI, endI, board, force) {
if (!force && !canMove(playerColor, startI, endI, board)) {
return false;
}
var eatenCellI = getEatenCellI(startI, endI);
if (eatenCellI !== -1) {
replaceEatenCell(playerColor, eatenCellI, board);
}
var tmpCell = board[startI];
board[startI] = board[endI];
board[endI] = tmpCell;
return true;
}
// heuristics
function countPieces(board) {
var count = {
black: 0,
white: 0,
};
for (var i = 0; i < board.length; i++) {
if (board[i] === BLACK_PIECE) {
count.black++;
} else if (board[i] === WHITE_PIECE) {
count.white++;
}
}
return count;
}
function getHeuristic(board) {
var count = countPieces(board);
return count.white - count.black;
}
// IA functions
function findNextPieceI(currI, board) {
for (var i = currI + 1; i < board.length; i++) {
if (board[i] === MIND_COLOR) {
return i;
}
}
return -2;
}
function findRandomInBestSolutions(moves) {
if (moves.length === 1) {
return moves[0];
}
return moves[randomIntAtoB(0, moves.length - 1)];
}
function findBestMoveFromTab(moves) {
if (!moves || !moves.length) {
return null;
}
var bestScore = moves[0].score;
for (var i = 1; i < moves.length; i++) {
if (moves[i].score > bestScore) {
bestScore = moves[i].score;
}
}
var bestMoves = moves.filter(function (move) {
return move.score === bestScore;
});
return findRandomInBestSolutions(bestMoves);
}
function tryMove(board, moves, startI, endI) {
if (isCorrectPos(getXFromI(startI)) && isCorrectPos(getZFromI(startI))
&& isCorrectPos(getXFromI(endI)) && isCorrectPos(getZFromI(endI))
&& move(MIND_COLOR, startI, endI, board)) {
moves.push({
startI: startI,
endI: endI,
score: getHeuristic(board),
});
move(MIND_COLOR, endI, startI, board, true);
}
}
function findBestMoveForPiece(startI, board) {
var moves = [];
var startX = getXFromI(startI);
var startZ = getZFromI(startI);
var endZ = startZ - 1;
var endX = startX + 1;
var endI = getIFromXZ(endX, endZ);
tryMove(board, moves, startI, endI);
endX = startX - 1;
endI = getIFromXZ(endX, endZ);
tryMove(board, moves, startI, endI);
endZ = startZ - 2;
endX = startX + 2;
endI = getIFromXZ(endX, endZ);
tryMove(board, moves, startI, endI);
endX = startX - 2;
endI = getIFromXZ(endX, endZ);
tryMove(board, moves, startI, endI);
return findBestMoveFromTab(moves);
}
//--- public functions / interface / API ----------------------------------------------------------
this.newRun = function()
{
};
this.endRun = function()
{
};
this.getAction = function ( COPY_BOARD ) // x is an array of [ ai, aj, ei, ej ]
{
var currI = -1;
var possibleMoves = [];
var currBestMove;
var bestMoveOfAll;
while (currI !== -2) {
currI = findNextPieceI(currI, COPY_BOARD);
if (currI !== -2) {
currBestMove = findBestMoveForPiece(currI, COPY_BOARD);
if (currBestMove) {
possibleMoves.push(currBestMove);
}
}
}
bestMoveOfAll = findBestMoveFromTab(possibleMoves);
if (bestMoveOfAll) {
return {
startX: getXFromI(bestMoveOfAll.startI),
startZ: getZFromI(bestMoveOfAll.startI),
endX: getXFromI(bestMoveOfAll.endI),
endZ: getZFromI(bestMoveOfAll.endI),
};
} else {
return -1;
}
};
}