/*
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);
}