diff --git a/README.md b/README.md
index 8fa47e9..9cba5b0 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# Raphael
A GUI-based Chess Player as well as a Chess Engine, coded in C++ and [SFML](https://www.sfml-dev.org/).
-The engine is still a work in progress and will be updated as time goes by. [Scroll to the bottom](https://github.com/Orbital-Web/Raphael#raphael-1) to see a list of features currently implemented.
+The engine is still a work in progress and will be updated as time goes by (though it is already quite competent). Its main features are negamax with alpha-beta pruning, transposition table, and iterative deepening. [Scroll to the bottom](https://github.com/Orbital-Web/Raphael#raphael-1) to see a full list of features currently implemented.
@@ -46,16 +46,16 @@ The Human Player is an extension of `cge::GamePlayer` which will return a move b
### Raphael
-Raphael is an extension of `cge::GamePlayer` which will use a negamax search tree to return the best move it can find.
+Raphael is an extension of `cge::GamePlayer` which at its core uses a negamax search tree to return the best move it can find.
-**General Improvements**
+**General Optimizations**
- [x] Alpha-beta pruning (`v1.0.0+`)
- [x] Move ordering (`v1.0.0+`)
- [x] Transposition table (`v1.0.0+`)
-- [ ] Iterative deepening
+- [x] Iterative deepening (`v1.0.0+`)
- [ ] Opening book
-- [x] Quiescence with capture (`v1.0.0+`)
-- [ ] Quiescence with check
+- [x] Quiescence with captures (`v1.0.0+`)
+- [ ] Quiescence with checks
- [ ] Time management
- [ ] Pondering
diff --git a/src/GameEngine/GameEngine.hpp b/src/GameEngine/GameEngine.hpp
index 5481105..7c2722c 100644
--- a/src/GameEngine/GameEngine.hpp
+++ b/src/GameEngine/GameEngine.hpp
@@ -3,7 +3,6 @@
#include "chess.hpp"
#include
#include
-#include
#include
#include
#include
diff --git a/src/GameEngine/GamePlayer.hpp b/src/GameEngine/GamePlayer.hpp
index 49d5ba5..1db6cc4 100644
--- a/src/GameEngine/GamePlayer.hpp
+++ b/src/GameEngine/GamePlayer.hpp
@@ -18,7 +18,7 @@ class GamePlayer {
GamePlayer(std::string name_in): name(name_in) {}
// Returns a valid move string. Should return immediately if halt becomes true
- virtual chess::Move get_move(chess::Board board, int t_remain, sf::Event& event, bool& halt) = 0;
+ virtual chess::Move get_move(chess::Board board, float t_remain, sf::Event& event, bool& halt) = 0;
// macros
#define whiteturn (board.sideToMove() == chess::Color::WHITE) // [bool] curently white's turn
diff --git a/src/GameEngine/HumanPlayer.hpp b/src/GameEngine/HumanPlayer.hpp
index 3e4d90e..54e55b4 100644
--- a/src/GameEngine/HumanPlayer.hpp
+++ b/src/GameEngine/HumanPlayer.hpp
@@ -15,7 +15,7 @@ class HumanPlayer: public GamePlayer {
// Asks the human to return a move using the UI. Should return immediately if halt becomes true
- chess::Move get_move(chess::Board board, int t_remain, sf::Event& event, bool& halt) {
+ chess::Move get_move(chess::Board board, float t_remain, sf::Event& event, bool& halt) {
auto sq_from = chess::NO_SQ;
auto sq_to = chess::NO_SQ;
diff --git a/src/Raphael/Raphael_v1.0.0.hpp b/src/Raphael/Raphael_v1.0.0.hpp
index 8d58bdd..bc658c2 100644
--- a/src/Raphael/Raphael_v1.0.0.hpp
+++ b/src/Raphael/Raphael_v1.0.0.hpp
@@ -6,6 +6,8 @@
#include "../GameEngine/chess.hpp"
#include
#include
+#include
+#include
@@ -19,6 +21,7 @@ class v1_0_0: public cge::GamePlayer {
int score;
};
TranspositionTable tt;
+ chess::Move itermove; // best move from previous iteration
@@ -31,12 +34,70 @@ class v1_0_0: public cge::GamePlayer {
// Uses Negamax to return the best move. Should return immediately if halt becomes true
- chess::Move get_move(chess::Board board, int t_remain, sf::Event& event, bool& halt) {
+ chess::Move get_move(chess::Board board, float t_remain, sf::Event& event, bool& halt) {
tt.clear();
- return negamax(board, 4, -INT_MAX, INT_MAX, halt).move;
+ return iterative_deepening(board, t_remain, halt);
}
private:
+ // Uses iterative deepening on Negamax to find best move
+ chess::Move iterative_deepening(chess::Board& board, const float t_remain, bool& halt) {
+ int depth = 1;
+ itermove = chess::Move::NO_MOVE;
+ Searchres res = {0, 0};
+
+ // stop search after an appropriate duration
+ int duration = search_time(t_remain);
+ auto _ = std::async(manage_time, std::ref(halt), duration);
+
+ // begin iterative deepening
+ while (!halt) {
+ auto iterres = negamax(board, depth, -INT_MAX, INT_MAX, halt);
+
+ // not timeout
+ if (!halt)
+ res = iterres;
+
+ // checkmate, no need to continue
+ if (abs(res.score)>=1073641824) {
+ // get absolute evaluation (i.e, set to white's perspective)
+ if (!whiteturn == (res.score > 0))
+ depth *= -1;
+ printf("Eval: #%d\n", depth);
+ halt = true;
+ return res.move;
+ }
+
+ itermove = res.move;
+ depth++;
+ }
+ // get absolute evaluation (i.e, set to white's perspective)
+ if (!whiteturn)
+ res.score *= -1;
+ printf("Eval: %.2f\tDepth %d\n", res.score/100.0, depth-1);
+ return res.move;
+ }
+
+
+ // Estimates the time (ms) it should spend on searching a move
+ int search_time(const float t_remain) {
+ return 5000;
+ }
+
+
+ // Sets halt to true if duration (ms) passes
+ // Must be called asynchronously
+ static void manage_time(bool& halt, const int duration) {
+ auto start = std::chrono::high_resolution_clock::now();
+ while (!halt) {
+ auto now = std::chrono::high_resolution_clock::now();
+ auto dtime = std::chrono::duration_cast(now - start).count();
+ if (dtime >= duration)
+ halt = true;
+ }
+ }
+
+
// The Negamax search algorithm to search for the best move
Searchres negamax(chess::Board& board, unsigned int depth, int alpha, int beta, bool& halt) {
// timeout
@@ -164,8 +225,14 @@ class v1_0_0: public cge::GamePlayer {
// Assigns a score to the given move
void score_move(chess::Move& move, const chess::Board& board) const {
- int score = 0;
+ // prioritize best move from previous iteraton
+ if (move == itermove) {
+ move.setScore(INT16_MAX);
+ return;
+ }
+
// calculate score
+ int score = 0;
int from = (int)board.at(move.from());
int to = (int)board.at(move.to());