Skip to content

Commit

Permalink
Merge GameAlgorithm into GameUtils
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolarevelant committed Nov 10, 2023
1 parent 2a29edd commit 1c78e68
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 171 deletions.
4 changes: 1 addition & 3 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Wextra -Wpedantic")

# wxWidgets
find_package(wxWidgets REQUIRED base core adv)
Expand All @@ -12,9 +12,7 @@ include(${wxWidgets_USE_FILE})
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR})

# build static libraries and link dependencies
add_subdirectory(GameAlgorithm)
add_subdirectory(GameUtils)
target_link_libraries(GameUtils PRIVATE GameAlgorithm)
add_subdirectory(MatchManager)
target_link_libraries(MatchManager PRIVATE GameUtils)
add_subdirectory(ChessboardSquare)
Expand Down
4 changes: 2 additions & 2 deletions src/Frame/Frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ wxPanel *Frame::createChessboard(wxWindow *parent) {

auto *grid = new ChessboardGrid();
if (!grid->Create(std::bind(&Frame::getBitmap, this, std::placeholders::_1, std::placeholders::_2),
std::bind(&Frame::getColor, this, std::placeholders::_1, std::placeholders::_2),
chessboardPanel, wxID_ANY, wxPoint(CHESSBOARD_BORDER_H, CHESSBOARD_BORDER_V))) {
std::bind(&Frame::getColor, this, std::placeholders::_1, std::placeholders::_2),
chessboardPanel, wxID_ANY, wxPoint(CHESSBOARD_BORDER_H, CHESSBOARD_BORDER_V))) {
#ifdef DEBUG
std::cerr << "Cannot create ChessboardGrid" << std::endl;
#endif
Expand Down
4 changes: 0 additions & 4 deletions src/Frame/README.md

This file was deleted.

1 change: 0 additions & 1 deletion src/GameAlgorithm/CMakeLists.txt

This file was deleted.

110 changes: 0 additions & 110 deletions src/GameAlgorithm/GameAlgorithm.cpp

This file was deleted.

38 changes: 0 additions & 38 deletions src/GameAlgorithm/GameAlgorithm.h

This file was deleted.

4 changes: 0 additions & 4 deletions src/GameAlgorithm/README.md

This file was deleted.

110 changes: 108 additions & 2 deletions src/GameUtils/GameUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
// Copyright (C) 2023 Nicola Revelant

#include "GameUtils.h"
#include "GameAlgorithm/GameAlgorithm.h"

#include <algorithm>
#include <climits>
#include <random>
#include <vector>

GameUtils::AlgorithmThread::AlgorithmThread(wxEvtHandler *evtHandler, const Disposition &disposition, int gameDifficulty,
int id) : wxThread(wxTHREAD_DETACHED), mDisposition(disposition) {
Expand All @@ -12,7 +16,7 @@ GameUtils::AlgorithmThread::AlgorithmThread(wxEvtHandler *evtHandler, const Disp
}

void *GameUtils::AlgorithmThread::Entry() {
auto *data = GameAlgorithm::calculateBestMove(mDisposition, mGameDifficulty);
auto *data = calculateBestMove(mDisposition, mGameDifficulty);

if (TestDestroy()) {
free(data);
Expand Down Expand Up @@ -185,3 +189,105 @@ bool GameUtils::addMoveStep(MoveList &moves, const Disposition &disposition, int

return true;
}

struct {
bool operator()(GameUtils::Move *a, GameUtils::Move *b) const { return a->score < b->score; }
} sortAscending;

struct {
bool operator()(GameUtils::Move *a, GameUtils::Move *b) const { return a->score > b->score; }
} sortDiscending;

GameUtils::Move *GameUtils::calculateBestMove(const GameUtils::Disposition &disposition, int depth) {
if (depth < 0) return nullptr;

GameUtils::Move *res_move = nullptr;
int bestScore = INT_MIN;
int alpha = INT_MIN, beta = INT_MAX;

std::vector<GameUtils::Move *> moves = GameUtils::findMoves(disposition, false);
std::shuffle(moves.begin(), moves.end(), std::random_device());
std::sort(moves.begin(), moves.end(), sortAscending);
for (GameUtils::Move *move: moves) {
int score = minimax(move, move->score, false, depth, alpha, beta);
if (score == INT_MAX) {
bestScore = INT_MAX;
res_move = move;
break;
}

if (score > bestScore) {
bestScore = score;
res_move = move;
}

if (score > alpha) {
alpha = score;
}
}

if (moves.empty()) {
return nullptr;
} else if (res_move == nullptr) {
// bestScore equals INT_MIN
res_move = moves.front();
}

for (GameUtils::Move *move: moves) {
if (move != res_move)
delete move;
}

return res_move;
}

int GameUtils::minimax(const GameUtils::Move *start_move, int oldScore, bool maximizing, int depth, int alpha,
int beta) {
if (depth == 0) return oldScore; // depth limit reached

int bestScore, score;

if (maximizing) {
bestScore = INT_MIN;
std::vector<GameUtils::Move *> moves = GameUtils::findMoves(start_move->disposition, false);
std::sort(moves.begin(), moves.end(), sortAscending);
for (GameUtils::Move *move: moves) {
score = minimax(move, oldScore + move->score, false, depth - 1, alpha, beta);
if (score > bestScore) {
bestScore = score;

if (score > alpha) {
alpha = score;
if (beta <= alpha)
break; // ignore other moves because parent won't choose this path
}
}
}

for (GameUtils::Move *move: moves) {
delete move;
}
return bestScore;
}

bestScore = INT_MAX;
std::vector<GameUtils::Move *> moves = GameUtils::findMoves(start_move->disposition, true);
std::sort(moves.begin(), moves.end(), sortDiscending);
for (GameUtils::Move *move: moves) {
score = minimax(move, oldScore - move->score, true, depth - 1, alpha, beta);
if (score < bestScore) {
bestScore = score;

if (score < beta) {
beta = score;
if (beta <= alpha)
break; // ignore other moves because parent won't choose this path
}
}
}

for (GameUtils::Move *move: moves) {
delete move;
}
return bestScore;
}
34 changes: 27 additions & 7 deletions src/GameUtils/GameUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
*/
class GameUtils {
public:

enum PieceType {
EMPTY = 0,
PC_PAWN,
Expand Down Expand Up @@ -47,9 +46,8 @@ class GameUtils {
};

struct Move {
Move(const Disposition disposition, bool eatenFromPawn, int score) : disposition(disposition),
eatenFromPawn(eatenFromPawn),
score(score) {}
Move(const Disposition disposition, bool eatenFromPawn, int score) :
disposition(disposition), eatenFromPawn(eatenFromPawn), score(score) {}

/**
* The disposition after the move
Expand Down Expand Up @@ -80,12 +78,34 @@ class GameUtils {
*/
static MoveList findMoves(const Disposition &disposition, bool player);

/**
* Calculates the best move for the computer
* @param disposition Current pieces' disposition
* @param depth How many recursion levels are allowed
* @return A possible move for the computer, or nullptr
*/
static GameUtils::Move *calculateBestMove(const GameUtils::Disposition &disposition, int depth);

private:
GameUtils() = default;

/**
* Add a move step to find how long the move is
*/
static bool addMoveStep(MoveList &moves, const Disposition &disposition,
int source_position, bool row_offset, bool col_offset, int score);
};
int source_position, bool row_offset, bool col_offset, int score);

/**
* Calculates the score of the best move
* @param start_move
* @param oldScore The current score
* @param maximizing True if PC
* @param depth How many levels of recursion to do
* @param alpha Used by alpha-beta pruning
* @param beta Used by alpha-beta pruning
* @return The score after the best move, or INT_MIN if maximizing, or INT_MAX otherwise
*/
static int minimax(const GameUtils::Move *start_move, int oldScore, bool maximizing, int depth, int alpha, int beta);
};

#endif //ITALIAN_DRAUGHTS_MINIMAX_THREAD_H
#endif // ITALIAN_DRAUGHTS_MINIMAX_THREAD_H

0 comments on commit 1c78e68

Please sign in to comment.