From 10c6852c67524e9a59833ab453f4c3d1621e177e Mon Sep 17 00:00:00 2001 From: sergeypospelov Date: Sat, 23 May 2020 14:53:24 +0300 Subject: [PATCH 01/15] Makefile now also depends on .hpp files --- Makefile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index b6b2ffa..20fa832 100644 --- a/Makefile +++ b/Makefile @@ -48,22 +48,22 @@ $(MAIN): $(OBJDIR) $(OBJDIR)/$(MAIN).o $(MAIN_TEST_MODEL): $(OBJDIR) $(OBJDIR)/$(MAIN_TEST_MODEL).o -$(OBJDIR)/%.o: $(SRCDIR_MODEL)/%.cpp +$(OBJDIR)/%.o: $(SRCDIR_MODEL)/%.cpp $(SRCDIR_MODEL)/%.hpp $(CXX) $(CXXFLAGS) $(INC) -c -MMD -o $@ $< -$(OBJDIR)/%.o: $(SRCDIR_NETWORK)/%.cpp +$(OBJDIR)/%.o: $(SRCDIR_NETWORK)/%.cpp $(SRCDIR_NETWORK)/%.hpp $(CXX) $(CXXFLAGS) $(INC) -c -MMD -o $@ $< -$(OBJDIR)/%.o: $(SRCDIR_UI)/%.cpp +$(OBJDIR)/%.o: $(SRCDIR_UI)/%.cpp $(SRCDIR_UI)/%.hpp $(CXX) $(CXXFLAGS) $(INC) -c -MMD -o $@ $< -$(OBJDIR)/%.o: $(SRCDIR_CONTROLLER)/%.cpp +$(OBJDIR)/%.o: $(SRCDIR_CONTROLLER)/%.cpp $(SRCDIR_CONTROLLER)/%.hpp $(CXX) $(CXXFLAGS) $(INC) -c -MMD -o $@ $< -$(OBJDIR)/%.o: $(SRCDIR_AI)/%.cpp +$(OBJDIR)/%.o: $(SRCDIR_AI)/%.cpp $(SRCDIR_AI)/%.hpp $(CXX) $(CXXFLAGS) $(INC) -c -MMD -o $@ $< -$(OBJDIR)/%.o: $(SRCDIR_TEST_MODEL)/%.cpp +$(OBJDIR)/%.o: $(SRCDIR_TEST_MODEL)/%.cpp $(SRCDIR_TEST_MODEL)/%.hpp $(CXX) $(CXXFLAGS) $(INC) -c -MMD -o $@ $< $(OBJDIR)/$(MAIN).o: $(MAIN).cpp From 4a93f40b2a84d0486be0c5437c398df21ed6e7cb Mon Sep 17 00:00:00 2001 From: Grigoryev Date: Sat, 23 May 2020 07:54:06 -0400 Subject: [PATCH 02/15] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=BF=D0=BE=D1=82=D0=BE=D0=BA=D0=B8=20=D0=B8=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=BC=D0=B5=D0=BD=D1=8F=D0=BB=20=D0=BE=D1=86=D0=B5=D0=BD?= =?UTF-8?q?=D0=BA=D1=83=20=D0=BF=D0=BE=D0=B7=D0=B8=D1=86=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ai/CompPlayer.cpp | 190 ++++++++++++++++++++++++++++---------------- ai/CompPlayer.hpp | 7 +- model/GameState.cpp | 16 ++-- model/GameState.hpp | 5 +- 4 files changed, 138 insertions(+), 80 deletions(-) diff --git a/ai/CompPlayer.cpp b/ai/CompPlayer.cpp index a60f579..0c58f1c 100644 --- a/ai/CompPlayer.cpp +++ b/ai/CompPlayer.cpp @@ -1,6 +1,6 @@ #include "CompPlayer.hpp" #include -#include +#include static const int INF = (int)1e9; @@ -32,8 +32,44 @@ bool CompPlayer::send_move(const BoardCell &from, const BoardCell &to) { return false; } -int CompPlayer::score(GameState G) const { - int ordw = 0, ordb = 0, queenw = 0, queenb = 0, killw = 0, killb = 0; +int CompPlayer::score(GameState G) { + int white_position[8][8] = + { + {64, 63, 62, 61, 60, 59, 58, 57}, + {56, 55, 54, 53, 52, 51, 50, 49}, + {48, 47, 46, 45, 44, 43, 42, 41}, + {40, 39, 38, 37, 36, 35, 34, 33}, + {32, 31, 30, 29, 28, 27, 26, 25}, + {24, 23, 22, 21, 20, 19, 18, 17}, + {16, 15, 14, 13, 12, 11, 10, 9}, + {8, 7, 6, 5, 4, 3, 2, 1} + }; + + int black_position[8][8] = + { + {1, 2, 3, 4, 5, 6, 7, 8}, + {9, 10, 11, 12, 13, 14, 15, 16}, + {17, 18, 19, 20, 21, 22, 23, 24}, + {25, 26, 27, 28, 29, 30, 31, 32}, + {33, 34, 35, 36, 37, 38, 39, 40}, + {41, 42, 43, 44, 45, 46, 47, 48}, + {49, 50, 51, 52, 53, 54, 55, 56}, + {57, 58, 59, 60, 61, 62, 63, 64} + }; + + int only_queen[8][8] = + { + {1, 2, 3, 4, 4, 3, 2, 1}, + {9, 10, 11, 12, 12, 11, 10, 9}, + {17, 18, 19, 20, 20, 19, 18, 17}, + {25, 26, 27, 28, 28, 27, 26, 25}, + {25, 26, 27, 28, 28, 27, 26, 25}, + {17, 18, 19, 20, 20, 19, 18, 17}, + {9, 10, 11, 12, 12, 11, 10, 9}, + {1, 2, 3, 4, 4, 3, 2, 1} + }; + int ordw = 0, ordb = 0, queenw = 0, queenb = 0; + int sum_with_w = 0, sum_with_b = 0, sum_out_w = 0, sum_out_b = 0; for (int i = 0; i < G.SIZE; i++) for (int j = 0; j < G.SIZE; j++) { if (G.board[i][j] == 'w') @@ -44,105 +80,125 @@ int CompPlayer::score(GameState G) const { queenw++; if (G.board[i][j] == 'B') queenb++; - if ((G.board[i][j] == 'w' || G.board[i][j] == 'W') && - G.kill(FIRST, BoardCell(i, j))) - killw++; - if ((G.board[i][j] == 'b' || G.board[i][j] == 'B') && - G.kill(SECOND, BoardCell(i, j))) - killb++; + if (G.board[i][j] == 'w' || G.board[i][j] == 'W') + sum_with_w += white_position[i][j], sum_out_w += only_queen[i][j]; + if (G.board[i][j] == 'b' || G.board[i][j] == 'B') + sum_with_b += black_position[i][j], sum_out_b += only_queen[i][j]; } - return ordw - ordb + (2 * queenw + 5) * (2 * queenw + 5) * (2 * queenw + 5) - (2 * queenb + 5) * (2 * queenb + 5) * (2 * queenb + 5) + (killw + 7) * (killw + 7) - (killb + 7) * (killb + 7); + if (ordw + ordb > 0) + return 100 * (ordw - ordb) + 1000 * (queenw - queenb) + sum_with_w - sum_with_b; + else + return 1000 * (queenw - queenb) + sum_out_w - sum_out_b; } -std::pair CompPlayer::alpha_beta(GameState G, int alpha, int beta, - clock_t start_time, int seconds, - int deep, std::mt19937 gen) const { +void CompPlayer::alpha_beta(GameState G, int alpha, int beta, clock_t start_time, int seconds, + int deep, std::mt19937 gen, std::pair &total, bool flow) { state current = G.check_win(); if (current != GAME) { if (current == DRAW) - return std::make_pair(0, Move()); + total = std::make_pair(0, Move()); if (current == FIRST_WIN) - return std::make_pair(INF, Move()); - return std::make_pair(-INF, Move()); + total = std::make_pair(INF, Move()); + if (current == SECOND_WIN) + total = std::make_pair(-INF, Move()); + return; } - if (deep == 0) - return std::make_pair(score(G), Move()); - clock_t current_time = clock(); - if (1.0 * (current_time - start_time) / CLOCKS_PER_SEC > seconds) - return std::make_pair(score(G), Move()); - + if (deep == 0) { + total = std::make_pair(score(G), Move()); + return; + } + //clock_t current_time = clock(); + //if (1.0 * (current_time - start_time) / CLOCKS_PER_SEC > seconds) + //return std::make_pair(score(G), Move()); + number_of_player player = G.who_moves(); - std::vector kill; - std::vector ordinary; + std::vector moves; for (int i = 0; i < G.SIZE; i++) for (int j = 0; j < G.SIZE; j++) { - std::vector correct = - G.get_list_of_correct_moves(player, BoardCell(i, j)); - for (int z = 0; z < (int)correct.size(); z++) { - if (G.is_kill(player, BoardCell(i, j), correct[z])) - kill.push_back(Move(BoardCell(i, j), correct[z])); - else - ordinary.push_back(Move(BoardCell(i, j), correct[z])); - } + std::vector correct = G.get_list_of_correct_moves(player, BoardCell(i, j)); + for (int z = 0; z < (int)correct.size(); z++) + moves.push_back(Move(BoardCell(i, j), correct[z])); } - shuffle(kill.begin(), kill.end(), gen); - shuffle(ordinary.begin(), ordinary.end(), gen); + shuffle(moves.begin(), moves.end(), gen); + Move best_move; int current_score = (player == FIRST ? -INF : INF); - for (int i = 0; i < (int)kill.size(); i++) { + bool fl = (G.find_kill(player) == BoardCell(-1, -1)); + + if (flow) { + std::vector act; + std::vector > res((int)moves.size()); + for (int i = 0; i < (int)moves.size(); i++) { GameState cop = G; - cop.move(player, kill[i].from, kill[i].to); - std::pair result = - alpha_beta(cop, alpha, beta, start_time, seconds, deep, gen); - if (player == FIRST) { - if (result.first >= current_score) { - best_move = kill[i]; - current_score = result.first; - } - alpha = std::max(alpha, result.first); - if (alpha > beta) - return std::make_pair(alpha, kill[i]); - } else { - if (result.first <= current_score) { - best_move = kill[i]; - current_score = result.first; + cop.move(player, moves[i].from, moves[i].to); + act.push_back(std::thread(alpha_beta, cop, alpha, beta, start_time, seconds, deep - fl, gen, std::ref(res[i]), false)); + } + for (int i = 0; i < (int)moves.size(); i++) + act[i].join(); + + for (int i = 0; i < (int)moves.size(); i++) { + if (player == FIRST) { + if (res[i].first >= current_score) { + best_move = moves[i]; + current_score = res[i].first; + } + alpha = std::max(alpha, res[i].first); + if (alpha > beta) { + total = std::make_pair(alpha, moves[i]); + return; + } + } else { + if (res[i].first <= current_score) { + best_move = moves[i]; + current_score = res[i].first; + } + beta = std::min(beta, res[i].first); + if (alpha > beta) { + total = std::make_pair(beta, moves[i]); + return; + } } - beta = std::min(beta, result.first); - if (alpha > beta) - return std::make_pair(beta, kill[i]); } + total = std::make_pair(current_score, best_move); + return; } - for (int i = 0; i < (int)ordinary.size(); i++) { + + for (int i = 0; i < (int)moves.size(); i++) { GameState cop = G; - cop.move(player, ordinary[i].from, ordinary[i].to); - std::pair result = - alpha_beta(cop, alpha, beta, start_time, seconds, deep - 1, gen); + cop.move(player, moves[i].from, moves[i].to); + std::pair result; + alpha_beta(cop, alpha, beta, start_time, seconds, deep - fl, gen, result, false); if (player == FIRST) { if (result.first >= current_score) { - best_move = ordinary[i]; + best_move = moves[i]; current_score = result.first; } alpha = std::max(alpha, result.first); - if (alpha > beta) - return std::make_pair(alpha, ordinary[i]); + if (alpha > beta) { + total = std::make_pair(alpha, moves[i]); + return; + } } else { if (result.first <= current_score) { - best_move = ordinary[i]; + best_move = moves[i]; current_score = result.first; } beta = std::min(beta, result.first); - if (alpha > beta) - return std::make_pair(beta, ordinary[i]); + if (alpha > beta) { + total = std::make_pair(beta, moves[i]); + return; + } } } - return std::make_pair(current_score, best_move); + total = std::make_pair(current_score, best_move); + return; } Move CompPlayer::get_next_move(GameState G, int seconds, int deep) const { std::mt19937 gen(time(0)); clock_t start = clock(); - std::pair result = - alpha_beta(G, -INF, INF, start, seconds, deep, gen); - std::cout << result.first << '\n'; + std::pair result; + alpha_beta(G, -INF, INF, start, seconds, deep, gen, result, true); + //std::cout << result.first << '\n'; return result.second; } diff --git a/ai/CompPlayer.hpp b/ai/CompPlayer.hpp index 9caf185..10a18f3 100644 --- a/ai/CompPlayer.hpp +++ b/ai/CompPlayer.hpp @@ -11,10 +11,9 @@ class CompPlayer : public controller::IPlayer { int seconds_; int deep_; - std::pair alpha_beta(GameState G, int alpha, int beta, - clock_t start_time, int seconds, int deep, - std::mt19937 gen) const; - int score(GameState G) const; + static void alpha_beta(GameState G, int alpha, int beta, clock_t start_time, int seconds, int deep, + std::mt19937 gen, std::pair &total, bool flow); + static int score(GameState G); Move get_next_move(GameState G, int seconds, int deep) const; mutable GameState gs; diff --git a/model/GameState.cpp b/model/GameState.cpp index 619b310..a802e7a 100644 --- a/model/GameState.cpp +++ b/model/GameState.cpp @@ -190,8 +190,10 @@ void GameState::move(number_of_player player, BoardCell from, BoardCell to) { last_move = to; } else { BoardCell pos = find_kill(player); - if (pos != BoardCell(-1, -1)) - board[pos.x][pos.y] = '.'; + if (pos != BoardCell(-1, -1)) { + return; // Запрещаю не рубить + board[pos.x][pos.y] = '.'; // Зафук + } std::swap(board[to.x][to.y], board[from.x][from.y]); who_last = player; if (board[from.x][from.y] == 'W' || board[from.x][from.y] == 'B') @@ -214,15 +216,15 @@ GameState::get_list_of_correct_moves(number_of_player player, return pos; if (player == who_last && from != last_move) return pos; - for (int i = 0; i < SIZE; i++) - for (int j = 0; j < SIZE; j++) - if (kill(player, BoardCell(i, j)) && !kill(player, from)) - return pos; + BoardCell S = find_kill(player); + bool fl = kill(player, from); + if (S != BoardCell(-1, -1) && !fl) //Запрещаем не рубить + return pos; for (int i = 0; i < SIZE; i++) for (int j = 0; j < SIZE; j++) if (check_move(player, from, BoardCell(i, j))) { - if ((kill(player, from) && is_kill(player, from, BoardCell(i, j))) || (!kill(player, from))) + if ((fl && is_kill(player, from, BoardCell(i, j))) || !fl) pos.push_back(BoardCell(i, j)); } return pos; diff --git a/model/GameState.hpp b/model/GameState.hpp index ad06271..c1b63ce 100644 --- a/model/GameState.hpp +++ b/model/GameState.hpp @@ -26,11 +26,12 @@ class GameState { bool inside(BoardCell cell) const; bool kill(number_of_player who, BoardCell pos) const; bool is_kill(number_of_player who, BoardCell from, BoardCell to) const; - BoardCell find_kill(number_of_player who) const; - + public: GameState(); + BoardCell find_kill(number_of_player who) const; + number_of_player who_moves() const; bool check_move(number_of_player player, BoardCell from, BoardCell to) const; void move(number_of_player player, BoardCell from, BoardCell to); From 82ed7871450d281da92c376a647ac3c98e97b718 Mon Sep 17 00:00:00 2001 From: Yurafobus1 Date: Sat, 23 May 2020 15:06:54 +0300 Subject: [PATCH 03/15] history 1.2 --- fonts/1.otf | Bin 0 -> 160620 bytes model/Game.cpp | 7 ++++- model/Game.hpp | 2 ++ ui/Graphics.cpp | 73 +++++++++++++++++++++++++++++++++--------------- ui/Graphics.hpp | 7 ++++- 5 files changed, 65 insertions(+), 24 deletions(-) create mode 100644 fonts/1.otf diff --git a/fonts/1.otf b/fonts/1.otf new file mode 100644 index 0000000000000000000000000000000000000000..b6c6ccb72499eb967fbcd8e0fb4ac874012932b5 GIT binary patch literal 160620 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

