
// attack.cpp

// includes

#include "attack.h"
#include "board.h"
#include "colour.h"
#include "move.h"
#include "piece.h"
#include "util.h"
#include "vector.h"

// variables

int DeltaIncLine[DeltaNb];
int DeltaIncAll[DeltaNb];

int DeltaMask[DeltaNb];
int IncMask[IncNb];

static int PieceCode[PieceNb];

static int PieceDeltaSize[4][256]; // 4kB
static int PieceDeltaDelta[4][256][4]; // 16 kB

// prototypes

static void add_attack (int piece, int king, int target);

// functions

// attack_init()

void attack_init() {

    int delta, inc;
    int piece;
    int dir, dist;
    int size;
    int king;
    int from, to;
    int pos;

    // clear

    for (delta = 0; delta < DeltaNb; delta++) {
        DeltaIncLine[delta] = IncNone;
        DeltaIncAll[delta] = IncNone;
        DeltaMask[delta] = 0;
    }

    for (inc = 0; inc < IncNb; inc++) {
        IncMask[inc] = 0;
    }

    // pawn attacks

    DeltaMask[DeltaOffset-17] |= BlackPawnFlag;
    DeltaMask[DeltaOffset-15] |= BlackPawnFlag;

    DeltaMask[DeltaOffset+15] |= WhitePawnFlag;
    DeltaMask[DeltaOffset+17] |= WhitePawnFlag;

    // knight attacks

    for (dir = 0; dir < 8; dir++) {

        delta = KnightInc[dir];
        ASSERT(delta_is_ok(delta));

        ASSERT(DeltaIncAll[DeltaOffset+delta]==IncNone);
        DeltaIncAll[DeltaOffset+delta] = delta;
        DeltaMask[DeltaOffset+delta] |= KnightFlag;
    }

    // bishop/queen attacks

    for (dir = 0; dir < 4; dir++) {

        inc = BishopInc[dir];
        ASSERT(inc!=IncNone);

        IncMask[IncOffset+inc] |= BishopFlag;

        for (dist = 1; dist < 8; dist++) {

            delta = inc*dist;
            ASSERT(delta_is_ok(delta));

            ASSERT(DeltaIncLine[DeltaOffset+delta]==IncNone);
            DeltaIncLine[DeltaOffset+delta] = inc;
            ASSERT(DeltaIncAll[DeltaOffset+delta]==IncNone);
            DeltaIncAll[DeltaOffset+delta] = inc;
            DeltaMask[DeltaOffset+delta] |= BishopFlag;
        }
    }

    // rook/queen attacks

    for (dir = 0; dir < 4; dir++) {

        inc = RookInc[dir];
        ASSERT(inc!=IncNone);

        IncMask[IncOffset+inc] |= RookFlag;

        for (dist = 1; dist < 8; dist++) {

            delta = inc*dist;
            ASSERT(delta_is_ok(delta));

            ASSERT(DeltaIncLine[DeltaOffset+delta]==IncNone);
            DeltaIncLine[DeltaOffset+delta] = inc;
            ASSERT(DeltaIncAll[DeltaOffset+delta]==IncNone);
            DeltaIncAll[DeltaOffset+delta] = inc;
            DeltaMask[DeltaOffset+delta] |= RookFlag;
        }
    }

    // king attacks

    for (dir = 0; dir < 8; dir++) {

        delta = KingInc[dir];
        ASSERT(delta_is_ok(delta));

        DeltaMask[DeltaOffset+delta] |= KingFlag;
    }

    // PieceCode[]

    for (piece = 0; piece < PieceNb; piece++) {
        PieceCode[piece] = -1;
    }

    PieceCode[WN] = 0;
    PieceCode[WB] = 1;
    PieceCode[WR] = 2;
    PieceCode[WQ] = 3;

    PieceCode[BN] = 0;
    PieceCode[BB] = 1;
    PieceCode[BR] = 2;
    PieceCode[BQ] = 3;

    // PieceDeltaSize[][] & PieceDeltaDelta[][][]

    for (piece = 0; piece < 4; piece++) {
        for (delta = 0; delta < 256; delta++) {
            PieceDeltaSize[piece][delta] = 0;
        }
    }

    for (king = 0; king < SquareNb; king++) {

        if (SQUARE_IS_OK(king)) {

            for (from = 0; from < SquareNb; from++) {

                if (SQUARE_IS_OK(from)) {

                    // knight

                    for (pos = 0; (inc=KnightInc[pos]) != IncNone; pos++) {
                        to = from + inc;
                        if (SQUARE_IS_OK(to) && DISTANCE(to,king) == 1) {
                            add_attack(0,king-from,to-from);
                        }
                    }

                    // bishop

                    for (pos = 0; (inc=BishopInc[pos]) != IncNone; pos++) {
                        for (to = from+inc; SQUARE_IS_OK(to); to += inc) {
                            if (DISTANCE(to,king) == 1) {
                                add_attack(1,king-from,to-from);
                                break;
                            }
                        }
                    }

                    // rook

                    for (pos = 0; (inc=RookInc[pos]) != IncNone; pos++) {
                        for (to = from+inc; SQUARE_IS_OK(to); to += inc) {
                            if (DISTANCE(to,king) == 1) {
                                add_attack(2,king-from,to-from);
                                break;
                            }
                        }
                    }

                    // queen

                    for (pos = 0; (inc=QueenInc[pos]) != IncNone; pos++) {
                        for (to = from+inc; SQUARE_IS_OK(to); to += inc) {
                            if (DISTANCE(to,king) == 1) {
                                add_attack(3,king-from,to-from);
                                break;
                            }
                        }
                    }
                }
            }
        }
    }

    for (piece = 0; piece < 4; piece++) {
        for (delta = 0; delta < 256; delta++) {
            size = PieceDeltaSize[piece][delta];
            ASSERT(size>=0&&size<3);
            PieceDeltaDelta[piece][delta][size] = DeltaNone;
        }
    }
}

