/*
 
    Texel - A UCI chess engine.
 
    Copyright (C) 2012-2014  Peter Ă–sterlund, peterosterlund2@gmail.com
 
 
 
    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/>.
 
*/
 
 
 
/*
 
 * evaluate.hpp
 
 *
 
 *  Created on: Feb 25, 2012
 
 *      Author: petero
 
 */
 
 
 
#ifndef EVALUATE_HPP_
 
#define EVALUATE_HPP_
 
 
 
#include "parameters.hpp"
 
#include "piece.hpp"
 
#include "position.hpp"
 
#include "util/alignedAlloc.hpp"
 
 
 
class EvaluateTest;
 
 
 
/** Position evaluation routines. */
 
class Evaluate {
 
    friend class EvaluateTest;
 
private:
 
    struct PawnHashData {
 
        PawnHashData();
 
        U64 key;
 
        S16 current;        // For hash replacement policy
 
        S16 score;          // Positive score means good for white
 
        S16 passedBonusW;
 
        S16 passedBonusB;
 
        U64 passedPawns;    // The most advanced passed pawns for each file
 
                            // Contains both white and black pawns
 
        U64 outPostsW;      // Possible outpost squares for white
 
        U64 outPostsB;
 
        U64 stalePawns;     // Pawns that can not be used for "pawn breaks"
 
    };
 
 
 
    struct MaterialHashData {
 
        MaterialHashData();
 
        int id;
 
        int score;
 
        S16 wPawnIPF, bPawnIPF;
 
        S16 wKnightIPF, bKnightIPF;
 
        S16 castleIPF, queenIPF;
 
        S16 wPassedPawnIPF, bPassedPawnIPF;
 
        S16 kingSafetyIPF;
 
        S16 diffColorBishopIPF;
 
        S16 wKnightOutPostIPF, bKnightOutPostIPF;
 
        U8 endGame;
 
    };
 
 
 
    struct KingSafetyHashData {
 
        KingSafetyHashData();
 
        U64 key;
 
        int score;
 
        S16 current;        // For hash replacement policy
 
    };
 
 
 
public:
 
    struct EvalHashTables {
 
        EvalHashTables();
 
        std::vector<PawnHashData> pawnHash;
 
        std::vector<MaterialHashData> materialHash;
 
        vector_aligned<KingSafetyHashData> kingSafetyHash;
 
    };
 
 
 
    /** Constructor. */
 
    Evaluate(EvalHashTables& et);
 
 
 
    static int pieceValueOrder[Piece::nPieceTypes];
 
 
 
    static const int* psTab1[Piece::nPieceTypes];
 
    static const int* psTab2[Piece::nPieceTypes];
 
 
 
    /** Get evaluation hash tables. */
 
    static std::shared_ptr<EvalHashTables> getEvalHashTables();
 
 
 
    /**
 
     * Static evaluation of a position.
 
     * @param pos The position to evaluate.
 
     * @return The evaluation score, measured in centipawns.
 
     *         Positive values are good for the side to make the next move.
 
     */
 
    int evalPos(const Position& pos);
 
    int evalPosPrint(const Position& pos);
 
 
 
    /** Compute "swindle" score corresponding to an evaluation score when
 
     * the position is a known TB draw. */
 
    static int swindleScore(int evalScore);
 
 
 
    /**
 
     * Interpolate between (x1,y1) and (x2,y2).
 
     * If x < x1, return y1, if x > x2 return y2. Otherwise, use linear interpolation.
 
     */
 
    static int interpolate(int x, int x1, int y1, int x2, int y2);
 
 
 
    static const int IPOLMAX = 1024;
 
 
 
    /** Compute v1 + (v2-v1)*k/IPOLMAX */
 
    static int interpolate(int v1, int v2, int k);
 
 
 
    static void staticInitialize();
 
    static void updateEvalParams();
 
 
 
private:
 
    template <bool print> int evalPos(const Position& pos);
 
 
 
    /** Compute score based on piece square tables. Positive values are good for white. */
 
    int pieceSquareEval(const Position& pos);
 
 
 
    /** Get material score */
 
    int materialScore(const Position& pos, bool print);
 
 
 
    /** Compute material score. */
 
    void computeMaterialScore(const Position& pos, MaterialHashData& mhd, bool print) const;
 
 
 
    /** Implement the "when ahead trade pieces, when behind trade pawns" rule. */
 
    int tradeBonus(const Position& pos, int wCorr, int bCorr) const;
 
 
 
    /** Score castling ability. */
 
    int castleBonus(const Position& pos);
 
 
 
    PawnHashData& getPawnHashEntry(std::vector<PawnHashData>& pawnHash, U64 key);
 
    int pawnBonus(const Position& pos);
 
 
 
    /** Compute set of pawns that can not participate in "pawn breaks". */
 
    static U64 computeStalePawns(const Position& pos);
 
 
 
    /** Compute pawn hash data for pos. */
 
    void computePawnHashData(const Position& pos, PawnHashData& ph);
 
 
 
    /** Compute rook bonus. Rook on open/half-open file. */
 
    int rookBonus(const Position& pos);
 
 
 
    /** Compute bishop evaluation. */
 
    int bishopEval(const Position& pos, int oldScore);
 
 
 
    /** Compute knight evaluation. */
 
