/*  DreamChess
**
**  DreamChess is the legal property of its developers, whose names are too
**  numerous to list here. Please refer to the COPYRIGHT file distributed
**  with this source distribution.
**
**  This program is free software: you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation, either version 3 of the License, or
**  (at your option) any later version.
**
**  This program is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>

#include "board.h"
#include "move.h"
#include "hashing.h"

/* square_bit[i] is a bitboard that marks square 'i' on the board. */
bitboard_t square_bit[64];

/* Pieces values are used to calculate the material balance of the board. */
static int piece_value[] = {100, 100, 300, 300, 350, 350, 500, 500, 900, 900,
                            2000, 2000};

static void add_piece(board_t *board, int square, int piece)
/* Adds a piece to a board.
** Parameters: (board_t *) board: Pointer to the board to add the piece to.
**             (int) piece: The piece to add.
**             (int) square: The square where the piece should be added.
*/
{
    board->bitboard[piece] |= square_bit[square];
    board->bitboard[ALL + (piece & 1)] |= square_bit[square];
    board->material_value[piece & 1] += piece_value[piece];

    if ((piece & PIECE_MASK) == PAWN)
        board->num_pawns[piece & 1]++;

    board->hash_key ^= pieces_hash[piece][square];
}

static void remove_piece(board_t *board, int square, int piece)
/* Removes a piece from the board.
** Parameters: (board_t *) board: Pointer to the board to remove the piece
**                from.
**             (int) piece: The piece to remove.
**             (int) square: The square where to remove the piece from.
*/
{
    board->bitboard[piece] ^= square_bit[square];
    board->bitboard[ALL + (piece & 1)] ^= square_bit[square];
    board->material_value[piece & 1] -= piece_value[piece];

    if ((piece & PIECE_MASK) == PAWN)
        board->num_pawns[piece & 1]--;

    board->hash_key ^= pieces_hash[piece][square];
}

void setup_board(board_t *board)
{
    int i;

    clear_board(board);

    add_piece(board, SQUARE_A1, WHITE_ROOK);
    add_piece(board, SQUARE_B1, WHITE_KNIGHT);
    add_piece(board, SQUARE_C1, WHITE_BISHOP);
    add_piece(board, SQUARE_D1, WHITE_QUEEN);
    add_piece(board, SQUARE_E1, WHITE_KING);
    add_piece(board, SQUARE_F1, WHITE_BISHOP);
    add_piece(board, SQUARE_G1, WHITE_KNIGHT);
    add_piece(board, SQUARE_H1, WHITE_ROOK);
    add_piece(board, SQUARE_A8, BLACK_ROOK);
    add_piece(board, SQUARE_B8, BLACK_KNIGHT);
    add_piece(board, SQUARE_C8, BLACK_BISHOP);
    add_piece(board, SQUARE_D8, BLACK_QUEEN);
    add_piece(board, SQUARE_E8, BLACK_KING);
    add_piece(board, SQUARE_F8, BLACK_BISHOP);
    add_piece(board, SQUARE_G8, BLACK_KNIGHT);
    add_piece(board, SQUARE_H8, BLACK_ROOK);

    for (i = SQUARE_A2; i <= SQUARE_H2; i++)
        add_piece(board, i, WHITE_PAWN);

    for (i = SQUARE_A7; i <= SQUARE_H7; i++)
        add_piece(board, i, BLACK_PAWN);

    board->castle_flags = WHITE_CAN_CASTLE_KINGSIDE
                          | BLACK_CAN_CASTLE_KINGSIDE
                          | WHITE_CAN_CASTLE_QUEENSIDE
                          | BLACK_CAN_CASTLE_QUEENSIDE;

    board->en_passant = 0LL;

    board->current_player = SIDE_WHITE;

    board->hash_key = hash_key(board);

    board->fifty_moves = 0;
}