// add_attack()

static void add_attack(int piece, int king, int target) {

    int size;
    int i;

    ASSERT(piece>=0&&piece<4);
    ASSERT(delta_is_ok(king));
    ASSERT(delta_is_ok(target));

    size = PieceDeltaSize[piece][DeltaOffset+king];
    ASSERT(size>=0&&size<3);

    for (i = 0; i < size; i++) {
        if (PieceDeltaDelta[piece][DeltaOffset+king][i] == target) return; // already in the table
    }

    if (size < 2) {
        PieceDeltaDelta[piece][DeltaOffset+king][size] = target;
        size++;
        PieceDeltaSize[piece][DeltaOffset+king] = size;
    }
}

// is_attacked()

bool is_attacked(const board_t * board, int to, int colour) {

    int inc;
    int pawn;
    const sq_t * ptr;
    int from;
    int piece;
    int delta;
    int sq;

    ASSERT(board!=NULL);
    ASSERT(SQUARE_IS_OK(to));
    ASSERT(COLOUR_IS_OK(colour));

    // pawn attack

    inc = PAWN_MOVE_INC(colour);
    pawn = PAWN_MAKE(colour);

    if (board->square[to-(inc-1)] == pawn) return true;
    if (board->square[to-(inc+1)] == pawn) return true;

    // piece attack

    for (ptr = &board->piece[colour][0]; (from=*ptr) != SquareNone; ptr++) {

        piece = board->square[from];
        delta = to - from;

        if (PSEUDO_ATTACK(piece,delta)) {

            inc = DELTA_INC_ALL(delta);
            ASSERT(inc!=IncNone);

            sq = from;
            do {
                sq += inc;
                if (sq == to) return true;
            } while (board->square[sq] == Empty);
        }
    }

    return false;
}

// line_is_empty()

bool line_is_empty(const board_t * board, int from, int to) {

    int delta;
    int inc, sq;

    ASSERT(board!=NULL);
    ASSERT(SQUARE_IS_OK(from));
    ASSERT(SQUARE_IS_OK(to));

    delta = to - from;
    ASSERT(delta_is_ok(delta));

    inc = DELTA_INC_ALL(delta);
    ASSERT(inc!=IncNone);

    sq = from;
    do {
        sq += inc;
        if (sq == to) return true;
    } while (board->square[sq] == Empty);

    return false; // blocker
}

// is_pinned()

bool is_pinned(const board_t * board, int square, int colour) {

    int from, to;
    int inc;
    int sq, piece;

    ASSERT(board!=NULL);
    ASSERT(SQUARE_IS_OK(square));
    ASSERT(COLOUR_IS_OK(colour));

    from = square;
    to = KING_POS(board,colour);

    inc = DELTA_INC_LINE(to-from);
    if (inc == IncNone) return false; // not a line

    sq = from;
    do sq += inc;
    while (board->square[sq] == Empty);

    if (sq != to) return false; // blocker

    sq = from;
    do sq -= inc;
    while ((piece=board->square[sq]) == Empty);

    return COLOUR_IS(piece,COLOUR_OPP(colour)) && SLIDER_ATTACK(piece,inc);
}

