ソースを参照

quiescence search now works!

Nicolas Winkler 7 年 前
コミット
841ac96342
8 ファイル変更195 行追加31 行削除
  1. 4 0
      src/ChessGame.cpp
  2. 81 0
      src/Evaluate.cpp
  3. 28 0
      src/Evaluate.h
  4. 13 8
      src/Minimax.cpp
  5. 6 2
      src/Minimax.h
  6. 35 12
      src/TimeManager.cpp
  7. 7 5
      src/TimeManager.h
  8. 21 4
      src/UciParser.cpp

+ 4 - 0
src/ChessGame.cpp

@@ -32,6 +32,10 @@ void ChessGame::move(Move move)
     MoveInfo mi;
     mi.move = move;
     mi.movedPiece = board.getAtPosition(move.origin);
+    if (mi.movedPiece == PieceType::KING) {
+        if (abs(mi.move.origin.getColumn() - mi.move.destination.getColumn()) == 2)
+            mi.move.isCastling = true;
+    }
     doMove(mi);
 }
 

+ 81 - 0
src/Evaluate.cpp

@@ -0,0 +1,81 @@
+#include "Evaluate.h"
+
+using namespace chessy;
+
+
+
+namespace chessy
+{
+    namespace values
+    {
+        const MoveValue PAWN =      1.0f;
+        const MoveValue KNIGHT =    3.0f;
+        const MoveValue BISHOP =    3.0f;
+        const MoveValue ROOK =      5.0f;
+        const MoveValue QUEEN =     9.0f;
+    }
+}
+
+
+#include <random>
+static typename std::mt19937 e(1034345);
+static std::uniform_real_distribution<MoveValue> ee(0, 0.001);
+
+template<Side side>
+MoveValue chessy::evaluatePositives(const ChessGame& game)
+{
+    MoveValue piecePoints = 0;
+    const Board& bd = game.getBoard();
+    Bitboard p = bd.getPawns<side>();
+    Bitboard n = bd.getKnights<side>();
+    Bitboard b = bd.getBishops<side>();
+    Bitboard r = bd.getRooks<side>();
+    Bitboard q = bd.getQueens<side>();
+    Bitboard k = bd.getKing<side>();
+    piecePoints += values::PAWN *   p.popcount();
+    piecePoints += values::KNIGHT * n.popcount();
+    piecePoints += values::BISHOP * b.popcount();
+    piecePoints += values::ROOK *   r.popcount();
+    piecePoints += values::QUEEN *  q.popcount();
+
+    for (auto knight : PositionSet{ n })
+        piecePoints += KnightMoveGenerator{ knight, bd.get<side>() }.getBitboard().popcount() * 0.05;
+    for (auto bishop : PositionSet{ b })
+        piecePoints += PrimitiveBishopMoveGenerator{ bishop, bd.get<otherSide(side)>(), bd.get<side>() }.getBitboard().popcount() * 0.03;
+    for (auto rook : PositionSet{ r })
+        piecePoints += PrimitiveRookMoveGenerator{ rook, bd.get<otherSide(side)>(), bd.get<side>() }.getBitboard().popcount() * 0.03;
+    for (auto queen : PositionSet{ q })
+        piecePoints += PrimitiveQueenMoveGenerator{ queen, bd.get<otherSide(side)>(), bd.get<side>() }.getBitboard().popcount() * 0.012;
+
+    return piecePoints;// +ee(e);
+    /*
+    constexpr Side other = otherSide(side);
+    p = bd.getPawns<other>();
+    n = bd.getKnights<other>();
+    b = bd.getBishops<other>();
+    r = bd.getRooks<other>();
+    q = bd.getQueens<other>();
+    k = bd.getKing<other>();
+    piecePoints -= 1 * p.popcount();
+    piecePoints -= 3 * n.popcount();
+    piecePoints -= 3 * b.popcount();
+    piecePoints -= 5 * r.popcount();
+    piecePoints -= 9 * q.popcount();
+
+    //for (auto knight : PositionSet{ n })
+    //    piecePoints -= KnightMoveGenerator{ knight, bd.get<otherSide(side)>() }.getBitboard().popcount() * 0.2;
+    for (auto bishop : PositionSet{ b })
+    piecePoints -= PrimitiveBishopMoveGenerator{ bishop, bd.get<side>(), bd.get<otherSide(side)>() }.getBitboard().popcount() * 0.2;
+    */
+}
+
+
+MoveValue chessy::evaluate(const ChessGame& game)
+{
+    if (game.getTurn() == WHITE_SIDE)
+        return evaluatePositives<WHITE_SIDE>(game) -
+        evaluatePositives<BLACK_SIDE>(game);
+    else
+        return evaluatePositives<BLACK_SIDE>(game) -
+        evaluatePositives<WHITE_SIDE>(game);
+}

