/*
    Copyright 2015 Harm Jetten

    This file is part of Moby Dam.

    Moby Dam 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.

    Moby Dam 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 Moby Dam.  If not, see <http://www.gnu.org/licenses/>.
*/

/* eval.c: static evaluation of the board position */

#include "core.h"

u16 idx_p[IDXSZ];                 /* pattern indexing table */
s16 tbl_pat[PHASES][9*PATSZ];     /* integer pattern weights */
s16 tbl_feat[PHASES][FEATSZ];     /* integer feature weights */

/* get the game phase */
/* bb -> current board */
/* returns: the game phase (0-3) */
/* 0 for  >=32 pieces */
/* 1 for 24-31 pieces */
/* 2 for 16-23 pieces */
/* 3 for  <=15 pieces */
static int game_phase(bitboard *bb)
{
    int phase;

    phase = 4 - popcount(bb->white | bb->black)/8;
    phase = min(phase, 3);
    phase = max(phase, 0);
    return phase;
}

/* evaluate current board position */
/* bb -> current board */
/* returns: evaluation score for side to move */
s32 eval_board(bitboard *bb)
{
    u64 wm, bm, wk, bk;
    s32 score, pieces, ftval;
    int phase;

    phase = game_phase(bb);
    score = 0;

    /* material */

    /* using fixed men (and kings) values, not the */
    /* calculated feature weights which seem way too low */
    pieces = popcount(bb->white) - popcount(bb->black);
    ftval = pieces*VAL_MAN;
    debugf("PIECES %d %d\n", pieces, ftval);
    score += ftval;

    if (bb->kings != 0)
    {
        static s32 kingval[PHASES] = /* a king is valued at VAL_MAN plus: */
            { 4*VAL_MAN/3, 7*VAL_MAN/3, 7*VAL_MAN/3, 7*VAL_MAN/3 };

        wk = bb->white & bb->kings;
        bk = bb->black & bb->kings;
        pieces = popcount(wk) - popcount(bk);
        ftval = pieces*kingval[phase];
        debugf("KINGS %d %d\n", pieces, ftval);
        score += ftval;
    }

    wm = bb->white & ~bb->kings;
    bm = bb->black & ~bb->kings;

    /* features */

#ifdef DISTR
    int distr = 0; /* distribution (balance) over wings */
    distr -= abs(popcount(wm & (COL1 | COL2 | COL3)) -
                 popcount(wm & (COL8 | COL9 | COL10)));
    distr += abs(popcount(bm & (COL1 | COL2 | COL3)) -
                 popcount(bm & (COL8 | COL9 | COL10)));
    ftval = distr*tbl_feat[phase][DISTR];
    debugf("DISTR %d %d\n", distr, ftval);
    score += ftval;
#endif

    /* patterns */

    /* The board is divided into 9 slightly overlapping */
    /* 4x4 patterns/regions, of 8 squares each. */
    /* Pattern weights have been obtained from analysis of */
    /* many game positions using logistic regression by */
    /* gradient descent algorithm. */
    /*
       -------------
       | 0 | 1 | 2 |
       -------------
       | 3 | 4 | 5 |
       -------------
       | 6 | 7 | 8 |
       -------------
    */
    u64 wx, bx;
    s32 v = 0;
    s16 *tblp = &tbl_pat[phase][0];

    wx  = (wm & (S01|S02|S11|S12 | S18|S19|S28|S29 | S34|S35|S44|S45)) >> 0;
    bx  = (bm & (S01|S02|S11|S12 | S18|S19|S28|S29 | S34|S35|S44|S45)) >> 0;
    wx |= (wm & (S06|S07|S16|S17 | S22|S23|S32|S33 | S39|S40|S49|S50)) >> 3;
    bx |= (bm & (S06|S07|S16|S17 | S22|S23|S32|S33 | S39|S40|S49|S50)) >> 3;
    wx |= (wx >> 7);
    bx |= (bx >> 7);
    v += tblp[0*PATSZ + 2*idx_p[(u8) wx] + idx_p[(u8) bx]];
    v += tblp[4*PATSZ + 2*idx_p[(u8) (wx >> 18)] + idx_p[(u8) (bx >> 18)]];
    v += tblp[8*PATSZ + 2*idx_p[(u8) (wx >> 36)] + idx_p[(u8) (bx >> 36)]];

    wx  = (wm & (S02|S03|S12|S13 | S19|S20|S29|S30)) >> 1;
    bx  = (bm & (S02|S03|S12|S13 | S19|S20|S29|S30)) >> 1;
    wx |= (wm & (S08|S09|S18|S19 | S24|S25|S34|S35)) >> 5;
    bx |= (bm & (S08|S09|S18|S19 | S24|S25|S34|S35)) >> 5;
    wx |= (wx >> 7);
    bx |= (bx >> 7);
    v += tblp[1*PATSZ + 2*idx_p[(u8) wx] + idx_p[(u8) bx]];
    v += tblp[5*PATSZ + 2*idx_p[(u8) (wx >> 18)] + idx_p[(u8) (bx >> 18)]];

    wx  = (wm & (S04|S05|S14|S15 | S31|S32|S41|S42)) >> 3;
    bx  = (bm & (S04|S05|S14|S15 | S31|S32|S41|S42)) >> 3;
    wx |= (wm & (S09|S10|S19|S20 | S36|S37|S46|S47)) >> 6;
    bx |= (bm & (S09|S10|S19|S20 | S36|S37|S46|S47)) >> 6;
    wx |= (wx >> 7);
    bx |= (bx >> 7);
    v += tblp[2*PATSZ + 2*idx_p[(u8) wx] + idx_p[(u8) bx]];
    v += tblp[6*PATSZ + 2*idx_p[(u8) (wx >> 30)] + idx_p[(u8) (bx >> 30)]];

    wx  = (wm & (S16|S17|S26|S27 | S32|S33|S42|S43)) >> 16;
    bx  = (bm & (S16|S17|S26|S27 | S32|S33|S42|S43)) >> 16;
    wx |= (wm & (S21|S22|S31|S32 | S38|S39|S48|S49)) >> 20;
    bx |= (bm & (S21|S22|S31|S32 | S38|S39|S48|S49)) >> 20;
    wx |= (wx >> 7);
    bx |= (bx >> 7);
    v += tblp[3*PATSZ + 2*idx_p[(u8) wx] + idx_p[(u8) bx]];
    v += tblp[7*PATSZ + 2*idx_p[(u8) (wx >> 18)] + idx_p[(u8) (bx >> 18)]];

#ifdef _DEBUG
    /* Using BMI2 PEXT instruction for debugging only, */
    /* as an additional implementation to check results against; */
    /* the release version uses only the non-PEXT code above, */
    /* which is slightly slower than using PEXT, but makes */
    /* it run well on Pentiums, Celerons and pre-Zen3 AMD. */

    int i, x;
    s32 w, v2 = 0;

    static u64 patlist[] = {
        (S01 | S02 | S06 | S07 | S11 | S12 | S16 | S17),
        (S02 | S03 | S08 | S09 | S12 | S13 | S18 | S19),
        (S04 | S05 | S09 | S10 | S14 | S15 | S19 | S20),

        (S16 | S17 | S21 | S22 | S26 | S27 | S31 | S32),
        (S18 | S19 | S22 | S23 | S28 | S29 | S32 | S33),
        (S19 | S20 | S24 | S25 | S29 | S30 | S34 | S35),

        (S31 | S32 | S36 | S37 | S41 | S42 | S46 | S47),
        (S32 | S33 | S38 | S39 | S42 | S43 | S48 | S49),
        (S34 | S35 | S39 | S40 | S44 | S45 | S49 | S50),
    };

    for (i = 0; i < elements(patlist); i++)
    {
        x = 2*idx_p[_pext_u64(wm, patlist[i])] +
            idx_p[_pext_u64(bm, patlist[i])];
        w = tblp[i*PATSZ + x];
        debugf("PAT%d %d\n", i, w); 
        v2 += w;
    }

    if (v != v2)
    {
        fprintf(stderr, "eval.c: error v=%d v2=%d\n", v, v2);
    }
#endif

    debugf("PATval %d\n", v);
    score += v;

    if (bb->side != W)
    {
        score = -score;
    }
    return score;
}