int setup_board_fen(board_t *board, char *fen)
{
	int i = 0;
	int square = 56;
	int len = strlen(fen);
	char *endptr;
	int done = 0;

	clear_board(board);

	while (done < 64)
	{
		char c;
		char piece=0;

		if (i == len)
			return 1;

		c = fen[i++];

		if ((c >= '1') && (c <= '8'))
		{
			square += c - '0';
			done += c - '0';
			continue;
		}

		switch(toupper(c))
		{
			case 'P':
				piece = PAWN;
				break;
			case 'N':
				piece = KNIGHT;
				break;
			case 'B':
				piece = BISHOP;
				break;
			case 'R':
				piece = ROOK;
				break;
			case 'Q':
				piece = QUEEN;
				break;
			case 'K':
				piece = KING;
				break;
			case '/':
				square -= 16;
				continue;
		}

		if (c >= 'a')
			piece += SIDE_BLACK;
		else
			piece += SIDE_WHITE;

		if (square < 0 || square > 63)
			return 1;

		add_piece(board, square++, piece);
		done++;
	}

	/* Skip space */
	if (++i >= len)
		return 1;

	if (fen[i] == 'w')
		board->current_player = SIDE_WHITE;
	else if (fen[i] == 'b')
		board->current_player = SIDE_BLACK;
	else
		return 1;

	i += 2;

	if (i >= len)
		return 1;

	board->castle_flags = 0;

	if (fen[i] != '-')
		while ((fen[i] != ' ') && (i < len))
			switch (fen[i++])
			{
			case 'K':
				board->castle_flags |= WHITE_CAN_CASTLE_KINGSIDE;
				break;
			case 'Q':
				board->castle_flags |= WHITE_CAN_CASTLE_QUEENSIDE;
				break;
			case 'k':
				board->castle_flags |= BLACK_CAN_CASTLE_KINGSIDE;
				break;
			case 'q':
				board->castle_flags |= BLACK_CAN_CASTLE_QUEENSIDE;
			}
	else
		i++;

	/* Skip space */
	if (++i >= len)
		return 1;

	board->en_passant = 0LL;

	if (fen[i] != '-')
	{
		if ((fen[i] < 'a') || (fen[i] > 'h'))
			return 1;

		board->en_passant = 1LL << (fen[i++] - 'a');

		if (i >= len)
			return 1;

		if ((fen[i] < '1') || (fen[i] > '8'))
			return 1;

		board->en_passant <<= 8 * (fen[i] - '1');
	}

	i += 2;

	if (i >= len)
		return 1;

	errno = 0;
	board->fifty_moves = strtol(fen + i, &endptr, 10);
	if (errno || (fen + i == endptr))
		return 1;

	i = endptr - fen + 1;

	if (i >= len)
		return 1;

	/* FIXME Implement move counter, legality check */

	return 0;
}

void board_init(void)
{
    int i;
    for (i = 0; i < 64; i++)
        square_bit[i] = 1LL << i;
}

void clear_board(board_t *board)
{
    int i;
    for (i = 0; i < NR_BITBOARDS; i++)
        board->bitboard[i] = 0LL;

    board->num_pawns[SIDE_WHITE] = 0;
    board->num_pawns[SIDE_BLACK] = 0;

    board->material_value[SIDE_WHITE] = 0;
    board->material_value[SIDE_BLACK] = 0;
}

int find_black_piece(board_t *board, int square)
{
    bitboard_t mask = square_bit[square];

    /* We need to check kings first because a fake king might be on the
    ** same square as a rook. In that case we want to find the king so
    ** that the illegality of the previous move can be detected.
    */
    if (board->bitboard[BLACK_KING] & square_bit[square])
        return BLACK_KING;

    /* Check for other pieces in order of frequency. */
    if (board->bitboard[BLACK_PAWN] & mask)
        return BLACK_PAWN;
    if (board->bitboard[BLACK_KNIGHT] & mask)
        return BLACK_KNIGHT;
    if (board->bitboard[BLACK_BISHOP] & mask)
        return BLACK_BISHOP;
    if (board->bitboard[BLACK_ROOK] & mask)
        return BLACK_ROOK;
    if (board->bitboard[BLACK_QUEEN] & mask)
        return BLACK_QUEEN;

    return NONE;
}

int find_white_piece(board_t *board, int square)
{
    bitboard_t mask = square_bit[square];

    /* We need to check kings first because a fake king might be on the
    ** same square as a rook. In that case we want to find the king so
    ** that the illegality of the previous move can be detected.
    */
    if (board->bitboard[WHITE_KING] & mask)
        return WHITE_KING;

    /* Check for other pieces in order of frequency. */
    if (board->bitboard[WHITE_PAWN] & mask)
        return WHITE_PAWN;
    if (board->bitboard[WHITE_KNIGHT] & mask)
        return WHITE_KNIGHT;
    if (board->bitboard[WHITE_BISHOP] & mask)
        return WHITE_BISHOP;
    if (board->bitboard[WHITE_ROOK] & mask)
        return WHITE_ROOK;
    if (board->bitboard[WHITE_QUEEN] & mask)
        return WHITE_QUEEN;

    return NONE;
}