// attack_is_ok()

bool attack_is_ok(const attack_t * attack) {

    int i;
    int sq, inc;

    if (attack == NULL) return false;

    // checks

    if (attack->dn < 0 || attack->dn > 2) return false;

    for (i = 0; i < attack->dn; i++) {
        sq = attack->ds[i];
        if (!SQUARE_IS_OK(sq)) return false;
        inc = attack->di[i];
        if (inc != IncNone && !inc_is_ok(inc)) return false;
    }

    if (attack->ds[attack->dn] != SquareNone) return false;
    if (attack->di[attack->dn] != IncNone) return false;

    return true;
}

// attack_set()

void attack_set(attack_t * attack, const board_t * board) {

    int me, opp;
    const sq_t * ptr;
    int from, to;
    int inc;
    int pawn;
    int delta, piece;
    int sq;

    ASSERT(attack!=NULL);
    ASSERT(board!=NULL);

    // init

    attack->dn = 0;

    me = board->turn;
    opp = COLOUR_OPP(me);

    to = KING_POS(board,me);

    // pawn attacks

    inc = PAWN_MOVE_INC(opp);
    pawn = PAWN_MAKE(opp);

    from = to - (inc-1);
    if (board->square[from] == pawn) {
        attack->ds[attack->dn] = from;
        attack->di[attack->dn] = IncNone;
        attack->dn++;
    }

    from = to - (inc+1);
    if (board->square[from] == pawn) {
        attack->ds[attack->dn] = from;
        attack->di[attack->dn] = IncNone;
        attack->dn++;
    }

    // piece attacks

    for (ptr = &board->piece[opp][1]; (from=*ptr) != SquareNone; ptr++) { // HACK: no king

        piece = board->square[from];

        delta = to - from;
        ASSERT(delta_is_ok(delta));

        if (PSEUDO_ATTACK(piece,delta)) {

            inc = IncNone;

            if (PIECE_IS_SLIDER(piece)) {

                // check for blockers

                inc = DELTA_INC_LINE(delta);
                ASSERT(inc!=IncNone);

                sq = from;
                do sq += inc;
                while (board->square[sq] == Empty);

                if (sq != to) continue; // blocker => next attacker
            }

            attack->ds[attack->dn] = from;
            attack->di[attack->dn] = -inc; // HACK
            attack->dn++;
        }
    }

    attack->ds[attack->dn] = SquareNone;
    attack->di[attack->dn] = IncNone;

    // debug

    ASSERT(attack_is_ok(attack));
}

// piece_attack_king()

bool piece_attack_king(const board_t * board, int piece, int from, int king) {

    const inc_t * inc_ptr;
    int code;
    const int * delta_ptr;
    int delta, inc;
    int to;
    int sq;

    ASSERT(board!=NULL);
    ASSERT(piece_is_ok(piece));
    ASSERT(SQUARE_IS_OK(from));
    ASSERT(SQUARE_IS_OK(king));

    inc_ptr = PIECE_INC(piece);

    code = PieceCode[piece];
    ASSERT(code>=0&&code<4);

    if (PIECE_IS_SLIDER(piece)) {

        for (delta_ptr = PieceDeltaDelta[code][DeltaOffset+(king-from)]; (delta=*delta_ptr) != DeltaNone; delta_ptr++) {

            ASSERT(delta_is_ok(delta));

            inc = DeltaIncLine[DeltaOffset+delta];
            ASSERT(inc!=IncNone);

            to = from + delta;

            sq = from;
            do {
                sq += inc;
                if (sq == to && SQUARE_IS_OK(to)) {
                    ASSERT(DISTANCE(to,king)==1);
                    return true;
                }
            } while (board->square[sq] == Empty);
        }

    } else { // non-slider

        for (delta_ptr = PieceDeltaDelta[code][DeltaOffset+(king-from)]; (delta=*delta_ptr) != DeltaNone; delta_ptr++) {

            ASSERT(delta_is_ok(delta));

            to = from + delta;

            if (SQUARE_IS_OK(to)) {
                ASSERT(DISTANCE(to,king)==1);
                return true;
            }
        }
    }

    return false;
}

// end of attack.cpp

