/*
 
    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/>.
 
*/
 
 
 
/*
 
 * transpositionTable.cpp
 
 *
 
 *  Created on: Feb 25, 2012
 
 *      Author: petero
 
 */
 
 
 
#include "transpositionTable.hpp"
 
#include "position.hpp"
 
#include "moveGen.hpp"
 
#include "textio.hpp"
 
 
 
#include <iostream>
 
#include <iomanip>
 
 
 
using namespace std;
 
 
 
 
 
void
 
TranspositionTable::reSize(int log2Size) {
 
    const size_t numEntries = ((size_t)1) << log2Size;
 
    table.resize(numEntries);
 
    generation = 0;
 
}
 
 
 
void
 
TranspositionTable::insert(U64 key, const Move& sm, int type, int ply, int depth, int evalScore) {
 
    if (depth < 0) depth = 0;
 
    size_t idx0 = getIndex(key);
 
    U64 key2 = getStoredKey(key);
 
    TTEntry ent0, ent1;
 
    ent0.load(table[idx0]);
 
    size_t idx = idx0;
 
    TTEntry* ent = &ent0;
 
    if (ent0.getKey() != key2) {
 
        size_t idx1 = idx0 ^ 1;
 
        ent1.load(table[idx1]);
 
        idx = idx1;
 
        ent = &ent1;
 
        if (ent1.getKey() != key2)
 
            if (ent1.betterThan(ent0, generation)) {
 
                idx = idx0;
 
                ent = &ent0;
 
            }
 
    }
 
    bool doStore = true;
 
    if ((ent->getKey() == key2) && (ent->getDepth() > depth) && (ent->getType() == type)) {
 
        if (type == TType::T_EXACT)
 
            doStore = false;
 
        else if ((type == TType::T_GE) && (sm.score() <= ent->getScore(ply)))
 
            doStore = false;
 
        else if ((type == TType::T_LE) && (sm.score() >= ent->getScore(ply)))
 
            doStore = false;
 
    }
 
    if (doStore) {
 
        if ((ent->getKey() != key2) || (sm.from() != sm.to()))
 
            ent->setMove(sm);
 
        ent->setKey(key2);
 
        ent->setScore(sm.score(), ply);
 
        ent->setDepth(depth);
 
        ent->setGeneration((S8)generation);
 
        ent->setType(type);
 
        ent->setEvalScore(evalScore);
 
        ent->store(table[idx]);
 
    }
 
}
 
 
 
void
 
TranspositionTable::extractPVMoves(const Position& rootPos, const Move& mFirst, std::vector<Move>& pv) {
 
    Position pos(rootPos);
 
    Move m(mFirst);
 
    UndoInfo ui;
 
    std::vector<U64> hashHistory;
 
    while (true) {
 
        pv.push_back(m);
 
        pos.makeMove(m, ui);
 
        if (contains(hashHistory, pos.zobristHash()))
 
            break;
 
        hashHistory.push_back(pos.zobristHash());
 
        TTEntry ent;
 
        ent.clear();
 
        probe(pos.historyHash(), ent);
 
        if (ent.getType() == TType::T_EMPTY)
 
            break;
 
        ent.getMove(m);
 
        MoveList moves;
 
        MoveGen::pseudoLegalMoves(pos, moves);
 
        MoveGen::removeIllegal(pos, moves);
 
        bool contains = false;
 
        for (int mi = 0; mi < moves.size; mi++)
 
            if (moves[mi].equals(m)) {
 
                contains = true;
 
                break;
 
            }
 
        if  (!contains)
 
            break;
 
    }
 
}
 
 
 
/** Extract the PV starting from posIn, using hash entries, both exact scores and bounds. */
 
std::string
 
TranspositionTable::extractPV(const Position& posIn) {
 
    std::string ret;
 
    Position pos(posIn);
 
    bool first = true;
 
    TTEntry ent;
 
    ent.clear();
 
    probe(pos.historyHash(), ent);
 
    UndoInfo ui;
 
    std::vector<U64> hashHistory;
 
    bool repetition = false;
 
    while (ent.getType() != TType::T_EMPTY) {
 
        Move m;
 
        ent.getMove(m);
 
        MoveList moves;
 
        MoveGen::pseudoLegalMoves(pos, moves);
 
        MoveGen::removeIllegal(pos, moves);
 
        bool valid = false;
 
        for (int mi = 0; mi < moves.size; mi++)
 
            if (moves[mi].equals(m)) {
 
                valid = true;
 
                break;
 
            }
 
        if  (!valid)
 
            break;
 
        if (repetition)
 
            break;
 
        if (!first)
 
            ret += ' ';
 
        if (ent.getType() == TType::T_LE)
 
            ret += '<';
 
        else if (ent.getType() == TType::T_GE)
 
            ret += '>';
 
        std::string moveStr = TextIO::moveToString(pos, m, false);
 
        ret += moveStr;
 
        pos.makeMove(m, ui);
 
        if (contains(hashHistory, pos.zobristHash()))
 
            repetition = true;
 
        hashHistory.push_back(pos.zobristHash());
 
        probe(pos.historyHash(), ent);
 
        first = false;
 
    }
 
    return ret;
 
}
 
 
 
void
 
TranspositionTable::printStats() const {
 
    int unused = 0;
 
    int thisGen = 0;
 
    std::vector<int> depHist;
 
    const int maxDepth = 20*8;
 
    depHist.resize(maxDepth);
 
    for (size_t i = 0; i < table.size(); i++) {
 
        TTEntry ent;
 
        ent.load(table[i]);
 
        if (ent.getType() == TType::T_EMPTY) {
 
            unused++;
 
        } else {
 
            if (ent.getGeneration() == generation)
 
                thisGen++;
 
            if (ent.getDepth() < maxDepth)
 
                depHist[ent.getDepth()]++;
 
        }
 
    }
 
    double w = 100.0 / table.size();
 
    std::stringstream ss;
 
    ss.precision(2);
 
    ss << std::fixed << "hstat: size:" << table.size()
 
       << " unused:" << unused << " (" << (unused*w) << "%)"
 
       << " thisGen:" << thisGen << " (" << (thisGen*w) << "%)" << std::endl;
 
    cout << ss.str();
 
    for (int i = 0; i < maxDepth; i++) {
 
        int c = depHist[i];
 
        if (c > 0) {
 
            std::stringstream ss;
 
            ss.precision(2);
 
            ss << std::setw(4) << i
 
               << ' ' << std::setw(8) << c
 
               << " " << std::setw(6) << std::fixed << (c*w);
 
            std::cout << "hstat:" << ss.str() << std::endl;
 
        }
 
    }
 
}