/*
 
    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/>.
 
*/
 
 
 
/*
 
 * computerPlayer.cpp
 
 *
 
 *  Created on: Feb 25, 2012
 
 *      Author: petero
 
 */
 
 
 
#include "computerPlayer.hpp"
 
#include "textio.hpp"
 
#include "tbprobe.hpp"
 
 
 
#include <iostream>
 
 
 
std::string ComputerPlayer::engineName;
 
 
 
 
 
static StaticInitializer<ComputerPlayer> cpInit;
 
 
 
void
 
ComputerPlayer::staticInitialize() {
 
    std::string name = "Texel 1.05";
 
    if (sizeof(char*) == 4)
 
        name += " 32-bit";
 
    if (sizeof(char*) == 8)
 
        name += " 64-bit";
 
    engineName = name;
 
}
 
 
 
void
 
ComputerPlayer::initEngine() {
 
    Parameters::instance();
 
 
 
    auto tbInit = []() {
 
        TBProbe::initialize(UciParams::gtbPath->getStringPar(),
 
                            UciParams::gtbCache->getIntPar(),
 
                            UciParams::rtbPath->getStringPar());
 
    };
 
    UciParams::gtbPath->addListener(tbInit);
 
    UciParams::gtbCache->addListener(tbInit, false);
 
    UciParams::rtbPath->addListener(tbInit, false);
 
 
 
    knightMobScore.addListener(Evaluate::updateEvalParams);
 
    castleFactor.addListener(Evaluate::updateEvalParams, false);
 
    pV.addListener([]() { pieceValue[Piece::WPAWN]   = pieceValue[Piece::BPAWN]   = pV; });
 
    nV.addListener([]() { pieceValue[Piece::WKNIGHT] = pieceValue[Piece::BKNIGHT] = nV; });
 
    bV.addListener([]() { pieceValue[Piece::WBISHOP] = pieceValue[Piece::BBISHOP] = bV; });
 
    rV.addListener([]() { pieceValue[Piece::WROOK]   = pieceValue[Piece::BROOK]   = rV; });
 
    qV.addListener([]() { pieceValue[Piece::WQUEEN]  = pieceValue[Piece::BQUEEN]  = qV; });
 
    kV.addListener([]() { pieceValue[Piece::WKING]   = pieceValue[Piece::BKING]   = kV; });
 
}
 
 
 
ComputerPlayer::ComputerPlayer()
 
    : tt(15), pd(tt),
 
      book(false)
 
{
 
    initEngine();
 
    et = Evaluate::getEvalHashTables();
 
    minTimeMillis = 10000;
 
    maxTimeMillis = 10000;
 
    maxDepth = 100;
 
    maxNodes = -1;
 
    verbose = true;
 
    bookEnabled = true;
 
    currentSearch = nullptr;
 
}
 
 
 
std::string
 