void execute_move(board_t *board, move_t move)
{
    if (board->current_player)
    {
        /* Black is the side to move. Remove white phantom kings from the
        ** board.
        */
        if (board->castle_flags & WHITE_PHANTOM_KINGS_KINGSIDE)
        {
            board->castle_flags ^= WHITE_PHANTOM_KINGS_KINGSIDE;
            board->bitboard[WHITE_KING] ^= WHITE_PHANTOM_KINGSIDE;
            board->bitboard[WHITE_ALL] ^= WHITE_PHANTOM_KINGSIDE;
            board->bitboard[WHITE_ALL] |= board->bitboard[WHITE_ROOK];
        }
        else if (board->castle_flags & WHITE_PHANTOM_KINGS_QUEENSIDE)
        {
            board->castle_flags ^= WHITE_PHANTOM_KINGS_QUEENSIDE;
            board->bitboard[WHITE_KING] ^= WHITE_PHANTOM_QUEENSIDE;
            board->bitboard[WHITE_ALL] ^= WHITE_PHANTOM_QUEENSIDE;
            board->bitboard[WHITE_ALL] |= board->bitboard[WHITE_ROOK];
        }
    }
    else
    {
        /* White is the side to move. Remove black phantom kings from the
        ** board.
        */
        if (board->castle_flags & BLACK_PHANTOM_KINGS_KINGSIDE)
        {
            board->castle_flags ^= BLACK_PHANTOM_KINGS_KINGSIDE;
            board->bitboard[BLACK_KING] ^= BLACK_PHANTOM_KINGSIDE;
            board->bitboard[BLACK_ALL] ^= BLACK_PHANTOM_KINGSIDE;
            board->bitboard[BLACK_ALL] |= board->bitboard[BLACK_ROOK];
        }
        else if (board->castle_flags & BLACK_PHANTOM_KINGS_QUEENSIDE)
        {
            board->castle_flags ^= BLACK_PHANTOM_KINGS_QUEENSIDE;
            board->bitboard[BLACK_KING] ^= BLACK_PHANTOM_QUEENSIDE;
            board->bitboard[BLACK_ALL] ^= BLACK_PHANTOM_QUEENSIDE;
            board->bitboard[BLACK_ALL] |= board->bitboard[BLACK_ROOK];
        }
    }

    switch (move & MOVE_NO_PROMOTION_MASK)
    {
    case NORMAL_MOVE:
        remove_piece(board, MOVE_GET(move, SOURCE), MOVE_GET(move, PIECE));
        add_piece(board, MOVE_GET(move, DEST), MOVE_GET(move, PIECE));
        if ((MOVE_GET(move, PIECE) & PIECE_MASK) == PAWN)
            board->fifty_moves = 0;
        else
            board->fifty_moves++;
        break;

    case CAPTURE_MOVE:
        remove_piece(board, MOVE_GET(move, SOURCE), MOVE_GET(move, PIECE));
        remove_piece(board, MOVE_GET(move, DEST), MOVE_GET(move, CAPTURED));
        add_piece(board, MOVE_GET(move, DEST), MOVE_GET(move, PIECE));
        board->fifty_moves = 0;
        break;

    case CAPTURE_MOVE_EN_PASSANT:
        remove_piece(board, MOVE_GET(move, SOURCE), MOVE_GET(move, PIECE));

        /* Remove the captured pawn. */
        remove_piece(board, MOVE_GET(move, DEST) + (MOVE_GET(move, PIECE) & 1? 8 : -8),
                     MOVE_GET(move, CAPTURED));
        add_piece(board, MOVE_GET(move, DEST), MOVE_GET(move, PIECE));
        board->fifty_moves = 0;
        break;

    case CASTLING_MOVE_KINGSIDE:
        {
            /* We have to move the rook as well. */
            int rook = ROOK + board->current_player;

            remove_piece(board, MOVE_GET(move, SOURCE), MOVE_GET(move, PIECE));
            add_piece(board, MOVE_GET(move, DEST), MOVE_GET(move, PIECE));
            remove_piece(board, MOVE_GET(move, DEST) + 1, rook);
            add_piece(board, MOVE_GET(move, DEST) - 1, rook);

            /* Add phantom kings to detect whether or not
            ** this move is legal.
            */
            if (!board->current_player)
            {
                /* White castling. */
                board->castle_flags |= WHITE_PHANTOM_KINGS_KINGSIDE
                                       | WHITE_HAS_CASTLED;
                board->bitboard[WHITE_KING] |= WHITE_PHANTOM_KINGSIDE;
                board->bitboard[WHITE_ALL] |= WHITE_PHANTOM_KINGSIDE;
            }
            else
            {
                /* Black castling. */
                board->castle_flags |= BLACK_PHANTOM_KINGS_KINGSIDE
                                       | BLACK_HAS_CASTLED;
                board->bitboard[BLACK_KING] |= BLACK_PHANTOM_KINGSIDE;
                board->bitboard[BLACK_ALL] |= BLACK_PHANTOM_KINGSIDE;
            }
            board->fifty_moves++;
            break;
        }
    case CASTLING_MOVE_QUEENSIDE:
        {
            /* We have to move the rook as well. */
            int rook = ROOK + board->current_player;

            remove_piece(board, MOVE_GET(move, SOURCE), MOVE_GET(move, PIECE));
            add_piece(board, MOVE_GET(move, DEST), MOVE_GET(move, PIECE));
            remove_piece(board, MOVE_GET(move, DEST) - 2, rook);
            add_piece(board, MOVE_GET(move, DEST) + 1, rook);

            /* Add phantom kings to detect whether or not
            ** this move is legal.
            */
            if (!board->current_player)
            {
                /* White castling. */
                board->castle_flags |= WHITE_PHANTOM_KINGS_QUEENSIDE
                                       | WHITE_HAS_CASTLED;
                board->bitboard[WHITE_KING] |= WHITE_PHANTOM_QUEENSIDE;
                board->bitboard[WHITE_ALL] |= WHITE_PHANTOM_QUEENSIDE;
            }
            else
            {
                /* Black castling. */
                board->castle_flags |= BLACK_PHANTOM_KINGS_QUEENSIDE
                                       | BLACK_HAS_CASTLED;
                board->bitboard[BLACK_KING] |= BLACK_PHANTOM_QUEENSIDE;
                board->bitboard[BLACK_ALL] |= BLACK_PHANTOM_QUEENSIDE;
            }
            board->fifty_moves++;
            break;
        }
    case RESIGN_MOVE:
        /* Resign or checkmate, do nothing. */
        return;
    case STALEMATE_MOVE:
        /* Stalemate, do nothing. */
        return;
    }

    /* Promotion moves. */
    switch (move & MOVE_PROMOTION_MASK)
    {
    case PROMOTION_MOVE_KNIGHT:
        remove_piece(board, MOVE_GET(move, DEST), MOVE_GET(move, PIECE));
        add_piece(board, MOVE_GET(move, DEST), KNIGHT + board->current_player);
        break;
    case PROMOTION_MOVE_BISHOP:
        remove_piece(board, MOVE_GET(move, DEST), MOVE_GET(move, PIECE));
        add_piece(board, MOVE_GET(move, DEST), BISHOP + board->current_player);
        break;
    case PROMOTION_MOVE_ROOK:
        remove_piece(board, MOVE_GET(move, DEST), MOVE_GET(move, PIECE));
        add_piece(board, MOVE_GET(move, DEST), ROOK + board->current_player);
        break;
    case PROMOTION_MOVE_QUEEN:
        remove_piece(board, MOVE_GET(move, DEST), MOVE_GET(move, PIECE));
        add_piece(board, MOVE_GET(move, DEST), QUEEN + board->current_player);
    }

    /* Reset en passant possibility. */
    if (board->en_passant)
    {
        int square;
        for (square = 0; square < 64; square++)
        {
            if (board->en_passant & square_bit[square])
            {
                board->hash_key ^= ep_hash[square];
                break;
            }
        }
        board->en_passant = 0LL;
    }

    /* Set en passant possibility in case of a double pawn push. */
    if ((MOVE_GET(move, PIECE) == WHITE_PAWN) && (MOVE_GET(move, DEST) -
        MOVE_GET(move, SOURCE) == 16))
    {
        board->en_passant = square_bit[MOVE_GET(move, SOURCE) + 8];
        board->hash_key ^= ep_hash[MOVE_GET(move, SOURCE) + 8];
    }
    else if ((MOVE_GET(move, PIECE) == BLACK_PAWN) && (MOVE_GET(move, SOURCE) -
             MOVE_GET(move, DEST) == 16))
    {
        board->en_passant = square_bit[MOVE_GET(move, DEST) + 8];
        board->hash_key ^= ep_hash[MOVE_GET(move, DEST) + 8];
    }

    /* Set castling possibilities. If the king moves castling is not allowed. */
    switch (MOVE_GET(move, PIECE))
    {
    case WHITE_KING:
        if (board->castle_flags & WHITE_CAN_CASTLE_KINGSIDE)
        {
            board->hash_key ^= castle_hash[SIDE_WHITE + CASTLE_KINGSIDE];
            board->castle_flags ^= WHITE_CAN_CASTLE_KINGSIDE;
        }
        if (board->castle_flags & WHITE_CAN_CASTLE_QUEENSIDE)
        {
            board->hash_key ^= castle_hash[SIDE_WHITE + CASTLE_QUEENSIDE];
            board->castle_flags ^= WHITE_CAN_CASTLE_QUEENSIDE;
        }
        break;
    case BLACK_KING:
        if (board->castle_flags & BLACK_CAN_CASTLE_KINGSIDE)
        {
            board->hash_key ^= castle_hash[SIDE_BLACK + CASTLE_KINGSIDE];
            board->castle_flags ^= BLACK_CAN_CASTLE_KINGSIDE;
        }
        if (board->castle_flags & BLACK_CAN_CASTLE_QUEENSIDE)
        {
            board->hash_key ^= castle_hash[SIDE_BLACK + CASTLE_QUEENSIDE];
            board->castle_flags ^= BLACK_CAN_CASTLE_QUEENSIDE;
        }
    }

    /* Any activety in the corners will make castling impossible, either
    ** because the rook moves, or because it is captured.
    */
    switch (MOVE_GET(move, SOURCE))
    {
    case SQUARE_A1:
        if (board->castle_flags & WHITE_CAN_CASTLE_QUEENSIDE)
        {
            board->hash_key ^= castle_hash[SIDE_WHITE + CASTLE_QUEENSIDE];
            board->castle_flags ^= WHITE_CAN_CASTLE_QUEENSIDE;
        }
        break;
    case SQUARE_H1:
        if (board->castle_flags & WHITE_CAN_CASTLE_KINGSIDE)
        {
            board->hash_key ^= castle_hash[SIDE_WHITE + CASTLE_KINGSIDE];
            board->castle_flags ^= WHITE_CAN_CASTLE_KINGSIDE;
        }
        break;
    case SQUARE_A8:
        if (board->castle_flags & BLACK_CAN_CASTLE_QUEENSIDE)
        {
            board->hash_key ^= castle_hash[SIDE_BLACK + CASTLE_QUEENSIDE];
            board->castle_flags ^= BLACK_CAN_CASTLE_QUEENSIDE;
        }
        break;
    case SQUARE_H8:
        if (board->castle_flags & BLACK_CAN_CASTLE_KINGSIDE)
        {
            board->hash_key ^= castle_hash[SIDE_BLACK + CASTLE_KINGSIDE];
            board->castle_flags ^= BLACK_CAN_CASTLE_KINGSIDE;
        }
    }
    switch (MOVE_GET(move, DEST))
    {
    case SQUARE_A1:
        if (board->castle_flags & WHITE_CAN_CASTLE_QUEENSIDE)
        {
            board->hash_key ^= castle_hash[SIDE_WHITE + CASTLE_QUEENSIDE];
            board->castle_flags ^= WHITE_CAN_CASTLE_QUEENSIDE;
        }
        break;
    case SQUARE_H1:
        if (board->castle_flags & WHITE_CAN_CASTLE_KINGSIDE)
        {
            board->hash_key ^= castle_hash[SIDE_WHITE + CASTLE_KINGSIDE];
            board->castle_flags ^= WHITE_CAN_CASTLE_KINGSIDE;
        }
        break;
    case SQUARE_A8:
        if (board->castle_flags & BLACK_CAN_CASTLE_QUEENSIDE)
        {
            board->hash_key ^= castle_hash[SIDE_BLACK + CASTLE_QUEENSIDE];
            board->castle_flags ^= BLACK_CAN_CASTLE_QUEENSIDE;
        }
        break;
    case SQUARE_H8:
        if (board->castle_flags & BLACK_CAN_CASTLE_KINGSIDE)
        {
            board->hash_key ^= castle_hash[SIDE_BLACK + CASTLE_KINGSIDE];
            board->castle_flags ^= BLACK_CAN_CASTLE_KINGSIDE;
        }
    }

    /* Switch players. */
    board->current_player = OPPONENT(board->current_player);
    board->hash_key ^= black_to_move;
}