/* load a weights table */
/* phase = game phase */
/* file = weights file path/name */
/* file contains one floating point weight value per line */
/* returns: TRUE if successful */
static bool load_table(int phase, char *file)
{
    FILE *fp;
    char line[80];
    char *str;
    int i, n;
    double w;
    double factor = 10000.0; /* scaling factor */
    s32 sw;                  /* scaled weight */

    if (file == NULL)
    {
        fprintf(stderr, "error locating phase %d evaluation table\n", phase);
        return FALSE;
    }
    fp = fopen(file, "r");
    if (fp == NULL)
    {
        fprintf(stderr, "error opening %s\n", file);
        return FALSE;
    }
    for (i = 0; i < elements(tbl_pat[0]) + FEATSZ; i++)
    {
        str = fgets(line, sizeof line, fp);
        if (str == NULL)
        {
            if (i >= elements(tbl_pat[0]))
            {
                break;               /* leave remaining feature weights at 0 */
            }
            fprintf(stderr, "eof error reading %s at %d\n", file, i);
            fclose(fp);
            return FALSE;
        }
        while (*str != '\0')         /* get rid of any brackets present */
        {
            if (*str == '[' || *str == ']')
            {
                *str = ' ';
            }
            str++;
        }
        n = sscanf(line, "%lf", &w);
        if (n != 1)
        {
            fprintf(stderr, "scanf error reading %s at %d\n", file, i);
            fclose(fp);
            return FALSE;
        }
        sw = (s32) round(w*factor);
        sw = max(sw, -32767);        /* clamp to s16 range */
        sw = min(sw, 32767);
        if (i < elements(tbl_pat[0]))
        {
            tbl_pat[phase][i] = (s16) sw;
        }
        else
        {
            tbl_feat[phase][i - elements(tbl_pat[0])] = (s16) sw;
        }
    }
    fclose(fp);
    logf("success loading %s\n", file);
    return TRUE;
}

/* initialize evaluation structures */
/* dirs = directory/ies of evaluation table files */
/* returns: TRUE if successful */
bool init_eval(char *dirs)
{
    char *dbpath;
    char weightfile[20];
    int phase;
    u64 men, pcbit, mask;
    u16 x;
    bool result = TRUE;

    /* set up indexing table for 8-bit to pattern */
    for (men = 0; men < IDXSZ; men++)
    {
        x = 0;
        mask = IDXSZ - 1;
        while (mask != 0)
        {
            pcbit = mask & -mask;
            mask -= pcbit;
            x = 3*x + ((men & pcbit) != 0);
        }
        idx_p[men] = x;
    }

    /* read in the weights */
    for (phase = 0; phase < PHASES; phase++)
    {
        sprintf(weightfile, "eval-ph%d", phase);
        dbpath = locate_dbfile(dirs, weightfile);
        if (!load_table(phase, dbpath))
        {
            logf("init_eval: error loading table %s\n", weightfile);
            result = FALSE;
        }
    }
    return result;
}
