Skip to content

Commit

Permalink
Add toggle first player button
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolarevelant committed Aug 26, 2023
1 parent 7854d13 commit 8a20524
Show file tree
Hide file tree
Showing 11 changed files with 144 additions and 87 deletions.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
22 changes: 11 additions & 11 deletions src/ChessboardGrid/ChessboardGrid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,36 +46,36 @@ bool ChessboardGrid::Create(const wxColour &darkColor, const wxColour &lightColo
return true;
}

void ChessboardGrid::updateIcons(const wxBitmap &pcPawn, const wxBitmap &pcDame,
const wxBitmap &plPawn, const wxBitmap &plDame) {
m_pcPawn = pcPawn;
m_pcDame = pcDame;
m_plPawn = plPawn;
m_plDame = plDame;
void ChessboardGrid::updateIcons(const wxBitmap &firstPawn, const wxBitmap &firstDame,
const wxBitmap &secondPawn, const wxBitmap &secondDame) {
mFirstPawn = firstPawn;
mFirstDame = firstDame;
mSecondPawn = secondPawn;
mSecondDame = secondDame;
}

void ChessboardGrid::OnItemMouseClicked(wxMouseEvent &evt) {
wxPostEvent(this, evt);
}

void ChessboardGrid::updateDisposition(const GameUtils::Disposition &newDisposition) {
void ChessboardGrid::updateDisposition(const GameUtils::Disposition &newDisposition, bool pcIsFirstPlayer) {
for (int i = 0; i < 64; i++) {
chessboard[i]->SetBorder();
switch (newDisposition[i]) {
case GameUtils::EMPTY:
chessboard[i]->SetBitmap(wxNullBitmap);
break;
case GameUtils::PC_PAWN:
chessboard[i]->SetBitmap(m_pcPawn);
chessboard[i]->SetBitmap(pcIsFirstPlayer ? mFirstPawn : mSecondPawn);
break;
case GameUtils::PC_DAME:
chessboard[i]->SetBitmap(m_pcDame);
chessboard[i]->SetBitmap(pcIsFirstPlayer ? mFirstDame : mSecondDame);
break;
case GameUtils::PL_PAWN:
chessboard[i]->SetBitmap(m_plPawn);
chessboard[i]->SetBitmap(pcIsFirstPlayer ? mSecondPawn : mFirstPawn);
break;
case GameUtils::PL_DAME:
chessboard[i]->SetBitmap(m_plDame);
chessboard[i]->SetBitmap(pcIsFirstPlayer ? mSecondDame : mFirstDame);
break;
}
}
Expand Down
25 changes: 12 additions & 13 deletions src/ChessboardGrid/ChessboardGrid.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,31 +26,30 @@ class ChessboardGrid : public wxPanel {
* @param winId Window ID, or wxID_ANY
* @param pos Position relative to the parent
*/
ChessboardGrid(const wxColour &darkColor, const wxColour &lightColor, int squareSize,
wxWindow *parent, wxWindowID winId = wxID_ANY,
const wxPoint &pos = wxDefaultPosition);
ChessboardGrid(const wxColour &darkColor, const wxColour &lightColor, int squareSize, wxWindow *parent,
wxWindowID winId = wxID_ANY, const wxPoint &pos = wxDefaultPosition);

/**
* Creates a new ChessboardGrid using two-step construction
*/
bool Create(const wxColour &darkColor, const wxColour &lightColor, int squareSize,
wxWindow *parent, wxWindowID winId = wxID_ANY,
const wxPoint &pos = wxDefaultPosition);
bool Create(const wxColour &darkColor, const wxColour &lightColor, int squareSize, wxWindow *parent,
wxWindowID winId = wxID_ANY, const wxPoint &pos = wxDefaultPosition);

/**
* Updates the icons
* @param pcPawn PC pawn
* @param pcDame PC dame
* @param plPawn PL pawn
* @param plDame PL dame
* @param firstPawn First player's pawn
* @param firstDame First player's dame
* @param secondPawn Second player's pawn
* @param secondDame Second player's dame
*/
void updateIcons(const wxBitmap &pcPawn, const wxBitmap &pcDame, const wxBitmap &plPawn, const wxBitmap &plDame);
void updateIcons(const wxBitmap &firstPawn, const wxBitmap &firstDame, const wxBitmap &secondPawn, const wxBitmap &secondDame);

/**
* Updates the disposition of the pieces in the chessboard and clears every border
* @param newDisposition The new disposition to apply
* @param pcIsFirstPlayer True if PC is the first player
*/
void updateDisposition(const GameUtils::Disposition &newDisposition);
void updateDisposition(const GameUtils::Disposition &newDisposition, bool pcIsFirstPlayer);

/**
* Set a new border for the square located at an specific location
Expand All @@ -68,7 +67,7 @@ class ChessboardGrid : public wxPanel {
ChessboardGrid(const ChessboardGrid &); // prevents copy-constructor
std::array<ChessboardSquare *, 64> chessboard{};

wxBitmap m_pcPawn, m_pcDame, m_plPawn, m_plDame;
wxBitmap mFirstPawn = wxNullBitmap, mFirstDame = wxNullBitmap, mSecondPawn = wxNullBitmap, mSecondDame = wxNullBitmap;
GameUtils::MoveList moves; // list of moves the player can do

int m_squareSize{};
Expand Down
35 changes: 23 additions & 12 deletions src/Frame/Frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ wxMenuBar *Frame::createMenuBar() {
menuFile->Append(wxID_EXIT, _("&Exit"), _("Leave the game"));

auto *menuSettings = new wxMenu;
menuSettings->Append(CHANGE_GD, _("&Change difficulty"), _("Change difficulty"));
menuSettings->Append(CHANGE_GD, _("Change &difficulty"), _("Change difficulty"));
menuSettings->Append(TOGGLE_FIRST_PLAYER, _("&Toggle first player"), _("Toggle first player"));

auto *menuHelp = new wxMenu;
menuHelp->Append(wxID_ABOUT, _("About " PROJECT_PRETTY_NAME), _("Open about dialog"));
Expand All @@ -79,23 +80,24 @@ wxMenuBar *Frame::createMenuBar() {
menuBar->Bind(wxEVT_MENU, &Frame::newMatchClicked, this, NEW_MATCH);
menuBar->Bind(wxEVT_MENU, &Frame::closeFrame, this, wxID_EXIT);
menuBar->Bind(wxEVT_MENU, &Frame::changeDifficultyClicked, this, CHANGE_GD);
menuBar->Bind(wxEVT_MENU, &Frame::flipFirstPlayer, this, TOGGLE_FIRST_PLAYER);
menuBar->Bind(wxEVT_MENU, &Frame::aboutClicked, this, wxID_ABOUT);

return menuBar;
}

wxPanel *Frame::createChessboard(wxWindow *parent, const std::string &path) {
// load images
wxBitmap pcPawn{path + "/images/pcPawn.png"};
if (!pcPawn.IsOk()) return nullptr;
wxBitmap pcDame(path + "/images/pcDame.png");
if (!pcDame.IsOk()) return nullptr;
wxBitmap plPawn(path + "/images/plPawn.png");
if (!plPawn.IsOk()) return nullptr;
wxBitmap plDame(path + "/images/plDame.png");
if (!plDame.IsOk()) return nullptr;
wxSize imageSize = pcPawn.GetSize();
if (pcDame.GetSize() != imageSize || plPawn.GetSize() != imageSize || plDame.GetSize() != imageSize) return nullptr;
wxBitmap firstPawn{path + "/images/firstPawn.png"};
if (!firstPawn.IsOk()) return nullptr;
wxBitmap firstDame(path + "/images/firstDame.png");
if (!firstDame.IsOk()) return nullptr;
wxBitmap secondPawn(path + "/images/secondPawn.png");
if (!secondPawn.IsOk()) return nullptr;
wxBitmap secondDame(path + "/images/secondDame.png");
if (!secondDame.IsOk()) return nullptr;
wxSize imageSize = firstPawn.GetSize();
if (firstDame.GetSize() != imageSize || secondPawn.GetSize() != imageSize || secondDame.GetSize() != imageSize) return nullptr;
if (imageSize.GetWidth() != imageSize.GetHeight()) return nullptr;

auto *chessboardPanel = new wxPanel(parent, wxID_ANY);
Expand All @@ -105,7 +107,7 @@ wxPanel *Frame::createChessboard(wxWindow *parent, const std::string &path) {
resources.getColor("light", DEF_LIGHT_COLOR),
imageSize.GetWidth(), chessboardPanel, wxID_ANY,
wxPoint(CHESSBOARD_BORDER_H, CHESSBOARD_BORDER_V));
grid->updateIcons(pcPawn, pcDame, plPawn, plDame);
grid->updateIcons(firstPawn, firstDame, secondPawn, secondDame);
int width, height;
grid->GetSize(&width, &height);
chessboardPanel->SetMinSize(wxSize(width + CHESSBOARD_BORDER_H * 2, height + CHESSBOARD_BORDER_V * 2));
Expand Down Expand Up @@ -175,6 +177,15 @@ void Frame::changeDifficultyClicked(wxCommandEvent &) {
}
}

void Frame::flipFirstPlayer(wxCommandEvent &) {
if (chessboardManager->isPlaying()) {
wxMessageDialog dialog(this, _("Are you sure you want to leave the game?"), _("New match"), wxYES_NO);
if (dialog.ShowModal() != wxID_YES) return;
}

chessboardManager->flipFirstPlayer();
}

void Frame::aboutClicked(wxCommandEvent &) {
wxAboutDialogInfo dialog;
dialog.SetName(wxFrame::GetTitle());
Expand Down
9 changes: 7 additions & 2 deletions src/Frame/Frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include "../ChessboardGrid/ChessboardGrid.h"
#include "wx/wx.h"

#define DEF_BORDER_COLOR wxColour()
#define DEF_DARK_COLOR wxColour(32, 32, 32)
#define DEF_LIGHT_COLOR wxColour(140, 140, 140)

Expand Down Expand Up @@ -38,7 +37,8 @@ class Frame : public wxFrame {

enum MenuItems {
NEW_MATCH = 1,
CHANGE_GD
CHANGE_GD,
TOGGLE_FIRST_PLAYER,
};

Resources resources{DATA_PATH};
Expand Down Expand Up @@ -82,6 +82,11 @@ class Frame : public wxFrame {
*/
void changeDifficultyClicked(wxCommandEvent &);

/**
* Change first player
*/
void flipFirstPlayer(wxCommandEvent &);

/**
* Shows about dialog
*/
Expand Down
12 changes: 9 additions & 3 deletions src/GameUtils/GameUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@ GameUtils::AlgorithmThread::AlgorithmThread(wxEvtHandler *evtHandler, const Disp
}

void *GameUtils::AlgorithmThread::Entry() {
wxCommandEvent evt(wxEVT_MENU, m_id);
evt.SetClientData(GameAlgorithm::calculateBestMove(m_disposition, m_gameDifficult));
m_evtHandler->AddPendingEvent(evt);
auto *data = GameAlgorithm::calculateBestMove(m_disposition, m_gameDifficult);

if (TestDestroy()) {
free(data);
} else {
auto *evt = new wxCommandEvent(wxEVT_MENU, m_id);
evt->SetClientData(data);
wxQueueEvent(m_evtHandler, evt);
}

return nullptr;
}
Expand Down
100 changes: 60 additions & 40 deletions src/MatchManager/MatchManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ MatchManager::MatchManager(ChessboardGrid *chessboard, const wxColour &focusColo
chessboard->Bind(wxEVT_MENU, &MatchManager::onThreadFinish, this, THREAD_ID);

chessboardGrid = chessboard;
m_threadRunning = false;
m_isPlaying = false;
m_isEnd = false;
algorithmThread = nullptr;
mIsPlaying = false;
mIsEnd = false;
mIsPcFirstPlayer = false;
}

MatchManager::~MatchManager() {
Expand All @@ -29,52 +30,64 @@ MatchManager::~MatchManager() {
}
}

bool MatchManager::newMatch() {
if (algorithmThread) {
algorithmThread->Delete();
algorithmThread = nullptr;
}

setDefaultLayout();
chessboardGrid->updateDisposition(m_disposition, mIsPcFirstPlayer);

// deletes all moves before re-assignment
for (GameUtils::Move *move: moves)
delete move;

mIsEnd = false;
if (mIsPcFirstPlayer) {
moves.clear();
makePCMove();
} else {
mIsPlaying = false;
moves = GameUtils::findMoves(m_disposition, true);
notifyUpdate(TURN_PLAYER);
}

return true;
}

void MatchManager::setOnUpdateListener(const std::function<void(enum UpdateType)> &listener) {
m_onUpdate = listener;

// if the game is over it doesn't call the listener
if (!m_isEnd) notifyUpdate(m_threadRunning ? TURN_PC : TURN_PLAYER);
if (!mIsEnd) notifyUpdate(algorithmThread ? TURN_PC : TURN_PLAYER);
}

bool MatchManager::changeDifficulty(int newDifficulty) {
if (m_threadRunning) return false;

if (newDifficulty < minGD || newDifficulty > maxGD)
return false;

gameDifficulty = newDifficulty;
if (isPlaying() || m_isEnd) newMatch();
else notifyUpdate(TURN_PLAYER);
newMatch();
return true;
}

int MatchManager::getDifficulty() const {
return gameDifficulty;
bool MatchManager::flipFirstPlayer() {
mIsPcFirstPlayer = !mIsPcFirstPlayer;
newMatch();
return true;
}

bool MatchManager::newMatch() {
if (m_threadRunning) return false;

setDefaultLayout();
chessboardGrid->updateDisposition(m_disposition);
m_isEnd = false;
m_isPlaying = false;

// deletes all moves before re-assignment
for (GameUtils::Move *move: moves)
delete move;

moves = GameUtils::findMoves(m_disposition, true);
notifyUpdate(TURN_PLAYER);
return true;
int MatchManager::getDifficulty() const {
return gameDifficulty;
}

bool MatchManager::isPlaying() const {
return m_isPlaying;
return mIsPlaying;
}

void MatchManager::onChessboardSquareClick(wxMouseEvent &event) {
if (m_threadRunning || m_isEnd) return;
if (algorithmThread || mIsEnd) return;

int currentPos = event.GetId();

Expand Down Expand Up @@ -126,32 +139,39 @@ void MatchManager::onChessboardSquareClick(wxMouseEvent &event) {
}

// legal move
chessboardGrid->updateDisposition(m_disposition = move->disposition);
chessboardGrid->updateDisposition(m_disposition = move->disposition, mIsPcFirstPlayer);
selectedPos = selectedNone;

auto *thread = new GameUtils::AlgorithmThread(chessboardGrid, m_disposition, gameDifficulty, THREAD_ID);
if (thread->Create() != wxTHREAD_NO_ERROR || thread->Run() != wxTHREAD_NO_ERROR) {
exit(1);
}
makePCMove();
}

m_isPlaying = true;
m_threadRunning = true;
void MatchManager::makePCMove() {
mIsPlaying = true;
notifyUpdate(TURN_PC);
algorithmThread = new GameUtils::AlgorithmThread(chessboardGrid, m_disposition, gameDifficulty, THREAD_ID);
if (algorithmThread->Create() != wxTHREAD_NO_ERROR || algorithmThread->Run() != wxTHREAD_NO_ERROR) {
std::cerr << "Cannot execute thread" << std::endl;
exit(1);
}
}

void MatchManager::onThreadFinish(wxCommandEvent &evt) {
m_threadRunning = false;
if (!algorithmThread) {
std::cerr << "Unwanted thread finished" << std::endl;
delete static_cast<GameUtils::Move *>(evt.GetClientData());
}
algorithmThread = nullptr;

auto *pcMove = static_cast<GameUtils::Move *>(evt.GetClientData());
if (pcMove == nullptr) {
// PC cannot do anything, player won
m_isEnd = true;
m_isPlaying = false;
mIsEnd = true;
mIsPlaying = false;
notifyUpdate(PLAYER_WON);
return;
}

chessboardGrid->updateDisposition(m_disposition = pcMove->disposition);
chessboardGrid->updateDisposition(m_disposition = pcMove->disposition, mIsPcFirstPlayer);
delete pcMove;

// deletes all moves before re-assignment
Expand All @@ -161,8 +181,8 @@ void MatchManager::onThreadFinish(wxCommandEvent &evt) {
moves = GameUtils::findMoves(m_disposition, true);
if (moves.empty()) {
// Player cannot do anything, PC won
m_isEnd = true;
m_isPlaying = false;
mIsEnd = true;
mIsPlaying = false;
notifyUpdate(PC_WON);
return;
}
Expand Down
Loading

0 comments on commit 8a20524

Please sign in to comment.