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