// move.cpp
#include "common.h"
// handy macros
#define IS_VALID(li,co) (((li) >= 0) && ((li) < 8) && ((co) >= 0) && ((co) < 8))
#define IS_FREE(li,co) (IS_VALID ((li), (co)) && (move->slots[(li)][(co)].part == PART_NONE))
#define CAN_PLAY(li,co) (IS_VALID ((li), (co)) && ((move->slots[(li)][(co)].part == PART_NONE) || (move->slots[(li)][(co)].color != boardslot->color)))
// prototypes of local functions
static void AddPossibleMove (boardmove_t **possiblemoves, int *possiblemove_count, int color, int part, int source_line, int source_column, int target_line, int target_column, int promotion_type, bool has_captured, bool is_enpassant);
void Move_SetSlot (boardmove_t *move, int line, int column, int color, int part_type)
{
// this function populates a board's slot at the given line,column coordinates with the
// given part of the given color
move->slots[line][column].flags = 0; // reset flags
move->slots[line][column].color = color; // set part color
move->slots[line][column].part = part_type; // set part type
return; // finished, board slot is set
}
bool Move_IsKingThreatenedAtLocation (boardmove_t *move, int color, int at_line, int at_column, int *threat_line, int *threat_column)
{
// this function returns TRUE if the specified color is safe at the specified location.
// In case it is not, it returns FALSE and sets the threat's line and column parameters.
// Note the use of the threat_line and threat_column output parameters as iterator
// variables.
boardslot_t *boardslot;
int movement_direction;
int line;
int column;
int index_line;
int index_column;
// cycle through all the board
for (line = 0; line < 8; line++)
for (column = 0; column < 8; column++)
{
boardslot = &move->slots[line][column]; // quick access to grid slot
if ((boardslot->part == PART_NONE) || (boardslot->color == color))
continue; // if this location is empty or ours, it doesn't threaten us
// update new possible threat position
*threat_line = line;
*threat_column = column;
//////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////// PAWN //////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// is it a pawn ? (note: pawns can only threaten kings normally, never "en passant")
if (boardslot->part == PART_PAWN)
{
// figure out movement direction
if (boardslot->color == COLOR_WHITE)
movement_direction = 1;
else
movement_direction = -1;
// see if pawn can take our piece on either of its sides
if ((column > 0) && (line + movement_direction == at_line) && (column - 1 == at_column))
return (true); // this part threatens us, it can take our piece on its left
else if ((column < 7) && (line + movement_direction == at_line) && (column + 1 == at_column))
return (true); // this part threatens us, it can take our piece on its right
}
//////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////// ROOK //////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// else is it a rook ?
else if (boardslot->part == PART_ROOK)
{
// is rook in the same column as our king ?
if (column == at_column)
{
// is our king above ?
if (at_line > line)
{
// see if rook can threaten our king by moving upwards
for (index_line = line + 1; index_line < 8; index_line++)
if (index_line == at_line)
return (true); // this part threatens us
else if (!IS_FREE (index_line, column))
break; // if part can no longer move this way, stop searching
}
// else our king must be below
else
{
// see if rook can threaten our king by moving downwards
for (index_line = line - 1; index_line >= 0; index_line--)
if (index_line == at_line)
return (true); // this part threatens us
else if (!IS_FREE (index_line, column))
break; // if part can no longer move this way, stop searching
}
}
// else is rook in the same line as our king ?
else if (line == at_line)
{
// is our king on the right ?
if (at_column > column)
{
// see if rook can threaten our king by moving right
for (index_column = column + 1; index_column < 8; index_column++)
if (index_column == at_column)
return (true); // this part threatens us
else if (!IS_FREE (line, index_column))
break; // if part can no longer move this way, stop searching
}
// else our king must be on the left
else
{
// see if rook can threaten our king by moving left
for (index_column = column - 1; index_column >= 0; index_column--)
if (index_column == at_column)
return (true); // this part threatens us
else if (!IS_FREE (line, index_column))
break; // if part can no longer move this way, stop searching
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////// KNIGHT /////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// else is it a knight ?
else if (boardslot->part == PART_KNIGHT)
{
if ((column > 0) && (line < 6) && (line + 2 == at_line) && (column - 1 == at_column))
return (true); // this part threatens us on its NNW move
else if ((column < 7) && (line < 6) && (line + 2 == at_line) && (column + 1 == at_column))
return (true); // this part threatens us on its NNE move
else if ((column < 6) && (line < 7) && (line + 1 == at_line) && (column + 2 == at_column))
return (true); // this part threatens us on its ENE move
else if ((column < 6) && (line > 0) && (line - 1 == at_line) && (column + 2 == at_column))
return (true); // this part threatens us on its ESE move
else if ((column > 0) && (line > 1) && (line - 2 == at_line) && (column - 1 == at_column))
return (true); // this part threatens us on its SSW move
else if ((column < 7) && (line > 1) && (line - 2 == at_line) && (column + 1 == at_column))
return (true); // this part threatens us on its SSE move
else if ((column > 1) && (line < 7) && (line + 1 == at_line) && (column - 2 == at_column))
return (true); // this part threatens us on its WNW move
else if ((column > 1) && (line > 0) && (line - 1 == at_line) && (column - 2 == at_column))
return (true); // this part threatens us on its WSW move
}
//////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////// BISHOP /////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// else is it a bishop ?
else if (boardslot->part == PART_BISHOP)
{
// is bishop in the same SWNE diagonal as our king ?
if (line - at_line == column - at_column)
{
// is our king NE ?
if (at_line > line)
{
// see how far bishop can move NE
for (index_line = line + 1, index_column = column + 1; (index_line < 8) && (index_column < 8); index_line++, index_column++)
if ((index_line == at_line) && (index_column == at_column))
return (true); // this part threatens us
else if (!IS_FREE (index_line, index_column))
break; // if part can no longer move this way, stop searching
}
// else our king must be SW
else
{
// see how far bishop can move SW
for (index_line = line - 1, index_column = column - 1; (index_line >= 0) && (index_column >= 0); index_line--, index_column--)
if ((index_line == at_line) && (index_column == at_column))
return (true); // this part threatens us
else if (!IS_FREE (index_line, index_column))
break; // if part can no longer move this way, stop searching
}
}
// else is bishop in the same SENW diagonal as our king ?
else if (line - at_line == -(column - at_column))
{
// is our king NW ?
if (at_line > line)
{
// see how far bishop can move NW
for (index_line = line + 1, index_column = column - 1; (index_line < 8) && (index_column >= 0); index_line++, index_column--)
if ((index_line == at_line) && (index_column == at_column))
return (true); // this part threatens us
else if (!IS_FREE (index_line, index_column))
break; // if part can no longer move this way, stop searching
}
// else our king must be SE
else
{
// see how far bishop can move SE
for (index_line = line - 1, index_column = column + 1; (index_line >= 0) && (index_column < 8); index_line--, index_column++)
if ((index_line == at_line) && (index_column == at_column))
return (true); // this part threatens us
else if (!IS_FREE (index_line, index_column))
break; // if part can no longer move this way, stop searching
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////// QUEEN //////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// else is it a queen ?
else if (boardslot->part == PART_QUEEN)
{
// is queen in the same column as our king ?
if (column == at_column)
{
// is our king above ?
if (at_line > line)
{
// see if queen can threaten our king by moving upwards
for (index_line = line + 1; index_line < 8; index_line++)
if (index_line == at_line)
return (true); // this part threatens us
else if (!IS_FREE (index_line, column))
break; // if part can no longer move this way, stop searching
}
// else our king must be below
else
{
// see if queen can threaten our king by moving downwards
for (index_line = line - 1; index_line >= 0; index_line--)
if (index_line == at_line)
return (true); // this part threatens us
else if (!IS_FREE (index_line, column))
break; // if part can no longer move this way, stop searching
}
}
// else is queen in the same line as our king ?
else if (line == at_line)
{
// is our king on the right ?
if (at_column > column)
{
// see if queen can threaten our king by moving right
for (index_column = column + 1; index_column < 8; index_column++)
if (index_column == at_column)
return (true); // this part threatens us
else if (!IS_FREE (line, index_column))
break; // if part can no longer move this way, stop searching
}
// else our king must be on the left
else
{
// see if queen can threaten our king by moving left
for (index_column = column - 1; index_column >= 0; index_column--)
if (index_column == at_column)
return (true); // this part threatens us
else if (!IS_FREE (line, index_column))
break; // if part can no longer move this way, stop searching
}
}
// else is queen in the same SWNE diagonal as our king ?
else if (line - at_line == column - at_column)
{
// is our king NE ?
if (at_line > line)
{
// see how far queen can move NE
for (index_line = line + 1, index_column = column + 1; (index_line < 8) && (index_column < 8); index_line++, index_column++)
if ((index_line == at_line) && (index_column == at_column))
return (true); // this part threatens us
else if (!IS_FREE (index_line, index_column))
break; // if part can no longer move this way, stop searching
}
// else our king must be SW
else
{
// see how far queen can move SW
for (index_line = line - 1, index_column = column - 1; (index_line >= 0) && (index_column >= 0); index_line--, index_column--)
if ((index_line == at_line) && (index_column == at_column))
return (true); // this part threatens us
else if (!IS_FREE (index_line, index_column))
break; // if part can no longer move this way, stop searching
}
}
// else is queen in the same SENW diagonal as our king ?
else if (line - at_line == -(column - at_column))
{
// is our king NW ?
if (at_line > line)
{
// see how far queen can move NW
for (index_line = line + 1, index_column = column - 1; (index_line < 8) && (index_column >= 0); index_line++, index_column--)
if ((index_line == at_line) && (index_column == at_column))
return (true); // this part threatens us
else if (!IS_FREE (index_line, index_column))
break; // if part can no longer move this way, stop searching
}
// else our king must be SE
else
{
// see how far queen can move SE
for (index_line = line - 1, index_column = column + 1; (index_line >= 0) && (index_column < 8); index_line--, index_column++)
if ((index_line == at_line) && (index_column == at_column))
return (true); // this part threatens us
else if (!IS_FREE (index_line, index_column))
break; // if part can no longer move this way, stop searching
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////// KING //////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// else is it a king ?
else if (boardslot->part == PART_KING)
{
if ((line < 7) && (column < 7) && (line + 1 == at_line) && (column + 1 == at_column))
return (true); // this part threatens us on its NE move
else if ((line > 0) && (column < 7) && (line - 1 == at_line) && (column + 1 == at_column))
return (true); // this part threatens us on its SE move
else if ((line < 7) && (column > 0) && (line + 1 == at_line) && (column - 1 == at_column))
return (true); // this part threatens us on its NW move
else if ((line > 0) && (column > 0) && (line - 1 == at_line) && (column - 1 == at_column))
return (true); // this part threatens us on its SW move
else if ((line < 7) && (line + 1 == at_line) && (column == at_column))
return (true); // this part threatens us on an upwards move
else if ((line > 0) && (line - 1 == at_line) && (column == at_column))
return (true); // this part threatens us on a downwards move
else if ((column < 7) && (line == at_line) && (column + 1 == at_column))
return (true); // this part threatens us on a right move
else if ((column > 0) && (line == at_line) && (column - 1 == at_column))
return (true); // this part threatens us on a left move
}
}
return (false); // this king looks safe at this location
}
bool Move_IsCheck (boardmove_t *move, int color)
{
// this function returns TRUE if the king of the specified color is under check
boardslot_t *boardslot;
int line;
int column;
int threat_line;
int threat_column;
// cycle through all the grid again and see if the king we want is in check
for (line = 0; line < 8; line++)
for (column = 0; column < 8; column++)
{
boardslot = &move->slots[line][column]; // quick access to grid slot
if ((boardslot->color != color) || (boardslot->part != PART_KING))
continue; // if this slot is not our king, skip it
// is this king currently threatened ?
if (Move_IsKingThreatenedAtLocation (move, color, line, column, &threat_line, &threat_column))
return (true); // yes, it is
else
return (false); // no, this king is safe
}
// code should never reach here (it would mean that no king is on the board)
return (false); // no king of such color found on board, no check possible, return FALSE
}
bool Move_IsStaleMate (boardmove_t *move, int color)
{
// this function returns TRUE if the specified color is stalemate (no valid move possible)
boardslot_t *boardslot;
int movement_direction;
int line;
int column;
int index_line;
int index_column;
// cycle through all the board and find our parts
for (line = 0; line < 8; line++)
for (column = 0; column < 8; column++)
{
boardslot = &move->slots[line][column]; // quick access to grid slot
if ((boardslot->part == PART_NONE) || (boardslot->color != color))
continue; // if this location is empty or not ours, we aren't interested in it
//////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////// PAWN //////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// is it a pawn ?
if (boardslot->part == PART_PAWN)
{
// figure out movement direction
if (boardslot->color == COLOR_WHITE)
movement_direction = 1;
else
movement_direction = -1;
// see if pawn can move forward
if ((((movement_direction == 1) && (line < 7)) || ((movement_direction == -1) && (line > 0)))
&& (move->slots[line + movement_direction][column].part == PART_NONE) // target slot free
&& !Move_IsColorInCheckAfterTestMove (move, line, column, line + movement_direction, column, color))
return (false); // this move is possible
// see if pawn can take a piece on its left
if ((((movement_direction == 1) && (line < 7)) || ((movement_direction == -1) && (line > 0)))
&& (column > 0) // has room
&& (move->slots[line + movement_direction][column - 1].color != color) // target slot NOT our color
&& (move->slots[line + movement_direction][column - 1].part != PART_NONE) // target slot occupied
&& !Move_IsColorInCheckAfterTestMove (move, line, column, line + movement_direction, column - 1, color))
return (false); // this move is possible
// see if pawn can take a piece on its right
if ((((movement_direction == 1) && (line < 7)) || ((movement_direction == -1) && (line > 0)))
&& (column < 7) // has room
&& (move->slots[line + movement_direction][column + 1].color != color) // target slot NOT our color
&& (move->slots[line + movement_direction][column + 1].part != PART_NONE) // target slot occupied
&& !Move_IsColorInCheckAfterTestMove (move, line, column, line + movement_direction, column + 1, color))
return (false); // this move is possible
// if previous move was a pawn rush, see if pawn can take "en passant"
if ((move->part == PART_PAWN) // last move was a pawn
&& (move->target[1] == move->source[1]) // pawn moved in column
&& (abs (move->target[0] - move->source[0]) == 2) // pawn rushed
&& (move->target[0] == line) // pawn is in line with us
&& (move->target[1] - column == -1) // pawn is left to us
&& !Move_IsColorInCheckAfterTestMoveEP (move, line, column, line + movement_direction, column - 1, move->target[0], move->target[1], color))
return (false); // this move is possible
else if ((move->part == PART_PAWN) // last move was a pawn
&& (move->target[1] == move->source[1]) // pawn moved in column
&& (abs (move->target[0] - move->source[0]) == 2) // pawn rushed
&& (move->target[0] == line) // pawn is in line with us
&& (move->target[1] - column == 1) // pawn is right to us
&& !Move_IsColorInCheckAfterTestMoveEP (move, line, column, line + movement_direction, column + 1, move->target[0], move->target[1], color))
return (false); // this move is possible
}
//////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////// ROOK //////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// else is it a rook ?
else if (boardslot->part == PART_ROOK)
{
// see if rook can move upwards
for (index_line = line + 1; index_line < 8; index_line++)
if (!CAN_PLAY (index_line, column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, column, color))
return (false); // this move is possible
// see if rook can move downwards
for (index_line = line - 1; index_line >= 0; index_line--)
if (!CAN_PLAY (index_line, column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, column, color))
return (false); // this move is possible
// see if rook can move right
for (index_column = column + 1; index_column < 8; index_column++)
if (!CAN_PLAY (line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, line, index_column, color))
return (false); // this move is possible
// see if rook can move left
for (index_column = column - 1; index_column >= 0; index_column--)
if (!CAN_PLAY (line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, line, index_column, color))
return (false); // this move is possible
}
//////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////// KNIGHT /////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// else is it a knight ?
else if (boardslot->part == PART_KNIGHT)
{
// see if knight can move in either of his allowed directions NNW
if (CAN_PLAY (line + 2, column - 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 2, column - 1, color))
return (false); // knight can move NNW, we are not stalemate
else if (CAN_PLAY (line + 2, column + 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 2, column + 1, color))
return (false); // knight can move NNE, we are not stalemate
else if (CAN_PLAY (line + 1, column + 2) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 1, column + 2, color))
return (false); // knight can move ENE, we are not stalemate
else if (CAN_PLAY (line - 1, column + 2) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 1, column + 2, color))
return (false); // knight can move ESE, we are not stalemate
else if (CAN_PLAY (line - 2, column - 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 2, column - 1, color))
return (false); // knight can move SSW, we are not stalemate
else if (CAN_PLAY (line - 2, column + 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 2, column + 1, color))
return (false); // knight can move SSE, we are not stalemate
else if (CAN_PLAY (line + 1, column - 2) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 1, column - 2, color))
return (false); // knight can move WNW, we are not stalemate
else if (CAN_PLAY (line - 1, column - 2) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 1, column - 2, color))
return (false); // knight can move WSW, we are not stalemate
}
//////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////// BISHOP /////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// else is it a bishop ?
else if (boardslot->part == PART_BISHOP)
{
// see if bishop can move NE
for (index_line = line + 1, index_column = column + 1; (index_line < 8) && (index_column < 8); index_line++, index_column++)
if (!CAN_PLAY (index_line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color))
return (false); // this move is possible
// see if bishop can move SE
for (index_line = line - 1, index_column = column + 1; (index_line >= 0) && (index_column < 8); index_line--, index_column++)
if (!CAN_PLAY (index_line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color))
return (false); // this move is possible
// see if bishop can move NW
for (index_line = line + 1, index_column = column - 1; (index_line < 8) && (index_column >= 0); index_line++, index_column--)
if (!CAN_PLAY (index_line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color))
return (false); // this move is possible
// see if bishop can move SW
for (index_line = line - 1, index_column = column - 1; (index_line >= 0) && (index_column >= 0); index_line--, index_column--)
if (!CAN_PLAY (index_line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color))
return (false); // this move is possible
}
//////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////// QUEEN //////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// else is it a queen ?
else if (boardslot->part == PART_QUEEN)
{
// see if queen can move upwards
for (index_line = line + 1; index_line < 8; index_line++)
if (!CAN_PLAY (index_line, column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, column, color))
return (false); // this move is possible
// see if queen can move downwards
for (index_line = line - 1; index_line >= 0; index_line--)
if (!CAN_PLAY (index_line, column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, column, color))
return (false); // this move is possible
// see if queen can move right
for (index_column = column + 1; index_column < 8; index_column++)
if (!CAN_PLAY (line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, line, index_column, color))
return (false); // this move is possible
// see if queen can move left
for (index_column = column - 1; index_column >= 0; index_column--)
if (!CAN_PLAY (line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, line, index_column, color))
return (false); // this move is possible
// see if queen can move NE
for (index_line = line + 1, index_column = column + 1; (index_line < 8) && (index_column < 8); index_line++, index_column++)
if (!CAN_PLAY (index_line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color))
return (false); // this move is possible
// see if queen can move SE
for (index_line = line - 1, index_column = column + 1; (index_line >= 0) && (index_column < 8); index_line--, index_column++)
if (!CAN_PLAY (index_line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color))
return (false); // this move is possible
// see if queen can move NW
for (index_line = line + 1, index_column = column - 1; (index_line < 8) && (index_column >= 0); index_line++, index_column--)
if (!CAN_PLAY (index_line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color))
return (false); // this move is possible
// see if queen can move SW
for (index_line = line - 1, index_column = column - 1; (index_line >= 0) && (index_column >= 0); index_line--, index_column--)
if (!CAN_PLAY (index_line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color))
return (false); // this move is possible
}
//////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////// KING //////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// else is it a king ?
else if (boardslot->part == PART_KING)
{
// see if king can move in either of his allowed directions
if (CAN_PLAY (line + 1, column) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 1, column, color))
return (false); // king can move up, we are not stalemate
else if (CAN_PLAY (line - 1, column) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 1, column, color))
return (false); // king can move down, we are not stalemate
else if (CAN_PLAY (line, column + 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line, column + 1, color))
return (false); // king can move right, we are not stalemate
else if (CAN_PLAY (line, column - 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line, column - 1, color))
return (false); // king can move left, we are not stalemate
else if (CAN_PLAY (line + 1, column + 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 1, column + 1, color))
return (false); // king can move NE, we are not stalemate
else if (CAN_PLAY (line - 1, column + 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 1, column + 1, color))
return (false); // king can move SE, we are not stalemate
else if (CAN_PLAY (line + 1, column - 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 1, column - 1, color))
return (false); // king can move NW, we are not stalemate
else if (CAN_PLAY (line - 1, column - 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 1, column - 1, color))
return (false); // king can move SW, we are not stalemate
}
}
return (true); // found no legal move, we are indeed stalemate
}
bool Move_IsMoveValid (boardmove_t *move, int from_line, int from_column, int to_line, int to_column)
{
// this function returns TRUE if the specified move is valid, FALSE otherwise
// FIXME: doesn't support castling testing so far! (even though it shouldn't be necessary)
boardslot_t *boardslot;
int movement_direction;
int index_line;
int index_column;
boardslot = &move->slots[from_line][from_column]; // quick access to grid slot
// consistency check
if (!IS_VALID (from_line, from_column) || !IS_VALID (to_line, to_column))
return (false); // if movement is out of bounds, it's obviously invalid
//////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////// PAWN //////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// is it a pawn ?
if (boardslot->part == PART_PAWN)
{
// figure out movement direction
if (boardslot->color == COLOR_WHITE)
movement_direction = 1;
else
movement_direction = -1;
// quick checks
if (abs (to_line - from_line) > 2)
return (false); // pawns cannot make moves longer than 2 rows
else if (abs (to_column - from_column) > 1)
return (false); // pawns cannot make moves aside more than 1 column
// do we want pawn to rush forward
// OR do we want pawn to move forward
// OR do we want pawn to take a piece on its left
// OR do we want pawn to take a piece on its right ?
if ((((from_line == 1) || (from_line == 6)) // pawn on its initial slot
&& (from_line + 2 * movement_direction == to_line) && (from_column == to_column) // target position is the position we want
&& (move->slots[from_line + movement_direction][to_column].part == PART_NONE) // intermediate slot free
&& (move->slots[to_line][to_column].part == PART_NONE)) // target slot free
|| ((((movement_direction == 1) && (from_line < 7)) || ((movement_direction == -1) && (from_line > 0))) // has room
&& (from_line + movement_direction == to_line) && (from_column == to_column) // target position is the position we want
&& (move->slots[to_line][to_column].part == PART_NONE)) // target slot free
|| ((((movement_direction == 1) && (from_line < 7)) || ((movement_direction == -1) && (from_line > 0)))
&& (from_column > 0) // has room
&& (from_line + movement_direction == to_line) && (from_column - 1 == to_column) // target position is the position we want
&& (move->slots[to_line][to_column].color != boardslot->color) // target slot NOT our color
&& (move->slots[to_line][to_column].part != PART_NONE)) // target slot occupied
|| ((((movement_direction == 1) && (from_line < 7)) || ((movement_direction == -1) && (from_line > 0)))
&& (from_column < 7) // has room
&& (from_line + movement_direction == to_line) && (from_column + 1 == to_column) // target position is the position we want
&& (move->slots[to_line][to_column].color != boardslot->color) // target slot NOT our color
&& (move->slots[to_line][to_column].part != PART_NONE))) // target slot occupied
{
if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color))
return (true); // this move is possible
return (false); // else this pawn can't move in the claimed way (his king would be in check)
}
// if previous move was a pawn rush, see if pawn can take "en passant"
if (IS_VALID (move->target[0], move->target[1]) // last move is valid
&& (move->part == PART_PAWN) // last move was a pawn
&& (move->target[1] == move->source[1]) // pawn moved in column
&& (abs (move->target[0] - move->source[0]) == 2) // pawn rushed
&& (move->target[0] == from_line) // pawn is in line with us
&& (move->target[1] - from_column == -1) // pawn is left to us
&& (from_line + movement_direction == to_line) && (from_column - 1 == to_column) // target position is the position we want
&& !Move_IsColorInCheckAfterTestMoveEP (move, from_line, from_column, to_line, to_column, move->target[0], move->target[1], boardslot->color))
return (true); // this move is possible
else if (IS_VALID (move->target[0], move->target[1]) // last move is valid
&& (move->part == PART_PAWN) // last move was a pawn
&& (move->target[1] == move->source[1]) // pawn moved in column
&& (abs (move->target[0] - move->source[0]) == 2) // pawn rushed
&& (move->target[0] == from_line) // pawn is in line with us
&& (move->target[1] - from_column == 1) // pawn is right to us
&& (from_line + movement_direction == to_line) && (from_column + 1 == to_column) // target position is the position we want
&& !Move_IsColorInCheckAfterTestMoveEP (move, from_line, from_column, to_line, to_column, move->target[0], move->target[1], boardslot->color))
return (true); // this move is possible
return (false); // this pawn can't move in the claimed way
}
//////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////// ROOK //////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// else is it a rook ?
else if (boardslot->part == PART_ROOK)
{
// quick checks
if ((to_column != from_column) && (to_line != from_line))
return (false); // rooks can only move horizontally or vertically
// do we want the rook to move upwards ?
if (to_line > from_line)
{
// see if rook can move upwards
for (index_line = from_line + 1; index_line < 8; index_line++)
if (!CAN_PLAY (index_line, to_column))
return (false); // if rook can no longer move this way, stop searching
else if (index_line == to_line)
{
if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color))
return (true); // this move is possible
return (false); // else this rook can't move in the claimed way (its king would be in check)
}
else if (move->slots[index_line][to_column].part != PART_NONE)
return (false); // rook can take a part there BUT it's not the location we want
}
// else do we want the rook to move downwards ?
else if (to_line < from_line)
{
// see if rook can move downwards
for (index_line = from_line - 1; index_line >= 0; index_line--)
if (!CAN_PLAY (index_line, to_column))
return (false); // if rook can no longer move this way, stop searching
else if (index_line == to_line)
{
if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color))
return (true); // this move is possible
return (false); // else this rook can't move in the claimed way (its king would be in check)
}
else if (move->slots[index_line][to_column].part != PART_NONE)
return (false); // rook can take a part there BUT it's not the location we want
}
// else do we want the rook to move right ?
else if (to_column > from_column)
{
// see if rook can move right
for (index_column = from_column + 1; index_column < 8; index_column++)
if (!CAN_PLAY (to_line, index_column))
return (false); // if rook can no longer move this way, stop searching
else if (index_column == to_column)
{
if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color))
return (true); // this move is possible
return (false); // else this rook can't move in the claimed way (its king would be in check)
}
else if (move->slots[to_line][index_column].part != PART_NONE)
return (false); // rook can take a part there BUT it's not the location we want
}
// else do we want the rook to move left ?
else if (to_column < from_column)
{
// see if rook can move left
for (index_column = from_column - 1; index_column >= 0; index_column--)
if (!CAN_PLAY (to_line, index_column))
return (false); // if rook can no longer move this way, stop searching
else if (index_column == to_column)
{
if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color))
return (true); // this move is possible
return (false); // else this rook can't move in the claimed way (its king would be in check)
}
else if (move->slots[to_line][index_column].part != PART_NONE)
return (false); // rook can take a part there BUT it's not the location we want
}
return (false); // this rook can't move in the claimed way
}
//////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////// KNIGHT /////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// else is it a knight ?
else if (boardslot->part == PART_KNIGHT)
{
// do we want to move that knight in one of the allowed directions ?
if (((from_line + 2 == to_line) && (from_column - 1 == to_column)) // NNW
|| ((from_line + 2 == to_line) && (from_column + 1 == to_column)) // NNE
|| ((from_line + 1 == to_line) && (from_column + 2 == to_column)) // ENE
|| ((from_line - 1 == to_line) && (from_column + 2 == to_column)) // ESE
|| ((from_line - 2 == to_line) && (from_column - 1 == to_column)) // SSW
|| ((from_line - 2 == to_line) && (from_column + 1 == to_column)) // SSE
|| ((from_line + 1 == to_line) && (from_column - 2 == to_column)) // WNW
|| ((from_line - 1 == to_line) && (from_column - 2 == to_column))) // WSW
{
if (!CAN_PLAY (to_line, to_column))
return (false); // if knight can't move there (out of board, or some of our parts there), return false
else if (Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color))
return (false); // if knight would leave his king in check there, return false
return (true); // else this move is safe
}
return (false); // this knight can't move in the claimed way
}
//////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////// BISHOP /////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// else is it a bishop ?
else if (boardslot->part == PART_BISHOP)
{
// quick checks
if (abs (to_column - from_column) != abs (to_line - from_line))
return (false); // bishops can only move diagonally
// do we want to move the bishop NE ?
if ((to_line > from_line) && (to_column > from_column))
{
// see if bishop can move NE
for (index_line = from_line + 1, index_column = from_column + 1; (index_line < 8) && (index_column < 8); index_line++, index_column++)
if (!CAN_PLAY (index_line, index_column))
return (false); // if bishop can no longer move this way, stop searching
else if ((index_line == to_line) && (index_column == to_column))
{
if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, index_line, index_column, boardslot->color))
return (true); // this move is possible
return (false); // else this bishop can't move in the claimed way (his king would be in check)
}
else if (move->slots[index_line][index_column].part != PART_NONE)
return (false); // bishop can take a part there BUT it's not the location we want
}
// else do we want to move the bishop SE ?
else if ((to_line < from_line) && (to_column > from_column))
{
// see if bishop can move SE
for (index_line = from_line - 1, index_column = from_column + 1; (index_line >= 0) && (index_column < 8); index_line--, index_column++)
if (!CAN_PLAY (index_line, index_column))
return (false); // if bishop can no longer move this way, stop searching
else if ((index_line == to_line) && (index_column == to_column))
{
if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, index_line, index_column, boardslot->color))
return (true); // this move is possible
return (false); // else this bishop can't move in the claimed way (his king would be in check)
}
else if (move->slots[index_line][index_column].part != PART_NONE)
return (false); // bishop can take a part there BUT it's not the location we want
}
// else do we want to move the bishop NW ?
else if ((to_line > from_line) && (to_column < from_column))
{
// see if bishop can move NW
for (index_line = from_line + 1, index_column = from_column - 1; (index_line < 8) && (index_column >= 0); index_line++, index_column--)
if (!CAN_PLAY (index_line, index_column))
return (false); // if bishop can no longer move this way, stop searching
else if ((index_line == to_line) && (index_column == to_column))
{
if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, index_line, index_column, boardslot->color))
return (true); // this move is possible
return (false); // else this bishop can't move in the claimed way (his king would be in check)
}
else if (move->slots[index_line][index_column].part != PART_NONE)
return (false); // bishop can take a part there BUT it's not the location we want
}
// else do we want to move the bishop SW ?
else if ((to_line < from_line) && (to_column < from_column))
{
// see if bishop can move SW
for (index_line = from_line - 1, index_column = from_column - 1; (index_line >= 0) && (index_column >= 0); index_line--, index_column--)
if (!CAN_PLAY (index_line, index_column))
return (false); // if bishop can no longer move this way, stop searching
else if ((index_line == to_line) && (index_column == to_column))
{
if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, index_line, index_column, boardslot->color))
return (true); // this move is possible
return (false); // else this bishop can't move in the claimed way (his king would be in check)
}
else if (move->slots[index_line][index_column].part != PART_NONE)
return (false); // bishop can take a part there BUT it's not the location we want
}
return (false); // this bishop can't move in the claimed way
}
//////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////// QUEEN //////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// else is it a queen ?
else if (boardslot->part == PART_QUEEN)
{
// quick checks
if ((to_column != from_column) && (to_line != from_line)
&& (abs (to_column - from_column) != abs (to_line - from_line)))
return (false); // queens can only move horizontally, vertically or diagonally
// do we want to move that queen vertically ?
if (from_column == to_column)
{
// do we want to move her upwards ?
if (to_line > from_line)
{
// see if queen can move upwards
for (index_line = from_line + 1; index_line < 8; index_line++)
if (!CAN_PLAY (index_line, to_column))
return (false); // if queen can no longer move this way, stop searching
else if (index_line == to_line)
{
if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color))
return (true); // this move is possible
return (false); // else this queen can't move in the claimed way (her king would be in check)
}
else if (move->slots[index_line][to_column].part != PART_NONE)
return (false); // queen can take a part there BUT it's not the location we want
}
// else do we want to move her downwards ?
else if (to_line < from_line)
{
// see if queen can move downwards
for (index_line = from_line - 1; index_line >= 0; index_line--)
if (!CAN_PLAY (index_line, to_column))
return (false); // if queen can no longer move this way, stop searching
else if (index_line == to_line)
{
if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color))
return (true); // this move is possible
return (false); // else this queen can't move in the claimed way (her king would be in check)
}
else if (move->slots[index_line][to_column].part != PART_NONE)
return (false); // queen can take a part there BUT it's not the location we want
}
return (false); // this queen can't move in the claimed way
}
// else do we want to move that queen horizontally ?
else if (from_line == to_line)
{
// do we want this queen to move right ?
if (to_column > from_column)
{
// see if queen can move right
for (index_column = from_column + 1; index_column < 8; index_column++)
if (!CAN_PLAY (to_line, index_column))
return (false); // if queen can no longer move this way, stop searching
else if (index_column == to_column)
{
if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color))
return (true); // this move is possible
return (false); // else this queen can't move in the claimed way (her king would be in check)
}
else if (move->slots[to_line][index_column].part != PART_NONE)
return (false); // queen can take a part there BUT it's not the location we want
}
// else do we want this queen to move left ?
else if (to_column < from_column)
{
// see if queen can move left
for (index_column = from_column - 1; index_column >= 0; index_column--)
if (!CAN_PLAY (to_line, index_column))
return (false); // if queen can no longer move this way, stop searching
else if (index_column == to_column)
{
if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color))
return (true); // this move is possible
return (false); // else this queen can't move in the claimed way (her king would be in check)
}
else if (move->slots[to_line][index_column].part != PART_NONE)
return (false); // queen can take a part there BUT it's not the location we want
}
return (false); // this queen can't move in the claimed way
}
// else do we want to move the queen NE ?
else if ((to_line > from_line) && (to_column > from_column))
{
// see if queen can move NE
for (index_line = from_line + 1, index_column = from_column + 1; (index_line < 8) && (index_column < 8); index_line++, index_column++)
if (!CAN_PLAY (index_line, index_column))
return (false); // if queen can no longer move this way, stop searching
else if ((index_line == to_line) && (index_column == to_column))
{
if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, index_line, index_column, boardslot->color))
return (true); // this move is possible
return (false); // else this queen can't move in the claimed way (her king would be in check)
}
else if (move->slots[index_line][index_column].part != PART_NONE)
return (false); // queen can take a part there BUT it's not the location we want
}
// else do we want to move the queen SE ?
else if ((to_line < from_line) && (to_column > from_column))
{
// see if queen can move SE
for (index_line = from_line - 1, index_column = from_column + 1; (index_line >= 0) && (index_column < 8); index_line--, index_column++)
if (!CAN_PLAY (index_line, index_column))
return (false); // if queen can no longer move this way, stop searching
else if ((index_line == to_line) && (index_column == to_column))
{
if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, index_line, index_column, boardslot->color))
return (true); // this move is possible
return (false); // else this queen can't move in the claimed way (her king would be in check)
}
else if (move->slots[index_line][index_column].part != PART_NONE)
return (false); // queen can take a part there BUT it's not the location we want
}
// else do we want to move the queen NW ?
else if ((to_line > from_line) && (to_column < from_column))
{
// see if queen can move NW
for (index_line = from_line + 1, index_column = from_column - 1; (index_line < 8) && (index_column >= 0); index_line++, index_column--)
if (!CAN_PLAY (index_line, index_column))
return (false); // if queen can no longer move this way, stop searching
else if ((index_line == to_line) && (index_column == to_column))
{
if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, index_line, index_column, boardslot->color))
return (true); // this move is possible
return (false); // else this queen can't move in the claimed way (her king would be in check)
}
else if (move->slots[index_line][index_column].part != PART_NONE)
return (false); // queen can take a part there BUT it's not the location we want
}
// else do we want to move the queen SW ?
else if ((to_line < from_line) && (to_column < from_column))
{
// see if queen can move SW
for (index_line = from_line - 1, index_column = from_column - 1; (index_line >= 0) && (index_column >= 0); index_line--, index_column--)
if (!CAN_PLAY (index_line, index_column))
return (false); // if queen can no longer move this way, stop searching
else if ((index_line == to_line) && (index_column == to_column))
{
if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, index_line, index_column, boardslot->color))
return (true); // this move is possible
return (false); // else this queen can't move in the claimed way (her king would be in check)
}
else if (move->slots[index_line][index_column].part != PART_NONE)
return (false); // queen can take a part there BUT it's not the location we want
}
return (false); // this queen can't move in the claimed way
}
//////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////// KING //////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// else is it a king ?
else if (boardslot->part == PART_KING)
{
// do we want to move that king in one of the allowed directions ?
if (((from_line + 1 == to_line) && (from_column == to_column)) // up
|| ((from_line - 1 == to_line) && (from_column == to_column)) // down
|| ((from_line == to_line) && (from_column + 1 == to_column)) // right
|| ((from_line == to_line) && (from_column - 1 == to_column)) // left
|| ((from_line + 1 == to_line) && (from_column + 1 == to_column)) // NE
|| ((from_line - 1 == to_line) && (from_column + 1 == to_column)) // SE
|| ((from_line + 1 == to_line) && (from_column - 1 == to_column)) // NW
|| ((from_line - 1 == to_line) && (from_column - 1 == to_column))) // SW
{
if (!CAN_PLAY (to_line, to_column))
return (false); // if king can't move there, return false
else if (Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color))
return (false); // if king would be in check there, return false
return (true); // else this move is safe
}
return (false); // if not, this king can't move in the claimed way
}
return (false); // this move is not possible, else we'd have returned earlier
}
bool Move_FindRandomMove (boardmove_t *move, int color, boardmove_t *random_move)
{
// this function returns TRUE if it can find a random move (most of the time blunderous)
// and sets its coordinates in the given output parameters
boardslot_t *boardslot;
int movement_direction;
int line;
int column;
int index_line;
int index_column;
boardmove_t *possiblemoves; // mallocated
int possiblemove_count;
int move_index;
// assume no possible move until told otherwise
possiblemoves = NULL;
possiblemove_count = 0;
// cycle through all the board and find our parts
for (line = 0; line < 8; line++)
for (column = 0; column < 8; column++)
{
boardslot = &move->slots[line][column]; // quick access to grid slot
if ((boardslot->part == PART_NONE) || (boardslot->color != color))
continue; // if this location is empty or not ours, we aren't interested in it
//////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////// PAWN //////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// is it a pawn ?
if (boardslot->part == PART_PAWN)
{
// figure out movement direction
if (boardslot->color == COLOR_WHITE)
movement_direction = 1;
else
movement_direction = -1;
// see if pawn can move forward
if ((((movement_direction == 1) && (line < 7)) || ((movement_direction == -1) && (line > 0)))
&& (move->slots[line + movement_direction][column].part == PART_NONE) // target slot free
&& !Move_IsColorInCheckAfterTestMove (move, line, column, line + movement_direction, column, color))
{
if (((movement_direction == 1) && (line + movement_direction == 7))
|| ((movement_direction == -1) && (line + movement_direction == 0)))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_PAWN, line, column, line + movement_direction, column, PART_QUEEN, false, false); // save promotional move
else
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_PAWN, line, column, line + movement_direction, column, PART_NONE, false, false); // save possible move
}
// see if pawn can take a piece on its left
if ((((movement_direction == 1) && (line < 7)) || ((movement_direction == -1) && (line > 0)))
&& (column > 0) // has room
&& (move->slots[line + movement_direction][column - 1].color != color) // target slot NOT our color
&& (move->slots[line + movement_direction][column - 1].part != PART_NONE) // target slot occupied
&& !Move_IsColorInCheckAfterTestMove (move, line, column, line + movement_direction, column - 1, color))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_PAWN, line, column, line + movement_direction, column - 1, PART_NONE, true, false); // save possible move
// see if pawn can take a piece on its right
if ((((movement_direction == 1) && (line < 7)) || ((movement_direction == -1) && (line > 0)))
&& (column < 7) // has room
&& (move->slots[line + movement_direction][column + 1].color != color) // target slot NOT our color
&& (move->slots[line + movement_direction][column + 1].part != PART_NONE) // target slot occupied
&& !Move_IsColorInCheckAfterTestMove (move, line, column, line + movement_direction, column + 1, color))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_PAWN, line, column, line + movement_direction, column + 1, PART_NONE, true, false); // save possible move
// if previous move was a pawn rush, see if pawn can take "en passant"
if ((move->part == PART_PAWN) // last move was a pawn
&& (move->target[1] == move->source[1]) // pawn moved in column
&& (abs (move->target[0] - move->source[0]) == 2) // pawn rushed
&& (move->target[0] == line) // pawn is in line with us
&& (move->target[1] - column == -1) // pawn is left to us
&& !Move_IsColorInCheckAfterTestMoveEP (move, line, column, line + movement_direction, column - 1, move->target[0], move->target[1], color))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_PAWN, line, column, line + movement_direction, column - 1, PART_NONE, true, true); // save possible move
if ((move->part == PART_PAWN) // last move was a pawn
&& (move->target[1] == move->source[1]) // pawn moved in column
&& (abs (move->target[0] - move->source[0]) == 2) // pawn rushed
&& (move->target[0] == line) // pawn is in line with us
&& (move->target[1] - column == 1) // pawn is right to us
&& !Move_IsColorInCheckAfterTestMoveEP (move, line, column, line + movement_direction, column + 1, move->target[0], move->target[1], color))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_PAWN, line, column, line + movement_direction, column + 1, PART_NONE, true, true); // save possible move
}
//////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////// ROOK //////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// else is it a rook ?
else if (boardslot->part == PART_ROOK)
{
// see if rook can move upwards
for (index_line = line + 1; index_line < 8; index_line++)
if (!CAN_PLAY (index_line, column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, column, color))
{
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_ROOK, line, column, index_line, column, PART_NONE, (move->slots[index_line][column].part != PART_NONE), false); // save possible move
if (move->slots[index_line][column].part != PART_NONE)
break; // this move is possible, but no further moves are possible in the same direction
}
// see if rook can move downwards
for (index_line = line - 1; index_line >= 0; index_line--)
if (!CAN_PLAY (index_line, column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, column, color))
{
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_ROOK, line, column, index_line, column, PART_NONE, (move->slots[index_line][column].part != PART_NONE), false); // save possible move
if (move->slots[index_line][column].part != PART_NONE)
break; // this move is possible, but no further moves are possible in the same direction
}
// see if rook can move right
for (index_column = column + 1; index_column < 8; index_column++)
if (!CAN_PLAY (line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, line, index_column, color))
{
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_ROOK, line, column, line, index_column, PART_NONE, (move->slots[line][index_column].part != PART_NONE), false); // save possible move
if (move->slots[line][index_column].part != PART_NONE)
break; // this move is possible, but no further moves are possible in the same direction
}
// see if rook can move left
for (index_column = column - 1; index_column >= 0; index_column--)
if (!CAN_PLAY (line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, line, index_column, color))
{
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_ROOK, line, column, line, index_column, PART_NONE, (move->slots[line][index_column].part != PART_NONE), false); // save possible move
if (move->slots[line][index_column].part != PART_NONE)
break; // this move is possible, but no further moves are possible in the same direction
}
}
//////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////// KNIGHT /////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// else is it a knight ?
else if (boardslot->part == PART_KNIGHT)
{
// see if knight can move NNW
if (CAN_PLAY (line + 2, column - 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 2, column - 1, color))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KNIGHT, line, column, line + 2, column - 1, PART_NONE, (move->slots[line + 2][column - 1].part != PART_NONE), false); // save possible move
// see if knight can move NNE
if (CAN_PLAY (line + 2, column + 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 2, column + 1, color))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KNIGHT, line, column, line + 2, column + 1, PART_NONE, (move->slots[line + 2][column + 1].part != PART_NONE), false); // save possible move
// see if knight can move ENE
if (CAN_PLAY (line + 1, column + 2) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 1, column + 2, color))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KNIGHT, line, column, line + 1, column + 2, PART_NONE, (move->slots[line + 1][column + 2].part != PART_NONE), false); // save possible move
// see if knight can move ESE
if (CAN_PLAY (line - 1, column + 2) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 1, column + 2, color))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KNIGHT, line, column, line - 1, column + 2, PART_NONE, (move->slots[line - 1][column + 2].part != PART_NONE), false); // save possible move
// see if knight can move SSW
if (CAN_PLAY (line - 2, column - 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 2, column - 1, color))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KNIGHT, line, column, line - 2, column - 1, PART_NONE, (move->slots[line - 2][column - 1].part != PART_NONE), false); // save possible move
// see if knight can move SSE
if (CAN_PLAY (line - 2, column + 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 2, column + 1, color))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KNIGHT, line, column, line - 2, column + 1, PART_NONE, (move->slots[line - 2][column + 1].part != PART_NONE), false); // save possible move
// see if knight can move WNW
if (CAN_PLAY (line + 1, column - 2) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 1, column - 2, color))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KNIGHT, line, column, line + 1, column - 2, PART_NONE, (move->slots[line + 1][column - 2].part != PART_NONE), false); // save possible move
// see if knight can move WSW
if (CAN_PLAY (line - 1, column - 2) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 1, column - 2, color))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KNIGHT, line, column, line - 1, column - 2, PART_NONE, (move->slots[line - 1][column - 2].part != PART_NONE), false); // save possible move
}
//////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////// BISHOP /////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// else is it a bishop ?
else if (boardslot->part == PART_BISHOP)
{
// see if bishop can move NE
for (index_line = line + 1, index_column = column + 1; (index_line < 8) && (index_column < 8); index_line++, index_column++)
if (!CAN_PLAY (index_line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color))
{
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_BISHOP, line, column, index_line, index_column, PART_NONE, (move->slots[index_line][index_column].part != PART_NONE), false); // save possible move
if (move->slots[index_line][index_column].part != PART_NONE)
break; // this move is possible, but no further moves are possible in the same direction
}
// see if bishop can move SE
for (index_line = line - 1, index_column = column + 1; (index_line >= 0) && (index_column < 8); index_line--, index_column++)
if (!CAN_PLAY (index_line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color))
{
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_BISHOP, line, column, index_line, index_column, PART_NONE, (move->slots[index_line][index_column].part != PART_NONE), false); // save possible move
if (move->slots[index_line][index_column].part != PART_NONE)
break; // this move is possible, but no further moves are possible in the same direction
}
// see if bishop can move NW
for (index_line = line + 1, index_column = column - 1; (index_line < 8) && (index_column >= 0); index_line++, index_column--)
if (!CAN_PLAY (index_line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color))
{
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_BISHOP, line, column, index_line, index_column, PART_NONE, (move->slots[index_line][index_column].part != PART_NONE), false); // save possible move
if (move->slots[index_line][index_column].part != PART_NONE)
break; // this move is possible, but no further moves are possible in the same direction
}
// see if bishop can move SW
for (index_line = line - 1, index_column = column - 1; (index_line >= 0) && (index_column >= 0); index_line--, index_column--)
if (!CAN_PLAY (index_line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color))
{
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_BISHOP, line, column, index_line, index_column, PART_NONE, (move->slots[index_line][index_column].part != PART_NONE), false); // save possible move
if (move->slots[index_line][index_column].part != PART_NONE)
break; // this move is possible, but no further moves are possible in the same direction
}
}
//////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////// QUEEN //////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// else is it a queen ?
else if (boardslot->part == PART_QUEEN)
{
// see if queen can move upwards
for (index_line = line + 1; index_line < 8; index_line++)
if (!CAN_PLAY (index_line, column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, column, color))
{
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_QUEEN, line, column, index_line, column, PART_NONE, (move->slots[index_line][column].part != PART_NONE), false); // save possible move
if (move->slots[index_line][column].part != PART_NONE)
break; // this move is possible, but no further moves are possible in the same direction
}
// see if queen can move downwards
for (index_line = line - 1; index_line >= 0; index_line--)
if (!CAN_PLAY (index_line, column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, column, color))
{
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_QUEEN, line, column, index_line, column, PART_NONE, (move->slots[index_line][column].part != PART_NONE), false); // save possible move
if (move->slots[index_line][column].part != PART_NONE)
break; // this move is possible, but no further moves are possible in the same direction
}
// see if queen can move right
for (index_column = column + 1; index_column < 8; index_column++)
if (!CAN_PLAY (line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, line, index_column, color))
{
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_QUEEN, line, column, line, index_column, PART_NONE, (move->slots[line][index_column].part != PART_NONE), false); // save possible move
if (move->slots[line][index_column].part != PART_NONE)
break; // this move is possible, but no further moves are possible in the same direction
}
// see if queen can move left
for (index_column = column - 1; index_column >= 0; index_column--)
if (!CAN_PLAY (line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, line, index_column, color))
{
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_QUEEN, line, column, line, index_column, PART_NONE, (move->slots[line][index_column].part != PART_NONE), false); // save possible move
if (move->slots[line][index_column].part != PART_NONE)
break; // this move is possible, but no further moves are possible in the same direction
}
// see if queen can move NE
for (index_line = line + 1, index_column = column + 1; (index_line < 8) && (index_column < 8); index_line++, index_column++)
if (!CAN_PLAY (index_line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color))
{
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_QUEEN, line, column, index_line, index_column, PART_NONE, (move->slots[index_line][index_column].part != PART_NONE), false); // save possible move
if (move->slots[index_line][index_column].part != PART_NONE)
break; // this move is possible, but no further moves are possible in the same direction
}
// see if queen can move SE
for (index_line = line - 1, index_column = column + 1; (index_line >= 0) && (index_column < 8); index_line--, index_column++)
if (!CAN_PLAY (index_line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color))
{
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_QUEEN, line, column, index_line, index_column, PART_NONE, (move->slots[index_line][index_column].part != PART_NONE), false); // save possible move
if (move->slots[index_line][index_column].part != PART_NONE)
break; // this move is possible, but no further moves are possible in the same direction
}
// see if queen can move NW
for (index_line = line + 1, index_column = column - 1; (index_line < 8) && (index_column >= 0); index_line++, index_column--)
if (!CAN_PLAY (index_line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color))
{
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_QUEEN, line, column, index_line, index_column, PART_NONE, (move->slots[index_line][index_column].part != PART_NONE), false); // save possible move
if (move->slots[index_line][index_column].part != PART_NONE)
break; // this move is possible, but no further moves are possible in the same direction
}
// see if queen can move SW
for (index_line = line - 1, index_column = column - 1; (index_line >= 0) && (index_column >= 0); index_line--, index_column--)
if (!CAN_PLAY (index_line, index_column))
break; // if part can no longer move this way, stop searching
else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color))
{
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_QUEEN, line, column, index_line, index_column, PART_NONE, (move->slots[index_line][index_column].part != PART_NONE), false); // save possible move
if (move->slots[index_line][index_column].part != PART_NONE)
break; // this move is possible, but no further moves are possible in the same direction
}
}
//////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////// KING //////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// else is it a king ?
else if (boardslot->part == PART_KING)
{
// see if king can move up
if (CAN_PLAY (line + 1, column) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 1, column, color))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KING, line, column, line + 1, column, PART_NONE, (move->slots[line + 1][column].part != PART_NONE), false); // save possible move
// see if king can move down
if (CAN_PLAY (line - 1, column) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 1, column, color))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KING, line, column, line - 1, column, PART_NONE, (move->slots[line - 1][column].part != PART_NONE), false); // save possible move
// see if king can move right
if (CAN_PLAY (line, column + 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line, column + 1, color))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KING, line, column, line, column + 1, PART_NONE, (move->slots[line][column + 1].part != PART_NONE), false); // save possible move
// see if king can move left
if (CAN_PLAY (line, column - 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line, column - 1, color))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KING, line, column, line, column - 1, PART_NONE, (move->slots[line][column - 1].part != PART_NONE), false); // save possible move
// see if king can move NE
if (CAN_PLAY (line + 1, column + 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 1, column + 1, color))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KING, line, column, line + 1, column + 1, PART_NONE, (move->slots[line + 1][column + 1].part != PART_NONE), false); // save possible move
// see if king can move SE
if (CAN_PLAY (line - 1, column + 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 1, column + 1, color))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KING, line, column, line - 1, column + 1, PART_NONE, (move->slots[line - 1][column + 1].part != PART_NONE), false); // save possible move
// see if king can move NW
if (CAN_PLAY (line + 1, column - 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 1, column - 1, color))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KING, line, column, line + 1, column - 1, PART_NONE, (move->slots[line + 1][column - 1].part != PART_NONE), false); // save possible move
// see if king can move SW
if (CAN_PLAY (line - 1, column - 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 1, column - 1, color))
AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KING, line, column, line - 1, column - 1, PART_NONE, (move->slots[line - 1][column - 1].part != PART_NONE), false); // save possible move
}
}
// now that all the table has been parsed, see if we have some possible moves
if (possiblemove_count == 0)
return (false); // if none, return FALSE (it means that we are stalemate, but there's a faster function to check that)
move_index = rand () % possiblemove_count; // select a possible move at random
memcpy (random_move, &possiblemoves[move_index], sizeof (boardmove_t)); // copy it into destination variable
SAFE_free ((void **) &possiblemoves); // free the possible moves array, we no longer need them
return (true); // we did find some possible moves
}
int Move_CountPartsByColorAndType (boardmove_t *move, int color, int part_type)
{
// this function returns the amount of parts of the specified color and type left on board
int line;
int column;
int count;
count = 0; // assume none so far
// cycle through all the board...
for (line = 0; line < 8; line++)
for (column = 0; column < 8; column++)
if ((move->slots[line][column].color == color) && (move->slots[line][column].part == part_type))
count++; // sum up all the parts of the same colour and type we find
return (count); // and return their quantity
}
bool Move_IsColorInCheckAfterTestMove (boardmove_t *move, int source_line, int source_column, int target_line, int target_column, int color)
{
// helper function to play a test move on a temporary board (which must have been previously allocated)
static boardmove_t temp_move; // declare this static so as not to allocate/free it continuously
memcpy (temp_move.slots, move->slots, sizeof (move->slots)); // have a copy of the table, then make the move
memcpy (&temp_move.slots[target_line][target_column], &temp_move.slots[source_line][source_column], sizeof (boardslot_t));
memset (&temp_move.slots[source_line][source_column], 0, sizeof (boardslot_t)); // erase the source slot
return (Move_IsCheck (&temp_move, color)); // return whether the final board has the given color in check
}
bool Move_IsColorInCheckAfterTestMoveEP (boardmove_t *move, int source_line, int source_column, int target_line, int target_column, int clear_line, int clear_column, int color)
{
// helper function to play a test move on a temporary board (which must have been previously allocated)
// En Passant version -- cleans the specified target before testing
static boardmove_t temp_move; // declare this static so as not to allocate/free it continuously
memcpy (temp_move.slots, move->slots, sizeof (move->slots)); // have a copy of the table, then make the move
memcpy (&temp_move.slots[target_line][target_column], &temp_move.slots[source_line][source_column], sizeof (boardslot_t));
memset (&temp_move.slots[source_line][source_column], 0, sizeof (boardslot_t)); // erase the source slot
memset (&temp_move.slots[clear_line][clear_column], 0, sizeof (boardslot_t)); // erase the "en passant" target
return (Move_IsCheck (&temp_move, color)); // return whether the final board has the given color in check
}
void Move_DescribeInFEN (boardmove_t *move)
{
// convert a board and its part placements into a Forsyth Edwards notation, writing in the fen_string buffer
boardslot_t *slot;
int line;
int column;
int free_slots;
int length;
// first reset the string
move->fen_string[0] = 0;
////////////////////////////////////////////////////////
// first part of the FEN notation is the parts placement
// cycle through each column, line after line, starting up left and going downwards right
for (line = 7; line >= 0; line--)
{
free_slots = 0; // no free slot in that line yet
for (column = 0; column < 8; column++)
{
slot = &move->slots[line][column]; // quick access to current slot
if (slot->part == PART_ROOK)
{
// if there are free slots to mention, do it
if (free_slots > 0)
{
length = wcslen (move->fen_string); // append the free slots count
swprintf_s (&move->fen_string[length], WCHAR_SIZEOF (move->fen_string) - length, L"%d", free_slots);
free_slots = 0; // reset the free slots count
}
wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), (slot->color == COLOR_BLACK ? L"r" : (slot->color == COLOR_WHITE ? L"R": L"?")));
}
else if (slot->part == PART_KNIGHT)
{
// if there are free slots to mention, do it
if (free_slots > 0)
{
length = wcslen (move->fen_string); // append the free slots count
swprintf_s (&move->fen_string[length], WCHAR_SIZEOF (move->fen_string) - length, L"%d", free_slots);
free_slots = 0; // reset the free slots count
}
wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), (slot->color == COLOR_BLACK ? L"n" : (slot->color == COLOR_WHITE ? L"N": L"?")));
}
else if (slot->part == PART_BISHOP)
{
// if there are free slots to mention, do it
if (free_slots > 0)
{
length = wcslen (move->fen_string); // append the free slots count
swprintf_s (&move->fen_string[length], WCHAR_SIZEOF (move->fen_string) - length, L"%d", free_slots);
free_slots = 0; // reset the free slots count
}
wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), (slot->color == COLOR_BLACK ? L"b" : (slot->color == COLOR_WHITE ? L"B": L"?")));
}
else if (slot->part == PART_QUEEN)
{
// if there are free slots to mention, do it
if (free_slots > 0)
{
length = wcslen (move->fen_string); // append the free slots count
swprintf_s (&move->fen_string[length], WCHAR_SIZEOF (move->fen_string) - length, L"%d", free_slots);
free_slots = 0; // reset the free slots count
}
wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), (slot->color == COLOR_BLACK ? L"q" : (slot->color == COLOR_WHITE ? L"Q": L"?")));
}
else if (slot->part == PART_KING)
{
// if there are free slots to mention, do it
if (free_slots > 0)
{
length = wcslen (move->fen_string); // append the free slots count
swprintf_s (&move->fen_string[length], WCHAR_SIZEOF (move->fen_string) - length, L"%d", free_slots);
free_slots = 0; // reset the free slots count
}
wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), (slot->color == COLOR_BLACK ? L"k" : (slot->color == COLOR_WHITE ? L"K": L"?")));
}
else if (slot->part == PART_PAWN)
{
// if there are free slots to mention, do it
if (free_slots > 0)
{
length = wcslen (move->fen_string); // append the free slots count
swprintf_s (&move->fen_string[length], WCHAR_SIZEOF (move->fen_string) - length, L"%d", free_slots);
free_slots = 0; // reset the free slots count
}
wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), (slot->color == COLOR_BLACK ? L"p" : (slot->color == COLOR_WHITE ? L"P": L"?")));
}
else
free_slots++; // we found one free slot more
// are we at the end of a line ?
if (column == 7)
{
// if there are free slots to mention, do it
if (free_slots > 0)
{
length = wcslen (move->fen_string); // append the free slots count
swprintf_s (&move->fen_string[length], WCHAR_SIZEOF (move->fen_string) - length, L"%d", free_slots);
free_slots = 0; // reset the free slots count
}
if (line > 0)
wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"/"); // at the end of each line, drop a slash (except on the last line)
}
}
}
//////////////////////////////////////////////////////
// second part of the FEN notation is the side on move
// deduce the side to move according to last move's color
if (move->color == COLOR_WHITE)
wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L" b"); // black to move
else
wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L" w"); // white to move (this also catches the beginning of a game)
///////////////////////////////////////////////////////////////
// third part of the FEN notation is the castling possibilities
wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L" ");
if (!(move->sides[COLOR_BLACK].longcastle_allowed | move->sides[COLOR_WHITE].longcastle_allowed | move->sides[COLOR_BLACK].shortcastle_allowed | move->sides[COLOR_WHITE].shortcastle_allowed))
wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"-"); // neither side can castle
else
{
if (move->sides[COLOR_WHITE].shortcastle_allowed)
wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"K"); // white can castle kingside
if (move->sides[COLOR_WHITE].longcastle_allowed)
wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"Q"); // white can castle queenside
if (move->sides[COLOR_BLACK].shortcastle_allowed)
wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"k"); // black can castle kingside
if (move->sides[COLOR_BLACK].longcastle_allowed)
wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"q"); // black can castle queenside
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// fourth part of the FEN notation is the optional position for a pawn that can be taken en passant
wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L" "); // spacer
if ((move->part == PART_PAWN) // last move was a pawn
&& (move->target[1] == move->source[1]) // pawn moved in column
&& (abs (move->target[0] - move->source[0]) == 2)) // pawn rushed
{
// column
if (move->source[1] == 0) wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"a");
else if (move->source[1] == 1) wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"b");
else if (move->source[1] == 2) wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"c");
else if (move->source[1] == 3) wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"d");
else if (move->source[1] == 4) wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"e");
else if (move->source[1] == 5) wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"f");
else if (move->source[1] == 6) wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"g");
else if (move->source[1] == 7) wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"h");
else wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"?");
// line (it's the line the pawn would be on if it had made a "normal" move)
length = wcslen (move->fen_string);
swprintf_s (&move->fen_string[length], WCHAR_SIZEOF (move->fen_string) - length, L"%d", 1 + (move->target[0] + move->source[0]) / 2);
}
else
wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"-"); // last move was not a pawn rush
return; // finished
}
bool Move_SetupFromFEN (boardmove_t *move, wchar_t *fen_string)
{
// this function sets up the given board according to the Forsyth-Edwards description fen_string makes of it
// FIXME : load fen, then load PGN, then back arrow.
int char_index;
int length;
int current_line;
int current_column;
int enpassant_line;
int enpassant_column;
// reset the chess grid
memset (&move->slots, 0, sizeof (move->slots));
// reset the taken pieces for both sides
SAFE_free ((void **) &move->sides[COLOR_WHITE].takenparts); // release memory space
move->sides[COLOR_WHITE].takenpart_count = 0;
SAFE_free ((void **) &move->sides[COLOR_BLACK].takenparts); // release memory space
move->sides[COLOR_BLACK].takenpart_count = 0;
// reset the moves array comments and the moves array itself
SAFE_free ((void **) &move->comment);
// DISallow both sides to castle, until told otherwise
move->sides[COLOR_WHITE].shortcastle_allowed = false;
move->sides[COLOR_WHITE].longcastle_allowed = false;
move->sides[COLOR_BLACK].shortcastle_allowed = false;
move->sides[COLOR_BLACK].longcastle_allowed = false;
// get the length of the FEN string
length = wcslen (fen_string);
// now parse the board from top left to bottom right, placing parts on the fly
current_line = 7;
current_column = 0;
for (char_index = 0; char_index < length; char_index++)
{
// is it a number ?
if (iswdigit (fen_string[char_index]))
{
current_column += _wtoi (&fen_string[char_index]); // skip as many columns as needed
if (current_column > 8)
return (false); // consistency check: something's wrong with this notation, return FALSE
}
// else is it a line skip ?
else if (fen_string[char_index] == L'/')
{
// were we reading the last line ?
if (current_line == 0)
{
char_index++; // skip this character
break; // stop reading parts placement
}
current_line--; // proceed to next line, decrescending
current_column = 0; // and begin at the first column on that line
}
// else is it a blank space ? meaning parts have been read
else if (fen_string[char_index] == L' ')
break; // stop reading parts placement
// else it's a part. Check first if the current line/column is valid
else if (IS_VALID (current_line, current_column))
{
if (fen_string[char_index] == L'r')
Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_ROOK); // black rook
else if (fen_string[char_index] == L'R')
Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_ROOK); // white rook
else if (fen_string[char_index] == L'n')
Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_KNIGHT); // black knight
else if (fen_string[char_index] == L'N')
Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_KNIGHT); // white knight
else if (fen_string[char_index] == L'b')
Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_BISHOP); // black bishop
else if (fen_string[char_index] == L'B')
Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_BISHOP); // white bishop
else if (fen_string[char_index] == L'q')
Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_QUEEN); // black queen
else if (fen_string[char_index] == L'Q')
Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_QUEEN); // white queen
else if (fen_string[char_index] == L'k')
Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_KING); // black king
else if (fen_string[char_index] == L'K')
Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_KING); // white king
else if (fen_string[char_index] == L'p')
Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_PAWN); // black pawn
else if (fen_string[char_index] == L'P')
Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_PAWN); // white pawn
current_column++; // proceed to next column
}
else
return (false); // invalid position, something's wrong with this notation, return FALSE
}
// a space has been reached: next thing to read is the side on move
char_index++;
if (char_index >= length)
return (false); // consistency check: something's wrong with this notation, return FALSE
if (towlower (fen_string[char_index]) == L'w')
move->color = COLOR_BLACK; // white to move
else if (towlower (fen_string[char_index]) == L'b')
move->color = COLOR_WHITE; // black to move
else
return (false); // consistency check: something's wrong with this notation, return FALSE
// there should be a space after this
char_index++;
if ((char_index >= length) || (fen_string[char_index] != L' '))
return (false); // consistency check: something's wrong with this notation, return FALSE
// a space has been reached: next thing to read is the castling possibilities
char_index++;
if (char_index >= length)
return (false); // consistency check: something's wrong with this notation, return FALSE
for (; char_index < length; char_index++)
{
if (fen_string[char_index] == L'k')
move->sides[COLOR_BLACK].shortcastle_allowed = true; // short castling allowed for black
else if (fen_string[char_index] == L'K')
move->sides[COLOR_WHITE].shortcastle_allowed = true; // short castling allowed for white
else if (fen_string[char_index] == L'q')
move->sides[COLOR_BLACK].longcastle_allowed = true; // long castling allowed for black
else if (fen_string[char_index] == L'Q')
move->sides[COLOR_WHITE].longcastle_allowed = true; // long castling allowed for white
else if (fen_string[char_index] == L'-')
continue; // no side can castle (explicitly)
else if (fen_string[char_index] == L' ')
break; // if blank space, stop reading castling possibilities
}
// is there a free space after this ?
if (char_index < length)
{
char_index++; // if so, skip it
// is there enough room for an en passant position AND is it specified ?
if ((char_index + 2 <= length) && (fen_string[char_index] != L'-'))
{
// read column
if (towlower (fen_string[char_index]) == L'a') enpassant_column = 0;
else if (towlower (fen_string[char_index]) == L'b') enpassant_column = 1;
else if (towlower (fen_string[char_index]) == L'c') enpassant_column = 2;
else if (towlower (fen_string[char_index]) == L'd') enpassant_column = 3;
else if (towlower (fen_string[char_index]) == L'e') enpassant_column = 4;
else if (towlower (fen_string[char_index]) == L'f') enpassant_column = 5;
else if (towlower (fen_string[char_index]) == L'g') enpassant_column = 6;
else if (towlower (fen_string[char_index]) == L'h') enpassant_column = 7;
else return (false); // consistency check: something's wrong with this notation, return FALSE
// read line
enpassant_line = _wtoi (&fen_string[char_index + 1]) - 1;
if ((enpassant_line != 2) && (enpassant_line != 5))
return (false); // consistency check: something's wrong with this notation, return FALSE
// setup move data
move->part = PART_PAWN;
if (enpassant_line == 2)
{
move->source[0] = 1; // rush from line 1 to line 3
move->target[0] = 3;
move->color = COLOR_WHITE;
}
else
{
move->source[0] = 6; // rush from line 6 to line 4
move->target[0] = 4;
move->color = COLOR_BLACK;
}
move->source[1] = enpassant_column;
move->target[1] = enpassant_column;
}
}
// table was setup correctly, save FEN string in move structure
wcscpy_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), fen_string);
return (true); // and return TRUE
}
bool Move_SetupFromStyle12 (boardmove_t *move, wchar_t *positions, int move_color, int pawnrush_column,
bool can_white_castle_short, bool can_white_castle_long, bool can_black_castle_short, bool can_black_castle_long, wchar_t *pretty_movestring)
{
// this function sets up the given board according to the Style12 ICC/FICS description style12_string makes of it
int pos_index;
int current_line;
int current_column;
// reset the chess grid
memset (&move->slots, 0, sizeof (move->slots));
// now parse the line from left to right, placing parts on the fly
for (current_line = 0; current_line < 8; current_line++)
for (current_column = 0; current_column < 8; current_column++)
{
pos_index = (7 - current_line) * 8 + current_column; // compute position in line
if (positions[pos_index] == L'r')
Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_ROOK); // black rook
else if (positions[pos_index] == L'R')
Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_ROOK); // white rook
else if (positions[pos_index] == L'n')
Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_KNIGHT); // black knight
else if (positions[pos_index] == L'N')
Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_KNIGHT); // white knight
else if (positions[pos_index] == L'b')
Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_BISHOP); // black bishop
else if (positions[pos_index] == L'B')
Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_BISHOP); // white bishop
else if (positions[pos_index] == L'q')
Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_QUEEN); // black queen
else if (positions[pos_index] == L'Q')
Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_QUEEN); // white queen
else if (positions[pos_index] == L'k')
Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_KING); // black king
else if (positions[pos_index] == L'K')
Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_KING); // white king
else if (positions[pos_index] == L'p')
Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_PAWN); // black pawn
else if (positions[pos_index] == L'P')
Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_PAWN); // white pawn
}
// save move color
move->color = move_color;
// allow or disallow both sides to castle, as told
move->sides[COLOR_WHITE].shortcastle_allowed = can_white_castle_short;
move->sides[COLOR_WHITE].longcastle_allowed = can_white_castle_long;
move->sides[COLOR_BLACK].shortcastle_allowed = can_black_castle_short;
move->sides[COLOR_BLACK].longcastle_allowed = can_black_castle_long;
// is the last move a pawn rush ?
if (pawnrush_column != -1)
{
if ((pawnrush_column < 0) || (pawnrush_column > 7))
return (false); // consistency check: something's wrong with this notation, return FALSE
move->part = PART_PAWN;
if (move->color == COLOR_WHITE)
{
move->source[0] = 1; // rush from line 1 to line 3
move->target[0] = 3;
}
else
{
move->source[0] = 6; // rush from line 6 to line 4
move->target[0] = 4;
}
move->source[1] = pawnrush_column;
move->target[1] = pawnrush_column;
}
// finally, save the FEN string with which we initialized this board
Move_DescribeInFEN (move);
return (true); // finished, no error encountered
}
static void AddPossibleMove (boardmove_t **possiblemoves, int *possiblemove_count, int color, int part, int source_line, int source_column, int target_line, int target_column, int promotion_type, bool has_captured, bool is_enpassant)
{
// helper function that resizes the given possiblemoves array and adds a possible move to it
// TODO: raise or clear the is_check and is_stalemate move flags in the returned move.
// Not crucial as this function is only called by Board_FindRandomMove(), the result is then
// translated in SAN and then fed to the chess engine to order a blunderous move. The move
// is then played normally using Board_AppendMove() using source and target locations, and this
// call does evaluate the actual move and set the flags correctly in the final moves array.
*possiblemoves = (boardmove_t *) SAFE_realloc (*possiblemoves, *possiblemove_count, (*possiblemove_count) + 1, sizeof (boardmove_t), true);
(*possiblemoves)[*possiblemove_count].color = color;
(*possiblemoves)[*possiblemove_count].part = part;
(*possiblemoves)[*possiblemove_count].source[0] = source_line;
(*possiblemoves)[*possiblemove_count].source[1] = source_column;
(*possiblemoves)[*possiblemove_count].target[0] = target_line;
(*possiblemoves)[*possiblemove_count].target[1] = target_column;
(*possiblemoves)[*possiblemove_count].promotion_type = promotion_type;
(*possiblemoves)[*possiblemove_count].has_captured = has_captured;
(*possiblemoves)[*possiblemove_count].is_enpassant = is_enpassant;
(*possiblemove_count)++; // possible moves array holds now one move more
return; // finished
}