+ 28 - 0
src/Evaluate.h

@@ -0,0 +1,28 @@
+#ifndef CHESSY_EVALUATE_H
+#define CHESSY_EVALUATE_H
+
+#include "ChessGame.h"
+
+namespace chessy
+{
+    using MoveValue = float;
+
+    namespace values
+    {
+        extern const MoveValue PAWN;
+        extern const MoveValue KNIGHT;
+        extern const MoveValue BISHOP;
+        extern const MoveValue ROOK;
+        extern const MoveValue QUEEN;
+    }
+
+    template<Side side>
+    MoveValue evaluatePositives(const ChessGame& game);
+
+    MoveValue evaluate(const ChessGame& game);
+}
+
+
+
+#endif // CHESSY_MIrIMAX_H
+

+ 13 - 8
src/Minimax.cpp

@@ -112,7 +112,7 @@ size_t chessy::perft(std::ostream& out, ChessGame& cg, int depth)
 #include <iostream>
 using namespace std;
 
-std::pair<Move, MoveValue> chessy::miniMax(ChessGame& cg, int depth)
+std::pair<Move, MoveValue> chessy::miniMax(ChessGame& cg, int depth, const TimeCheck& check)
 {
     std::vector<Move> moves;
     moves.reserve(200);
@@ -145,14 +145,16 @@ std::pair<Move, MoveValue> chessy::miniMax(ChessGame& cg, int depth)
         }
         else {
             lastValidMove = move;
-            val = -negamaxImplementation(cg, depth - 1, -beta, -alpha);
+            val = -negamaxImplementation(cg, depth - 1, -beta, -alpha, check);
         }
-        cout << move.asString() << ": " << val << endl;
+        //cout << move.asString() << ": " << val << endl;
         cg.undoMove(ui);
         if(val > alpha) {
             alpha = val;
             bestMove = move;
         }
