View file javascript/isCheckMate.js

File size: 9.41Kb
/*
*****************************************************************************
isCheckMate() pseudocode
*****************************************************************************

1) can king move out of checkmate?
2) can attacker be eaten?
3) can attacker be blocked?


for each of the possible squares the king can move to, use isSafe() to see if king can move there
  if one of them is safe, isCheckMate() = false

from king's position, scan out in all 8 directions to find attackers (note: there may be two!)

for each attacker found:
  - use isSafe() on attacker to see if attacker can be eaten; if so, isCheckMate() = false
  - if the attacker is not a knight
      o then for each square between the attacker and the king:
          . use isSafe() to determine if a move is possible to block the attacker; if so, isCheckMate() = false

if none of these is possible, isCheckMate() = true
*/

/* directions:
    0     1
 2  8  9 10  3
   15  * 11
 4 14 13 12  5
    6     7
 NOTE: directions 0 through 7 are actually positions and represent knights!
*****************************************************************************
  END PSEUDOCODE
*****************************************************************************
*/

/* reset getNextAttacker function */
curDir = -1;

/* IMPORTANT NOTE!!!
   getNextAttacker() will currently only work when called for one situation
   (ie: one set of coords, no changes to board between calls) and as such
   should NOT be used outside of isCheckmate()
   
   POSSIBLE FIXES:
   - if getNextAttacker() does not need to be nested, reseting the function
     before each call should do the trick
   - build a 3D lookup table based on targetRow, targetCol and targetColor;
     if current parameters exist in table, set curDir to value;
     else add entry to table, reset curDir;
     before exiting getNextAttacker(), update lookup table with curDir
*/
function getNextAttacker(targetRow, targetCol, targetColor, attackerCoords)
{
	var attackerColor = getOtherColor(targetColor);
	while(curDir <= 15)
	{
		var rowStep, colStep;
		
		/* start next direction */
		curDir++;
		
		switch(curDir)
		{
			case 0:
				if (isInBoard(targetRow + 2, targetCol - 1))
					if (board[targetRow + 2][targetCol - 1] == getPieceCode(attackerColor, "knight"))
					{
						attackerCoords.row = targetRow + 2;
						attackerCoords.col = targetCol - 1;
						return true;
					}
				break;
			case 1:
				if (isInBoard(targetRow + 2, targetCol + 1))
					if (board[targetRow + 2][targetCol + 1] == getPieceCode(attackerColor, "knight"))
					{
						attackerCoords.row = targetRow + 2;
						attackerCoords.col = targetCol + 1;
						return true;
					}
				break;
			case 2:
				if (isInBoard(targetRow + 1, targetCol - 2))
					if (board[targetRow + 1][targetCol - 2] == getPieceCode(attackerColor, "knight"))
					{
						attackerCoords.row = targetRow + 1;
						attackerCoords.col = targetCol - 2;
						return true;
					}
				break;
			case 3:
				if (isInBoard(targetRow + 1, targetCol + 2))
					if (board[targetRow + 1][targetCol + 2] == getPieceCode(attackerColor, "knight"))
					{
						attackerCoords.row = targetRow + 1;
						attackerCoords.col = targetCol + 2;
						return true;
					}
				break;
			case 4:
				if (isInBoard(targetRow - 1, targetCol - 2))
					if (board[targetRow - 1][targetCol - 2] == getPieceCode(attackerColor, "knight"))
					{
						attackerCoords.row = targetRow - 1;
						attackerCoords.col = targetCol - 2;
						return true;
					}
				break;
			case 5:
				if (isInBoard(targetRow - 1, targetCol + 2))
					if (board[targetRow - 1][targetCol + 2] == getPieceCode(attackerColor, "knight"))
					{
						attackerCoords.row = targetRow - 1;
						attackerCoords.col = targetCol + 2;
						return true;
					}
				break;
			case 6:
				if (isInBoard(targetRow - 2, targetCol - 1))
					if (board[targetRow - 2][targetCol - 1] == getPieceCode(attackerColor, "knight"))
					{
						attackerCoords.row = targetRow - 2;
						attackerCoords.col = targetCol - 1;
						return true;
					}
				break;
			case 7:
				if (isInBoard(targetRow - 2, targetCol - 1))
					if (board[targetRow - 2][targetCol - 1] == getPieceCode(attackerColor, "knight"))
					{
						attackerCoords.row = targetRow - 2;
						attackerCoords.col = targetCol - 1;
						return true;
					}
				break;
			case 8:
				rowStep = 1;
				colStep = -1;
				break;
			case 9:
				rowStep = 1;
				colStep = 0;
				break;
			case 10:
				rowStep = 1;
				colStep = 1;
				break;
			case 11:
				rowStep = 0;
				colStep = 1;
				break;
			case 12:
				rowStep = -1;
				colStep = 1;
				break;
			case 13:
				rowStep = -1;
				colStep = 0;
				break;
			case 14:
				rowStep = -1;
				colStep = -1;
				break;
			case 15:
				rowStep = 0;
				colStep = -1;
				break;
		}

		if (curDir > 7)
		{
			attackerFound = false;
			i = 1;
			while (isInBoard(targetRow + (i * rowStep), targetCol + (i * colStep)) && !attackerFound)
			{
				if (board[targetRow + (i * rowStep)][targetCol + (i * colStep)] != 0)
				{
					attackerFound = true;
					if (getPieceColor(board[targetRow + (i * rowStep)][targetCol + (i * colStep)]) == attackerColor)
						if (isAttacking(board[targetRow + (i * rowStep)][targetCol + (i * colStep)], targetRow + (i * rowStep), targetCol + (i * colStep), attackerColor, targetRow, targetCol))
						{
							attackerCoords.row = targetRow + (i * rowStep);
							attackerCoords.col = targetCol + (i * colStep);
						
							return true;
						}
				}
				i++;
			}
		}
	}
	
	/* return true if attacker found, false otherwise */
	return false;
}

