/*
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/>.
*/
/*
* tuigame.cpp
*
* Created on: Mar 4, 2012
* Author: petero
*/
#include "tuigame.hpp"
#include "uciprotocol.hpp"
#include "textio.hpp"
#include "evaluate.hpp"
#include "computerPlayer.hpp"
#include <iostream>
#include <fstream>
#include <iomanip>
TUIGame::TUIGame(const std::shared_ptr<Player>& whitePlayer,
const std::shared_ptr<Player>& blackPlayer)
: Game(whitePlayer, blackPlayer) {
}
bool
TUIGame::handleCommand(const std::string& moveStr) {
if (Game::handleCommand(moveStr))
return true;
if (startsWith(moveStr, "testsuite ")) {
std::string testSuiteCmd = moveStr.substr(moveStr.find_first_of(' ') + 1);
handleTestSuite(testSuiteCmd);
return true;
} else if (moveStr == "uci") {
whitePlayer.reset();
blackPlayer.reset();
UCIProtocol::main(true);
exit(0);
return false;
} else if (moveStr == "help") {
showHelp();
return true;
}
return false;
}
void
TUIGame::showHelp() {
std::cout << "Enter a move, or one of the following special commands:" << std::endl;
std::cout << " new - Start a new game" << std::endl;
std::cout << " undo - Undo last half-move" << std::endl;
std::cout << " redo - Redo next half-move" << std::endl;
std::cout << " swap - Swap sides" << std::endl;
std::cout << " go - Same as swap" << std::endl;
std::cout << " list - List all moves in current game" << std::endl;
std::cout << " setpos FEN - Set a position using a FEN string" << std::endl;
std::cout << " getpos - Print current position in FEN notation" << std::endl;
std::cout << " draw rep [move] - Claim draw by repetition" << std::endl;
std::cout << " draw 50 [move] - Claim draw by 50-move rule" << std::endl;
std::cout << " draw offer move - Play move and offer draw" << std::endl;
std::cout << " draw accept - Accept a draw offer" << std::endl;
std::cout << " resign - Resign the current game" << std::endl;
std::cout << " testsuite filename maxtime" << std::endl;
std::cout << " book on|off - Turn opening book on/off" << std::endl;
std::cout << " time t - Set computer thinking time, ms" << std::endl;
std::cout << " perft d - Run perft test to depth d" << std::endl;
std::cout << " uci - Switch to uci protocol." << std::endl;
std::cout << " help - Show this help" << std::endl;
std::cout << " quit - Terminate program" << std::endl;
}
void
TUIGame::handleTestSuite(const std::string& cmd) {
std::ifstream fr;
int lineNo = -1;
try {
size_t idx = cmd.find_first_of(' ');
if (idx == cmd.npos)
return;
std::string filename(cmd.substr(0, idx));
std::string timeStr(cmd.substr(idx + 1));
int timeLimit;
if (!str2Num(timeStr, timeLimit)) {
std::cout << "Error parsing number: " << timeStr << std::endl;
return;
}
// std::cout << "file:" << filename << " time:" << timeStr << " (" << timeLimit << ")" << std::endl;
fr.open(filename.c_str());
std::shared_ptr<Player> pl = whitePlayer->isHumanPlayer() ? blackPlayer : whitePlayer;
if (pl->isHumanPlayer()) {
std::cout << "No computer player available" << std::endl;
return;
}
std::shared_ptr<ComputerPlayer> cp = std::static_pointer_cast<ComputerPlayer>(pl);
int numRight = 0;
int numTotal = 0;
std::string line;
lineNo = 0;
while (getline(fr, line).good()) {
lineNo++;
if (startsWith(line, "#") || (line.length() == 0))
continue;
size_t idx1 = line.find(" bm ");
if (idx1 == line.npos) {
std::cout << "Parse error, line:" << lineNo << std::endl;
return;
}
std::string fen = line.substr(0, idx1);
size_t idx2 = line.find(";", idx1);
if (idx2 == line.npos) {
std::cout << "Parse error, line:" << lineNo << std::endl;
return;
}
std::string bm = line.substr(idx1+4, idx2 - (idx1+4));
// std::cout << "Line " << std::setw(3) << lineNo << ": fen:" << fen << " bm:" << bm << std::endl;
Position testPos = TextIO::readFEN(fen);
cp->clearTT();
std::pair<Move, std::string> ret = cp->searchPosition(testPos, timeLimit);
Move sm = ret.first;
std::string PV = ret.second;
Move m(sm);
std::vector<std::string> answers;
splitString(bm, answers);
bool correct = false;
for (size_t i = 0; i < answers.size(); i++) {
const std::string& a = answers[i];
Move am(TextIO::stringToMove(testPos, a));
if (am.isEmpty())
throw ChessParseError("Invalid move " + a);
if (am.equals(m)) {
correct = true;
break;
}
}
if (correct)
numRight++;
numTotal++;
std::cout << std::setw(3) << lineNo
<< ' ' << std::setw(6) << TextIO::moveToString(testPos, sm, false)
<< ' ' << std::setw(6) << sm.score()
<< ' ' << (correct ? 1 : 0)
<< ' ' << std::setw(3) << numRight
<< '/' << std::setw(3) << numTotal
<< ' ' << bm << " : " << PV << std::endl;
}
fr.close();
} catch (const std::ifstream::failure& ex) {
std::cout << "IO error: " << ex.what() << std::endl;
} catch (const ChessParseError& cpe) {
std::cout << "Parse error, line " << lineNo << ": " << cpe.what() << std::endl;
}
}
void
TUIGame::play() {
handleCommand("new");
while (true) {
// Print last move
if (currentMove > 0) {
Position prevPos(getPos());
prevPos.unMakeMove(moveList[currentMove - 1], uiInfoList[currentMove - 1]);
std::string moveStr= TextIO::moveToString(prevPos, moveList[currentMove - 1], false);
if (haveDrawOffer())
moveStr += " (offer draw)";
std::cout << "Last move: " << prevPos.getFullMoveCounter()
<< (prevPos.isWhiteMove() ? "." : "...")
<< ' ' << moveStr << std::endl;
}
/*
{
std::stringstream ss;
ss << "Hash: " << std::hex << std::setw(16) << std::setfill('0') << pos.zobristHash();
std::cout << ss.str() << std::endl;
}
*/
{
auto et = Evaluate::getEvalHashTables();
Evaluate eval(*et);
int evScore = eval.evalPos(getPos()) * (getPos().isWhiteMove() ? 1 : -1);
std::stringstream ss;
ss.precision(2);
ss << std::fixed << "Eval: " << (evScore / 100.0);
std::cout << ss.str() << std::endl;
}
// Check game state
std::cout << TextIO::asciiBoard(getPos());
std::string stateStr = getGameStateString();
if (stateStr.length() > 0)
std::cout << stateStr << std::endl;
if (getGameState() != Game::ALIVE)
activateHumanPlayer();
// Get command from current player and act on it
std::shared_ptr<Player> pl = getPos().isWhiteMove() ? whitePlayer : blackPlayer;
std::vector<Position> posList;
getHistory(posList);
std::string moveStr = pl->getCommand(getPos(), haveDrawOffer(), posList);
if (moveStr == "quit")
return;
bool ok = processString(moveStr);
if (!ok)
std::cout << "Invalid move: " << moveStr << std::endl;
}
}