+        if (check())
+            break;
     }
     if (bestMove.origin == 0 && bestMove.destination == 0) {
         bestMove = lastValidMove;
@@ -162,11 +164,14 @@ std::pair<Move, MoveValue> chessy::miniMax(ChessGame& cg, int depth)
 
 
 MoveValue chessy::negamaxImplementation(ChessGame& cg, int depth,
-        chessy::MoveValue alpha, chessy::MoveValue beta)
+        chessy::MoveValue alpha, chessy::MoveValue beta, const TimeCheck& check)
 {
-    float x;
+    if (depth < 3) {
+        if (check())
+            return alpha;
+    }
     if (depth <= 0)
-        return x = quiescence(cg, 8, -beta, -alpha);
+        return quiescence(cg, 3, alpha, beta);
         //return evaluate(cg);
 
     const Board& b = cg.getBoard();
@@ -194,7 +199,7 @@ MoveValue chessy::negamaxImplementation(ChessGame& cg, int depth,
         if (isCheck(cg.getTurn()))
             val = -1e+30;
         else {
-            val = -negamaxImplementation(cg, depth - 1, -beta, -alpha);
+            val = -negamaxImplementation(cg, depth - 1, -beta, -alpha, check);
             thereIsMove = true;
         }
         cg.undoMove(ui);
@@ -218,7 +223,7 @@ MoveValue chessy::quiescence(ChessGame& cg, int maxDepth,
     if (maxDepth <= 0)
         return standingPat;
 
-    if(standingPat >= beta)
+    if (standingPat >= beta)
         return beta;
     if(standingPat > alpha)
         alpha = standingPat;

+ 6 - 2
src/Minimax.h

@@ -7,10 +7,14 @@
 #include "MoveGeneration.h"
 #include "Evaluate.h"
 
+#include <functional>
+
 namespace chessy
 {
     using MoveValue = float;
 
+    using TimeCheck = std::function<bool()>;
+
     /*!
      * \brief conducts a performance test iterating through all positions
      *        up to a specified depth.
@@ -23,10 +27,10 @@ namespace chessy
      */
     size_t perft(std::ostream& out, ChessGame& chessGame, int depth);
 
-    std::pair<Move, MoveValue> miniMax(ChessGame& chessGame, int depth);
+    std::pair<Move, MoveValue> miniMax(ChessGame& chessGame, int depth, const TimeCheck& check);
 
     MoveValue negamaxImplementation(ChessGame& cg, int depth,
-            MoveValue alpha, MoveValue beta);
+            MoveValue alpha, MoveValue beta, const TimeCheck& check);
 
     MoveValue quiescence(ChessGame& cg, int maxDepth,
         MoveValue alpha, MoveValue beta);

+ 35 - 12
src/TimeManager.cpp

@@ -8,6 +8,7 @@ void TimeManager::startInfiniteSearch(void)
 
 void TimeManager::stop(void)
 {
+    shouldStop = true;
 }
 
 
@@ -28,30 +29,52 @@ bool TimeManager::isRunning(void) const
     return running;
 }
 
+chessy::Move TimeManager::getBestMove(void) const
+{
+    return bestMove;
+}
+
 
 void FixedSearchTimer::startSearch(void)
 {
-    /*TimeManager::searchThread = 
-        std::thread{ &FixedSearchTimer::search, this };
-    startPoint = std::chrono::steady_clock::now();*/
     startPoint = std::chrono::steady_clock::now();
-    search();
+    TimeManager::searchThread = 
+        std::thread{ &FixedSearchTimer::search, this };
+    /*using namespace std;
+    using namespace chrono;
+    startPoint = steady_clock::now();
+    search();*/
 }
 
 
 void FixedSearchTimer::search(void)
 {
-    chessy::Move best;
+    using namespace std;
+    using namespace chrono;
     int depth = 2;
+
+    auto timeCheck = [&]()
+    {
+        auto now = steady_clock::now();
+        if ((now - startPoint) > thinkTime)
+            shouldStop = true;
+        if (shouldStop)
+            return true;
+        else
+            return false;
+    };
+
     while (true) {
-        //best = minimax.calculateBest(depth);
-        ++depth;
-        
-        auto now = std::chrono::steady_clock::now();
-        if ((now - startPoint) > thinkTime) {
-            uci.sendCommand("bestmove", { best.asString() });
+        chessy::MoveValue value;
+        chessy::Move best;
+        tie(best, value) = chessy::miniMax(game, depth, timeCheck);
+        uci.write("info", "depth", depth, "currmove", best.asString(), "score", "cp", value);
+        if (best.destination != best.origin)
+            bestMove = best;
+        if (timeCheck())
             break;
-        }
+        ++depth;
     }
+    uci.write("bestmove", bestMove.asString());
 }
 

+ 7 - 5
src/TimeManager.h

@@ -14,19 +14,20 @@ namespace chessy
 
 class UciParser;
 
-
 class TimeManager
 {
-protected:
+public:
     using Millisecs = std::chrono::milliseconds;
+protected:
     chessy::ChessGame& game;
-    //chessy::MiniMax minimax;
-
-    bool running;
+    chessy::Move bestMove;
     std::thread searchThread;
 
     UciParser& uci;
 
+    bool running = false;
+    bool shouldStop = false;
+
 public:
     inline TimeManager(chessy::ChessGame& game, UciParser& uci) :
         game{ game }, uci{ uci } {}
@@ -37,6 +38,7 @@ public:
     virtual void join(void);
     virtual void startSearch(void);
     virtual bool isRunning(void) const;
+    virtual chessy::Move getBestMove(void) const;
 };
 
 

+ 21 - 4
src/UciParser.cpp

@@ -114,12 +114,14 @@ void UciParser::setoption(const vector<string>& args)
 
 void UciParser::ucinewgame(const vector<string>& args)
 {
+    stop({});
     return;
 }
 
 
 void UciParser::position(const vector<string>& args)
 {
+    stop({});
     try {
         size_t movesIndex = 0;
         if (args.at(0) == "fen") {
@@ -152,16 +154,28 @@ void UciParser::position(const vector<string>& args)
 
 void UciParser::go(const vector<string>& args)
 {
+    using namespace std;
+    using namespace chrono;
+
     stop({});
+    
+    milliseconds movetime(1000);
+    if (args.size() >= 2 && args[0] == "movetime") {
+        movetime = milliseconds(std::stoi(args[1]));
+    }
 
-    chessy::Move bestMove;
+    /*chessy::Move bestMove;
     chessy::MoveValue value;
-    int depth = 6;
+    int depth = 5;
     tie(bestMove, value) = chessy::miniMax(this->cg, depth);
 
-    write("info", "depth", depth, "score", "cp", value);
 
-    sendCommand("bestmove", { bestMove.asString() });
+    sendCommand("bestmove", { bestMove.asString() });*/
+
+    fst = make_unique<FixedSearchTimer>(cg, *this);
+    fst->setThinkTime(movetime);
+    fst->startSearch();
+
     /*fst = make_unique<FixedSearchTimer>(cg, *this);
 
     fst->setThinkTime(std::chrono::milliseconds{ 1000 });
@@ -192,18 +206,21 @@ void UciParser::stop(const vector<string>& args)
 
 void UciParser::quit(const vector<string>& args)
 {
+    stop({});
     quitting = true;
 }
 
 
 void UciParser::getfen(const vector<string>& args)
 {
+    stop({});
     out << cg.generateFen() << endl;
 }
 
 
 void UciParser::perft(const vector<string>& args)
 {
+    stop({});
     using chessy::perft;
     if (!args.empty()) {
         int depth = 0;