
// material.cpp

// includes

#include <cstring>

#include "board.h"
#include "colour.h"
#include "hash.h"
#include "material.h"
#include "option.h"
#include "piece.h"
#include "protocol.h"
#include "square.h"
#include "util.h"



// constants

static const uint32 TableSize = 256; // 4kB

static const int PawnPhase   = 0;
static const int KnightPhase = 1;
static const int BishopPhase = 1;
static const int RookPhase   = 2;
static const int QueenPhase  = 4;

static const int TotalPhase = PawnPhase * 16 + KnightPhase * 4 + BishopPhase * 4 + RookPhase * 4 + QueenPhase * 2;

// constants and variables

#define ABS(x) ((x)<0?-(x):(x))

//static /* const */ int MaterialWeight = 256; // 100%

static /*const*/ int PawnOpening   = 80; // was 100
static /*const*/ int PawnEndgame   = 90; // was 100
static /*const*/ int KnightOpening = 320;
static /*const*/ int KnightEndgame = 320;
static /*const*/ int BishopOpening = 325;
static /*const*/ int BishopEndgame = 325;
static /*const*/ int RookOpening   = 500;
static /*const*/ int RookEndgame   = 500;
static /*const*/ int QueenOpening  = 975;
static /*const*/ int QueenEndgame  = 975;

static /*const*/ int BishopPairOpening = 50;
static /*const*/ int BishopPairEndgame = 50;

static /*const*/ int Queen_Knight_combo = 15; // with no rooks
static /*const*/ int Rook_Bishop_combo = 15;  // with no queens
static /*const*/ int bad_trade_value = 50; // not used like in crafty... (will be for 3 minors vs queen)

static int bitbase_pieces = 2;

static const int RookPawnPenalty[17] = { 15,15,13,11,9,7,5,3,1,-1,-3,-5,-7,-9,-11,-13,-15 };

// types

typedef material_info_t entry_t;

struct material_t {
    entry_t * table;
    uint32 size;
    uint32 mask;
    uint32 used;
    sint64 read_nb;
    sint64 read_hit;
    sint64 write_nb;
    sint64 write_collision;
};

// variables

static material_t Material[1];

// prototypes

static void material_comp_info (material_info_t * info, const board_t * board);

// functions

// material_init()

void material_init() {

    // UCI options

    bitbase_pieces =  (option_get_int("Bitbase pieces") -2);

    //MaterialWeight = (option_get_int("Material") * 256 + 50) / 100;
    bad_trade_value = option_get_int("Bad Trade Value");

    BishopPairOpening = option_get_int("Bishop Pair Opening");
    BishopPairEndgame = option_get_int("Bishop Pair Endgame");

    Queen_Knight_combo = option_get_int("Queen Knight combo");
    Rook_Bishop_combo = option_get_int("Rook Bishop combo");

    PawnOpening = option_get_int("Opening Pawn Value");
    KnightOpening = option_get_int("Opening Knight Value");
    BishopOpening = option_get_int("Opening Bishop Value");
    RookOpening = option_get_int("Opening Rook Value");
    QueenOpening = option_get_int("Opening Queen Value");

    PawnEndgame = option_get_int("Endgame Pawn Value");
    KnightEndgame = option_get_int("Endgame Knight Value");
    BishopEndgame = option_get_int("Endgame Bishop Value");
    RookEndgame = option_get_int("Endgame Rook Value");
    QueenEndgame = option_get_int("Endgame Queen Value");

    // material table

    Material->size = 0;
    Material->mask = 0;
    Material->table = NULL;
}

// material_alloc()

void material_alloc() {

    ASSERT(sizeof(entry_t)==16);


    Material->size = TableSize;
    Material->mask = TableSize - 1;
    Material->table = (entry_t *) my_malloc(Material->size*sizeof(entry_t));

    material_clear();

}

// material_clear()

void material_clear() {

    if (Material->table != NULL) {
        memset(Material->table,0,Material->size*sizeof(entry_t));
    }

    Material->used = 0;
    Material->read_nb = 0;
    Material->read_hit = 0;
    Material->write_nb = 0;
    Material->write_collision = 0;
}

// material_get_info()

void material_get_info(material_info_t * info, const board_t * board) {

    uint64 key;
    entry_t * entry;

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

    // probe


    Material->read_nb++;

    key = board->material_key;
    entry = &Material->table[KEY_INDEX(key)&Material->mask];

    if (entry->lock == KEY_LOCK(key)) {

        // found

        Material->read_hit++;

        *info = *entry;

        return;
    }


    // calculation

    material_comp_info(info,board);

    // store

    Material->write_nb++;

    if (entry->lock == 0) { // HACK: assume free entry
        Material->used++;
    } else {
        Material->write_collision++;
    }

    *entry = *info;
    entry->lock = KEY_LOCK(key);

}