    int knightEval(const Position& pos);
 
 
 
    /** Bonus for threatening opponent pieces. */
 
    int threatBonus(const Position& pos);
 
 
 
    /** Bonus for own pieces protected by pawns. */
 
    int protectBonus(const Position& pos);
 
 
 
    /** Compute king safety for both kings. */
 
    int kingSafety(const Position& pos);
 
 
 
    KingSafetyHashData& getKingSafetyHashEntry(vector_aligned<KingSafetyHashData>& ksHash, U64 key);
 
    int kingSafetyKPPart(const Position& pos);
 
 
 
    static int castleMaskFactor[256];
 
    static int knightMobScoreA[64][9];
 
    static U64 knightKingProtectPattern[64];
 
    static U64 bishopKingProtectPattern[64];
 
 
 
    std::vector<PawnHashData>& pawnHash;
 
    const PawnHashData* phd;
 
 
 
    std::vector<MaterialHashData>& materialHash;
 
    const MaterialHashData* mhd;
 
 
 
    vector_aligned<KingSafetyHashData>& kingSafetyHash;
 
 
 
     // King safety variables
 
    U64 wKingZone, bKingZone;       // Squares close to king that are worth attacking
 
    int wKingAttacks, bKingAttacks; // Number of attacks close to white/black king
 
    U64 wAttacksBB, bAttacksBB;
 
    U64 wPawnAttacks, bPawnAttacks; // Squares attacked by white/black pawns
 
};
 
 
 
 
 
inline
 
Evaluate::PawnHashData::PawnHashData()
 
    : key((U64)-1), // Non-zero to avoid collision for positions with no pawns
 
      current(0), score(0),
 
      passedBonusW(0),
 
      passedBonusB(0),
 
      passedPawns(0) {
 
}
 
 
 
inline
 
Evaluate::MaterialHashData::MaterialHashData()
 
    : id(-1), score(0) {
 
}
 
 
 
inline
 
Evaluate::KingSafetyHashData::KingSafetyHashData()
 
    : key((U64)-1), score(0), current(0) {
 
}
 
 
 
inline
 
Evaluate::EvalHashTables::EvalHashTables() {
 
    pawnHash.resize(1<<16);
 
    kingSafetyHash.resize(1 << 15);
 
    materialHash.resize(1 << 14);
 
}
 
 
 
inline int
 
Evaluate::interpolate(int x, int x1, int y1, int x2, int y2) {
 
    if (x > x2) {
 
        return y2;
 
    } else if (x < x1) {
 
        return y1;
 
    } else {
 
        return (x - x1) * (y2 - y1) / (x2 - x1) + y1;
 
    }
 
}
 
 
 
inline int
 
Evaluate::interpolate(int v1, int v2, int k) {
 
    return v1 + (v2 - v1) * k / IPOLMAX;
 
}
 
 
 
inline int
 
Evaluate::materialScore(const Position& pos, bool print) {
 
    int mId = pos.materialId();
 
    int key = (mId >> 16) * 40507 + mId;
 
    MaterialHashData& newMhd = materialHash[key & (materialHash.size() - 1)];
 
    if ((newMhd.id != mId) || print)
 
        computeMaterialScore(pos, newMhd, print);
 
    mhd = &newMhd;
 
    return newMhd.score;
 
}
 
 
 
inline Evaluate::PawnHashData&
 
Evaluate::getPawnHashEntry(std::vector<Evaluate::PawnHashData>& pawnHash, U64 key) {
 
    int e0 = (int)key & (pawnHash.size() - 2);
 
    int e1 = e0 + 1;
 
    if (pawnHash[e0].key == key) {
 
        pawnHash[e0].current = 1;
 
        pawnHash[e1].current = 0;
 
        return pawnHash[e0];
 
    }
 
    if (pawnHash[e1].key == key) {
 
        pawnHash[e1].current = 1;
 
        pawnHash[e0].current = 0;
 
        return pawnHash[e1];
 
    }
 
    if (pawnHash[e0].current) {
 
        pawnHash[e1].current = 1;
 
        pawnHash[e0].current = 0;
 
        return pawnHash[e1];
 
    } else {
 
        pawnHash[e0].current = 1;
 
        pawnHash[e1].current = 0;
 
        return pawnHash[e0];
 
    }
 
}
 
 
 
inline Evaluate::KingSafetyHashData&
 
Evaluate::getKingSafetyHashEntry(vector_aligned<Evaluate::KingSafetyHashData>& ksHash, U64 key) {
 
    int e0 = (int)key & (ksHash.size() - 2);
 
    int e1 = e0 + 1;
 
    if (ksHash[e0].key == key) {
 
        ksHash[e0].current = 1;
 
        ksHash[e1].current = 0;
 
        return ksHash[e0];
 
    }
 
    if (ksHash[e1].key == key) {
 
        ksHash[e1].current = 1;
 
        ksHash[e0].current = 0;
 
        return ksHash[e1];
 
    }
 
    if (ksHash[e0].current) {
 
        ksHash[e1].current = 1;
 
        ksHash[e0].current = 0;
 
        return ksHash[e1];
 
    } else {
 
        ksHash[e0].current = 1;
 
        ksHash[e1].current = 0;
 
        return ksHash[e0];
 
    }
 
}
 
 
 
#endif /* EVALUATE_HPP_ */