WS}Lni08E6H?fb9kJmS>V*M+M>FzSRVgKd>H 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" + std::to_string((8 -to.x) * 10 + to.y)); + } return; } diff --git a/model/Game.hpp b/model/Game.hpp index 38d9cf4..4f2430c 100644 --- a/model/Game.hpp +++ b/model/Game.hpp @@ -24,6 +24,8 @@ class Game { friend void XMLCALL endElement(void *userData, const XML_Char *name); friend class TestGame; + + std::vector natation; }; #endif diff --git a/ui/Graphics.cpp b/ui/Graphics.cpp index 776772c..542a2ee 100644 --- a/ui/Graphics.cpp +++ b/ui/Graphics.cpp @@ -86,6 +86,12 @@ void Gra::draw_SafeLoad(std::list &render_list, sf::Vector2f lu_point, void Gra::drawing() { for (auto &elem : render_list) { window.draw(elem.picture); + if (elem.data2 != "") { + sf::Text texting(elem.data2, font, 40); + texting.setFillColor(sf::Color::Black); + texting.setPosition(elem.picture.getPosition()); + window.draw(texting); + } } window.display(); @@ -101,7 +107,8 @@ void Gra::update(Game &game, controller::IPlayer *player) { if (state.who_moves() == player->turn) { draw_possible(render_list, state, past); } - draw_history(render_list, game.number_of_states()); + draw_history(render_list, game.number_of_states(), game); + draw_current_player(render_list, game); } void Gra::compiling_event(Game &game) { @@ -153,6 +160,10 @@ void Gra::compiling_event(Game &game) { if (past.x == -5) { press_the_scroll = 1; } + + if (past.x == -6) { + //ТУТА + } } if (event.type == sf::Event::MouseButtonReleased) { press_the_scroll = 0; @@ -191,7 +202,7 @@ Gra::Frame &Gra::collision(sf::Vector2f posi) { std::queue> &Gra::get_events() { return events; } void Gra::draw_turn(std::list &render_list, - std::string , int number, + std::string str, int number, sf::Vector2f lu_point, sf::Vector2f rd_point) { sf::Vector2f size = rd_point - lu_point; @@ -202,13 +213,7 @@ void Gra::draw_turn(std::list &render_list, block.setFillColor(sf::Color::Green); block.setOutlineColor(sf::Color::Black); block.setOutlineThickness(2); - sf::Text text; - /*text.setString(str); - block.setSize(size / 2); - block.setPosition({0, 0}); - block.move(lu_point + size / 4); - //text.getTransform());*/ - render_list.push_back(Frame(block, 1, {-3, number}, "")); + render_list.push_back(Frame(block, 1, {-3, number}, str)); lu_point.x += size.x * 7 / 8; @@ -220,7 +225,7 @@ void Gra::draw_turn(std::list &render_list, render_list.push_back(Frame(block, 1, {-4, number}, "")); } -void Gra::draw_history(std::list &render_list, int maximum, +void Gra::draw_history(std::list &render_list, int maximum, Game& game, sf::Vector2f lu_point , sf::Vector2f rd_point ) { float dx = rd_point.x - lu_point.y; @@ -233,8 +238,13 @@ void Gra::draw_history(std::list &render_list, int maximum, delta = delta / 8; rd_point.y = lu_point.y + delta.y; - for (int i = (int)(maximum * possition_of_scroll); i < std::min(maximum, (int)(maximum * possition_of_scroll) + 8); i++) { - draw_turn(render_list, "", i, lu_point, rd_point); + + int start = (maximum * possition_of_scroll); + start = std::min(start, std::max(0, game.number_of_states()- 8)); + int finish = start + (std::min(8, game.number_of_states())); + + for (int i = start; i < finish; i++) { + draw_turn(render_list, game.natation[i], i, lu_point, rd_point); rd_point += delta; lu_point += delta; } @@ -259,13 +269,32 @@ void Gra::draw_history(std::list &render_list, int maximum, } Gra::Gra() { - window.setFramerateLimit(60); - b.loadFromFile("./sprites/b.png"); - B.loadFromFile("./sprites/B.png"); - W.loadFromFile("./sprites/W.png"); - w.loadFromFile("./sprites/w.png"); - sprites.emplace('b', &b); - sprites.emplace('B', &B); - sprites.emplace('W', &W); - sprites.emplace('w', &w); - } + window.setFramerateLimit(60); + b.loadFromFile("./sprites/b.png"); + B.loadFromFile("./sprites/B.png"); + W.loadFromFile("./sprites/W.png"); + w.loadFromFile("./sprites/w.png"); + sprites.emplace('b', &b); + sprites.emplace('B', &B); + sprites.emplace('W', &W); + sprites.emplace('w', &w); + + font.loadFromFile("./fonts/1.otf"); +} + + +void Gra::draw_current_player(std::list &render_list, Game& game, + sf::Vector2f lu_point, + sf::Vector2f rd_point) { + sf::Vector2f size = rd_point - lu_point; + sf::RectangleShape block; + block.setSize(size); + block.setPosition({0, 0}); + block.move(lu_point); + block.setFillColor(sf::Color::Green); + render_list.push_back(Frame(block, 0, {-1, 0}, std::to_string(game.return_current_state().who_moves() + 1) + " is moving")); + + block.move({0, 60}); + block.setFillColor(sf::Color::Blue); + render_list.push_back(Frame(block, 1, {-6, 0}, "secret button")); +} \ No newline at end of file diff --git a/ui/Graphics.hpp b/ui/Graphics.hpp index 3d4df1b..60774a3 100644 --- a/ui/Graphics.hpp +++ b/ui/Graphics.hpp @@ -57,6 +57,8 @@ class Gra { float possition_of_scroll = 0; // [0, 0.9] bool press_the_scroll = 0; + sf::Font font; + public: sf::RenderWindow window = {sf::VideoMode(1280, 720), "Chess"}; void update(Game &game, controller::IPlayer *player); @@ -77,7 +79,7 @@ class Gra { void draw_table(std::list &rendrer_list, GameState &, int collision, sf::Vector2f lu_point = sf::Vector2f(320, 40), sf::Vector2f rd_point = sf::Vector2f(960, 680)); - void draw_history(std::list &render_list, int maximum, + void draw_history(std::list &render_list, int maximum, Game&, sf::Vector2f lu_point = sf::Vector2f(40, 40), sf::Vector2f rd_point = sf::Vector2f(280, 680)); void draw_possible(std::list &render_list, GameState &game_state, @@ -87,6 +89,9 @@ class Gra { void draw_SafeLoad(std::list &render_list, sf::Vector2f lu_point = sf::Vector2f(40, 40), sf::Vector2f rd_point = sf::Vector2f(280, 120)); + void draw_current_player(std::list &render_list, Game&, + sf::Vector2f lu_point = sf::Vector2f(1000, 330), + sf::Vector2f rd_point = sf::Vector2f(1240, 390)); }; #endif From 89d5f0013829701f0f8b2ca066b6140b1debbcd0 Mon Sep 17 00:00:00 2001 From: Sergey Pospelov Date: Sat, 23 May 2020 16:41:03 +0300 Subject: [PATCH 04/15] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 538bdb3..6b2b58c 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,10 @@ Project from our (with sergeypospelov and Yurafobus1) team at spring 2020. `sudo apt-get install libcppunit-dev` +Для установки библиотеки tclap надо в командной строке ввести: + +`sudo apt-get install libtclap-dev` + install SFML `sudo apt-get install libsfml-dev` From 4fba4bc1eb1e533c770300a5a9746157630a0a6b Mon Sep 17 00:00:00 2001 From: Yurafobus1 Date: Sat, 23 May 2020 17:19:49 +0300 Subject: [PATCH 05/15] hot fix DEPRECATED_WARNING --- ui/Graphics.cpp | 2 +- ui/Graphics.hpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/Graphics.cpp b/ui/Graphics.cpp index 542a2ee..a982a49 100644 --- a/ui/Graphics.cpp +++ b/ui/Graphics.cpp @@ -88,7 +88,7 @@ void Gra::drawing() { window.draw(elem.picture); if (elem.data2 != "") { sf::Text texting(elem.data2, font, 40); - texting.setFillColor(sf::Color::Black); + texting.setColor(sf::Color::Black); texting.setPosition(elem.picture.getPosition()); window.draw(texting); } diff --git a/ui/Graphics.hpp b/ui/Graphics.hpp index 60774a3..bbd0dea 100644 --- a/ui/Graphics.hpp +++ b/ui/Graphics.hpp @@ -1,6 +1,8 @@ #ifndef CHECKERS_UI_DRAW_SMF_HPP_ #define CHECKERS_UI_DRAW_SMF_HPP_ +#define SFML_NO_DEPRECATED_WARNINGS + #include "Event.hpp" #include "Game.hpp" #include From b651b552c67aa5ba84e04ce91e95a6258afc0919 Mon Sep 17 00:00:00 2001 From: Grigoryev Date: Sat, 23 May 2020 12:37:15 -0400 Subject: [PATCH 06/15] =?UTF-8?q?=D0=98=D1=89=D0=B5=D0=BC=20=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=B1=D0=BA=D1=83=20:(?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ai/CompPlayer.cpp | 84 ++++++++++++++++++++------------------------- ai/CompPlayer.hpp | 10 +++--- model/Game.cpp | 1 - model/GameState.cpp | 72 +++++++++++++++----------------------- model/GameState.hpp | 10 +++--- ui/Graphics.cpp | 5 +-- 6 files changed, 77 insertions(+), 105 deletions(-) diff --git a/ai/CompPlayer.cpp b/ai/CompPlayer.cpp index 0c58f1c..ce4ce72 100644 --- a/ai/CompPlayer.cpp +++ b/ai/CompPlayer.cpp @@ -1,9 +1,13 @@ #include "CompPlayer.hpp" #include +#include #include static const int INF = (int)1e9; +int cnt = 0; +double len; + CompPlayer::CompPlayer(number_of_player turn, int seconds, int deep) : controller::IPlayer(turn), seconds_(seconds), deep_(deep) {} @@ -32,7 +36,7 @@ bool CompPlayer::send_move(const BoardCell &from, const BoardCell &to) { return false; } -int CompPlayer::score(GameState G) { +int CompPlayer::score(const GameState &G) { int white_position[8][8] = { {64, 63, 62, 61, 60, 59, 58, 57}, @@ -91,8 +95,9 @@ int CompPlayer::score(GameState G) { return 1000 * (queenw - queenb) + sum_out_w - sum_out_b; } -void CompPlayer::alpha_beta(GameState G, int alpha, int beta, clock_t start_time, int seconds, - int deep, std::mt19937 gen, std::pair &total, bool flow) { +void CompPlayer::alpha_beta(const GameState &G, int alpha, int beta, + int deep, std::pair &total, bool flow) { + cnt++; state current = G.check_win(); if (current != GAME) { if (current == DRAW) @@ -111,32 +116,44 @@ void CompPlayer::alpha_beta(GameState G, int alpha, int beta, clock_t start_time //if (1.0 * (current_time - start_time) / CLOCKS_PER_SEC > seconds) //return std::make_pair(score(G), Move()); + clock_t st = clock(); number_of_player player = G.who_moves(); std::vector moves; for (int i = 0; i < G.SIZE; i++) for (int j = 0; j < G.SIZE; j++) { - std::vector correct = G.get_list_of_correct_moves(player, BoardCell(i, j)); + std::vector correct; + G.get_list_of_correct_moves(player, BoardCell(i, j), correct); for (int z = 0; z < (int)correct.size(); z++) moves.push_back(Move(BoardCell(i, j), correct[z])); } - shuffle(moves.begin(), moves.end(), gen); + std::reverse(moves.begin(), moves.end()); + clock_t fn = clock(); + len += 1.0 * (fn - st) / CLOCKS_PER_SEC; Move best_move; int current_score = (player == FIRST ? -INF : INF); bool fl = (G.find_kill(player) == BoardCell(-1, -1)); + std::vector > res((int)moves.size()); if (flow) { std::vector act; - std::vector > res((int)moves.size()); for (int i = 0; i < (int)moves.size(); i++) { - GameState cop = G; - cop.move(player, moves[i].from, moves[i].to); - act.push_back(std::thread(alpha_beta, cop, alpha, beta, start_time, seconds, deep - fl, gen, std::ref(res[i]), false)); + GameState cop = G; + cop.move(player, moves[i].from, moves[i].to); + act.push_back(std::thread(alpha_beta, std::ref(cop), alpha, beta, + deep - fl, std::ref(res[i]), false)); } for (int i = 0; i < (int)moves.size(); i++) act[i].join(); - - for (int i = 0; i < (int)moves.size(); i++) { + } + + for (int i = 0; i < (int)moves.size(); i++) { + if (!flow) { + GameState cop = G; + cop.move(player, moves[i].from, moves[i].to); + alpha_beta(cop, alpha, beta, deep - fl, res[i], false); + } + if (player == FIRST) { if (res[i].first >= current_score) { best_move = moves[i]; @@ -145,6 +162,7 @@ void CompPlayer::alpha_beta(GameState G, int alpha, int beta, clock_t start_time alpha = std::max(alpha, res[i].first); if (alpha > beta) { total = std::make_pair(alpha, moves[i]); + cnt++; return; } } else { @@ -155,50 +173,22 @@ void CompPlayer::alpha_beta(GameState G, int alpha, int beta, clock_t start_time beta = std::min(beta, res[i].first); if (alpha > beta) { total = std::make_pair(beta, moves[i]); + cnt++; return; } } } total = std::make_pair(current_score, best_move); return; - } - - for (int i = 0; i < (int)moves.size(); i++) { - GameState cop = G; - cop.move(player, moves[i].from, moves[i].to); - std::pair result; - alpha_beta(cop, alpha, beta, start_time, seconds, deep - fl, gen, result, false); - if (player == FIRST) { - if (result.first >= current_score) { - best_move = moves[i]; - current_score = result.first; - } - alpha = std::max(alpha, result.first); - if (alpha > beta) { - total = std::make_pair(alpha, moves[i]); - return; - } - } else { - if (result.first <= current_score) { - best_move = moves[i]; - current_score = result.first; - } - beta = std::min(beta, result.first); - if (alpha > beta) { - total = std::make_pair(beta, moves[i]); - return; - } - } - } - total = std::make_pair(current_score, best_move); - return; } -Move CompPlayer::get_next_move(GameState G, int seconds, int deep) const { - std::mt19937 gen(time(0)); - clock_t start = clock(); +Move CompPlayer::get_next_move(const GameState &G, int seconds, int deep) const { std::pair result; - alpha_beta(G, -INF, INF, start, seconds, deep, gen, result, true); - //std::cout << result.first << '\n'; + (void)seconds; + cnt = 0, len = 0; + clock_t start = clock(); + alpha_beta(G, -INF, INF, deep, result, false); + clock_t finish = clock(); + std::cout << 1.0 * (finish - start) / CLOCKS_PER_SEC << '\n' << len << '\n'; return result.second; } diff --git a/ai/CompPlayer.hpp b/ai/CompPlayer.hpp index 10a18f3..9f1ba76 100644 --- a/ai/CompPlayer.hpp +++ b/ai/CompPlayer.hpp @@ -3,18 +3,16 @@ #include "GameState.hpp" #include "Player.hpp" -#include -#include class CompPlayer : public controller::IPlayer { private: int seconds_; int deep_; - static void alpha_beta(GameState G, int alpha, int beta, clock_t start_time, int seconds, int deep, - std::mt19937 gen, std::pair &total, bool flow); - static int score(GameState G); - Move get_next_move(GameState G, int seconds, int deep) const; + static void alpha_beta(const GameState &G, int alpha, int beta, + int deep, std::pair &total, bool flow); + static int score(const GameState &G); + Move get_next_move(const GameState &G, int seconds, int deep) const; mutable GameState gs; diff --git a/model/Game.cpp b/model/Game.cpp index 39f88d1..73c08ac 100644 --- a/model/Game.cpp +++ b/model/Game.cpp @@ -31,7 +31,6 @@ void Game::move(number_of_player player, BoardCell from, BoardCell to) { current.move(player, from, to); if (current != copy) { game.push_back(current); - natation.push_back(std::to_string(number_of_states() - 1) + "." + std::to_string((8 - from.x) * 10 + from.y) + ">" + std::to_string((8 -to.x) * 10 + to.y)); } diff --git a/model/GameState.cpp b/model/GameState.cpp index a802e7a..3d4be72 100644 --- a/model/GameState.cpp +++ b/model/GameState.cpp @@ -90,57 +90,43 @@ bool GameState::check_move(number_of_player player, BoardCell from, return check_queen(player, from, to); } -bool GameState::kill(number_of_player who, BoardCell pos) const { - for (int i = 0; i < SIZE; i++) - for (int j = 0; j < SIZE; j++) { - if (!check_move(who, pos, BoardCell(i, j))) - continue; - BoardCell cur = BoardCell(i, j); - bool fl = false; - int dy = (i + j == pos.x + pos.y ? -1 : 1); - while (cur.x != pos.x) { - if (board[cur.x][cur.y] != '.') - fl = true; - if (cur.x < pos.x) - cur.x++, cur.y += dy; - else - cur.x--, cur.y -= dy; - } - if (fl) - return true; - } - return false; -} - -BoardCell GameState::find_kill(number_of_player who) const { - for (int i = 0; i < SIZE; i++) - for (int j = 0; j < SIZE; j++) - if (kill(who, BoardCell(i, j))) - return BoardCell(i, j); - return BoardCell(-1, -1); -} - bool GameState::is_kill(number_of_player who, BoardCell from, BoardCell to) const { if (!check_move(who, from, to)) return false; - bool fl = false; int dy = (from.x + from.y == to.x + to.y ? -1 : 1); BoardCell cur = to; while (cur.x != from.x) { if (board[cur.x][cur.y] != '.') - fl = true; + return true; if (cur.x < from.x) cur.x++, cur.y += dy; else cur.x--, cur.y -= dy; } - return fl; + return false; +} + +bool GameState::kill(number_of_player who, BoardCell pos) const { + for (int i = 0; i < SIZE; i++) + for (int j = 0; j < SIZE; j++) { + if (is_kill(who, pos, BoardCell(i, j))) + return true; + } + return false; +} + +BoardCell GameState::find_kill(number_of_player who) const { + for (int i = 0; i < SIZE; i++) + for (int j = 0; j < SIZE; j++) + if (kill(who, BoardCell(i, j))) + return BoardCell(i, j); + return BoardCell(-1, -1); } char GameState::get_cell(BoardCell cell) const { - assert(inside(cell)); + if (!inside(cell)) assert(0); return board[cell.x][cell.y]; } @@ -208,26 +194,24 @@ void GameState::move(number_of_player player, BoardCell from, BoardCell to) { return; } -std::vector -GameState::get_list_of_correct_moves(number_of_player player, - BoardCell from) const { - std::vector pos; +void GameState::get_list_of_correct_moves(number_of_player player, BoardCell from, + std::vector &moves) const { if (who_moves() != player) - return pos; + return; if (player == who_last && from != last_move) - return pos; + return; BoardCell S = find_kill(player); bool fl = kill(player, from); if (S != BoardCell(-1, -1) && !fl) //Запрещаем не рубить - return pos; + return; for (int i = 0; i < SIZE; i++) for (int j = 0; j < SIZE; j++) if (check_move(player, from, BoardCell(i, j))) { - if ((fl && is_kill(player, from, BoardCell(i, j))) || !fl) - pos.push_back(BoardCell(i, j)); + if (!fl || is_kill(player, from, BoardCell(i, j))) + moves.push_back(BoardCell(i, j)); } - return pos; + return; } state GameState::check_win() const { diff --git a/model/GameState.hpp b/model/GameState.hpp index c1b63ce..ab8601b 100644 --- a/model/GameState.hpp +++ b/model/GameState.hpp @@ -20,23 +20,23 @@ class GameState { BoardCell last_move; char board[SIZE][SIZE]; - bool check_ordinary(number_of_player player, BoardCell from, - BoardCell to) const; + bool check_ordinary(number_of_player player, BoardCell from, BoardCell to) const; bool check_queen(number_of_player player, BoardCell from, BoardCell to) const; bool inside(BoardCell cell) const; - bool kill(number_of_player who, BoardCell pos) const; bool is_kill(number_of_player who, BoardCell from, BoardCell to) const; + bool kill(number_of_player who, BoardCell pos) const; public: GameState(); + GameState(const GameState &oth) = default; + GameState& operator=(const GameState &oth) = default; BoardCell find_kill(number_of_player who) const; number_of_player who_moves() const; bool check_move(number_of_player player, BoardCell from, BoardCell to) const; void move(number_of_player player, BoardCell from, BoardCell to); - std::vector get_list_of_correct_moves(number_of_player player, - BoardCell from) const; + void get_list_of_correct_moves(number_of_player player, BoardCell from, std::vector &moves) const; state check_win() const; char get_cell(BoardCell cell) const; void show() const; diff --git a/ui/Graphics.cpp b/ui/Graphics.cpp index a982a49..4f3c701 100644 --- a/ui/Graphics.cpp +++ b/ui/Graphics.cpp @@ -57,7 +57,8 @@ void Gra::draw_possible(std::list &render_list, GameState &game_state, BoardCell past, sf::Vector2f lu_point, sf::Vector2f rd_point) { sf::Vector2f size = rd_point - lu_point; - auto b = game_state.get_list_of_correct_moves(game_state.who_moves(), past); + std::vector b; + game_state.get_list_of_correct_moves(game_state.who_moves(), past, b); for (auto &a : b) { sf::RectangleShape block; block.setSize(size / 8); @@ -297,4 +298,4 @@ void Gra::draw_current_player(std::list &render_list, Game& game, block.move({0, 60}); block.setFillColor(sf::Color::Blue); render_list.push_back(Frame(block, 1, {-6, 0}, "secret button")); -} \ No newline at end of file +} From 2cde441a96fb199f3280bfd5b1be06959cd9f101 Mon Sep 17 00:00:00 2001 From: Saveliy Grigoryev Date: Sat, 23 May 2020 22:57:46 +0500 Subject: [PATCH 07/15] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B0?= =?UTF-8?q?=20=D1=81=20=D0=BD=D0=B8=D1=87=D1=8C=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/GameState.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model/GameState.cpp b/model/GameState.cpp index 3d4be72..8715a65 100644 --- a/model/GameState.cpp +++ b/model/GameState.cpp @@ -180,12 +180,12 @@ void GameState::move(number_of_player player, BoardCell from, BoardCell to) { return; // Запрещаю не рубить board[pos.x][pos.y] = '.'; // Зафук } - std::swap(board[to.x][to.y], board[from.x][from.y]); who_last = player; if (board[from.x][from.y] == 'W' || board[from.x][from.y] == 'B') move_to_draw++; type_last = 0; last_move = to; + std::swap(board[to.x][to.y], board[from.x][from.y]); } if (player == FIRST && to.x == 0 && board[to.x][to.y] == 'w') board[to.x][to.y] = 'W'; From 93bb72101e46f0b91002f91fe8b92107ab36701f Mon Sep 17 00:00:00 2001 From: Grigoryev Date: Sat, 23 May 2020 18:40:03 -0400 Subject: [PATCH 08/15] =?UTF-8?q?=D0=A3=D1=81=D0=BA=D0=BE=D1=80=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5,=20=D1=80=D0=B0=D0=B1=D0=BE=D1=87=D0=B0?= =?UTF-8?q?=D1=8F=20=D0=B3=D0=BB=D1=83=D0=B1=D0=B8=D0=BD=D0=B0=20-=208?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ai/CompPlayer.cpp | 62 ++++++++++++++++-------- model/GameState.cpp | 113 ++++++++++++++++++++++++-------------------- model/GameState.hpp | 13 +++-- ui/Graphics.cpp | 5 +- 4 files changed, 115 insertions(+), 78 deletions(-) diff --git a/ai/CompPlayer.cpp b/ai/CompPlayer.cpp index ce4ce72..ea35db4 100644 --- a/ai/CompPlayer.cpp +++ b/ai/CompPlayer.cpp @@ -5,7 +5,7 @@ static const int INF = (int)1e9; -int cnt = 0; +int cnt1 = 0, cnt2 = 0; double len; CompPlayer::CompPlayer(number_of_player turn, int seconds, int deep) @@ -97,42 +97,65 @@ int CompPlayer::score(const GameState &G) { void CompPlayer::alpha_beta(const GameState &G, int alpha, int beta, int deep, std::pair &total, bool flow) { - cnt++; - state current = G.check_win(); - if (current != GAME) { - if (current == DRAW) - total = std::make_pair(0, Move()); - if (current == FIRST_WIN) - total = std::make_pair(INF, Move()); - if (current == SECOND_WIN) - total = std::make_pair(-INF, Move()); + cnt1++; + if (G.move_to_draw >= G.DRAW_MOVE) { + total = std::make_pair(0, Move()); return; } + if (deep == 0) { total = std::make_pair(score(G), Move()); return; } + + bool exist_white = false, exist_black = false; + for (int i = 0; i < G.SIZE; i++) + for (int j = 0; j < G.SIZE; j++) { + if (G.board[i][j] == 'w' || G.board[i][j] == 'W') + exist_white = true; + if (G.board[i][j] == 'b' || G.board[i][j] == 'B') + exist_black = true; + } + if (!exist_white || !exist_black) { + if (exist_white) + total = std::make_pair(INF, Move()); + else + total = std::make_pair(-INF, Move()); + return; + } + //clock_t current_time = clock(); //if (1.0 * (current_time - start_time) / CLOCKS_PER_SEC > seconds) //return std::make_pair(score(G), Move()); - clock_t st = clock(); number_of_player player = G.who_moves(); std::vector moves; - for (int i = 0; i < G.SIZE; i++) - for (int j = 0; j < G.SIZE; j++) { + int delta, up, down, lef, rig; + if (player == SECOND) + up = 0, down = G.SIZE, lef = 0, rig = G.SIZE, delta = 1; + else + up = G.SIZE - 1, down = -1, lef = G.SIZE - 1, rig = -1, delta = -1; + + for (int i = up; i != down; i += delta) + for (int j = lef; j != rig; j += delta) { std::vector correct; G.get_list_of_correct_moves(player, BoardCell(i, j), correct); for (int z = 0; z < (int)correct.size(); z++) moves.push_back(Move(BoardCell(i, j), correct[z])); } - std::reverse(moves.begin(), moves.end()); clock_t fn = clock(); len += 1.0 * (fn - st) / CLOCKS_PER_SEC; + if (moves.empty()) { + if (player == FIRST) + total = std::make_pair(-INF, Move()); + else + total = std::make_pair(INF, Move()); + return; + } Move best_move; int current_score = (player == FIRST ? -INF : INF); - bool fl = (G.find_kill(player) == BoardCell(-1, -1)); + bool fl = !G.find_kill(player); std::vector > res((int)moves.size()); if (flow) { @@ -162,7 +185,7 @@ void CompPlayer::alpha_beta(const GameState &G, int alpha, int beta, alpha = std::max(alpha, res[i].first); if (alpha > beta) { total = std::make_pair(alpha, moves[i]); - cnt++; + cnt2++; return; } } else { @@ -173,7 +196,7 @@ void CompPlayer::alpha_beta(const GameState &G, int alpha, int beta, beta = std::min(beta, res[i].first); if (alpha > beta) { total = std::make_pair(beta, moves[i]); - cnt++; + cnt2++; return; } } @@ -185,10 +208,11 @@ void CompPlayer::alpha_beta(const GameState &G, int alpha, int beta, Move CompPlayer::get_next_move(const GameState &G, int seconds, int deep) const { std::pair result; (void)seconds; - cnt = 0, len = 0; + cnt1 = 0, cnt2 = 0, len = 0; clock_t start = clock(); alpha_beta(G, -INF, INF, deep, result, false); clock_t finish = clock(); - std::cout << 1.0 * (finish - start) / CLOCKS_PER_SEC << '\n' << len << '\n'; + std::cout << 1.0 * (finish - start) / CLOCKS_PER_SEC << '\n' << len << '\n' << + cnt1 << ' ' << cnt2 << '\n'; return result.second; } diff --git a/model/GameState.cpp b/model/GameState.cpp index 3d4be72..b20920a 100644 --- a/model/GameState.cpp +++ b/model/GameState.cpp @@ -22,7 +22,8 @@ bool GameState::inside(BoardCell cell) const { } bool GameState::check_ordinary(number_of_player player, BoardCell from, - BoardCell to) const { + BoardCell to, bool &die) const { + die = false; if ((player == SECOND && board[from.x][from.y] == 'w') || (player == FIRST && board[from.x][from.y] == 'b')) return false; @@ -40,13 +41,16 @@ bool GameState::check_ordinary(number_of_player player, BoardCell from, for (int dy = -1; dy <= 1; dy += 2) if (to == BoardCell(from.x + dx * 2, from.y + dy * 2) && (board[from.x + dx][from.y + dy] == oth1 || - board[from.x + dx][from.y + dy] == oth2)) + board[from.x + dx][from.y + dy] == oth2)) { + die = true; return true; + } return false; } bool GameState::check_queen(number_of_player player, BoardCell from, - BoardCell to) const { + BoardCell to, bool &die) const { + die = false; if ((player == SECOND && board[from.x][from.y] == 'W') || (player == FIRST && board[from.x][from.y] == 'B')) return false; @@ -64,6 +68,7 @@ bool GameState::check_queen(number_of_player player, BoardCell from, else to.x--, to.y -= dy; } + die = (cntw + cntb > 0); if (player == FIRST) if (cntw == 0 && cntb <= 1) return true; @@ -76,7 +81,7 @@ bool GameState::check_queen(number_of_player player, BoardCell from, } bool GameState::check_move(number_of_player player, BoardCell from, - BoardCell to) const { + BoardCell to, bool &die) const { if (!inside(from) || !inside(to)) return false; if (board[from.x][from.y] == '.') @@ -85,46 +90,47 @@ bool GameState::check_move(number_of_player player, BoardCell from, return false; if (board[from.x][from.y] == 'w' || board[from.x][from.y] == 'b') - return check_ordinary(player, from, to); + return check_ordinary(player, from, to, die); - return check_queen(player, from, to); + return check_queen(player, from, to, die); } -bool GameState::is_kill(number_of_player who, BoardCell from, - BoardCell to) const { - if (!check_move(who, from, to)) +bool GameState::kill(number_of_player who, BoardCell pos) const { + char small = (who == FIRST ? 'w' : 'b'); + char big = (who == FIRST ? 'W' : 'B'); + if (!inside(pos) || (board[pos.x][pos.y] != small && board[pos.x][pos.y] != big)) + return false; + if (board[pos.x][pos.y] == small) { + for (int sgn1 = -1; sgn1 <= 1; sgn1 += 2) + for (int sgn2 = -1; sgn2 <= 1; sgn2 += 2) { + bool die = false; + if (check_move(who, pos, BoardCell(pos.x + 2 * sgn1, pos.y + 2 * sgn2), die)) + if (die) + return true; + } return false; - - int dy = (from.x + from.y == to.x + to.y ? -1 : 1); - BoardCell cur = to; - while (cur.x != from.x) { - if (board[cur.x][cur.y] != '.') - return true; - if (cur.x < from.x) - cur.x++, cur.y += dy; - else - cur.x--, cur.y -= dy; } + for (int c = 2; c <= SIZE; c++) + for (int sgn1 = -1; sgn1 <= 1; sgn1 += 2) + for (int sgn2 = -1; sgn2 <= 1; sgn2 += 2) { + bool die = false; + if (check_move(who, pos, BoardCell(pos.x + c * sgn1, pos.y + c * sgn2), die)) + if (die) + return true; + } return false; } -bool GameState::kill(number_of_player who, BoardCell pos) const { +bool GameState::find_kill(number_of_player who) const { + char small = (who == FIRST ? 'w' : 'b'); + char big = (who == FIRST ? 'W' : 'B'); for (int i = 0; i < SIZE; i++) - for (int j = 0; j < SIZE; j++) { - if (is_kill(who, pos, BoardCell(i, j))) + for (int j = 0; j < SIZE; j++) + if ((board[i][j] == small || board[i][j] == big) && kill(who, BoardCell(i, j))) return true; - } return false; } -BoardCell GameState::find_kill(number_of_player who) const { - for (int i = 0; i < SIZE; i++) - for (int j = 0; j < SIZE; j++) - if (kill(who, BoardCell(i, j))) - return BoardCell(i, j); - return BoardCell(-1, -1); -} - char GameState::get_cell(BoardCell cell) const { if (!inside(cell)) assert(0); return board[cell.x][cell.y]; @@ -148,9 +154,10 @@ number_of_player GameState::who_moves() const { } void GameState::move(number_of_player player, BoardCell from, BoardCell to) { + bool die = false; if (who_moves() != player) return; - if (!check_move(player, from, to)) + if (!check_move(player, from, to, die)) return; if (player == who_last) if (from != last_move) @@ -158,10 +165,7 @@ void GameState::move(number_of_player player, BoardCell from, BoardCell to) { int dy = (to.x + to.y == from.x + from.y ? -1 : 1); BoardCell cop = to; - bool die = false; while (cop != from) { - if (board[cop.x][cop.y] != '.') - die = true; board[cop.x][cop.y] = '.'; if (cop.x < from.x) cop.x++, cop.y += dy; @@ -175,17 +179,16 @@ void GameState::move(number_of_player player, BoardCell from, BoardCell to) { type_last = 1; last_move = to; } else { - BoardCell pos = find_kill(player); - if (pos != BoardCell(-1, -1)) { + if (find_kill(player)) { return; // Запрещаю не рубить - board[pos.x][pos.y] = '.'; // Зафук + //board[pos.x][pos.y] = '.'; // Зафук } - std::swap(board[to.x][to.y], board[from.x][from.y]); who_last = player; if (board[from.x][from.y] == 'W' || board[from.x][from.y] == 'B') move_to_draw++; type_last = 0; last_move = to; + std::swap(board[to.x][to.y], board[from.x][from.y]); } if (player == FIRST && to.x == 0 && board[to.x][to.y] == 'w') board[to.x][to.y] = 'W'; @@ -200,17 +203,20 @@ void GameState::get_list_of_correct_moves(number_of_player player, BoardCell fro return; if (player == who_last && from != last_move) return; - BoardCell S = find_kill(player); bool fl = kill(player, from); - if (S != BoardCell(-1, -1) && !fl) //Запрещаем не рубить + if (!fl && find_kill(player)) //Запрещаем не рубить return; - - for (int i = 0; i < SIZE; i++) - for (int j = 0; j < SIZE; j++) - if (check_move(player, from, BoardCell(i, j))) { - if (!fl || is_kill(player, from, BoardCell(i, j))) - moves.push_back(BoardCell(i, j)); + + int bound = (board[from.x][from.y] >= 'a' && board[from.x][from.y] <= 'z' ? 2 : SIZE); + for (int c = 1; c <= bound; c++) + for (int sgn1 = -1; sgn1 <= 1; sgn1 += 2) + for (int sgn2 = -1; sgn2 <= 1; sgn2 += 2) { + bool die = false; + if (check_move(player, from, BoardCell(from.x + c * sgn1, from.y + c * sgn2), die)) { + if (!fl || die) + moves.push_back(BoardCell(from.x + c * sgn1, from.y + c * sgn2)); } + } return; } @@ -218,12 +224,19 @@ state GameState::check_win() const { if (move_to_draw >= DRAW_MOVE) return DRAW; number_of_player player = who_moves(); + char small = (player == FIRST ? 'w' : 'b'); + char big = (player == FIRST ? 'W' : 'B'); for (int i1 = 0; i1 < SIZE; i1++) - for (int j1 = 0; j1 < SIZE; j1++) + for (int j1 = 0; j1 < SIZE; j1++) { + if (board[i1][j1] != small && board[i1][j1] != big) + continue; for (int i2 = 0; i2 < SIZE; i2++) - for (int j2 = 0; j2 < SIZE; j2++) - if (check_move(player, BoardCell(i1, j1), BoardCell(i2, j2))) + for (int j2 = 0; j2 < SIZE; j2++) { + bool die = false; + if (check_move(player, BoardCell(i1, j1), BoardCell(i2, j2), die)) return GAME; + } + } if (player == FIRST) return SECOND_WIN; else diff --git a/model/GameState.hpp b/model/GameState.hpp index ab8601b..feb0d05 100644 --- a/model/GameState.hpp +++ b/model/GameState.hpp @@ -20,26 +20,25 @@ class GameState { BoardCell last_move; char board[SIZE][SIZE]; - bool check_ordinary(number_of_player player, BoardCell from, BoardCell to) const; - bool check_queen(number_of_player player, BoardCell from, BoardCell to) const; + bool check_ordinary(number_of_player player, BoardCell from, BoardCell to, bool &die) const; + bool check_queen(number_of_player player, BoardCell from, BoardCell to, bool &die) const; bool inside(BoardCell cell) const; - bool is_kill(number_of_player who, BoardCell from, BoardCell to) const; bool kill(number_of_player who, BoardCell pos) const; - + void show() const; + public: GameState(); GameState(const GameState &oth) = default; GameState& operator=(const GameState &oth) = default; - BoardCell find_kill(number_of_player who) const; + bool find_kill(number_of_player who) const; number_of_player who_moves() const; - bool check_move(number_of_player player, BoardCell from, BoardCell to) const; + bool check_move(number_of_player player, BoardCell from, BoardCell to, bool &die) const; void move(number_of_player player, BoardCell from, BoardCell to); void get_list_of_correct_moves(number_of_player player, BoardCell from, std::vector &moves) const; state check_win() const; char get_cell(BoardCell cell) const; - void show() const; friend bool operator!=(const GameState &fir, const GameState &sec); diff --git a/ui/Graphics.cpp b/ui/Graphics.cpp index 4f3c701..5167584 100644 --- a/ui/Graphics.cpp +++ b/ui/Graphics.cpp @@ -135,8 +135,9 @@ void Gra::compiling_event(Game &game) { int y = res.data[0]; BoardCell next = {y, x}; - - if (game.return_current_state().check_move(game.return_current_state().who_moves(), past, next)) { + + bool die = false; + if (game.return_current_state().check_move(game.return_current_state().who_moves(), past, next, die)) { events.push(std::shared_ptr(new controller::MoveEvent(past, next))); } past = next; From a2a33d77a31e5fde71e59f89e8f9a47fe5a29f4d Mon Sep 17 00:00:00 2001 From: Saveliy Grigoryev <54453219+sava-cska@users.noreply.github.com> Date: Sat, 23 May 2020 18:46:09 -0400 Subject: [PATCH 09/15] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6b2b58c..7201093 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Project from our (with sergeypospelov and Yurafobus1) team at spring 2020. `sudo apt-get install libtclap-dev` -install SFML +Для установки библиотеки sfml надо в командной строке ввести: `sudo apt-get install libsfml-dev` From 07105bb3408d782051c676bedb26ff1a30982f97 Mon Sep 17 00:00:00 2001 From: sergeypospelov Date: Sun, 24 May 2020 13:07:16 +0300 Subject: [PATCH 10/15] removed trash --- model/GameState.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/model/GameState.cpp b/model/GameState.cpp index 6100da5..b20920a 100644 --- a/model/GameState.cpp +++ b/model/GameState.cpp @@ -188,11 +188,7 @@ void GameState::move(number_of_player player, BoardCell from, BoardCell to) { move_to_draw++; type_last = 0; last_move = to; -<<<<<<< HEAD std::swap(board[to.x][to.y], board[from.x][from.y]); -======= - std::swap(board[to.x][to.y], board[from.x][from.y]); ->>>>>>> 2cde441a96fb199f3280bfd5b1be06959cd9f101 } if (player == FIRST && to.x == 0 && board[to.x][to.y] == 'w') board[to.x][to.y] = 'W'; From 831bf67624fba4d8b8b7c2f6f0eec90d5310b0cd Mon Sep 17 00:00:00 2001 From: Grigoryev Date: Sun, 24 May 2020 11:45:11 -0400 Subject: [PATCH 11/15] =?UTF-8?q?=D0=9E=D1=88=D0=B8=D0=B1=D0=BE=D1=87?= =?UTF-8?q?=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/GameState.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/model/GameState.cpp b/model/GameState.cpp index 6100da5..b20920a 100644 --- a/model/GameState.cpp +++ b/model/GameState.cpp @@ -188,11 +188,7 @@ void GameState::move(number_of_player player, BoardCell from, BoardCell to) { move_to_draw++; type_last = 0; last_move = to; -<<<<<<< HEAD std::swap(board[to.x][to.y], board[from.x][from.y]); -======= - std::swap(board[to.x][to.y], board[from.x][from.y]); ->>>>>>> 2cde441a96fb199f3280bfd5b1be06959cd9f101 } if (player == FIRST && to.x == 0 && board[to.x][to.y] == 'w') board[to.x][to.y] = 'W'; From d903db0304b37edcabbdc8df5c96337244c7c94f Mon Sep 17 00:00:00 2001 From: Grigoryev Date: Sun, 24 May 2020 17:34:25 -0400 Subject: [PATCH 12/15] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BF=D1=80=D0=B5=D1=84=D0=B8=D0=BA=D1=81?= =?UTF-8?q?=D0=BD=D1=8B=D0=B5=20=D1=81=D1=83=D0=BC=D0=BC=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ai/CompPlayer.cpp | 16 ++- ai/CompPlayer.hpp | 1 + model/GameState.cpp | 129 +++++++++++++++---- model/GameState.hpp | 7 +- test_model/TestGameState.cpp | 234 ++++++++++++++++++++++------------- test_model/TestGameState.hpp | 4 + 6 files changed, 275 insertions(+), 116 deletions(-) diff --git a/ai/CompPlayer.cpp b/ai/CompPlayer.cpp index ea35db4..18eee13 100644 --- a/ai/CompPlayer.cpp +++ b/ai/CompPlayer.cpp @@ -127,15 +127,17 @@ void CompPlayer::alpha_beta(const GameState &G, int alpha, int beta, //clock_t current_time = clock(); //if (1.0 * (current_time - start_time) / CLOCKS_PER_SEC > seconds) //return std::make_pair(score(G), Move()); - clock_t st = clock(); + number_of_player player = G.who_moves(); + std::vector moves; int delta, up, down, lef, rig; if (player == SECOND) up = 0, down = G.SIZE, lef = 0, rig = G.SIZE, delta = 1; else up = G.SIZE - 1, down = -1, lef = G.SIZE - 1, rig = -1, delta = -1; - + + clock_t st = clock(); for (int i = up; i != down; i += delta) for (int j = lef; j != rig; j += delta) { std::vector correct; @@ -145,6 +147,7 @@ void CompPlayer::alpha_beta(const GameState &G, int alpha, int beta, } clock_t fn = clock(); len += 1.0 * (fn - st) / CLOCKS_PER_SEC; + if (moves.empty()) { if (player == FIRST) total = std::make_pair(-INF, Move()); @@ -165,9 +168,12 @@ void CompPlayer::alpha_beta(const GameState &G, int alpha, int beta, cop.move(player, moves[i].from, moves[i].to); act.push_back(std::thread(alpha_beta, std::ref(cop), alpha, beta, deep - fl, std::ref(res[i]), false)); + if ((i + 1) % NUMBER_OF_THREADS == 0) { + for (int j = 0; j < (int)act.size(); j++) + act[j].join(); + act.clear(); + } } - for (int i = 0; i < (int)moves.size(); i++) - act[i].join(); } for (int i = 0; i < (int)moves.size(); i++) { @@ -210,7 +216,7 @@ Move CompPlayer::get_next_move(const GameState &G, int seconds, int deep) const (void)seconds; cnt1 = 0, cnt2 = 0, len = 0; clock_t start = clock(); - alpha_beta(G, -INF, INF, deep, result, false); + alpha_beta(G, -INF, INF, deep, result, true); clock_t finish = clock(); std::cout << 1.0 * (finish - start) / CLOCKS_PER_SEC << '\n' << len << '\n' << cnt1 << ' ' << cnt2 << '\n'; diff --git a/ai/CompPlayer.hpp b/ai/CompPlayer.hpp index 9f1ba76..6fad9b2 100644 --- a/ai/CompPlayer.hpp +++ b/ai/CompPlayer.hpp @@ -16,6 +16,7 @@ class CompPlayer : public controller::IPlayer { mutable GameState gs; + static const int NUMBER_OF_THREADS = 4; public: CompPlayer(number_of_player, int seconds, int deep); diff --git a/model/GameState.cpp b/model/GameState.cpp index b20920a..8802b27 100644 --- a/model/GameState.cpp +++ b/model/GameState.cpp @@ -14,6 +14,34 @@ GameState::GameState() { board[1][j] = 'b', board[SIZE - 3][j] = 'w', board[SIZE - 1][j] = 'w'; for (int j = 1; j < SIZE; j += 2) board[0][j] = 'b', board[2][j] = 'b', board[SIZE - 2][j] = 'w'; + + for (int i = 0; i < SIZE; i++) + for (int j = 0; j < SIZE; j++) { + if (i == 0 || j == 0) + white_up_down[i][j] = black_up_down[i][j] = 0; + else { + white_up_down[i][j] = white_up_down[i - 1][j - 1]; + black_up_down[i][j] = black_up_down[i - 1][j - 1]; + } + if (board[i][j] == 'w' || board[i][j] == 'W') + white_up_down[i][j]++; + if (board[i][j] == 'b' || board[i][j] == 'B') + black_up_down[i][j]++; + } + + for (int i = SIZE - 1; i >= 0; i--) + for (int j = 0; j < SIZE; j++) { + if (i == SIZE - 1 || j == 0) + white_down_up[i][j] = black_down_up[i][j] = 0; + else { + white_down_up[i][j] = white_down_up[i + 1][j - 1]; + black_down_up[i][j] = black_down_up[i + 1][j - 1]; + } + if (board[i][j] == 'w' || board[i][j] == 'W') + white_down_up[i][j]++; + if (board[i][j] == 'b' || board[i][j] == 'B') + black_down_up[i][j]++; + } return; } @@ -56,25 +84,28 @@ bool GameState::check_queen(number_of_player player, BoardCell from, return false; if (to.x + to.y != from.x + from.y && to.x - to.y != from.x - from.y) return false; - - int cntb = 0, cntw = 0, dy = (to.x + to.y == from.x + from.y ? -1 : 1); - while (to != from) { - if (board[to.x][to.y] == 'w' || board[to.x][to.y] == 'W') - cntw++; - if (board[to.x][to.y] == 'b' || board[to.x][to.y] == 'B') - cntb++; - if (to.x < from.x) - to.x++, to.y += dy; - else - to.x--, to.y -= dy; + + int bal_white = 0, bal_black = 0; + if (from.x - from.y == to.x - to.y) { + if (from.y < to.y) + std::swap(from, to); + bal_white = white_up_down[from.x - 1][from.y - 1] - white_up_down[to.x][to.y]; + bal_black = black_up_down[from.x - 1][from.y - 1] - black_up_down[to.x][to.y]; + } + else { + if (from.y < to.y) + std::swap(from, to); + bal_white = white_down_up[from.x + 1][from.y - 1] - white_down_up[to.x][to.y]; + bal_black = black_down_up[from.x + 1][from.y - 1] - black_down_up[to.x][to.y]; } - die = (cntw + cntb > 0); + + die = (bal_white + bal_black > 0); if (player == FIRST) - if (cntw == 0 && cntb <= 1) + if (bal_white == 0 && bal_black <= 1) return true; else return false; - else if (cntb == 0 && cntw <= 1) + else if (bal_black == 0 && bal_white <= 1) return true; else return false; @@ -110,14 +141,18 @@ bool GameState::kill(number_of_player who, BoardCell pos) const { } return false; } - for (int c = 2; c <= SIZE; c++) - for (int sgn1 = -1; sgn1 <= 1; sgn1 += 2) - for (int sgn2 = -1; sgn2 <= 1; sgn2 += 2) { + + for (int sgn1 = -1; sgn1 <= 1; sgn1 += 2) + for (int sgn2 = -1; sgn2 <= 1; sgn2 += 2) { + BoardCell cop = BoardCell(pos.x + 2 * sgn1, pos.y + 2 * sgn2); + while (inside(cop)) { bool die = false; - if (check_move(who, pos, BoardCell(pos.x + c * sgn1, pos.y + c * sgn2), die)) + if (check_move(who, pos, cop, die)) if (die) return true; - } + cop.x += sgn1, cop.y += sgn2; + } + } return false; } @@ -153,6 +188,43 @@ number_of_player GameState::who_moves() const { } } +void GameState::change_prefix(BoardCell pos, char news) { + if (board[pos.x][pos.y] == news) + return; + if (board[pos.x][pos.y] != '.') { + for (int shift = 0; pos.x + shift < SIZE && pos.y + shift < SIZE; shift++) { + if (board[pos.x][pos.y] == 'w' || board[pos.x][pos.y] == 'W') + white_up_down[pos.x + shift][pos.y + shift]--; + else + black_up_down[pos.x + shift][pos.y + shift]--; + } + + for (int shift = 0; pos.x - shift >= 0 && pos.y + shift < SIZE; shift++) { + if (board[pos.x][pos.y] == 'w' || board[pos.x][pos.y] == 'W') + white_down_up[pos.x - shift][pos.y + shift]--; + else + black_down_up[pos.x - shift][pos.y + shift]--; + } + } + + if (news != '.') { + for (int shift = 0; pos.x + shift < SIZE && pos.y + shift < SIZE; shift++) { + if (news == 'w' || news == 'W') + white_up_down[pos.x + shift][pos.y + shift]++; + else + black_up_down[pos.x + shift][pos.y + shift]++; + } + + for (int shift = 0; pos.x - shift >= 0 && pos.y + shift < SIZE; shift++) { + if (news == 'w' || news == 'W') + white_down_up[pos.x - shift][pos.y + shift]++; + else + black_down_up[pos.x - shift][pos.y + shift]++; + } + } + return; +} + void GameState::move(number_of_player player, BoardCell from, BoardCell to) { bool die = false; if (who_moves() != player) @@ -166,6 +238,7 @@ void GameState::move(number_of_player player, BoardCell from, BoardCell to) { int dy = (to.x + to.y == from.x + from.y ? -1 : 1); BoardCell cop = to; while (cop != from) { + change_prefix(cop, '.'); board[cop.x][cop.y] = '.'; if (cop.x < from.x) cop.x++, cop.y += dy; @@ -173,6 +246,8 @@ void GameState::move(number_of_player player, BoardCell from, BoardCell to) { cop.x--, cop.y -= dy; } if (die) { + change_prefix(to, board[from.x][from.y]); + change_prefix(from, board[to.x][to.y]); std::swap(board[to.x][to.y], board[from.x][from.y]); who_last = player; move_to_draw = 0; @@ -188,6 +263,8 @@ void GameState::move(number_of_player player, BoardCell from, BoardCell to) { move_to_draw++; type_last = 0; last_move = to; + change_prefix(to, board[from.x][from.y]); + change_prefix(from, board[to.x][to.y]); std::swap(board[to.x][to.y], board[from.x][from.y]); } if (player == FIRST && to.x == 0 && board[to.x][to.y] == 'w') @@ -207,14 +284,16 @@ void GameState::get_list_of_correct_moves(number_of_player player, BoardCell fro if (!fl && find_kill(player)) //Запрещаем не рубить return; - int bound = (board[from.x][from.y] >= 'a' && board[from.x][from.y] <= 'z' ? 2 : SIZE); - for (int c = 1; c <= bound; c++) - for (int sgn1 = -1; sgn1 <= 1; sgn1 += 2) - for (int sgn2 = -1; sgn2 <= 1; sgn2 += 2) { + for (int sgn1 = -1; sgn1 <= 1; sgn1 += 2) + for (int sgn2 = -1; sgn2 <= 1; sgn2 += 2) { + BoardCell cop = BoardCell(from.x + sgn1, from.y + sgn2); + while (inside(cop)) { bool die = false; - if (check_move(player, from, BoardCell(from.x + c * sgn1, from.y + c * sgn2), die)) { + if (check_move(player, from, cop, die)) { if (!fl || die) - moves.push_back(BoardCell(from.x + c * sgn1, from.y + c * sgn2)); + moves.push_back(cop); + } + cop.x += sgn1, cop.y += sgn2; } } return; diff --git a/model/GameState.hpp b/model/GameState.hpp index feb0d05..1cf7597 100644 --- a/model/GameState.hpp +++ b/model/GameState.hpp @@ -19,11 +19,16 @@ class GameState { int move_to_draw, type_last; BoardCell last_move; char board[SIZE][SIZE]; + int white_up_down[SIZE][SIZE], white_down_up[SIZE][SIZE]; + int black_up_down[SIZE][SIZE], black_down_up[SIZE][SIZE]; bool check_ordinary(number_of_player player, BoardCell from, BoardCell to, bool &die) const; bool check_queen(number_of_player player, BoardCell from, BoardCell to, bool &die) const; bool inside(BoardCell cell) const; bool kill(number_of_player who, BoardCell pos) const; + bool find_kill(number_of_player who) const; + + void change_prefix(BoardCell pos, char news); void show() const; public: @@ -31,8 +36,6 @@ class GameState { GameState(const GameState &oth) = default; GameState& operator=(const GameState &oth) = default; - bool find_kill(number_of_player who) const; - number_of_player who_moves() const; bool check_move(number_of_player player, BoardCell from, BoardCell to, bool &die) const; void move(number_of_player player, BoardCell from, BoardCell to); diff --git a/test_model/TestGameState.cpp b/test_model/TestGameState.cpp index 8b90ed5..fd8f06e 100644 --- a/test_model/TestGameState.cpp +++ b/test_model/TestGameState.cpp @@ -1,5 +1,4 @@ #include "TestGameState.hpp" -#include "GameState.hpp" void TestGameState::test_init() { GameState g; @@ -25,6 +24,37 @@ void TestGameState::test_init() { return; } +void TestGameState::calculate(GameState &G) { + for (int i = 0; i < G.SIZE; i++) + for (int j = 0; j < G.SIZE; j++) { + if (i == 0 || j == 0) + G.white_up_down[i][j] = G.black_up_down[i][j] = 0; + else { + G.white_up_down[i][j] = G.white_up_down[i - 1][j - 1]; + G.black_up_down[i][j] = G.black_up_down[i - 1][j - 1]; + } + if (G.board[i][j] == 'w' || G.board[i][j] == 'W') + G.white_up_down[i][j]++; + if (G.board[i][j] == 'b' || G.board[i][j] == 'B') + G.black_up_down[i][j]++; + } + + for (int i = G.SIZE - 1; i >= 0; i--) + for (int j = 0; j < G.SIZE; j++) { + if (i == G.SIZE - 1 || j == 0) + G.white_down_up[i][j] = G.black_down_up[i][j] = 0; + else { + G.white_down_up[i][j] = G.white_down_up[i + 1][j - 1]; + G.black_down_up[i][j] = G.black_down_up[i + 1][j - 1]; + } + if (G.board[i][j] == 'w' || G.board[i][j] == 'W') + G.white_down_up[i][j]++; + if (G.board[i][j] == 'b' || G.board[i][j] == 'B') + G.black_down_up[i][j]++; + } + return; +} + void TestGameState::test_ordinary() { GameState g; for (int i = 0; i < g.SIZE; i++) @@ -42,21 +72,24 @@ void TestGameState::test_ordinary() { g.board[5][5] = 'w'; g.board[6][4] = 'w'; g.board[4][3] = 'b'; - CPPUNIT_ASSERT(g.check_ordinary(FIRST, BoardCell(2, 3), BoardCell(0, 1))); - CPPUNIT_ASSERT(g.check_ordinary(FIRST, BoardCell(3, 2), BoardCell(2, 1))); - CPPUNIT_ASSERT(!g.check_ordinary(FIRST, BoardCell(1, 2), BoardCell(3, 4))); - CPPUNIT_ASSERT(!g.check_ordinary(FIRST, BoardCell(3, 5), BoardCell(3, 7))); - CPPUNIT_ASSERT(!g.check_ordinary(FIRST, BoardCell(5, 5), BoardCell(6, 6))); - CPPUNIT_ASSERT(g.check_ordinary(FIRST, BoardCell(4, 2), BoardCell(3, 3))); - CPPUNIT_ASSERT(g.check_ordinary(FIRST, BoardCell(3, 2), BoardCell(5, 4))); - - CPPUNIT_ASSERT(!g.check_ordinary(SECOND, BoardCell(4, 3), BoardCell(2, 5))); - CPPUNIT_ASSERT(g.check_ordinary(SECOND, BoardCell(1, 2), BoardCell(3, 4))); - CPPUNIT_ASSERT(!g.check_ordinary(SECOND, BoardCell(1, 2), BoardCell(2, 2))); - CPPUNIT_ASSERT(g.check_ordinary(SECOND, BoardCell(0, 5), BoardCell(1, 6))); - CPPUNIT_ASSERT(g.check_ordinary(SECOND, BoardCell(1, 2), BoardCell(2, 1))); - CPPUNIT_ASSERT(!g.check_ordinary(SECOND, BoardCell(1, 2), BoardCell(0, 3))); - CPPUNIT_ASSERT(g.check_ordinary(SECOND, BoardCell(4, 3), BoardCell(2, 1))); + calculate(g); + + bool die = false; + CPPUNIT_ASSERT(g.check_ordinary(FIRST, BoardCell(2, 3), BoardCell(0, 1), die)); + CPPUNIT_ASSERT(g.check_ordinary(FIRST, BoardCell(3, 2), BoardCell(2, 1), die)); + CPPUNIT_ASSERT(!g.check_ordinary(FIRST, BoardCell(1, 2), BoardCell(3, 4), die)); + CPPUNIT_ASSERT(!g.check_ordinary(FIRST, BoardCell(3, 5), BoardCell(3, 7), die)); + CPPUNIT_ASSERT(!g.check_ordinary(FIRST, BoardCell(5, 5), BoardCell(6, 6), die)); + CPPUNIT_ASSERT(g.check_ordinary(FIRST, BoardCell(4, 2), BoardCell(3, 3), die)); + CPPUNIT_ASSERT(g.check_ordinary(FIRST, BoardCell(3, 2), BoardCell(5, 4), die)); + + CPPUNIT_ASSERT(!g.check_ordinary(SECOND, BoardCell(4, 3), BoardCell(2, 5), die)); + CPPUNIT_ASSERT(g.check_ordinary(SECOND, BoardCell(1, 2), BoardCell(3, 4), die)); + CPPUNIT_ASSERT(!g.check_ordinary(SECOND, BoardCell(1, 2), BoardCell(2, 2), die)); + CPPUNIT_ASSERT(g.check_ordinary(SECOND, BoardCell(0, 5), BoardCell(1, 6), die)); + CPPUNIT_ASSERT(g.check_ordinary(SECOND, BoardCell(1, 2), BoardCell(2, 1), die)); + CPPUNIT_ASSERT(!g.check_ordinary(SECOND, BoardCell(1, 2), BoardCell(0, 3), die)); + CPPUNIT_ASSERT(g.check_ordinary(SECOND, BoardCell(4, 3), BoardCell(2, 1), die)); return; } @@ -76,19 +109,22 @@ void TestGameState::test_queen() { g.board[4][6] = 'W'; g.board[6][4] = 'b'; g.board[6][7] = 'B'; - CPPUNIT_ASSERT(g.check_queen(FIRST, BoardCell(4, 6), BoardCell(0, 2))); - CPPUNIT_ASSERT(g.check_queen(FIRST, BoardCell(4, 6), BoardCell(2, 4))); - CPPUNIT_ASSERT(g.check_queen(FIRST, BoardCell(4, 6), BoardCell(5, 7))); - CPPUNIT_ASSERT(!g.check_queen(FIRST, BoardCell(4, 6), BoardCell(4, 5))); - CPPUNIT_ASSERT(!g.check_queen(FIRST, BoardCell(6, 7), BoardCell(0, 1))); - CPPUNIT_ASSERT(!g.check_queen(FIRST, BoardCell(1, 6), BoardCell(4, 3))); - - CPPUNIT_ASSERT(g.check_queen(SECOND, BoardCell(1, 6), BoardCell(4, 3))); - CPPUNIT_ASSERT(g.check_queen(SECOND, BoardCell(1, 6), BoardCell(7, 0))); - CPPUNIT_ASSERT(!g.check_queen(SECOND, BoardCell(2, 6), BoardCell(4, 3))); - CPPUNIT_ASSERT(g.check_queen(SECOND, BoardCell(1, 3), BoardCell(5, 7))); - CPPUNIT_ASSERT(g.check_queen(SECOND, BoardCell(2, 6), BoardCell(5, 3))); - CPPUNIT_ASSERT(!g.check_queen(SECOND, BoardCell(4, 6), BoardCell(3, 7))); + calculate(g); + + bool die = false; + CPPUNIT_ASSERT(g.check_queen(FIRST, BoardCell(4, 6), BoardCell(0, 2), die)); + CPPUNIT_ASSERT(g.check_queen(FIRST, BoardCell(4, 6), BoardCell(2, 4), die)); + CPPUNIT_ASSERT(g.check_queen(FIRST, BoardCell(4, 6), BoardCell(5, 7), die)); + CPPUNIT_ASSERT(!g.check_queen(FIRST, BoardCell(4, 6), BoardCell(4, 5), die)); + CPPUNIT_ASSERT(!g.check_queen(FIRST, BoardCell(6, 7), BoardCell(0, 1), die)); + CPPUNIT_ASSERT(!g.check_queen(FIRST, BoardCell(1, 6), BoardCell(4, 3), die)); + + CPPUNIT_ASSERT(g.check_queen(SECOND, BoardCell(1, 6), BoardCell(4, 3), die)); + CPPUNIT_ASSERT(g.check_queen(SECOND, BoardCell(1, 6), BoardCell(7, 0), die)); + CPPUNIT_ASSERT(!g.check_queen(SECOND, BoardCell(2, 6), BoardCell(4, 3), die)); + CPPUNIT_ASSERT(g.check_queen(SECOND, BoardCell(1, 3), BoardCell(5, 7), die)); + CPPUNIT_ASSERT(g.check_queen(SECOND, BoardCell(2, 6), BoardCell(5, 3), die)); + CPPUNIT_ASSERT(!g.check_queen(SECOND, BoardCell(4, 6), BoardCell(3, 7), die)); return; } @@ -109,24 +145,27 @@ void TestGameState::test_check_move() { g.board[5][5] = 'w'; g.board[6][4] = 'w'; g.board[4][3] = 'b'; - CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(0, 0), BoardCell(1, 1))); - CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(2, 3), BoardCell(0, 5))); - CPPUNIT_ASSERT(g.check_move(FIRST, BoardCell(2, 3), BoardCell(0, 1))); - CPPUNIT_ASSERT(g.check_move(FIRST, BoardCell(3, 2), BoardCell(2, 1))); - CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(6, 4), BoardCell(8, 6))); - CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(1, 2), BoardCell(3, 4))); - CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(3, 5), BoardCell(3, 7))); - CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(5, 5), BoardCell(6, 6))); - - CPPUNIT_ASSERT(!g.check_move(SECOND, BoardCell(4, 3), BoardCell(2, 5))); - CPPUNIT_ASSERT(!g.check_move(SECOND, BoardCell(1, 7), BoardCell(0, 8))); - CPPUNIT_ASSERT(!g.check_move(SECOND, BoardCell(1, 4), BoardCell(3, 2))); - CPPUNIT_ASSERT(g.check_move(SECOND, BoardCell(1, 2), BoardCell(3, 4))); - CPPUNIT_ASSERT(!g.check_move(SECOND, BoardCell(1, 2), BoardCell(2, 2))); - CPPUNIT_ASSERT(g.check_move(SECOND, BoardCell(0, 5), BoardCell(1, 6))); - CPPUNIT_ASSERT(g.check_move(SECOND, BoardCell(1, 2), BoardCell(2, 1))); - CPPUNIT_ASSERT(!g.check_move(SECOND, BoardCell(1, 2), BoardCell(0, 3))); - CPPUNIT_ASSERT(g.check_move(SECOND, BoardCell(4, 3), BoardCell(2, 1))); + calculate(g); + + bool die = false; + CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(0, 0), BoardCell(1, 1), die)); + CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(2, 3), BoardCell(0, 5), die)); + CPPUNIT_ASSERT(g.check_move(FIRST, BoardCell(2, 3), BoardCell(0, 1), die)); + CPPUNIT_ASSERT(g.check_move(FIRST, BoardCell(3, 2), BoardCell(2, 1), die)); + CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(6, 4), BoardCell(8, 6), die)); + CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(1, 2), BoardCell(3, 4), die)); + CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(3, 5), BoardCell(3, 7), die)); + CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(5, 5), BoardCell(6, 6), die)); + + CPPUNIT_ASSERT(!g.check_move(SECOND, BoardCell(4, 3), BoardCell(2, 5), die)); + CPPUNIT_ASSERT(!g.check_move(SECOND, BoardCell(1, 7), BoardCell(0, 8), die)); + CPPUNIT_ASSERT(!g.check_move(SECOND, BoardCell(1, 4), BoardCell(3, 2), die)); + CPPUNIT_ASSERT(g.check_move(SECOND, BoardCell(1, 2), BoardCell(3, 4), die)); + CPPUNIT_ASSERT(!g.check_move(SECOND, BoardCell(1, 2), BoardCell(2, 2), die)); + CPPUNIT_ASSERT(g.check_move(SECOND, BoardCell(0, 5), BoardCell(1, 6), die)); + CPPUNIT_ASSERT(g.check_move(SECOND, BoardCell(1, 2), BoardCell(2, 1), die)); + CPPUNIT_ASSERT(!g.check_move(SECOND, BoardCell(1, 2), BoardCell(0, 3), die)); + CPPUNIT_ASSERT(g.check_move(SECOND, BoardCell(4, 3), BoardCell(2, 1), die)); for (int i = 0; i < g.SIZE; i++) for (int j = 0; j < g.SIZE; j++) @@ -142,23 +181,25 @@ void TestGameState::test_check_move() { g.board[4][6] = 'W'; g.board[6][4] = 'b'; g.board[6][7] = 'B'; - CPPUNIT_ASSERT(g.check_move(FIRST, BoardCell(4, 6), BoardCell(0, 2))); - CPPUNIT_ASSERT(g.check_move(FIRST, BoardCell(4, 6), BoardCell(2, 4))); - CPPUNIT_ASSERT(g.check_move(FIRST, BoardCell(4, 6), BoardCell(5, 7))); - CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(4, 6), BoardCell(4, 5))); - CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(4, 6), BoardCell(3, 7))); - CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(6, 7), BoardCell(0, 1))); - CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(1, 6), BoardCell(4, 3))); - CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(4, 6), BoardCell(6, 4))); - - CPPUNIT_ASSERT(g.check_move(SECOND, BoardCell(1, 6), BoardCell(4, 3))); - CPPUNIT_ASSERT(g.check_move(SECOND, BoardCell(1, 6), BoardCell(7, 0))); - CPPUNIT_ASSERT(!g.check_move(SECOND, BoardCell(2, 6), BoardCell(4, 3))); - CPPUNIT_ASSERT(g.check_move(SECOND, BoardCell(1, 3), BoardCell(5, 7))); - CPPUNIT_ASSERT(g.check_move(SECOND, BoardCell(2, 6), BoardCell(5, 3))); - CPPUNIT_ASSERT(!g.check_move(SECOND, BoardCell(4, 6), BoardCell(3, 7))); - CPPUNIT_ASSERT(!g.check_move(SECOND, BoardCell(6, 7), BoardCell(1, 2))); - CPPUNIT_ASSERT(!g.check_move(SECOND, BoardCell(1, 2), BoardCell(2, 2))); + calculate(g); + + CPPUNIT_ASSERT(g.check_move(FIRST, BoardCell(4, 6), BoardCell(0, 2), die)); + CPPUNIT_ASSERT(g.check_move(FIRST, BoardCell(4, 6), BoardCell(2, 4), die)); + CPPUNIT_ASSERT(g.check_move(FIRST, BoardCell(4, 6), BoardCell(5, 7), die)); + CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(4, 6), BoardCell(4, 5), die)); + CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(4, 6), BoardCell(3, 7), die)); + CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(6, 7), BoardCell(0, 1), die)); + CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(1, 6), BoardCell(4, 3), die)); + CPPUNIT_ASSERT(!g.check_move(FIRST, BoardCell(4, 6), BoardCell(6, 4), die)); + + CPPUNIT_ASSERT(g.check_move(SECOND, BoardCell(1, 6), BoardCell(4, 3), die)); + CPPUNIT_ASSERT(g.check_move(SECOND, BoardCell(1, 6), BoardCell(7, 0), die)); + CPPUNIT_ASSERT(!g.check_move(SECOND, BoardCell(2, 6), BoardCell(4, 3), die)); + CPPUNIT_ASSERT(g.check_move(SECOND, BoardCell(1, 3), BoardCell(5, 7), die)); + CPPUNIT_ASSERT(g.check_move(SECOND, BoardCell(2, 6), BoardCell(5, 3), die)); + CPPUNIT_ASSERT(!g.check_move(SECOND, BoardCell(4, 6), BoardCell(3, 7), die)); + CPPUNIT_ASSERT(!g.check_move(SECOND, BoardCell(6, 7), BoardCell(1, 2), die)); + CPPUNIT_ASSERT(!g.check_move(SECOND, BoardCell(1, 2), BoardCell(2, 2), die)); return; } @@ -190,6 +231,8 @@ void TestGameState::test_kill() { g.board[5][5] = 'w'; g.board[6][4] = 'w'; g.board[4][3] = 'b'; + calculate(g); + CPPUNIT_ASSERT(g.kill(FIRST, BoardCell(2, 3))); CPPUNIT_ASSERT(!g.kill(FIRST, BoardCell(3, 5))); CPPUNIT_ASSERT(!g.kill(FIRST, BoardCell(6, 4))); @@ -214,6 +257,8 @@ void TestGameState::test_kill() { g.board[4][6] = 'W'; g.board[6][4] = 'b'; g.board[6][7] = 'B'; + calculate(g); + CPPUNIT_ASSERT(g.kill(FIRST, BoardCell(4, 6))); CPPUNIT_ASSERT(!g.kill(FIRST, BoardCell(3, 4))); CPPUNIT_ASSERT(!g.kill(FIRST, BoardCell(1, 5))); @@ -228,8 +273,8 @@ void TestGameState::test_kill() { void TestGameState::test_find_kill() { GameState g; - CPPUNIT_ASSERT(g.find_kill(FIRST) == BoardCell(-1, -1)); - CPPUNIT_ASSERT(g.find_kill(SECOND) == BoardCell(-1, -1)); + CPPUNIT_ASSERT(!g.find_kill(FIRST)); + CPPUNIT_ASSERT(!g.find_kill(SECOND)); for (int i = 0; i < g.SIZE; i++) for (int j = 0; j < g.SIZE; j++) g.board[i][j] = '.'; @@ -245,11 +290,10 @@ void TestGameState::test_find_kill() { g.board[5][5] = 'w'; g.board[6][4] = 'w'; g.board[4][3] = 'b'; - BoardCell A = g.find_kill(FIRST); - BoardCell B = g.find_kill(SECOND); - CPPUNIT_ASSERT(A == BoardCell(2, 3) || A == BoardCell(3, 2)); - CPPUNIT_ASSERT(B == BoardCell(1, 2) || B == BoardCell(2, 6) || - B == BoardCell(4, 3)); + calculate(g); + + CPPUNIT_ASSERT(g.find_kill(FIRST)); + CPPUNIT_ASSERT(g.find_kill(SECOND)); for (int i = 0; i < g.SIZE; i++) for (int j = 0; j < g.SIZE; j++) @@ -265,8 +309,9 @@ void TestGameState::test_find_kill() { g.board[4][6] = 'W'; g.board[6][4] = 'b'; g.board[6][7] = 'B'; - A = g.find_kill(FIRST); - CPPUNIT_ASSERT(A == BoardCell(4, 6)); + calculate(g); + + CPPUNIT_ASSERT(g.find_kill(FIRST)); return; } @@ -288,6 +333,8 @@ void TestGameState::test_who_moves() { g.board[5][5] = 'w'; g.board[6][4] = 'w'; g.board[4][3] = 'b'; + calculate(g); + g.last_move = BoardCell(2, 3); g.who_last = FIRST; g.type_last = 1; @@ -316,6 +363,8 @@ void TestGameState::test_who_moves() { g.board[4][6] = 'W'; g.board[6][4] = 'b'; g.board[6][7] = 'B'; + calculate(g); + g.last_move = BoardCell(4, 6); g.who_last = FIRST; g.type_last = 0; @@ -343,6 +392,8 @@ void TestGameState::test_move() { g.board[4][2] = 'w'; g.board[4][6] = 'W'; g.board[6][7] = 'W'; + calculate(g); + GameState cop = g; g.move(FIRST, BoardCell(3, 4), BoardCell(4, 3)); CPPUNIT_ASSERT(!(cop != g)); @@ -373,9 +424,9 @@ void TestGameState::test_move() { BoardCell(i, j) != BoardCell(6, 7) && BoardCell(i, j) != BoardCell(5, 5)) CPPUNIT_ASSERT(g.board[i][j] == cop.board[i][j]); - CPPUNIT_ASSERT(g.board[6][7] == '.'); - CPPUNIT_ASSERT(g.board[4][6] == '.'); - CPPUNIT_ASSERT(g.board[5][5] == 'W'); + CPPUNIT_ASSERT(g.board[6][7] == 'W'); + CPPUNIT_ASSERT(g.board[4][6] == 'W'); + CPPUNIT_ASSERT(g.board[5][5] == '.'); return; } @@ -395,17 +446,30 @@ void TestGameState::test_get_list() { g.board[4][6] = 'W'; g.board[6][4] = 'b'; g.board[6][7] = 'W'; - std::vector A{BoardCell(4, 5), BoardCell(5, 6), BoardCell(7, 6)}; + calculate(g); + + std::vector A; std::vector B{BoardCell(5, 5)}; - std::vector C{BoardCell(7, 3), BoardCell(7, 5)}; - std::vector D{BoardCell(0, 2), BoardCell(2, 4), BoardCell(3, 5), - BoardCell(5, 5), BoardCell(5, 7), BoardCell(7, 3)}; + std::vector C; + std::vector D{BoardCell(7, 3), BoardCell(0, 2)}; + + std::vector result; + g.get_list_of_correct_moves(FIRST, BoardCell(6, 7), result); + CPPUNIT_ASSERT(result == A); + result.clear(); + + g.get_list_of_correct_moves(FIRST, BoardCell(4, 6), result); + CPPUNIT_ASSERT(result == D); + result.clear(); - CPPUNIT_ASSERT(g.get_list_of_correct_moves(FIRST, BoardCell(6, 7)) == A); - CPPUNIT_ASSERT(g.get_list_of_correct_moves(FIRST, BoardCell(4, 6)) == D); g.who_last = FIRST; - CPPUNIT_ASSERT(g.get_list_of_correct_moves(SECOND, BoardCell(3, 7)) == B); - CPPUNIT_ASSERT(g.get_list_of_correct_moves(SECOND, BoardCell(6, 4)) == C); + g.get_list_of_correct_moves(SECOND, BoardCell(3, 7), result); + CPPUNIT_ASSERT(result == B); + result.clear(); + + g.get_list_of_correct_moves(SECOND, BoardCell(6, 4), result); + CPPUNIT_ASSERT(result == C); + result.clear(); return; } @@ -421,6 +485,8 @@ void TestGameState::test_check_win() { g.board[i][j] = '.'; for (int i = 0; i < g.SIZE; i++) g.board[0][i] = 'b', g.board[1][i] = 'w'; + calculate(g); + g.who_last = SECOND; CPPUNIT_ASSERT(g.check_win() == SECOND_WIN); g.who_last = FIRST; diff --git a/test_model/TestGameState.hpp b/test_model/TestGameState.hpp index 973133a..42c6d88 100644 --- a/test_model/TestGameState.hpp +++ b/test_model/TestGameState.hpp @@ -5,8 +5,12 @@ #include #include +#include "GameState.hpp" + class TestGameState : public CppUnit::TestFixture { private: + void calculate(GameState &G); + void test_init(); void test_ordinary(); void test_queen(); From 0eba24221f7a94d85a46178bdf0dd0d487f12021 Mon Sep 17 00:00:00 2001 From: Grigoryev Date: Mon, 25 May 2020 14:41:45 -0400 Subject: [PATCH 13/15] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8?= =?UTF-8?q?=20=D1=81=20=D0=BF=D0=BE=D1=82=D0=BE=D0=BA=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ai/CompPlayer.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ai/CompPlayer.cpp b/ai/CompPlayer.cpp index 18eee13..e5fa2dc 100644 --- a/ai/CompPlayer.cpp +++ b/ai/CompPlayer.cpp @@ -6,7 +6,6 @@ static const int INF = (int)1e9; int cnt1 = 0, cnt2 = 0; -double len; CompPlayer::CompPlayer(number_of_player turn, int seconds, int deep) : controller::IPlayer(turn), seconds_(seconds), deep_(deep) {} @@ -137,7 +136,6 @@ void CompPlayer::alpha_beta(const GameState &G, int alpha, int beta, else up = G.SIZE - 1, down = -1, lef = G.SIZE - 1, rig = -1, delta = -1; - clock_t st = clock(); for (int i = up; i != down; i += delta) for (int j = lef; j != rig; j += delta) { std::vector correct; @@ -145,8 +143,6 @@ void CompPlayer::alpha_beta(const GameState &G, int alpha, int beta, for (int z = 0; z < (int)correct.size(); z++) moves.push_back(Move(BoardCell(i, j), correct[z])); } - clock_t fn = clock(); - len += 1.0 * (fn - st) / CLOCKS_PER_SEC; if (moves.empty()) { if (player == FIRST) @@ -174,6 +170,9 @@ void CompPlayer::alpha_beta(const GameState &G, int alpha, int beta, act.clear(); } } + for (int j = 0; j < (int)act.size(); j++) + act[j].join(); + act.clear(); } for (int i = 0; i < (int)moves.size(); i++) { @@ -214,11 +213,11 @@ void CompPlayer::alpha_beta(const GameState &G, int alpha, int beta, Move CompPlayer::get_next_move(const GameState &G, int seconds, int deep) const { std::pair result; (void)seconds; - cnt1 = 0, cnt2 = 0, len = 0; + cnt1 = 0, cnt2 = 0; clock_t start = clock(); alpha_beta(G, -INF, INF, deep, result, true); clock_t finish = clock(); - std::cout << 1.0 * (finish - start) / CLOCKS_PER_SEC << '\n' << len << '\n' << + std::cout << 1.0 * (finish - start) / CLOCKS_PER_SEC << '\n' << cnt1 << ' ' << cnt2 << '\n'; return result.second; } From 3128fe8765302007d26a71d5e8b4027595add43d Mon Sep 17 00:00:00 2001 From: sergeypospelov Date: Wed, 27 May 2020 14:53:46 +0300 Subject: [PATCH 14/15] +give up button --- Makefile | 2 +- ai/CompPlayer.cpp | 6 ++++-- ai/CompPlayer.hpp | 2 +- main.cpp | 6 +++++- ui/Graphics.cpp | 4 ++-- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 20fa832..18246e2 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ CXX = g++ -CXXFLAGS = -std=c++17 -O2 -pedantic -Wall -Wextra -Werror +CXXFLAGS = -std=c++17 -O2 -pedantic -Wall -Wextra -Werror -fno-stack-protector -fstack-protector LDFLAGS = -lexpat -lsfml-network -lsfml-system -lsfml-window -lsfml-graphics -lpthread -lcppunit INC = -Inetwork -Imodel -Iui -Icontroller -Iai diff --git a/ai/CompPlayer.cpp b/ai/CompPlayer.cpp index e5fa2dc..d3d1a89 100644 --- a/ai/CompPlayer.cpp +++ b/ai/CompPlayer.cpp @@ -162,9 +162,9 @@ void CompPlayer::alpha_beta(const GameState &G, int alpha, int beta, for (int i = 0; i < (int)moves.size(); i++) { GameState cop = G; cop.move(player, moves[i].from, moves[i].to); - act.push_back(std::thread(alpha_beta, std::ref(cop), alpha, beta, + act.push_back(std::thread(alpha_beta, std::ref(cop), alpha, beta, deep - fl, std::ref(res[i]), false)); - if ((i + 1) % NUMBER_OF_THREADS == 0) { + if (i % NUMBER_OF_THREADS == 0) { for (int j = 0; j < (int)act.size(); j++) act[j].join(); act.clear(); @@ -175,6 +175,8 @@ void CompPlayer::alpha_beta(const GameState &G, int alpha, int beta, act.clear(); } +// std::cerr << "OK!" << std::endl; + for (int i = 0; i < (int)moves.size(); i++) { if (!flow) { GameState cop = G; diff --git a/ai/CompPlayer.hpp b/ai/CompPlayer.hpp index 6fad9b2..02f99ea 100644 --- a/ai/CompPlayer.hpp +++ b/ai/CompPlayer.hpp @@ -16,7 +16,7 @@ class CompPlayer : public controller::IPlayer { mutable GameState gs; - static const int NUMBER_OF_THREADS = 4; + static const int NUMBER_OF_THREADS = 2; public: CompPlayer(number_of_player, int seconds, int deep); diff --git a/main.cpp b/main.cpp index 6e790eb..7b7cc99 100644 --- a/main.cpp +++ b/main.cpp @@ -106,6 +106,10 @@ int main(int argc, char **argv) { // TODO: make own main for every game mode game_state = game_test.return_current_state(); + if (mode == "ai") { + (dynamic_cast(enemy))->set_game_state(game_state); + } + while (!core.get_events().empty()) { controller::Event *event = core.get_events().front().get(); controller::MoveEvent *move = @@ -115,7 +119,7 @@ int main(int argc, char **argv) { // TODO: make own main for every game mode controller::GiveUpEvent *giveUp = dynamic_cast(event); - controller::process(giveUp, enemy, player, game_test, mode); + controller::process(giveUp, player, enemy, game_test, mode); // currently, there is only MoveEvent. // TODO: need to process another events! diff --git a/ui/Graphics.cpp b/ui/Graphics.cpp index 5167584..b254c3d 100644 --- a/ui/Graphics.cpp +++ b/ui/Graphics.cpp @@ -164,7 +164,7 @@ void Gra::compiling_event(Game &game) { } if (past.x == -6) { - //ТУТА + events.push(std::shared_ptr(new controller::GiveUpEvent())); } } if (event.type == sf::Event::MouseButtonReleased) { @@ -298,5 +298,5 @@ void Gra::draw_current_player(std::list &render_list, Game& game, block.move({0, 60}); block.setFillColor(sf::Color::Blue); - render_list.push_back(Frame(block, 1, {-6, 0}, "secret button")); + render_list.push_back(Frame(block, 1, {-6, 0}, "Give up")); } From 507d1cf45601e39c8dd01f51c3bcca36ff6ff18d Mon Sep 17 00:00:00 2001 From: Yurafobus1 Date: Wed, 27 May 2020 20:33:08 +0300 Subject: [PATCH 15/15] correct_natation --- ai/CompPlayer.cpp | 2 +- ai/CompPlayer.hpp | 2 +- model/Game.cpp | 8 +++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ai/CompPlayer.cpp b/ai/CompPlayer.cpp index d3d1a89..78d4d20 100644 --- a/ai/CompPlayer.cpp +++ b/ai/CompPlayer.cpp @@ -164,7 +164,7 @@ void CompPlayer::alpha_beta(const GameState &G, int alpha, int beta, cop.move(player, moves[i].from, moves[i].to); act.push_back(std::thread(alpha_beta, std::ref(cop), alpha, beta, deep - fl, std::ref(res[i]), false)); - if (i % NUMBER_OF_THREADS == 0) { + if ((i+1) % NUMBER_OF_THREADS == 0) { for (int j = 0; j < (int)act.size(); j++) act[j].join(); act.clear(); diff --git a/ai/CompPlayer.hpp b/ai/CompPlayer.hpp index 02f99ea..6fad9b2 100644 --- a/ai/CompPlayer.hpp +++ b/ai/CompPlayer.hpp @@ -16,7 +16,7 @@ class CompPlayer : public controller::IPlayer { mutable GameState gs; - static const int NUMBER_OF_THREADS = 2; + static const int NUMBER_OF_THREADS = 4; public: CompPlayer(number_of_player, int seconds, int deep); diff --git a/model/Game.cpp b/model/Game.cpp index 73c08ac..06c16b2 100644 --- a/model/Game.cpp +++ b/model/Game.cpp @@ -26,17 +26,23 @@ GameState Game::watch_state(int number) const { GameState& Game::return_current_state() { return current; } +static char to_char(int a) { + return a + 96; +} + void Game::move(number_of_player player, BoardCell from, BoardCell to) { GameState copy = current; current.move(player, from, to); if (current != copy) { game.push_back(current); natation.push_back(std::to_string(number_of_states() - 1) + "." + - std::to_string((8 - from.x) * 10 + from.y) + ">" + std::to_string((8 -to.x) * 10 + to.y)); + to_char(from.y + 1) + std::to_string(8 - from.x) + ">" + to_char(to.y + 1) + std::to_string(8 - to.x)); } return; } + + void Game::save_to_file(const std::string &file) const { std::ofstream os(file); os << "\n";