void unmake_move(board_t *board, move_t move, bitboard_t
                 old_en_passant,                int
                 old_castle_flags, int old_fifty_moves)
{
    int castle_diff;

    /* Switch players. */
    board->current_player = OPPONENT(board->current_player);
    board->hash_key ^= black_to_move;

    /* Promotion moves. */
    switch (move & MOVE_PROMOTION_MASK)
    {
    case PROMOTION_MOVE_KNIGHT:
        remove_piece(board, MOVE_GET(move, DEST), KNIGHT + board->current_player);
        add_piece(board, MOVE_GET(move, DEST), MOVE_GET(move, PIECE));
        break;
    case PROMOTION_MOVE_BISHOP:
        remove_piece(board, MOVE_GET(move, DEST), BISHOP + board->current_player);
        add_piece(board, MOVE_GET(move, DEST), MOVE_GET(move, PIECE));
        break;
    case PROMOTION_MOVE_ROOK:
        remove_piece(board, MOVE_GET(move, DEST), ROOK + board->current_player);
        add_piece(board, MOVE_GET(move, DEST), MOVE_GET(move, PIECE));
        break;
    case PROMOTION_MOVE_QUEEN:
        remove_piece(board, MOVE_GET(move, DEST), QUEEN + board->current_player);
        add_piece(board, MOVE_GET(move, DEST), MOVE_GET(move, PIECE));
    }

    switch (move & MOVE_NO_PROMOTION_MASK)
    {
    case NORMAL_MOVE:
        remove_piece(board, MOVE_GET(move, DEST), MOVE_GET(move, PIECE));
        add_piece(board, MOVE_GET(move, SOURCE), MOVE_GET(move, PIECE));
        break;

    case CAPTURE_MOVE:
        remove_piece(board, MOVE_GET(move, DEST), MOVE_GET(move, PIECE));
        add_piece(board, MOVE_GET(move, DEST), MOVE_GET(move, CAPTURED));
        add_piece(board, MOVE_GET(move, SOURCE), MOVE_GET(move, PIECE));
        break;

    case CAPTURE_MOVE_EN_PASSANT:
        remove_piece(board, MOVE_GET(move, DEST), MOVE_GET(move, PIECE));
        add_piece(board, MOVE_GET(move, DEST) + (MOVE_GET(move, PIECE) & 1? 8 : -8),
                  MOVE_GET(move, CAPTURED));
        add_piece(board, MOVE_GET(move, SOURCE), MOVE_GET(move, PIECE));
        break;

    case CASTLING_MOVE_KINGSIDE:
        {
            /* We have to move the rook as well. */
            int rook = ROOK + board->current_player;

            /* Remove the phantom kings. */
            if (!board->current_player)
            {
                board->bitboard[WHITE_KING] ^= WHITE_PHANTOM_KINGSIDE;
                board->bitboard[WHITE_ALL] ^= WHITE_PHANTOM_KINGSIDE;

                /* Put back the rook. */
                board->bitboard[WHITE_ALL] |= board->bitboard[WHITE_ROOK];
                board->castle_flags ^= WHITE_HAS_CASTLED
                                       | WHITE_PHANTOM_KINGS_KINGSIDE;
            }
            else
            {
                board->bitboard[BLACK_KING] ^= BLACK_PHANTOM_KINGSIDE;
                board->bitboard[BLACK_ALL] ^= BLACK_PHANTOM_KINGSIDE;

                /* Put back the rook. */
                board->bitboard[BLACK_ALL] |= board->bitboard[BLACK_ROOK];
                board->castle_flags ^= BLACK_HAS_CASTLED
                                       | BLACK_PHANTOM_KINGS_KINGSIDE;
            }

            remove_piece(board, MOVE_GET(move, DEST) - 1, rook);
            add_piece(board, MOVE_GET(move, DEST) + 1, rook);
            remove_piece(board, MOVE_GET(move, DEST), MOVE_GET(move, PIECE));
            add_piece(board, MOVE_GET(move, SOURCE), MOVE_GET(move, PIECE));

            break;
        }
    case CASTLING_MOVE_QUEENSIDE:
        {
            /* We have to move the rook as well. */
            int rook = ROOK + board->current_player;

            /* Remove the phantom kings. */
            if (!board->current_player)
            {
                board->bitboard[WHITE_KING] ^= WHITE_PHANTOM_QUEENSIDE;
                board->bitboard[WHITE_ALL] ^= WHITE_PHANTOM_QUEENSIDE;

                /* Put back the rook. */
                board->bitboard[WHITE_ALL] |= board->bitboard[WHITE_ROOK];
                board->castle_flags ^= WHITE_HAS_CASTLED
                                       | WHITE_PHANTOM_KINGS_QUEENSIDE;
            }
            else
            {
                board->bitboard[BLACK_KING] ^= BLACK_PHANTOM_QUEENSIDE;
                board->bitboard[BLACK_ALL] ^= BLACK_PHANTOM_QUEENSIDE;

                /* Put back the rook. */
                board->bitboard[BLACK_ALL] |= board->bitboard[BLACK_ROOK];
                board->castle_flags ^= BLACK_HAS_CASTLED
                                       | BLACK_PHANTOM_KINGS_QUEENSIDE;
            }

            remove_piece(board, MOVE_GET(move, DEST) + 1, rook);
            add_piece(board, MOVE_GET(move, DEST) - 2, rook);
            remove_piece(board, MOVE_GET(move, DEST), MOVE_GET(move, PIECE));
            add_piece(board, MOVE_GET(move, SOURCE), MOVE_GET(move, PIECE));

            break;
        }
    case RESIGN_MOVE:
        /* Resign or checkmate, do nothing. */
        return;
    case STALEMATE_MOVE:
        /* Stalemate, do nothing. */
        return;
    }

    /* Restore en passant possibility. */
    if (board->en_passant || old_en_passant)
    {
        int square;
        for (square = 0; square < 64; square++)
        {
            if (board->en_passant & square_bit[square])
                board->hash_key ^= ep_hash[square];
            if (old_en_passant & square_bit[square])
                board->hash_key ^= ep_hash[square];
        }
        board->en_passant = old_en_passant;
    }

    castle_diff = board->castle_flags ^ old_castle_flags;

    /* Restore phantom kings. */
    switch ((castle_diff & PHANTOM_FLAGS) & old_castle_flags)
    {
    case WHITE_PHANTOM_KINGS_KINGSIDE:
        board->castle_flags |= WHITE_PHANTOM_KINGS_KINGSIDE;
        board->bitboard[WHITE_KING] |= WHITE_PHANTOM_KINGSIDE;
        board->bitboard[WHITE_ALL] |= WHITE_PHANTOM_KINGSIDE;
        break;

    case WHITE_PHANTOM_KINGS_QUEENSIDE:
        board->castle_flags |= WHITE_PHANTOM_KINGS_QUEENSIDE;
        board->bitboard[WHITE_KING] |= WHITE_PHANTOM_QUEENSIDE;
        board->bitboard[WHITE_ALL] |= WHITE_PHANTOM_QUEENSIDE;
        break;

    case BLACK_PHANTOM_KINGS_KINGSIDE:
        board->castle_flags |= BLACK_PHANTOM_KINGS_KINGSIDE;
        board->bitboard[BLACK_KING] |= BLACK_PHANTOM_KINGSIDE;
        board->bitboard[BLACK_ALL] |= BLACK_PHANTOM_KINGSIDE;
        break;

    case BLACK_PHANTOM_KINGS_QUEENSIDE:
        board->castle_flags |= BLACK_PHANTOM_KINGS_QUEENSIDE;
        board->bitboard[BLACK_KING] |= BLACK_PHANTOM_QUEENSIDE;
        board->bitboard[BLACK_ALL] |= BLACK_PHANTOM_QUEENSIDE;
        break;
    }

    /* Restore castle flags. */
    if (castle_diff & 15)
    {
        int i;
        for (i = 0; i < 4; i++)
            if (castle_diff & (1 << i))
                board->hash_key ^= castle_hash[i];
        board->castle_flags = old_castle_flags;
    }
    board->fifty_moves = old_fifty_moves;
}
