/*----------------------------------------------------------------------
 | MOVE.C							940426
 |
 | Contains all move procedures.
 +----------------------------------------------------------------------*/


#include "var.h"


static void     CheckRedundancies(void);


/*----------------------------------------------------------------------
 | DoMove							940426
 |
 | Performs a move internally and updates the board. Color to move
 | (toMove) is updated.
 +----------------------------------------------------------------------*/
void            DoMove(MoveType * move)
{
    BoardIndexType  square,
                    square1,
                    square2;
    SquareType      piece;
    Signed2        *evalTable;
    HashType       *hashTable;

    lastMovedPieceSquare = move->to;
    if (maxGamePlies > nrGamePlies &&
        EqualMoves(move, &(game[nrGamePlies]))) {
        ++nrGamePlies;
    } else {
        SPrintMove(gameString[nrGamePlies], *move);
        game[nrGamePlies++] = *move;
        maxGamePlies = nrGamePlies;
    }

    castling[nrGamePlies] = castling[nrGamePlies - 1];  /* Update castling and */
    HashIs(hashValue[nrGamePlies], hashValue[nrGamePlies - 1]); /* hash and */
    startHashIndex[nrGamePlies] = startHashIndex[nrGamePlies - 1];      /* index. */

    piece = board[move->from];

    /* Converting move? */
    if (IsPawn(piece) || move->capturedPiece != 0) {
        startHashIndex[nrGamePlies] = nrGamePlies;
    }
    GetEvalTable(evalTable, toMove, piece);
    positionalValue[toMove] += evalTable[move->to] - evalTable[move->from];
    GetHashTable(hashTable, toMove, piece);
    HashXorIs(hashValue[nrGamePlies], hashTable[move->from]);
    HashXorIs(hashValue[nrGamePlies], hashTable[move->to]);

    square = Square(H, 1);
    if (move->from == square || move->to == square) {
        if (castling[nrGamePlies] & WhiteShort) {
            castling[nrGamePlies] &= ~WhiteShort;
            HashXorIs(hashValue[nrGamePlies],
                      castlingHashTable[White][Short]);
        }
    }                           /* White short */
    square = Square(A, 1);
    if (move->from == square || move->to == square) {
        if (castling[nrGamePlies] & WhiteLong) {
            castling[nrGamePlies] &= ~WhiteLong;
            HashXorIs(hashValue[nrGamePlies],
                      castlingHashTable[White][Long]);
        }
    }                           /* White long */
    square = Square(H, 8);
    if (move->from == square || move->to == square) {
        if (castling[nrGamePlies] & BlackShort) {
            castling[nrGamePlies] &= ~BlackShort;
            HashXorIs(hashValue[nrGamePlies],
                      castlingHashTable[Black][Short]);
        }
    }                           /* Black short */
    square = Square(A, 8);
    if (move->from == square || move->to == square) {
        if (castling[nrGamePlies] & BlackLong) {
            castling[nrGamePlies] &= ~BlackLong;
            HashXorIs(hashValue[nrGamePlies],
                      castlingHashTable[Black][Long]);
        }
    }                           /* Black long */
    if (IsKing(piece)) {
        kingSquare[toMove] = move->to;
        if (toMove == White) {
            if (castling[nrGamePlies] & WhiteShort) {
                castling[nrGamePlies] &= ~WhiteShort;
                HashXorIs(hashValue[nrGamePlies],
                          castlingHashTable[White][Short]);
            }
            if (castling[nrGamePlies] & WhiteLong) {
                castling[nrGamePlies] &= ~WhiteLong;
                HashXorIs(hashValue[nrGamePlies],
                          castlingHashTable[White][Long]);
            }
        } else {
            if (castling[nrGamePlies] & BlackShort) {
                castling[nrGamePlies] &= ~BlackShort;
                HashXorIs(hashValue[nrGamePlies],
                          castlingHashTable[Black][Short]);
            }
            if (castling[nrGamePlies] & BlackLong) {
                castling[nrGamePlies] &= ~BlackLong;
                HashXorIs(hashValue[nrGamePlies],
                          castlingHashTable[Black][Long]);
            }
        }
    }
    /* En passant */
    if (IsEnPassantMove(move->type)) {
        square = pawnMoveTable[!toMove][move->to][0];
        material[!toMove] -= pieceValue[board[square]];
        pieces[!toMove][pieceIndex[square]] =
            pieces[!toMove][--nrPieces[!toMove]];
        pieceIndex[pieces[!toMove][pieceIndex[square]].square] =
            pieceIndex[square];
        board[square] = Empty;
        positionalValue[!toMove] -= pawnEvalTable[!toMove][square];
        HashXorIs(hashValue[nrGamePlies], pawnHashTable[!toMove][square]);
        /* Capture */
    } else if (IsCaptureMove(move->type)) {
        material[!toMove] -= pieceValue[board[move->to]];
        pieces[!toMove][pieceIndex[move->to]] =
            pieces[!toMove][--nrPieces[!toMove]];
        pieceIndex[pieces[!toMove][pieceIndex[move->to]].square] =
            pieceIndex[move->to];
        GetEvalTable(evalTable, !toMove, board[move->to]);
        positionalValue[!toMove] -= evalTable[move->to];
        GetHashTable(hashTable, !toMove, board[move->to]);
        HashXorIs(hashValue[nrGamePlies], hashTable[move->to]);
    }
    /* Castling */
    if (IsShortCastleMove(move->type)) {
        square1 = Square(H, toMove == White ? 1 : 8);
        square2 = Square(F, toMove == White ? 1 : 8);
        board[square2] = board[square1];
        board[square1] = Empty;
        pieces[toMove][pieceIndex[square1]].square = square2;
        pieceIndex[square2] = pieceIndex[square1];
        pieceIndex[square1] = 0;
        positionalValue[toMove] += rookEvalTable[toMove][square2] -
            rookEvalTable[toMove][square1];
        HashXorIs(hashValue[nrGamePlies], rookHashTable[toMove][square2]);
        HashXorIs(hashValue[nrGamePlies], rookHashTable[toMove][square1]);
    } else if (IsLongCastleMove(move->type)) {
        square1 = Square(A, toMove == White ? 1 : 8);
        square2 = Square(D, toMove == White ? 1 : 8);
        board[square2] = board[square1];
        board[square1] = Empty;
        pieces[toMove][pieceIndex[square1]].square = square2;
        pieceIndex[square2] = pieceIndex[square1];
        pieceIndex[square1] = 0;
        positionalValue[toMove] += rookEvalTable[toMove][square2] -
            rookEvalTable[toMove][square1];
        HashXorIs(hashValue[nrGamePlies], rookHashTable[toMove][square2]);
        HashXorIs(hashValue[nrGamePlies], rookHashTable[toMove][square1]);
    }
    /* Promotion */
    if (IsPromotionMove(move->type)) {
        material[toMove] -= pieceValue[piece];
        board[move->from] = move->promotionPiece;
        material[toMove] += pieceValue[board[move->from]];
        pieces[toMove][pieceIndex[move->from]].type =
            move->promotionPiece;
        GetEvalTable(evalTable, toMove, move->promotionPiece);
        positionalValue[toMove] += evalTable[move->to] -
            pawnEvalTable[toMove][move->to];
        GetHashTable(hashTable, toMove, move->promotionPiece);
        HashXorIs(hashValue[nrGamePlies], hashTable[move->to]);
        HashXorIs(hashValue[nrGamePlies], pawnHashTable[toMove][move->to]);
    }
    board[move->to] = board[move->from];
    board[move->from] = Empty;
    pieces[toMove][pieceIndex[move->from]].square = move->to;
    pieceIndex[move->to] = pieceIndex[move->from];
    pieceIndex[move->from] = 0;
    if (IsDoubleMove(move->type)) {
        epSquare[nrGamePlies] = pawnMoveTable[toMove][move->from][0];
        HashXorIs(hashValue[nrGamePlies], enPassantHashTable[File(move->from)]);
    } else {
        epSquare[nrGamePlies] = NoEp;
    }
    if (epSquare[nrGamePlies - 1] != NoEp && nrGamePlies > 0) {
        HashXorIs(hashValue[nrGamePlies],
                  enPassantHashTable[File(epSquare[nrGamePlies - 1])]);
    }
    HashXorIs(hashValue[nrGamePlies], movedHash);
    toMove = !toMove;
#ifdef Debug
    CheckRedundancies();
#endif
}                               /* DoMove */


