From cbdf377c86c87cbfe3f8701159f5dd82115c6ea6 Mon Sep 17 00:00:00 2001 From: Marcus Penate Date: Fri, 15 Apr 2022 23:13:15 -0400 Subject: [PATCH] Finished version, might have memory leaks --- .gitignore | 3 + .vscode/settings.json | 51 +++++++ .vscode/tasks.json | 36 ++++- board.cpp | 320 ++++++++++++++++++++++++++++++++++++++---- board.hpp | 10 +- game.cpp | 306 +++++++++++++++++++++++++++++++++++++++- game.hpp | 10 +- main.cpp | 6 +- piece.hpp | 2 +- 9 files changed, 703 insertions(+), 41 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index 7b05f41..b106a04 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ chess +chess.exe +SDL2.dll +SDL2/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c165195 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,51 @@ +{ + "files.associations": { + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "cctype": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "ratio": "cpp", + "string": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "limits": "cpp", + "new": "cpp", + "ostream": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp" + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 1a7ab04..7b68b01 100755 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -6,6 +6,10 @@ "command": "/usr/bin/g++", "args": [ "*.cpp", + "-Wall", + "-Wextra", + "-pedantic", + "-fsanitize=address", "-std=c++1z", "-fdiagnostics-color=always", "-g", @@ -21,9 +25,39 @@ ], "group": { "kind": "build", - "isDefault": true + "isDefault": false }, "detail": "Task generated by Debugger." + }, + { + "type": "cppbuild", + "label": "C/C++: mingw build project", + "command": "/usr/bin/x86_64-w64-mingw32-g++", + "args": [ + "-std=gnu++17", + "-g", + "-static-libstdc++", + "-static-libgcc", + "-o", + "./chess.exe", + "*.cpp", + "-ISDL2/include/", + "-LSDL2/lib/", + "-lmingw32", + "-lSDL2main", + "-lSDL2" + ], + "options": { + "cwd": "${fileDirname}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "" } ], "version": "2.0.0" diff --git a/board.cpp b/board.cpp index d15ed6c..77549d9 100755 --- a/board.cpp +++ b/board.cpp @@ -9,6 +9,7 @@ Board::Board() { load_FEN(std::string("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")); + //load_FEN(std::string("socqkcos/pppppppp/8/8/8/8/PPPPPPPP/SOCQKCOS w KQkq - 0 1")); } Board::Board(std::string board_fen) @@ -31,7 +32,7 @@ void Board::load_FEN(std::string board_fen) int x=0,y=0; std::string black_team("kqbnrpcos"); std::string white_team("KQBNRPCOS"); - for(int i = 0; i < board_fen.size() && !(x==BOARD_SIZE-1 && y==BOARD_SIZE); i++) + for(unsigned int i = 0; i < board_fen.size() && board_fen[i] != ' '; i++) { if (board_fen[i] == '/') { @@ -135,11 +136,125 @@ void Board::load_FEN(std::string board_fen) full_turn_count = strtol(full_turn_count_loc, nullptr, 10); } +std::string Board::make_FEN() +{ + std::string str_out = ""; + + std::string black_team("kqbnrpcos"); + std::string white_team("KQBNRPCOS"); + + for (int y = 0; y < BOARD_SIZE; y++) + { + int skipped_count = 0; + for (int x = 0; x < BOARD_SIZE; x++) + { + Piece cur_piece = game_board[x][y]; + if (cur_piece.get_type() != NO_TYPE) + { + if (skipped_count != 0) + { + str_out += skipped_count+'0'; + } + + if (cur_piece.get_vis() == HIDDEN) + { + cur_piece = Piece((Type)(cur_piece.get_type()+UNKNOWN-BISHOP), cur_piece.get_team(), SHOWN); + } + + if (cur_piece.get_team() == WHITE) + { + str_out+=white_team[cur_piece.get_type()]; + } + else if (cur_piece.get_team() == BLACK) + { + str_out+=black_team[cur_piece.get_type()]; + } + + skipped_count = 0; + } + else + { + skipped_count++; + } + } + + if (skipped_count != 0) + { + str_out += skipped_count+'0'; + } + + str_out += "/"; + } + + str_out += " "; + + if (active_color == WHITE) + { + str_out += "w "; + } + else + { + str_out += "b "; + } + + if (able_to_castle[0][0] || able_to_castle[0][1] || able_to_castle[1][0] || able_to_castle[1][1]) + { + if (able_to_castle[WHITE][KING]) + { + str_out += "K"; + } + + if (able_to_castle[WHITE][QUEEN]) + { + str_out += "Q"; + } + + if (able_to_castle[BLACK][KING]) + { + str_out += "k"; + } + + if (able_to_castle[BLACK][QUEEN]) + { + str_out += "q"; + } + } + else + { + str_out += "-"; + } + + str_out += " "; + + if (en_passant == -1) + { + str_out += "- "; + } + else + { + int ep_x = en_passant%BOARD_SIZE; + int ep_y = en_passant/BOARD_SIZE; + + str_out += ep_x+'a'; + str_out += ep_y+'0'; + + str_out += " "; + } + + str_out += std::to_string(half_turn_count); + + str_out += " "; + + str_out += std::to_string(full_turn_count); + + return str_out; +} + void Board::draw_board(SDL_Surface* dest_surface) { bool light_tile = true; - std::vector target_spaces = get_moves_for_space(selected_space); + std::vector target_spaces = get_moves_for_space(selected_space, true); for (int x = 0; x < BOARD_SIZE; x++) { @@ -205,7 +320,13 @@ void Board::draw_board(SDL_Surface* dest_surface) SDL_FillRect(piece_surface, nullptr, SDL_MapRGB(piece_surface->format, 0xFF, 0x00, 0xFF)); SDL_SetColorKey(piece_surface, SDL_TRUE, SDL_MapRGB(piece_surface->format, 0xFF, 0x00, 0xFF)); - if (0 == Sprite::get(game_board[x][y], piece_surface)) + Piece cur_piece = game_board[x][y]; + if (cur_piece.get_vis() == HIDDEN && cur_piece.get_team() == BLACK) + { + cur_piece = Piece(UNKNOWN, BLACK, SHOWN); + } + + if (0 == Sprite::get(cur_piece, piece_surface)) { SDL_BlitSurface(piece_surface, nullptr, dest_surface, &dest_rect); } @@ -262,14 +383,20 @@ int Board::get_full_turn_count() void Board::set_selected_space(int x, int y) { if (game_board[x][y].get_team() != WHITE) + { + selected_space = -1; return; + } selected_space = x+y*BOARD_SIZE; } void Board::set_selected_space(int space) { if (game_board[space%BOARD_SIZE][space/BOARD_SIZE].get_team() != WHITE) + { + selected_space = -1; return; + } selected_space = space; } @@ -286,16 +413,18 @@ int Board::get_selected_space() return selected_space; } -std::vector Board::get_moves_for_space(int x, int y) +std::vector Board::get_moves_for_space(int x, int y, bool check_for_check) { if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE || game_board[x][y].get_type() == NO_TYPE) return std::vector(); int forward_direction = (game_board[x][y].get_team() == WHITE)?-1:1; + bool is_team_in_check = (check_for_check && is_check(game_board[x][y].get_team())); + std::vector out_spaces; - Type piece_type = (game_board[x][y].get_vis() == VIS_NONE)?UNKNOWN:game_board[x][y].get_type(); + Type piece_type = (game_board[x][y].get_vis() == HIDDEN)?UNKNOWN:game_board[x][y].get_type(); if (PAWN == piece_type) { @@ -327,7 +456,7 @@ std::vector Board::get_moves_for_space(int x, int y) if (QUEEN == piece_type || ROOK == piece_type) { - for (int x1 = x-1; x1 > 0; x1--) + for (int x1 = x-1; x1 >= 0; x1--) { MoveType result = can_move(x+y*BOARD_SIZE, x1+y*BOARD_SIZE); if (BLOCKED == result) @@ -347,7 +476,7 @@ std::vector Board::get_moves_for_space(int x, int y) break; } - for (int y1 = y-1; y1 > 0; y1--) + for (int y1 = y-1; y1 >= 0; y1--) { MoveType result = can_move(x+y*BOARD_SIZE, x+y1*BOARD_SIZE); if (BLOCKED == result) @@ -357,7 +486,7 @@ std::vector Board::get_moves_for_space(int x, int y) break; } - for (int y1 = y+1; y1 > 0; y1++) + for (int y1 = y+1; y1 < BOARD_SIZE; y1++) { MoveType result = can_move(x+y*BOARD_SIZE, x+y1*BOARD_SIZE); if (BLOCKED == result) @@ -441,19 +570,7 @@ std::vector Board::get_moves_for_space(int x, int y) continue; } - Piece old_piece = game_board[x1][y1]; - game_board[x1][y1] = game_board[x][y]; - game_board[x][y] = Piece(); - - bool was_check = is_check(game_board[x1][y1].get_team()); - - game_board[x][y] = game_board[x1][y1]; - game_board[x1][y1] = old_piece; - - if (!was_check) - { - out_spaces.push_back(x1+y1*BOARD_SIZE); - } + out_spaces.push_back(x1+y1*BOARD_SIZE); } if (able_to_castle[game_board[x][y].get_team()][KING] && @@ -511,10 +628,10 @@ std::vector Board::get_moves_for_space(int x, int y) if (CAPTURE != result) { - int x1 = x+2*unknown_offsets[i][0]; - int y1 = y+2*unknown_offsets[i][1]; + x1 = x+2*unknown_offsets[i][0]; + y1 = y+2*unknown_offsets[i][1]; - if (x1 >= 0 && x1 < BOARD_SIZE && y1 >= 0 && y1 < BOARD_SIZE || BLOCKED == can_move(x+y*BOARD_SIZE, x1+y1*BOARD_SIZE)) + if (x1 >= 0 && x1 < BOARD_SIZE && y1 >= 0 && y1 < BOARD_SIZE && BLOCKED != can_move(x+y*BOARD_SIZE, x1+y1*BOARD_SIZE)) { out_spaces.push_back(x1+y1*BOARD_SIZE); } @@ -525,14 +642,20 @@ std::vector Board::get_moves_for_space(int x, int y) out_spaces.push_back(x+y*BOARD_SIZE); } + if (is_team_in_check) + { + std::vector::iterator new_end = std::remove_if(out_spaces.begin(), out_spaces.end(), [this, x, y](const int& target_space){ return !this->does_move_solve_check(x+y*BOARD_SIZE, target_space); }); + out_spaces.erase(new_end, out_spaces.end()); + } + return out_spaces; } -std::vector Board::get_moves_for_space(int space) +std::vector Board::get_moves_for_space(int space, bool check_for_check) { if (space < 0 || space >= BOARD_SIZE*BOARD_SIZE) return std::vector(); - return get_moves_for_space(space%BOARD_SIZE, space/BOARD_SIZE); + return get_moves_for_space(space%BOARD_SIZE, space/BOARD_SIZE, check_for_check); } bool Board::is_check(Team team) @@ -557,13 +680,13 @@ bool Board::is_check(Team team) return false; } - for(int x = 0; x < BOARD_SIZE && king_space == -1; x++) + for(int x = 0; x < BOARD_SIZE; x++) { - for(int y = 0; y < BOARD_SIZE && king_space == -1; y++) + for(int y = 0; y < BOARD_SIZE; y++) { if (game_board[x][y].get_team() == enemy_team) { - std::vector possible_moves = get_moves_for_space(x,y); + std::vector possible_moves = get_moves_for_space(x,y, false); if (possible_moves.end() != std::find(possible_moves.begin(), possible_moves.end(), king_space)) { return true; @@ -575,6 +698,121 @@ bool Board::is_check(Team team) return false; } +bool Board::is_mate(Team team) +{ + if (!is_check(team)) + { + return false; + } + + for(int x = 0; x < BOARD_SIZE; x++) + { + for(int y = 0; y < BOARD_SIZE; y++) + { + if (game_board[x][y].get_team() == team) + { + std::vector possible_moves = get_moves_for_space(x,y,false); + if (possible_moves.end() != std::find_if(possible_moves.begin(), possible_moves.end(), [this, x, y](const int& target_space){ return this->does_move_solve_check(x+y*BOARD_SIZE, target_space); })) + { + return false; + } + } + } + } + + return true; +} + +void Board::do_move(int space_from, int space_to, bool holding_k) +{ + if (space_from < 0 || space_to < 0 || space_from >= BOARD_SIZE*BOARD_SIZE || space_to >= BOARD_SIZE*BOARD_SIZE) + { + return; + } + + int xf=space_from%BOARD_SIZE,yf=space_from/BOARD_SIZE,xt=space_to%BOARD_SIZE,yt=space_to/BOARD_SIZE; + + Piece cur_piece = game_board[xf][yf]; + Piece target_piece = game_board[xt][yt]; + Team enemy_team = (cur_piece.get_team() == WHITE)?BLACK:WHITE; + + en_passant = -1; + + if (cur_piece.get_team() == BLACK) + { + full_turn_count++; + } + + if (cur_piece.get_type() == PAWN) + { + half_turn_count = 0; + if (yf-yt == 2 || yt-yf == 2) + { + int ep_y = (yt+yf)/2; + en_passant = xf+ep_y*BOARD_SIZE; + } + + if (yt == 7 && cur_piece.get_team() == BLACK) + { + cur_piece = Piece((holding_k)?KNIGHT:QUEEN, BLACK, SHOWN); + } + + if (yt == 0 && cur_piece.get_team() == WHITE) + { + cur_piece = Piece((holding_k)?KNIGHT:QUEEN, WHITE, SHOWN); + } + } + + if (cur_piece.get_type() == ROOK && cur_piece.get_vis() == SHOWN) + { + if (xf == 7) + { + able_to_castle[cur_piece.get_team()][KING] = false; + } + if (xf == 0) + { + able_to_castle[cur_piece.get_team()][QUEEN] = false; + } + } + + bool castling = false; + if (cur_piece.get_type() == KING) + { + if (target_piece.get_type() == ROOK && target_piece.get_team() == cur_piece.get_team() && target_piece.get_vis() == SHOWN) + { + castling = true; + } + + able_to_castle[cur_piece.get_team()][KING] = false; + able_to_castle[cur_piece.get_team()][QUEEN] = false; + } + + if (xf == xt && yf == yt && cur_piece.get_vis() == HIDDEN) + { + cur_piece = Piece(cur_piece.get_type(), cur_piece.get_team(), SHOWN); + } + + if (castling) + { + game_board[xf][yf] = target_piece; + } + else + { + game_board[xf][yf] = Piece(); + } + game_board[xt][yt] = cur_piece; + + if(is_check(enemy_team)) + { + able_to_castle[enemy_team][KING] = false; + able_to_castle[enemy_team][QUEEN] = false; + } + + active_color = (active_color == WHITE)?BLACK:WHITE; + + selected_space = -1; +} + MoveType Board::can_move(int space_from, int space_to) { if (space_from < 0 || space_to < 0 || space_from >= BOARD_SIZE*BOARD_SIZE || space_to >= BOARD_SIZE*BOARD_SIZE) @@ -602,5 +840,29 @@ MoveType Board::can_move(int space_from, int space_to) return CAPTURE; } + if (game_board[xf][yf].get_type() == KING) + { + if (able_to_castle[game_board[xf][yf].get_team()][KING] && + game_board[7][yf].get_team() == game_board[xf][yf].get_team() && game_board[7][yf].get_type() == ROOK && game_board[7][yf].get_vis() == SHOWN && + game_board[6][yf].get_type() == NO_TYPE && game_board[5][yf].get_type() == NO_TYPE) + { + return FREE; + } + + if (able_to_castle[game_board[xf][yf].get_team()][QUEEN] && + game_board[0][yf].get_team() == game_board[xf][yf].get_team() && game_board[0][yf].get_type() == ROOK && game_board[0][yf].get_vis() == SHOWN && + game_board[1][yf].get_type() == NO_TYPE && game_board[2][yf].get_type() == NO_TYPE && game_board[3][yf].get_type() == NO_TYPE) + { + return FREE; + } + } + return BLOCKED; +} + +bool Board::does_move_solve_check(int space_from, int space_to) +{ + Board test_board(make_FEN()); + test_board.do_move(space_from, space_to, false); + return !test_board.is_check(game_board[space_from%BOARD_SIZE][space_from/BOARD_SIZE].get_team()); } \ No newline at end of file diff --git a/board.hpp b/board.hpp index 28246da..03f43e5 100755 --- a/board.hpp +++ b/board.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include "SDL2/SDL.h" #include #include @@ -32,10 +32,13 @@ public: void get_selected_space(int* x, int* y); int get_selected_space(); - std::vector get_moves_for_space(int x, int y); - std::vector get_moves_for_space(int space); + std::vector get_moves_for_space(int x, int y, bool check_for_check); + std::vector get_moves_for_space(int space, bool check_for_check); bool is_check(Team team); + bool is_mate(Team team); + + void do_move(int space_from, int space_to, bool holding_k); private: Piece game_board[BOARD_SIZE][BOARD_SIZE]; Team active_color; @@ -45,4 +48,5 @@ private: int selected_space; MoveType can_move(int space_from, int space_to); + bool does_move_solve_check(int space_from, int space_to); }; diff --git a/game.cpp b/game.cpp index f46a9cf..c0b7ece 100644 --- a/game.cpp +++ b/game.cpp @@ -1,6 +1,10 @@ #include "game.hpp" #include +#include +#include +#include +#include uint64_t time_milli() { using namespace std::chrono; @@ -12,6 +16,7 @@ Game::Game(SDL_Window* window, SDL_Surface* surface) this->window = window; this->surface = surface; running = false; + holding_k = false; } Game::Game(SDL_Window* window, SDL_Surface* surface, std::string board_fen) @@ -20,6 +25,7 @@ Game::Game(SDL_Window* window, SDL_Surface* surface, std::string board_fen) this->surface = surface; board = Board(board_fen); running = false; + holding_k = false; } void Game::run() @@ -44,22 +50,34 @@ void Game::run() void Game::tick() { SDL_Event e; - while(SDL_PollEvent(&e) != 0) + while(running && SDL_PollEvent(&e) != 0) { if (e.type == SDL_QUIT || (e.type == SDL_KEYUP && e.key.keysym.sym == SDLK_q)) { running = false; } - - if (e.type == SDL_MOUSEBUTTONDOWN) + else if (e.type == SDL_KEYUP && e.key.keysym.sym == SDLK_k) + { + holding_k = false; + } + else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_k) + { + holding_k = true; + } + else if (e.type == SDL_MOUSEBUTTONDOWN) { int x,y; SDL_GetMouseState(&x, &y); x/=SPRITE_SIZE; y/=SPRITE_SIZE; - board.set_selected_space(x,y); + process_click(x,y); } } + + if (BLACK == board.get_active_color()) + { + do_ai_move(); + } } void Game::draw() @@ -67,4 +85,284 @@ void Game::draw() board.draw_board(surface); SDL_UpdateWindowSurface(window); +} + +void Game::process_click(int x, int y) +{ + if (WHITE == board.get_active_color()) + { + int current_selected_space = board.get_selected_space(); + int target_selected_space = x+y*BOARD_SIZE; + if (current_selected_space == -1) + { + board.set_selected_space(x,y); + } + else + { + std::vector target_spaces = board.get_moves_for_space(current_selected_space, true); + + if (target_spaces.end() != std::find(target_spaces.begin(), target_spaces.end(), target_selected_space)) + { + board.do_move(current_selected_space, target_selected_space, holding_k); + } + else + { + board.set_selected_space(target_selected_space); + } + } + } +} + +void Game::do_ai_move() +{ + int best_move_weight = std::numeric_limits::min(); + int best_move_from = -1; + int best_move_to = -1; + bool best_move_makes_queen = false; + for(int x = 0; x < BOARD_SIZE; x++) + { + for(int y = 0; y < BOARD_SIZE; y++) + { + Piece cur_piece = board.get_piece(x,y); + if (cur_piece.get_team() == BLACK) + { + std::vector piece_moves = board.get_moves_for_space(x,y, true); + for (unsigned int i = 0; i < piece_moves.size(); i++) + { + Board test_board(board.make_FEN()); + test_board.do_move(x+y*BOARD_SIZE, piece_moves[i], false); + + int board_value = minimax(test_board, 3, std::numeric_limits::min(), std::numeric_limits::max(), false); + + if (board_value > best_move_weight) + { + best_move_weight = board_value; + best_move_from = x+y*BOARD_SIZE; + best_move_to = piece_moves[i]; + best_move_makes_queen = false; + } + + if (cur_piece.get_type() == PAWN || (piece_moves[i]/BOARD_SIZE) == 7) + { + Board test_board1 = Board(board.make_FEN()); + test_board1.do_move(x+y*BOARD_SIZE, piece_moves[i], true); + + if (test_board1.make_FEN().compare(test_board.make_FEN()) != 0) + { + board_value = minimax(test_board1, 3, std::numeric_limits::min(), std::numeric_limits::max(), false); + + if (board_value > best_move_weight) + { + best_move_weight = board_value; + best_move_from = x+y*BOARD_SIZE; + best_move_to = piece_moves[i]; + best_move_makes_queen = true; + } + } + } + } + } + } + } + + board.do_move(best_move_from, best_move_to, best_move_makes_queen); +} + +int Game::minimax(Board current_board, int depth, int a, int b, bool maximizing) +{ + if (depth == 0 || current_board.is_mate(current_board.get_active_color())) + { + return board_heuristic(current_board); + } + + if (maximizing) + { + int best_move_weight = std::numeric_limits::min(); + for(int x = 0; x < BOARD_SIZE; x++) + { + for(int y = 0; y < BOARD_SIZE; y++) + { + Piece cur_piece = current_board.get_piece(x,y); + if (cur_piece.get_team() == BLACK) + { + std::vector piece_moves = current_board.get_moves_for_space(x,y, true); + for (unsigned int i = 0; i < piece_moves.size(); i++) + { + Board test_board(current_board.make_FEN()); + test_board.do_move(x+y*BOARD_SIZE, piece_moves[i], false); + + int board_value = minimax(test_board, depth-1, a, b, false); + + if (board_value > best_move_weight) + { + best_move_weight = board_value; + } + + if (best_move_weight >= b) + { + return best_move_weight; + } + + if (best_move_weight > a) + { + a = best_move_weight; + } + + if (cur_piece.get_type() == PAWN || (piece_moves[i]/BOARD_SIZE) == 7) + { + test_board = Board(current_board.make_FEN()); + test_board.do_move(x+y*BOARD_SIZE, piece_moves[i], true); + + board_value = minimax(test_board, depth-1, a, b, false); + + if (board_value > best_move_weight) + { + best_move_weight = board_value; + } + + if (best_move_weight >= b) + { + return best_move_weight; + } + + if (best_move_weight > a) + { + a = best_move_weight; + } + } + } + } + } + } + + return best_move_weight; + } + else + { + int best_move_weight = std::numeric_limits::max(); + for(int x = 0; x < BOARD_SIZE; x++) + { + for(int y = 0; y < BOARD_SIZE; y++) + { + Piece cur_piece = current_board.get_piece(x,y); + if (cur_piece.get_team() == WHITE) + { + std::vector piece_moves = current_board.get_moves_for_space(x,y, true); + for (unsigned int i = 0; i < piece_moves.size(); i++) + { + Board test_board(current_board.make_FEN()); + test_board.do_move(x+y*BOARD_SIZE, piece_moves[i], false); + + int board_value = minimax(test_board, depth-1, a, b, true); + + if (board_value < best_move_weight) + { + best_move_weight = board_value; + } + + if (best_move_weight <= a) + { + return best_move_weight; + } + + if (best_move_weight < b) + { + b = best_move_weight; + } + + if (cur_piece.get_type() == PAWN || (piece_moves[i]/BOARD_SIZE) == 0) + { + test_board = Board(current_board.make_FEN()); + test_board.do_move(x+y*BOARD_SIZE, piece_moves[i], true); + + board_value = minimax(test_board, depth-1, a, b, true); + + if (board_value < best_move_weight) + { + best_move_weight = board_value; + } + + if (best_move_weight <= a) + { + return best_move_weight; + } + + if (best_move_weight < b) + { + b = best_move_weight; + } + } + } + } + } + } + + return best_move_weight; + } +} + +int Game::board_heuristic(Board current_board) +{ + int heuristic = 0; + int piece_weights[2][7] = {{-900, -90, -30, -30, -50, -10, 0}, {900, 90, 30, 30, 50, 10, 0}}; + + int unknown_white_count = 0; + int unknown_black_count = 0; + for(int x = 0; x < BOARD_SIZE; x++) + { + for(int y = 0; y < BOARD_SIZE; y++) + { + Piece cur_piece = current_board.get_piece(x,y); + + if (cur_piece.get_type() == NO_TYPE) + { + continue; + } + + if (cur_piece.get_vis() == HIDDEN) + { + if (cur_piece.get_team() == WHITE) + { + piece_weights[WHITE][UNKNOWN] += piece_weights[WHITE][cur_piece.get_type()]; + unknown_white_count++; + } + else + { + piece_weights[BLACK][UNKNOWN] += piece_weights[BLACK][cur_piece.get_type()]; + unknown_black_count++; + } + } + } + } + + if (unknown_white_count != 0) + { + piece_weights[WHITE][UNKNOWN] /= unknown_white_count; + } + if (unknown_black_count != 0) + { + piece_weights[BLACK][UNKNOWN] /= unknown_black_count; + } + + for(int x = 0; x < BOARD_SIZE; x++) + { + for(int y = 0; y < BOARD_SIZE; y++) + { + Piece cur_piece = current_board.get_piece(x,y); + + if (cur_piece.get_type() == NO_TYPE) + { + continue; + } + + if (cur_piece.get_vis() == HIDDEN) + { + cur_piece = Piece(UNKNOWN, cur_piece.get_team(), SHOWN); + } + + heuristic += piece_weights[cur_piece.get_team()][cur_piece.get_type()]; + } + } + + return heuristic; } \ No newline at end of file diff --git a/game.hpp b/game.hpp index dd4d8e1..02b0e14 100644 --- a/game.hpp +++ b/game.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include "SDL2/SDL.h" #include "board.hpp" @@ -15,10 +15,18 @@ private: void tick(); void draw(); + void process_click(int x, int y); + + void do_ai_move(); + int minimax(Board current_board, int depth, int a, int b, bool maximizing); + int board_heuristic(Board current_board); + SDL_Window* window; SDL_Surface* surface; Board board; bool running; + + bool holding_k; }; \ No newline at end of file diff --git a/main.cpp b/main.cpp index 9c8320f..27880b7 100755 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,13 @@ -#include +#include "SDL2/SDL.h" #include "app_consts.hpp" #include "sprites.hpp" #include "game.hpp" -int main(int argc, char** argv) +int main(int argc, char *argv[]) { + (void)argc; + (void)argv; //Sourced from lazyfoo's SDL2 tutorials, not really any special code here, just standard init process for the window SDL_Window* window = NULL; diff --git a/piece.hpp b/piece.hpp index f888b15..3ed7214 100644 --- a/piece.hpp +++ b/piece.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include "SDL2/SDL.h" #include "app_consts.hpp"