Code viewer for Mind: Checkers Mind


// =================================================================================================
// 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;
    }
	};



}