/*----------------------------------------------------------------------
 | UndoMove							940426
 |
 | Takes back a move internally and updates the board. Color to move
 | (toMove) is updated.
 +----------------------------------------------------------------------*/
void            UndoMove(void)
{
    BoardIndexType  square,
                    square1,
                    square2;
    MoveType       *move;
    SquareType      piece;
    Signed2        *table;

    move = &(game[--nrGamePlies]);
    if (nrGamePlies > 0) {
        lastMovedPieceSquare = game[nrGamePlies - 1].to;
    } else {
        lastMovedPieceSquare = 0;
    }
    toMove = !toMove;
    pieces[toMove][pieceIndex[move->to]].square = move->from;
    pieceIndex[move->from] = pieceIndex[move->to];
    pieceIndex[move->to] = 0;
    board[move->from] = board[move->to];
    board[move->to] = Empty;
    piece = board[move->from];
    if (IsKing(piece)) {
        kingSquare[toMove] = move->from;
    }
    /* Promotion */
    if (IsPromotionMove(move->type)) {
        material[toMove] -= pieceValue[board[move->from]];
        board[move->from] = Pawn | toMove;
        material[toMove] += pieceValue[board[move->from]];
        pieces[toMove][pieceIndex[move->from]].type = Pawn | toMove;
        GetEvalTable(table, toMove, piece);
        positionalValue[toMove] -= table[move->to] -
            pawnEvalTable[toMove][move->to];
        piece = Pawn | toMove;
    }
    /* Castling */
    if (IsShortCastleMove(move->type)) {
        square2 = Square(H, toMove == White ? 1 : 8);
        square1 = Square(F, toMove == White ? 1 : 8);
        board[square2] = board[square1];
        board[square1] = Empty;
        pieces[toMove][pieceIndex[square1]].square = square2;
        pieceIndex[square2] = pieceIndex[square1];
        pieceIndex[square1] = 0;
        positionalValue[toMove] -= rookEvalTable[toMove][square1] -
            rookEvalTable[toMove][square2];
    } else if (IsLongCastleMove(move->type)) {
        square2 = Square(A, toMove == White ? 1 : 8);
        square1 = Square(D, toMove == White ? 1 : 8);
        board[square2] = board[square1];
        board[square1] = Empty;
        pieces[toMove][pieceIndex[square1]].square = square2;
        pieceIndex[square2] = pieceIndex[square1];
        pieceIndex[square1] = 0;
        positionalValue[toMove] -= rookEvalTable[toMove][square1] -
            rookEvalTable[toMove][square2];
    }
    /* En passant */
    if (IsEnPassantMove(move->type)) {
        square = pawnMoveTable[!toMove][move->to][0];
        material[!toMove] += pieceValue[move->capturedPiece];
        pieceIndex[square] = nrPieces[!toMove]++;
        pieces[!toMove][pieceIndex[square]].type = move->capturedPiece;
        pieces[!toMove][pieceIndex[square]].square = square;
        board[square] = move->capturedPiece;
        positionalValue[!toMove] += pawnEvalTable[!toMove][square];
        /* Capture */
    } else if (IsCaptureMove(move->type)) {
        material[!toMove] += pieceValue[move->capturedPiece];
        pieceIndex[move->to] = nrPieces[!toMove]++;
        pieces[!toMove][pieceIndex[move->to]].type = move->capturedPiece;
        pieces[!toMove][pieceIndex[move->to]].square = move->to;
        board[move->to] = move->capturedPiece;
        GetEvalTable(table, !toMove, move->capturedPiece);
        positionalValue[!toMove] += table[move->to];
    }
    GetEvalTable(table, toMove, piece);
    positionalValue[toMove] -= table[move->to] - table[move->from];
}                               /* UndoMove */


/*----------------------------------------------------------------------
 | CheckRedundancies						940427
 |
 | Checks if the board, pieces and pieceIndex are consistent.
 +----------------------------------------------------------------------*/
static void     CheckRedundancies(void)
{
    Signed2         i,
                    j;
    BoardIndexType  square;
    char            s[MaxCommandLength];

    for (i = A; i <= H; i++) {
        for (j = 1; j <= 8; j++) {
            square = Square(i, j);
            SPrintSquare(s, square);
            if (!IsEmpty(board[square])) {
                if (pieceIndex[square] >= nrPieces[Color(board[square])]) {
                    printf("ERROR in nrPieces on square %s\n", s);
                }
                if (pieces[Color(board[square])][pieceIndex[square]].type
                    != board[square]) {
                    printf("ERROR in type on square %s\n", s);
                }
                if (pieces[Color(board[square])][pieceIndex[square]].square
                    != square) {
                    printf("ERROR in square on square %s\n", s);
                }
            }
        }
    }
}                               /* CheckRedundancies */