function isInBoard(row, col)
{
	if ((row >= 0) && (row <= 7) && (col >= 0) && (col <= 7))
		return true;
	else
		return false;
}

/* NOTE: isAttacking() assumes no piece exists between attacker and target, such is the case in getNextAttacker() */
function isAttacking(attackerPiece, attackerRow, attackerCol, attackerColor, targetRow, targetCol)
{
	var rowDiff = Math.abs(attackerRow - targetRow);
	var colDiff = Math.abs(attackerCol - targetCol);

	switch(attackerPiece & COLOR_MASK)
	{
		case PAWN:
			var forwardDir = -1;
			if (attackerColor == "white")
				forwardDir = 1;

			if ((colDiff == 1) && ((targetRow - attackerRow) == forwardDir))
				return true;
			break;
			
		case ROOK:
			if ((rowDiff == 0) || (colDiff == 0))
				return true;
			break;
			
		case KNIGHT:
			if (((rowDiff == 2) && (colDiff == 1)) || ((rowDiff == 1) && (colDiff == 2)))
				return true;
			break;
			
		case BISHOP:
			if (rowDiff == colDiff)
				return true;
			break;
			
		case QUEEN:
			return (isAttacking(ROOK, attackerRow, attackerCol, attackerColor, targetRow, targetCol) || isAttacking(BISHOP, attackerRow, attackerCol, attackerColor, targetRow, targetCol));
			break;
			
		case KING:
			if ((rowDiff <= 1) && (colDiff <= 1))
				return true;
			break;
	}

	return false;
}

function canBlockAttacker(attackerPiece, attackerRow, attackerCol, attackerColor, targetRow, targetCol)
{
	var tmpAttackerPiece = attackerPiece & COLOR_MASK;

	/* Knights can never be blocked */
	if (tmpAttackerPiece == KNIGHT)
		return false;

	/* setup loop parameters */
	var rowDiff = attackerRow - targetRow;
	var colDiff = attackerCol - targetCol;
	
	var rowStep = 0;
	if (rowDiff != 0)
		rowStep = rowDiff / Math.abs(rowDiff);
	
	var colStep = 0;
	if (colDiff != 0)
		colStep = colDiff / Math.abs(colDiff);

	var numSteps = Math.max(Math.abs(rowDiff), Math.abs(colDiff));

	/* for each square between the attacker and the target... */
	for (i = 1; i < numSteps; i++)
		/* if a piece of the target's color can move there, the attack can be blocked */
		if (!isSafe(targetRow + (i * rowStep), targetCol + (i * colStep), attackerColor))
			return true;

	return false;
}

function isCheckMate(curColor)
{
	var kingRow = 0;
	var kingCol = 0;
	var targetKing = getPieceCode(curColor, "king");
	
	/* find king */
	for (var i = 0; i < 8; i++)
		for (var j = 0; j < 8; j++)
			if (board[i][j] == targetKing)
			{
				kingRow = i;
				kingCol = j;
			}
	
	/* temporarily remove king from board */
	board[kingRow][kingCol] = 0;
	
	/* check the squares around the king for a safe move */
	for (var i = -1; i <= 1; i++)
		for (var j = -1; j <= 1; j++)
			if (((i != 0) || (j != 0)) && isInBoard(kingRow + i, kingCol + j))
				if ((board[kingRow + i][kingCol + j] == 0) && (isSafe(kingRow + i, kingCol + j, curColor)))
					return false;

	/* return king to board */
	board[kingRow][kingCol] = targetKing;

	var attackerColor = getOtherColor(curColor);
	
	/* for each attacker... (can be two) */
	var attackerCoords = {row:0, col:0};
	while(getNextAttacker(kingRow, kingCol, curColor, attackerCoords))
	{
		var attackerRow = attackerCoords.row;
		var attackerCol = attackerCoords.col;

		/* can attacker be captured */
		var canBeCaptured = !isSafe(attackerRow, attackerCol, attackerColor);

		/* temporarily remove king from board */
		board[kingRow][kingCol] = 0;
	
		/* can attacker be blocked */
		var canBeBlocked = false;
		if (canBlockAttacker(board[attackerRow][attackerCol], attackerRow, attackerCol, attackerColor, kingRow, kingCol))
			canBeBlocked = true;

		/* return king to board */
		board[kingRow][kingCol] = targetKing;
		
		if (!canBeCaptured && !canBeBlocked)
			return true;
	}

	return false;
}