Subversion Repositories Games.Chess Giants

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.     Texel - A UCI chess engine.
  3.     Copyright (C) 2012-2014  Peter Ă–sterlund, peterosterlund2@gmail.com
  4.  
  5.     This program is free software: you can redistribute it and/or modify
  6.     it under the terms of the GNU General Public License as published by
  7.     the Free Software Foundation, either version 3 of the License, or
  8.     (at your option) any later version.
  9.  
  10.     This program is distributed in the hope that it will be useful,
  11.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.     GNU General Public License for more details.
  14.  
  15.     You should have received a copy of the GNU General Public License
  16.     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17. */
  18.  
  19. /*
  20.  * game.cpp
  21.  *
  22.  *  Created on: Feb 25, 2012
  23.  *      Author: petero
  24.  */
  25.  
  26. #include "game.hpp"
  27. #include "moveGen.hpp"
  28. #include "textio.hpp"
  29. #include "util/timeUtil.hpp"
  30.  
  31. #include <iostream>
  32. #include <iomanip>
  33. #include <cassert>
  34.  
  35.  
  36. Game::Game(const std::shared_ptr<Player>& whitePlayer,
  37.            const std::shared_ptr<Player>& blackPlayer) {
  38.     this->whitePlayer = whitePlayer;
  39.     this->blackPlayer = blackPlayer;
  40.     handleCommand("new");
  41. }
  42.  
  43. bool
  44. Game::processString(const std::string& str) {
  45.     if (handleCommand(str))
  46.         return true;
  47.     if (getGameState() != ALIVE)
  48.         return false;
  49.  
  50.     Move m = TextIO::stringToMove(pos, str);
  51.     if (m.isEmpty())
  52.         return false;
  53.  
  54.     UndoInfo ui;
  55.     pos.makeMove(m, ui);
  56.     TextIO::fixupEPSquare(pos);
  57.     if (currentMove < (int)moveList.size()) {
  58.         moveList.erase(moveList.begin() + currentMove, moveList.end());
  59.         uiInfoList.erase(uiInfoList.begin() + currentMove, uiInfoList.end());
  60.         drawOfferList.erase(drawOfferList.begin() + currentMove, drawOfferList.end());
  61.     }
  62.     moveList.push_back(m);
  63.     uiInfoList.push_back(ui);
  64.     drawOfferList.push_back(pendingDrawOffer);
  65.     pendingDrawOffer = false;
  66.     currentMove++;
  67.     return true;
  68. }
  69.  
  70. std::string
  71. Game::getGameStateString() {
  72.     switch (getGameState()) {
  73.     case ALIVE:
  74.         return "";
  75.     case WHITE_MATE:
  76.         return "Game over, white mates!";
  77.     case BLACK_MATE:
  78.         return "Game over, black mates!";
  79.     case WHITE_STALEMATE:
  80.     case BLACK_STALEMATE:
  81.         return "Game over, draw by stalemate!";
  82.     case DRAW_REP:
  83.     {
  84.         std::string ret = "Game over, draw by repetition!";
  85.         if (drawStateMoveStr.length() > 0)
  86.             ret += " [" + drawStateMoveStr + "]";
  87.         return ret;
  88.     }
  89.     case DRAW_50:
  90.     {
  91.         std::string ret = "Game over, draw by 50 move rule!";
  92.         if (drawStateMoveStr.length() > 0)
  93.             ret += " [" + drawStateMoveStr + "]";
  94.         return ret;
  95.     }
  96.     case DRAW_NO_MATE:
  97.         return "Game over, draw by impossibility of mate!";
  98.     case DRAW_AGREE:
  99.         return "Game over, draw by agreement!";
  100.     case RESIGN_WHITE:
  101.         return "Game over, white resigns!";
  102.     case RESIGN_BLACK:
  103.         return "Game over, black resigns!";
  104.     default:
  105.         assert(false);
  106.         return "";
  107.     }
  108. }
  109.  
  110. Move
  111. Game::getLastMove() {
  112.     Move m;
  113.     if (currentMove > 0)
  114.         m = moveList[currentMove - 1];
  115.     return m;
  116. }
  117.  
  118. Game::GameState
  119. Game::getGameState() {
  120.     MoveList moves;
  121.     MoveGen::pseudoLegalMoves(pos, moves);
  122.     MoveGen::removeIllegal(pos, moves);
  123.     if (moves.size == 0) {
  124.         if (MoveGen::inCheck(pos))
  125.             return pos.isWhiteMove() ? BLACK_MATE : WHITE_MATE;
  126.         else
  127.             return pos.isWhiteMove() ? WHITE_STALEMATE : BLACK_STALEMATE;
  128.     }
  129.     if (insufficientMaterial())
  130.         return DRAW_NO_MATE;
  131.     if (resignState != ALIVE)
  132.         return resignState;
  133.     return drawState;
  134. }
  135.  
  136. bool
  137. Game::haveDrawOffer() {
  138.     if (currentMove > 0)
  139.         return drawOfferList[currentMove - 1];
  140.     else
  141.         return false;
  142. }
  143.  
  144. bool
  145. Game::handleCommand(const std::string& moveStr) {
  146.     if (moveStr == "new") {
  147.         moveList.clear();
  148.         uiInfoList.clear();
  149.         drawOfferList.clear();
  150.         currentMove = 0;
  151.         pendingDrawOffer = false;
  152.         drawState = ALIVE;
  153.         resignState = ALIVE;
  154.         pos = TextIO::readFEN(TextIO::startPosFEN);
  155.         whitePlayer->clearTT();
  156.         blackPlayer->clearTT();
  157.         activateHumanPlayer();
  158.         return true;
  159.     } else if (moveStr == "undo") {
  160.         if (currentMove > 0) {
  161.             pos.unMakeMove(moveList[currentMove - 1], uiInfoList[currentMove - 1]);
  162.             currentMove--;
  163.             pendingDrawOffer = false;
  164.             drawState = ALIVE;
  165.             resignState = ALIVE;
  166.             return handleCommand("swap");
  167.         } else {
  168.             std::cout << "Nothing to undo" << std::endl;
  169.         }
  170.         return true;
  171.     } else if (moveStr == "redo") {
  172.         if (currentMove < (int)moveList.size()) {
  173.             pos.makeMove(moveList[currentMove], uiInfoList[currentMove]);
  174.             currentMove++;
  175.             pendingDrawOffer = false;
  176.             return handleCommand("swap");
  177.         } else {
  178.             std::cout << "Nothing to redo" << std::endl;
  179.         }
  180.         return true;
  181.     } else if (moveStr == "swap" || moveStr == "go") {
  182.         std::swap(whitePlayer, blackPlayer);
  183.         return true;
  184.     } else if (moveStr == "list") {
  185.         listMoves();
  186.         return true;
  187.     } else if (startsWith(moveStr, "setpos ")) {
  188.         std::string fen = moveStr.substr(moveStr.find_first_of(' ') + 1);
  189.         try {
  190.             Position newPos(TextIO::readFEN(fen));
  191.             handleCommand("new");
  192.             pos = newPos;
  193.             activateHumanPlayer();
  194.         } catch (const ChessParseError& ex) {
  195.             std::cout << "Invalid FEN: " << fen << " (" << ex.what() << ")" << std::endl;
  196.         }
  197.         return true;
  198.     } else if (moveStr == "getpos") {
  199.         std::string fen(TextIO::toFEN(pos));
  200.         std::cout << fen << std::endl;
  201.         return true;
  202.     } else if (startsWith(moveStr, "draw ")) {
  203.         if (getGameState() == ALIVE) {
  204.             std::string drawCmd = moveStr.substr(moveStr.find_first_of(' ') + 1);
  205.             return handleDrawCmd(drawCmd);
  206.         } else {
  207.             return true;
  208.         }
  209.     } else if (moveStr == "resign") {
  210.         if (getGameState()== ALIVE) {
  211.             resignState = pos.isWhiteMove() ? RESIGN_WHITE : RESIGN_BLACK;
  212.             return true;
  213.         } else {
  214.             return true;
  215.         }
  216.     } else if (startsWith(moveStr, "book")) {
  217.         std::string bookCmd = moveStr.substr(moveStr.find_first_of(' ') + 1);
  218.         return handleBookCmd(bookCmd);
  219.     } else if (startsWith(moveStr, "time")) {
  220.         std::string timeStr = moveStr.substr(moveStr.find_first_of(' ') + 1);
  221.         int timeLimit;
  222.         if (!str2Num(timeStr, timeLimit)) {
  223.             std::cout << "Can not parse number: " << timeStr << std::endl;
  224.             return false;
  225.         }
  226.         whitePlayer->timeLimit(timeLimit, timeLimit);
  227.         blackPlayer->timeLimit(timeLimit, timeLimit);
  228.         return true;
  229.     } else if (startsWith(moveStr, "perft ")) {
  230.         std::string depthStr = moveStr.substr(moveStr.find_first_of(' ') + 1);
  231.         int depth;
  232.         if (!str2Num(depthStr, depth)) {
  233.             std::cout << "Can not parse number: " << depthStr << std::endl;
  234.             return false;
  235.         }
  236.         S64 t0 = currentTimeMillis();
  237.         U64 nodes = perfT(pos, depth);
  238.         S64 t1 = currentTimeMillis();
  239.         double t = (t1 - t0) * 1e-3;
  240.         std::stringstream ss;
  241.         ss.precision(3);
  242.         std::cout << "perft(" << depth << ") = " << nodes << ", t="
  243.                   << (ss << std::fixed << t).rdbuf()
  244.                   << "s" << std::endl;
  245.         return true;
  246.     } else {
  247.         return false;
  248.     }
  249. }
  250.  
  251. void
  252. Game::activateHumanPlayer() {
  253.     if (!(pos.isWhiteMove() ? whitePlayer : blackPlayer)->isHumanPlayer())
  254.         std::swap(whitePlayer, blackPlayer);
  255. }
  256.  
  257. void
  258. Game::getPosHistory(std::vector<std::string> ret) {
  259.     ret.clear();
  260.     Position pos(this->pos);
  261.     for (int i = currentMove; i > 0; i--)
  262.         pos.unMakeMove(moveList[i - 1], uiInfoList[i - 1]);
  263.     ret.push_back(TextIO::toFEN(pos)); // Store initial FEN
  264.  
  265.     std::string moves;
  266.     UndoInfo ui;
  267.     for (size_t i = 0; i < moveList.size(); i++) {
  268.         const Move& move = moveList[i];
  269.         std::string strMove(TextIO::moveToString(pos, move, false));
  270.         moves += ' ';
  271.         moves += strMove;
  272.         pos.makeMove(move, ui);
  273.     }
  274.     ret.push_back(moves); // Store move list string
  275.  
  276.     int numUndo = (int)moveList.size() - currentMove;
  277.     ret.push_back(num2Str(numUndo));
  278. }
  279.  
  280. std::string
  281. Game::getMoveListString(bool compressed) {
  282.     std::string ret;
  283.  
  284.     // Undo all moves in move history.
  285.     Position pos(this->pos);
  286.     for (int i = currentMove; i > 0; i--)
  287.         pos.unMakeMove(moveList[i - 1], uiInfoList[i - 1]);
  288.  
  289.     // Print all moves
  290.     std::string whiteMove;
  291.     std::string blackMove;
  292.     for (int i = 0; i < currentMove; i++) {
  293.         const Move& move = moveList[i];
  294.         std::string strMove = TextIO::moveToString(pos, move, false);
  295.         if (drawOfferList[i])
  296.             strMove += " (d)";
  297.         if (pos.isWhiteMove()) {
  298.             whiteMove = strMove;
  299.         } else {
  300.             blackMove = strMove;
  301.             if (whiteMove.length() == 0)
  302.                 whiteMove = "...";
  303.             if (compressed) {
  304.                 ret += num2Str(pos.getFullMoveCounter()) + ". " + whiteMove +
  305.                        " " + blackMove + " ";
  306.             } else {
  307.                 std::stringstream ss;
  308.                 ss << std::setw(3) << pos.getFullMoveCounter() << ".  "
  309.                    << std::setw(10) << std::left << whiteMove << " "
  310.                    << std::setw(10) << std::left << blackMove << std::endl;
  311.                 ret += ss.str();
  312.             }
  313.             whiteMove.clear();
  314.             blackMove.clear();
  315.         }
  316.         UndoInfo ui;
  317.         pos.makeMove(move, ui);
  318.     }
  319.     if ((whiteMove.length() > 0) || (blackMove.length() > 0)) {
  320.         if (whiteMove.length() == 0)
  321.             whiteMove = "...";
  322.         if (compressed) {
  323.             ret += num2Str(pos.getFullMoveCounter()) + ". " + whiteMove +
  324.                    " " + blackMove + " ";
  325.         } else {
  326.             std::stringstream ss;
  327.             ss << std::setw(3) << pos.getFullMoveCounter() << ".  "
  328.                << std::setw(10) << std::left << whiteMove << " "
  329.                << std::setw(10) << std::left << blackMove << std::endl;
  330.             ret += ss.str();
  331.         }
  332.     }
  333.     std::string gameResult = getPGNResultString();
  334.     if (gameResult != "*") {
  335.         ret += gameResult;
  336.         if (!compressed)
  337.             ret += '\n';
  338.     }
  339.     return ret;
  340. }
  341.  
  342. std::string
  343. Game::getPGNResultString() {
  344.     std::string gameResult = "*";
  345.     switch (getGameState()) {
  346.     case ALIVE:
  347.         break;
  348.     case WHITE_MATE:
  349.     case RESIGN_BLACK:
  350.         gameResult = "1-0";
  351.         break;
  352.     case BLACK_MATE:
  353.     case RESIGN_WHITE:
  354.         gameResult = "0-1";
  355.         break;
  356.     case WHITE_STALEMATE:
  357.     case BLACK_STALEMATE:
  358.     case DRAW_REP:
  359.     case DRAW_50:
  360.     case DRAW_NO_MATE:
  361.     case DRAW_AGREE:
  362.         gameResult = "1/2-1/2";
  363.         break;
  364.     }
  365.     return gameResult;
  366. }
  367.  
  368. /** Return a list of previous positions in this game, back to the last "zeroing" move. */
  369. void
  370. Game::getHistory(std::vector<Position>& posList) {
  371.     posList.clear();
  372.     Position pos(this->pos);
  373.     for (int i = currentMove; i > 0; i--) {
  374.         if (pos.getHalfMoveClock() == 0)
  375.             break;
  376.         pos.unMakeMove(moveList[i - 1], uiInfoList[i - 1]);
  377.         posList.push_back(pos);
  378.     }
  379.     std::reverse(posList.begin(), posList.end());
  380. }
  381.  
  382. void
  383. Game::listMoves() {
  384.     std::string movesStr = getMoveListString(false);
  385.     std::cout << movesStr;
  386. }
  387.  
  388. bool
  389. Game::handleDrawCmd(std::string drawCmd) {
  390.     if (startsWith(drawCmd, "rep") || startsWith(drawCmd, "50")) {
  391.         bool rep = startsWith(drawCmd, "rep");
  392.         Move m;
  393.         size_t idx = drawCmd.find_first_of(' ');
  394.         std::string ms;
  395.         if (idx != drawCmd.npos) {
  396.             ms = drawCmd.substr(idx + 1);
  397.             if (ms.length() > 0)
  398.                 m = TextIO::stringToMove(pos, ms);
  399.         }
  400.         bool valid;
  401.         if (rep) {
  402.             valid = false;
  403.             std::vector<Position> oldPositions;
  404.             if (!m.isEmpty()) {
  405.                 UndoInfo ui;
  406.                 Position tmpPos(pos);
  407.                 tmpPos.makeMove(m, ui);
  408.                 oldPositions.push_back(tmpPos);
  409.             }
  410.             oldPositions.push_back(pos);
  411.             Position tmpPos(pos);
  412.             for (int i = currentMove - 1; i >= 0; i--) {
  413.                 tmpPos.unMakeMove(moveList[i], uiInfoList[i]);
  414.                 oldPositions.push_back(tmpPos);
  415.             }
  416.             int repetitions = 0;
  417.             Position firstPos = oldPositions[0];
  418.             for (size_t i = 0; i < oldPositions.size(); i++) {
  419.                 if (oldPositions[i].drawRuleEquals(firstPos))
  420.                     repetitions++;
  421.             }
  422.             if (repetitions >= 3) {
  423.                 valid = true;
  424.             }
  425.         } else {
  426.             Position tmpPos(pos);
  427.             if (!m.isEmpty()) {
  428.                 UndoInfo ui;
  429.                 tmpPos.makeMove(m, ui);
  430.             }
  431.             valid = tmpPos.getHalfMoveClock() >= 100;
  432.         }
  433.         if (valid) {
  434.             drawState = rep ? DRAW_REP : DRAW_50;
  435.             drawStateMoveStr.clear();
  436.             if (!m.isEmpty())
  437.                 drawStateMoveStr = TextIO::moveToString(pos, m, false);
  438.         } else {
  439.             pendingDrawOffer = true;
  440.             if (!m.isEmpty())
  441.                 processString(ms);
  442.         }
  443.         return true;
  444.     } else if (startsWith(drawCmd, "offer ")) {
  445.         pendingDrawOffer = true;
  446.         size_t idx = drawCmd.find_first_of(' ');
  447.         if (idx != drawCmd.npos) {
  448.             std::string ms = drawCmd.substr(idx + 1);
  449.             if (!TextIO::stringToMove(pos, ms).isEmpty())
  450.                 processString(ms);
  451.         }
  452.         return true;
  453.     } else if (drawCmd == "accept") {
  454.         if (haveDrawOffer())
  455.             drawState = DRAW_AGREE;
  456.         return true;
  457.     } else {
  458.         return false;
  459.     }
  460. }
  461.  
  462. bool
  463. Game::handleBookCmd(const std::string& bookCmd) {
  464.     if (bookCmd == "off") {
  465.         whitePlayer->useBook(false);
  466.         blackPlayer->useBook(false);
  467.         return true;
  468.     } else if (bookCmd == "on") {
  469.         whitePlayer->useBook(true);
  470.         whitePlayer->useBook(true);
  471.         return true;
  472.     }
  473.     return false;
  474. }
  475.  
  476. bool
  477. Game::insufficientMaterial() {
  478.     if (pos.pieceTypeBB(Piece::WQUEEN) != 0) return false;
  479.     if (pos.pieceTypeBB(Piece::WROOK)  != 0) return false;
  480.     if (pos.pieceTypeBB(Piece::WPAWN)  != 0) return false;
  481.     if (pos.pieceTypeBB(Piece::BQUEEN) != 0) return false;
  482.     if (pos.pieceTypeBB(Piece::BROOK)  != 0) return false;
  483.     if (pos.pieceTypeBB(Piece::BPAWN)  != 0) return false;
  484.     int wb = BitBoard::bitCount(pos.pieceTypeBB(Piece::WBISHOP));
  485.     int wn = BitBoard::bitCount(pos.pieceTypeBB(Piece::WKNIGHT));
  486.     int bb = BitBoard::bitCount(pos.pieceTypeBB(Piece::BBISHOP));
  487.     int bn = BitBoard::bitCount(pos.pieceTypeBB(Piece::BKNIGHT));
  488.     if (wb + wn + bb + bn <= 1)
  489.         return true;    // King + bishop/knight vs king is draw
  490.     if (wn + bn == 0) {
  491.         // Only bishops. If they are all on the same color, the position is a draw.
  492.         U64 bMask = pos.pieceTypeBB(Piece::WBISHOP) | pos.pieceTypeBB(Piece::BBISHOP);
  493.         if (((bMask & BitBoard::maskDarkSq) == 0) ||
  494.                 ((bMask & BitBoard::maskLightSq) == 0))
  495.             return true;
  496.     }
  497.     return false;
  498. }
  499.  
  500. U64
  501. Game::perfT(Position& pos, int depth) {
  502.     if (depth == 0)
  503.         return 1;
  504.     U64 nodes = 0;
  505.     MoveList moves;
  506.     MoveGen::pseudoLegalMoves(pos, moves);
  507.     MoveGen::removeIllegal(pos, moves);
  508.     if (depth == 1)
  509.         return moves.size;
  510.     UndoInfo ui;
  511.     for (int mi = 0; mi < moves.size; mi++) {
  512.         const Move& m = moves[mi];
  513.         pos.makeMove(m, ui);
  514.         nodes += perfT(pos, depth - 1);
  515.         pos.unMakeMove(m, ui);
  516.     }
  517.     return nodes;
  518. }
  519.