// material_comp_info()

static void material_comp_info(material_info_t * info, const board_t * board) {

    int wp, wn, wb, wr, wq;
    int bp, bn, bb, br, bq;
    int wt, bt;
    int wm, bm;
    int colour;
    uint8 recog;
    uint8 flags;
    uint8 cflags[ColourNb];
    uint8 mul[ColourNb];
    sint16 phase;
    sint16 opening, endgame;
    int owf,obf,ewf,ebf; /* Thomas */
    int WhiteMinors,BlackMinors,WhiteMajors,BlackMajors;


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

    // init

    wp = board->number[WhitePawn12];
    wn = board->number[WhiteKnight12];
    wb = board->number[WhiteBishop12];
    wr = board->number[WhiteRook12];
    wq = board->number[WhiteQueen12];

    bp = board->number[BlackPawn12];
    bn = board->number[BlackKnight12];
    bb = board->number[BlackBishop12];
    br = board->number[BlackRook12];
    bq = board->number[BlackQueen12];

    wt = wq + wr + wb + wn + wp; // no king
    bt = bq + br + bb + bn + bp; // no king

    wm = wb + wn;
    bm = bb + bn;

    // recogniser

    recog = MAT_NONE;

    if (false) {

    } /*else if (wt == 0 && bt == 0) {

      recog = MAT_KK;

   }*/ else if (wt == 2 && bt == 1) {

        if (wr == 1 && wp == 1 && br == 1) recog = MAT_KRPKR;
        if (wb == 1 && wp == 1 && bb == 1) recog = MAT_KBPKB;

    } else if (wt == 1 && bt == 2) {

        if (wr == 1 && br == 1 && bp == 1) recog = MAT_KRKRP;
        if (wb == 1 && bb == 1 && bp == 1) recog = MAT_KBKBP;
    }

    // draw node (exact-draw recogniser)

    flags = 0; // TODO: MOVE ME
    cflags[0] = cflags[1] = 0;

    // bishop endgame

    if (wq+wr+wn == 0 && bq+br+bn == 0) { // only bishops
        if (wb == 1 && bb == 1) {
            if (wp-bp >= -2 && wp-bp <= +2) { // pawn diff <= 2
                flags |= DrawBishopFlag;
            }
        }
    }

    // multipliers

    mul[0] = mul[1] = 16; // 1

    // white multiplier

    if (wp == 0) { // white has no pawns

        int w_maj = wq * 2 + wr;
        int w_min = wb + wn;
        int w_tot = w_maj * 2 + w_min;

        int b_maj = bq * 2 + br;
        int b_min = bb + bn;
        int b_tot = b_maj * 2 + b_min;

        if (false) {

        } else if (w_tot == 1) {

            ASSERT(w_maj==0);
            ASSERT(w_min==1);

            // KBK* or KNK*, always insufficient

            mul[White] = 0;

        } else if (w_tot == 2 && wn == 2) {

            ASSERT(w_maj==0);
            ASSERT(w_min==2);

            // KNNK*, usually insufficient

            if (b_tot != 0 || bp == 0) {
                mul[White] = 0;
            } else { // KNNKP+, might not be draw
                mul[White] = 1; // 1/16
            }

        } else if (w_tot == 2 && wb == 2 && b_tot == 1 && bn == 1) {

            ASSERT(w_maj==0);
            ASSERT(w_min==2);
            ASSERT(b_maj==0);
            ASSERT(b_min==1);

            // KBBKN*, barely drawish (not at all?)

            mul[White] = 8; // 1/2

        } else if (w_tot-b_tot <= 1 && w_maj <= 2) {

            // no more than 1 minor up, drawish

            mul[White] = 2; // 1/8
        }

    } else if (wp == 1) { // white has one pawn

        int w_maj = wq * 2 + wr;
        int w_min = wb + wn;
        int w_tot = w_maj * 2 + w_min;

        int b_maj = bq * 2 + br;
        int b_min = bb + bn;
        int b_tot = b_maj * 2 + b_min;

        if (false) {

        } else if (b_min != 0) {

            // assume black sacrifices a minor against the lone pawn

            b_min--;
            b_tot--;

            if (false) {

            } else if (w_tot == 1) {

                ASSERT(w_maj==0);
                ASSERT(w_min==1);

                // KBK* or KNK*, always insufficient

                mul[White] = 4; // 1/4

            } else if (w_tot == 2 && wn == 2) {

                ASSERT(w_maj==0);
                ASSERT(w_min==2);

                // KNNK*, usually insufficient

                mul[White] = 4; // 1/4

            } else if (w_tot-b_tot <= 1 && w_maj <= 2) {

                // no more than 1 minor up, drawish

                mul[White] = 8; // 1/2
            }

        } else if (br != 0) {

            // assume black sacrifices a rook against the lone pawn

            b_maj--;
            b_tot -= 2;

            if (false) {

            } else if (w_tot == 1) {

                ASSERT(w_maj==0);
                ASSERT(w_min==1);

                // KBK* or KNK*, always insufficient

                mul[White] = 4; // 1/4

            } else if (w_tot == 2 && wn == 2) {

                ASSERT(w_maj==0);
                ASSERT(w_min==2);

                // KNNK*, usually insufficient

                mul[White] = 4; // 1/4

            } else if (w_tot-b_tot <= 1 && w_maj <= 2) {

                // no more than 1 minor up, drawish

                mul[White] = 8; // 1/2
            }
        }
    }

    // black multiplier

    if (bp == 0) { // black has no pawns

        int w_maj = wq * 2 + wr;
        int w_min = wb + wn;
        int w_tot = w_maj * 2 + w_min;

        int b_maj = bq * 2 + br;
        int b_min = bb + bn;
        int b_tot = b_maj * 2 + b_min;

        if (false) {

        } else if (b_tot == 1) {

            ASSERT(b_maj==0);
            ASSERT(b_min==1);

            // KBK* or KNK*, always insufficient

            mul[Black] = 0;

        } else if (b_tot == 2 && bn == 2) {

            ASSERT(b_maj==0);
            ASSERT(b_min==2);

            // KNNK*, usually insufficient

            if (w_tot != 0 || wp == 0) {
                mul[Black] = 0;
            } else { // KNNKP+, might not be draw
                mul[Black] = 1; // 1/16
            }

        } else if (b_tot == 2 && bb == 2 && w_tot == 1 && wn == 1) {

            ASSERT(b_maj==0);
            ASSERT(b_min==2);
            ASSERT(w_maj==0);
            ASSERT(w_min==1);

            // KBBKN*, barely drawish (not at all?)

            mul[Black] = 8; // 1/2

        } else if (b_tot-w_tot <= 1 && b_maj <= 2) {

            // no more than 1 minor up, drawish

            mul[Black] = 2; // 1/8
        }

    } else if (bp == 1) { // black has one pawn

        int w_maj = wq * 2 + wr;
        int w_min = wb + wn;
        int w_tot = w_maj * 2 + w_min;

        int b_maj = bq * 2 + br;
        int b_min = bb + bn;
        int b_tot = b_maj * 2 + b_min;

        if (false) {

        } else if (w_min != 0) {

            // assume white sacrifices a minor against the lone pawn

            w_min--;
            w_tot--;

            if (false) {

            } else if (b_tot == 1) {

                ASSERT(b_maj==0);
                ASSERT(b_min==1);

                // KBK* or KNK*, always insufficient

                mul[Black] = 4; // 1/4

            } else if (b_tot == 2 && bn == 2) {

                ASSERT(b_maj==0);
                ASSERT(b_min==2);

                // KNNK*, usually insufficient

                mul[Black] = 4; // 1/4

            } else if (b_tot-w_tot <= 1 && b_maj <= 2) {

                // no more than 1 minor up, drawish

                mul[Black] = 8; // 1/2
            }

        } else if (wr != 0) {

            // assume white sacrifices a rook against the lone pawn

            w_maj--;
            w_tot -= 2;

            if (false) {

            } else if (b_tot == 1) {

                ASSERT(b_maj==0);
                ASSERT(b_min==1);

                // KBK* or KNK*, always insufficient

                mul[Black] = 4; // 1/4

            } else if (b_tot == 2 && bn == 2) {

                ASSERT(b_maj==0);
                ASSERT(b_min==2);

                // KNNK*, usually insufficient

                mul[Black] = 4; // 1/4

            } else if (b_tot-w_tot <= 1 && b_maj <= 2) {

                // no more than 1 minor up, drawish

                mul[Black] = 8; // 1/2
            }
        }
    }

    // potential draw for white

    if (wt == wb+wp && wp >= 1) cflags[White] |= MatRookPawnFlag;
    if (wt == wb+wp && wb <= 1 && wp >= 1 && bt > bp) cflags[White] |= MatBishopFlag;

    if (wt == 2 && wn == 1 && wp == 1 && bt > bp) cflags[White] |= MatKnightFlag;

    // potential draw for black

    if (bt == bb+bp && bp >= 1) cflags[Black] |= MatRookPawnFlag;
    if (bt == bb+bp && bb <= 1 && bp >= 1 && wt > wp) cflags[Black] |= MatBishopFlag;

    if (bt == 2 && bn == 1 && bp == 1 && wt > wp) cflags[Black] |= MatKnightFlag;

    // king safety

    if (bq >= 1 && bq+br+bb+bn >= 2) cflags[White] |= MatKingFlag;
    if (wq >= 1 && wq+wr+wb+wn >= 2) cflags[Black] |= MatKingFlag;

    // phase (0: opening -> 256: endgame)

    phase = TotalPhase;

    phase -= wp * PawnPhase;
    phase -= wn * KnightPhase;
    phase -= wb * BishopPhase;
    phase -= wr * RookPhase;
    phase -= wq * QueenPhase;

    phase -= bp * PawnPhase;
    phase -= bn * KnightPhase;
    phase -= bb * BishopPhase;
    phase -= br * RookPhase;
    phase -= bq * QueenPhase;

    if (phase < 0) phase = 0;

    ASSERT(phase>=0&&phase<=TotalPhase);
    phase = (phase * 256 + (TotalPhase / 2)) / TotalPhase;

    ASSERT(phase>=0&&phase<=256);

    // material

    opening = 0;
    endgame = 0;

    /* Thomas */
    owf = wn*KnightOpening + wb*BishopOpening + wr*RookOpening + wq*QueenOpening;
    //info->pv[White] = owf;
    opening += owf;
    opening += wp * PawnOpening;

    obf = bn*KnightOpening + bb*BishopOpening + br*RookOpening + bq*QueenOpening;
    //info->pv[Black] = obf;
    opening -= obf;
    opening -= bp * PawnOpening;

    ewf = wn*KnightEndgame + wb*BishopEndgame + wr*RookEndgame + wq*QueenEndgame;
    endgame += wp * PawnEndgame;
    endgame += ewf;

    ebf = bn*KnightEndgame + bb*BishopEndgame + br*RookEndgame + bq*QueenEndgame;
    endgame -= bp * PawnEndgame;
    endgame -= ebf;

    //WhiteMinors = wn + wb;
    //BlackMinors = bn + bb;
    WhiteMajors = wq + wr;
    BlackMajors = bq + br;

    // Trade Bonus

    if (wm+WhiteMajors > bm+BlackMajors+1) { // pieces over majors
        opening += bad_trade_value;
        endgame += bad_trade_value;
    } else if (bm+BlackMajors > wm+WhiteMajors+1) {
        opening -= bad_trade_value;
        endgame -= bad_trade_value;
    } /*else if (WhiteMajors != BlackMajors) { // major over pawns
	if (WhiteMajors > BlackMajors && wm >= bm) {
		opening += bad_trade_value;
		endgame += bad_trade_value;
	} else if (BlackMajors > WhiteMajors && bm >= wm) {
		opening -= bad_trade_value;
		endgame -= bad_trade_value;
	}
   }*/


    // bishop pair

    if (wb >= 2) { // HACK: assumes different colours
        opening += BishopPairOpening;
        endgame += BishopPairEndgame;
    }

    if (bb >= 2) { // HACK: assumes different colours
        opening -= BishopPairOpening;
        endgame -= BishopPairEndgame;
    }

    // two knight penalty
    if (wn >= 2) {
        opening -= 10;
        endgame -= 10;
    }

    if (bn >= 2) {
        opening += 10;
        endgame += 10;
    }

    // two rook penalty
    if (wr >= 2) {
        opening -= 10;
        endgame -= 10;
    }

    if (br >= 2) {
        opening += 10;
        endgame += 10;
    }


    // rook score adjustment for number of pawns
    if (wr) {
        opening += RookPawnPenalty[wp+bp] * wr;
        endgame += RookPawnPenalty[wp+bp] * wr;
    }

    if (br) {
        opening -= RookPawnPenalty[wp+bp] * br;
        endgame -= RookPawnPenalty[wp+bp] * br;
    }

    // Queen and knight are better than queen and bishop.
    if (!wr && !br) {
        if (wq && wn) {
            opening += Queen_Knight_combo;
            endgame += Queen_Knight_combo;
        }
        if (bq && bn) {
            opening -= Queen_Knight_combo;
            opening -= Queen_Knight_combo;
        }
    }

    // Rook and bishop are better than rook and knight.
    if (!wq && !bq) {
        if (wr && wb) {
            opening += Rook_Bishop_combo;
            endgame += Rook_Bishop_combo;
        }
        if (br && bb) {
            opening -= Rook_Bishop_combo;
            endgame -= Rook_Bishop_combo;
        }
    }

    if (wp+bp+wm+bm+WhiteMajors+BlackMajors <= bitbase_pieces) flags |= MatBitbaseFlag;

    // store info

    info->recog = recog;
    info->flags = flags;
    info->cflags[0] = cflags[0];
    info->cflags[1] = cflags[1];
    info->mul[0] = mul[0];
    info->mul[1] = mul[1];
    info->phase = phase;
    info->opening = opening;
    info->endgame = endgame;
}

// end of material.cpp