ComputerPlayer::getCommand(const Position& posIn, bool drawOffer, const std::vector<Position>& history) {
 
    // Create a search object
 
    std::vector<U64> posHashList(200 + history.size());
 
    int posHashListSize = 0;
 
    for (size_t i = 0; i < history.size(); i++)
 
        posHashList[posHashListSize++] = history[i].zobristHash();
 
    tt.nextGeneration();
 
    Position pos(posIn);
 
    KillerTable kt;
 
    History ht;
 
    Search::SearchTables st(tt, kt, ht, *et);
 
    TreeLogger treeLog;
 
    Search sc(pos, posHashList, posHashListSize, st, pd, nullptr, treeLog);
 
 
 
    // Determine all legal moves
 
    MoveList moves;
 
    MoveGen::pseudoLegalMoves(pos, moves);
 
    MoveGen::removeIllegal(pos, moves);
 
    sc.scoreMoveList(moves, 0);
 
 
 
    // Test for "game over"
 
    if (moves.size == 0) {
 
        // Switch sides so that the human can decide what to do next.
 
        return "swap";
 
    }
 
 
 
    if (bookEnabled) {
 
        Move bookMove;
 
        book.getBookMove(pos, bookMove);
 
        if (!bookMove.isEmpty()) {
 
            std::cout << "Book moves: " << book.getAllBookMoves(pos) << std::endl;
 
            return TextIO::moveToString(pos, bookMove, false);
 
        }
 
    }
 
 
 
    // Find best move using iterative deepening
 
    currentSearch = ≻
 
    sc.setListener(listener);
 
    Move bestM;
 
    if ((moves.size == 1) && (canClaimDraw(pos, posHashList, posHashListSize, moves[0]) == "")) {
 
        bestM = moves[0];
 
        bestM.setScore(0);
 
    } else {
 
        sc.timeLimit(minTimeMillis, maxTimeMillis);
 
        bestM = sc.iterativeDeepening(moves, maxDepth, maxNodes, verbose, 1, false, 100);
 
    }
 
    currentSearch = nullptr;
 
    //        tt.printStats();
 
    std::string strMove = TextIO::moveToString(pos, bestM, false);
 
 
 
    // Claim draw if appropriate
 
    if (bestM.score() <= 0) {
 
        std::string drawClaim = canClaimDraw(pos, posHashList, posHashListSize, bestM);
 
        if (drawClaim != "")
 
            strMove = drawClaim;
 
    }
 
    return strMove;
 
}
 
 
 
std::string
 
ComputerPlayer::canClaimDraw(Position& pos, std::vector<U64>& posHashList,
 
                             int posHashListSize, const Move& move) {
 
    std::string drawStr;
 
    if (Search::canClaimDraw50(pos)) {
 
        drawStr = "draw 50";
 
    } else if (Search::canClaimDrawRep(pos, posHashList, posHashListSize, posHashListSize)) {
 
        drawStr = "draw rep";
 
    } else {
 
        std::string strMove = TextIO::moveToString(pos, move, false);
 
        posHashList[posHashListSize++] = pos.zobristHash();
 
        UndoInfo ui;
 
        pos.makeMove(move, ui);
 
        if (Search::canClaimDraw50(pos)) {
 
            drawStr = "draw 50 " + strMove;
 
        } else if (Search::canClaimDrawRep(pos, posHashList, posHashListSize, posHashListSize)) {
 
            drawStr = "draw rep " + strMove;
 
        }
 
        pos.unMakeMove(move, ui);
 
    }
 
    return drawStr;
 
}
 
 
 
void
 
ComputerPlayer::timeLimit(int minTimeLimit, int maxTimeLimit) {
 
    minTimeMillis = minTimeLimit;
 
    maxTimeMillis = maxTimeLimit;
 
    if (currentSearch != nullptr)
 
        currentSearch->timeLimit(minTimeLimit, maxTimeLimit);
 
}
 
 
 
std::pair<Move, std::string>
 
ComputerPlayer::searchPosition(Position& pos, int maxTimeMillis) {
 
    // Create a search object
 
    std::vector<U64> posHashList(200);
 
    tt.nextGeneration();
 
    KillerTable kt;
 
    History ht;
 
    Search::SearchTables st(tt, kt, ht, *et);
 
    TreeLogger treeLog;
 
    Search sc(pos, posHashList, 0, st, pd, nullptr, treeLog);
 
 
 
    // Determine all legal moves
 
    MoveList moves;
 
    MoveGen::pseudoLegalMoves(pos, moves);
 
    MoveGen::removeIllegal(pos, moves);
 
    sc.scoreMoveList(moves, 0);
 
 
 
    // Find best move using iterative deepening
 
    sc.timeLimit(maxTimeMillis, maxTimeMillis);
 
    Move bestM = sc.iterativeDeepening(moves, -1, -1, false);
 
 
 
    // Extract PV
 
    std::string PV = TextIO::moveToString(pos, bestM, false) + " ";
 
    UndoInfo ui;
 
    pos.makeMove(bestM, ui);
 
    PV += tt.extractPV(pos);
 
    pos.unMakeMove(bestM, ui);
 
 
 
//    tt.printStats();
 
 
 
    // Return best move and PV
 
    return std::pair<Move, std::string>(bestM, PV);
 
}