aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorfschildt <florian.schildt@protonmail.com>2025-09-28 11:23:04 +0200
committerfschildt <florian.schildt@protonmail.com>2025-09-28 11:23:04 +0200
commit4537e9bee3d054786857fa92824e2d9e8385bb36 (patch)
tree92ee1fea597f8164d09885286b71d812f6890259 /src
parent3d30e1ee9d1c9fb67cca8e3f178ba5fd05a2726e (diff)
minesweeper: now playable
Diffstat (limited to 'src')
-rw-r--r--src/games/Game.cpp14
-rw-r--r--src/games/Game.hpp5
-rw-r--r--src/games/minesweeper/Minesweeper.cpp387
-rw-r--r--src/games/minesweeper/Minesweeper.hpp109
-rw-r--r--src/games/snake/Snake.cpp24
-rw-r--r--src/games/snake/Snake.hpp8
-rw-r--r--src/games/tetris/Tetris.cpp75
-rw-r--r--src/games/tetris/Tetris.hpp21
-rw-r--r--src/main.cpp4
9 files changed, 378 insertions, 269 deletions
diff --git a/src/games/Game.cpp b/src/games/Game.cpp
index 628f5d4..4f7b7a6 100644
--- a/src/games/Game.cpp
+++ b/src/games/Game.cpp
@@ -9,23 +9,23 @@
std::unique_ptr<Game>
-Game::Select(GameType type)
+Game::Select(GameType type, RenderGroup& render_group)
{
switch (type) {
case NO_GAME: {
return nullptr;
- }
+ } break;
case TETRIS: {
- return std::make_unique<Tetris>();
+ return std::make_unique<Tetris>(render_group);
} break;
case SNAKE: {
- return std::make_unique<Snake>();
+ return std::make_unique<Snake>(render_group);
} break;
case MINESWEEPER: {
- return std::make_unique<Minesweeper>();
+ return std::make_unique<Minesweeper>(render_group);
} break;
InvalidDefaultCase;
@@ -35,5 +35,7 @@ Game::Select(GameType type)
}
-Game::~Game() {}
+Game::~Game()
+{
+}
diff --git a/src/games/Game.hpp b/src/games/Game.hpp
index 9af98b4..b09b618 100644
--- a/src/games/Game.hpp
+++ b/src/games/Game.hpp
@@ -17,10 +17,11 @@ public:
MINESWEEPER
};
+ static std::unique_ptr<Game> Select(GameType type, RenderGroup& render_group);
+
Game() = default;
virtual ~Game();
- static std::unique_ptr<Game> Select(GameType type);
- virtual bool Update(std::vector<SDL_Event> &events, RenderGroup &render_group) = 0;
+ virtual bool Update(std::vector<SDL_Event> &events) = 0;
};
diff --git a/src/games/minesweeper/Minesweeper.cpp b/src/games/minesweeper/Minesweeper.cpp
index abb9515..8f20268 100644
--- a/src/games/minesweeper/Minesweeper.cpp
+++ b/src/games/minesweeper/Minesweeper.cpp
@@ -3,72 +3,94 @@
#include <algorithm>
#include <random>
+#include <cstdio>
-// Todo: draw a difficulty selection menu at top-mid.
+// Todo: winning condition (maybe: total_cells - uncovered_cells = mine_count)
-// Note: How many mines?
-// - Beginner: 8x8 or 9x9 grid with 10 mines.
-// - Intermediate: 16x16 grid with 40 mines.
-// - Expert: 30x16 grid with 99 mines.
+Minesweeper::Minesweeper(RenderGroup& render_group)
+ : m_render_group {render_group}
+{
+ m_font.Init("fonts/dejavu_ttf/DejaVuSansMono.ttf", 16);
+ for (uint32_t i = 0; i < m_digit_glyphs.size(); ++i) {
+ m_font.LoadGlyph(m_digit_glyphs[i], i + '0');
+ }
+}
-Minesweeper::Minesweeper()
+void
+Minesweeper::Reset(Difficulty difficulty)
{
- float map_width = static_cast<float>(m_MapWidth);
- float map_height = static_cast<float>(m_MapHeight);
- float cell_size = 0.8f * std::min(m_WorldHeight / MAX_MAP_HEIGHT, m_WorldWidth / MAX_MAP_WIDTH);
+ int32_t mine_count;
+ if (difficulty == beginner) {
+ m_grid_width = 8;
+ m_grid_height = 8;
+ mine_count = 10;
+ }
+ else if(difficulty == intermediate) {
+ m_grid_width = 16;
+ m_grid_height = 16;
+ mine_count = 40;
+ }
+ else {
+ m_grid_width = 30;
+ m_grid_height = 16;
+ mine_count = 99;
+ }
+
+
+ float cell_size = 0.8f * std::min(m_world_height / MAX_MAP_HEIGHT, m_world_width / MAX_MAP_WIDTH);
float cell_size_without_border = 0.8f * cell_size;
- m_MapViewPos = {
- 0.1f * cell_size_without_border + (m_WorldWidth - cell_size * map_width) / 2,
- 0.1f * cell_size_without_border + (m_WorldHeight - cell_size * map_height) / 2
+ V2F32 grid_size = {
+ (float)m_grid_width * cell_size,
+ (float)m_grid_height * cell_size
+ };
+ m_grid_pos = {
+ (m_world_width - grid_size.x) / 2,
+ (m_world_height - grid_size.y) / 2,
};
- m_CellOuterViewSize = {cell_size, cell_size};
- m_CellInnerViewSize = {cell_size_without_border, cell_size_without_border};
- m_Font.Init("fonts/dejavu_ttf/DejaVuSansMono.ttf", 32);
- for (uint32_t i = 0; i < m_DigitGlyphs.size(); ++i) {
- m_Font.LoadGlyph(m_DigitGlyphs[i], i + '0');
- }
+ m_cell_outer_size = {cell_size, cell_size};
+ m_cell_inner_size = {cell_size_without_border, cell_size_without_border};
- Reinit();
-}
-
-void Minesweeper::Reinit() {
- int32_t mine_count = 40;
- memset(m_IsCoveredBitmap, 0xff, sizeof(m_IsCoveredBitmap));
- memset(m_IsFlaggedBitmap, 0 , sizeof(m_IsFlaggedBitmap));
+ memset(m_is_covered_bitmap, 0xff, sizeof(m_is_covered_bitmap));
+ memset(m_is_flagged_bitmap, 0 , sizeof(m_is_flagged_bitmap));
InitIsMineBitmap(mine_count);
InitAdjacentMineCounters();
}
-void Minesweeper::InitIsMineBitmap(int32_t mine_count) {
- assert(mine_count < m_MapWidth * m_MapHeight);
+void
+Minesweeper::InitIsMineBitmap(int32_t mine_count)
+{
+ assert(mine_count < m_grid_width * m_grid_height);
- memset(m_IsMineBitmap, 0 , sizeof(m_IsMineBitmap));
+ memset(m_is_mine_bitmap, 0 , sizeof(m_is_mine_bitmap));
std::mt19937 rng((std::random_device()()));
- std::uniform_int_distribution<int32_t> dist(0, m_MapWidth * m_MapHeight - 1);
+ std::uniform_int_distribution<int32_t> dist(0, m_grid_width * m_grid_height - 1);
+
while (mine_count) {
int32_t random_pos = dist(rng);
- int32_t x = random_pos / m_MapWidth;
- int32_t y = random_pos % m_MapWidth;
+ int32_t y = random_pos / m_grid_width;
+ int32_t x = random_pos % m_grid_width;
if (!IsMine(x, y)) {
- m_IsMineBitmap[y] |= 1 << x;
+ m_is_mine_bitmap[y] |= 1 << x;
mine_count--;
}
}
}
-void Minesweeper::InitAdjacentMineCounters() {
- for (int32_t y = 0; y < m_MapHeight; y++) {
+void
+Minesweeper::InitAdjacentMineCounters()
+{
+ for (int32_t y = 0; y < m_grid_height; y++) {
int32_t y0 = y > 0 ? y-1 : y;
- int32_t y1 = y < m_MapHeight-1 ? y+1 : y;
+ int32_t y1 = y < m_grid_height-1 ? y+1 : y;
- for (int32_t x = 0; x < m_MapHeight; x++) {
+ for (int32_t x = 0; x < m_grid_width; x++) {
int32_t x0 = x > 0 ? x-1 : x;
- int32_t x1 = x < m_MapWidth-1 ? x+1 : x;
+ int32_t x1 = x < m_grid_width-1 ? x+1 : x;
int32_t adjacent_mine_counter = 0;
for (int32_t inner_y = y0; inner_y <= y1; inner_y++) {
@@ -82,71 +104,78 @@ void Minesweeper::InitAdjacentMineCounters() {
adjacent_mine_counter = -1;
}
- m_AdjacentMineCounters[y * m_MapWidth + x] = adjacent_mine_counter;
+ m_adjacent_mine_counts[y * m_grid_width + x] = adjacent_mine_counter;
}
}
}
-bool Minesweeper::Update(std::vector<SDL_Event> &events, RenderGroup &render_group) {
+bool
+Minesweeper::Update(std::vector<SDL_Event>& events)
+{
Color clear_color = {0.3f, 0.2f, 0.3f};
- render_group.SetCameraSize(4.0f, 3.0f);
- render_group.Clear(clear_color);
-
- if (m_RunState == MinesweeperRunState::Restart) {
- Reinit();
- m_RunState = MinesweeperRunState::Resume;
- }
+ m_render_group.SetCameraSize(4.0f, 3.0f);
+ m_render_group.Clear(clear_color);
for (SDL_Event &event : events) {
- if (m_RunState == MinesweeperRunState::Exit) {
+ if (m_run_state == exit) {
return false;
}
- else if (m_RunState == MinesweeperRunState::Pause) {
- ProcessEventDuringPause(event, render_group);
+ else if (m_run_state == pause) {
+ ProcessEventDuringPause(event);
}
- else if (m_RunState == MinesweeperRunState::Resume) {
- ProcessEventDuringResume(event, render_group);
+ else if (m_run_state == resume) {
+ ProcessEventDuringResume(event);
}
}
- if (m_RunState == MinesweeperRunState::Pause) {
- DrawPauseMenu(render_group);
+ if (m_run_state == pause) {
+ DrawBoard();
+ DrawPauseMenu();
}
- else if (m_RunState == MinesweeperRunState::GameOver) {
- DrawGameOverMenu(render_group);
+ else if (m_run_state == start_menu) {
+ DrawStartMenu();
+ }
+ else if (m_run_state == resume) {
+ DrawBoard();
+ }
+ else if (m_run_state == game_over) {
+ DrawBoard();
+ DrawGameOverMenu();
}
- DrawBoard(render_group);
-
- bool keep_running = m_RunState != MinesweeperRunState::Exit;
+ bool keep_running = m_run_state != exit;
return keep_running;
}
-void Minesweeper::ProcessEventDuringPause(SDL_Event &event, RenderGroup &render_group) {
+void
+Minesweeper::ProcessEventDuringPause(SDL_Event &event)
+{
switch (event.type) {
case SDL_EVENT_KEY_DOWN: {
if (event.key.key == SDLK_ESCAPE) {
- m_RunState = MinesweeperRunState::Resume;
+ m_run_state = resume;
}
} break;
default:;
}
}
-void Minesweeper::ProcessEventDuringResume(SDL_Event &event, RenderGroup &render_group) {
+void
+Minesweeper::ProcessEventDuringResume(SDL_Event &event)
+{
switch (event.type) {
case SDL_EVENT_KEY_DOWN: {
if (event.key.key == SDLK_ESCAPE) {
- m_RunState = MinesweeperRunState::Pause;
+ m_run_state = pause;
}
} break;
case SDL_EVENT_MOUSE_BUTTON_DOWN: {
- V2F32 click_screen_pos = {event.button.x, (float)render_group.m_ScreenHeight -1 - event.button.y};
- V2F32 click_view_pos = ScreenPosToViewPos(click_screen_pos, render_group);
+ V2F32 click_screen_pos = {event.button.x, (float)m_render_group.m_ScreenHeight -1 - event.button.y};
+ V2F32 click_view_pos = ScreenPosToViewPos(click_screen_pos, m_render_group);
- float x_adjusted = click_view_pos.x - m_MapViewPos.x;
- float y_adjusted = click_view_pos.y - m_MapViewPos.y;
+ float x_adjusted = click_view_pos.x - m_grid_pos.x;
+ float y_adjusted = click_view_pos.y - m_grid_pos.y;
if (x_adjusted < 0.0f) {
break;
}
@@ -154,20 +183,21 @@ void Minesweeper::ProcessEventDuringResume(SDL_Event &event, RenderGroup &render
break;
}
- int32_t x = (int32_t)(x_adjusted / m_CellOuterViewSize.x);
- int32_t y = (int32_t)(y_adjusted / m_CellOuterViewSize.y);
- if (x >= m_MapWidth) {
+ int32_t x = (int32_t)(x_adjusted / m_cell_outer_size.x);
+ int32_t y = (int32_t)(y_adjusted / m_cell_outer_size.y);
+ if (x >= m_grid_width) {
break;
}
- if (y >= m_MapHeight) {
+ if (y >= m_grid_height) {
break;
}
if (event.button.button == 1) {
if (IsCovered(x, y)) {
if (IsMine(x, y)) {
- m_IsCoveredBitmap[y] &= ~(1 << x);
- m_RunState = MinesweeperRunState::GameOver;
+ m_is_covered_bitmap[y] &= ~(1 << x);
+ UncoverMines();
+ m_run_state = game_over;
}
else {
Uncover(x, y);
@@ -186,20 +216,21 @@ void Minesweeper::ProcessEventDuringResume(SDL_Event &event, RenderGroup &render
}
}
-// Todo: maybe find a more efficient non-naive solution
-void Minesweeper::Uncover(int32_t x, int32_t y) {
+void
+Minesweeper::Uncover(int32_t x, int32_t y)
+{
if (x < 0) return;
- if (x >= m_MapWidth) return;
+ if (x >= m_grid_width) return;
if (y < 0) return;
- if (y >= m_MapHeight) return;
+ if (y >= m_grid_height) return;
if (!IsCovered(x, y)) return;
- m_IsCoveredBitmap[y] &= ~(1 << x);
+ m_is_covered_bitmap[y] &= ~(1 << x);
if (IsFlagged(x, y)) {
ToggleFlag(x, y);
}
- if (m_AdjacentMineCounters[y*m_MapWidth + x] > 0) {
+ if (m_adjacent_mine_counts[y*m_grid_width + x] > 0) {
return;
}
Uncover(x-1, y-1);
@@ -214,137 +245,191 @@ void Minesweeper::Uncover(int32_t x, int32_t y) {
Uncover(x+1, y+1);
}
-void Minesweeper::ToggleFlag(int32_t x, int32_t y) {
- m_IsFlaggedBitmap[y] ^= (1 << x);
+void
+Minesweeper::UncoverMines()
+{
+ for (int32_t y{0}; y < m_grid_height; ++y) {
+ for (int32_t x{0}; x < m_grid_width; ++x) {
+ if (IsMine(x, y) && IsCovered(x, y)) {
+ m_is_covered_bitmap[y] &= ~(1 << x);
+ }
+ }
+ }
+}
+
+void
+Minesweeper::ToggleFlag(int32_t x, int32_t y)
+{
+ m_is_flagged_bitmap[y] ^= (1 << x);
}
-bool Minesweeper::IsCovered(int32_t x, int32_t y) {
- bool is_covered = m_IsCoveredBitmap[y] & 1 << x;
+bool
+Minesweeper::IsCovered(int32_t x, int32_t y)
+{
+ bool is_covered = m_is_covered_bitmap[y] & 1 << x;
return is_covered;
}
-bool Minesweeper::IsFlagged(int32_t x, int32_t y) {
- bool is_flagged = m_IsFlaggedBitmap[y] & 1 << x;
+bool
+Minesweeper::IsFlagged(int32_t x, int32_t y)
+{
+ bool is_flagged = m_is_flagged_bitmap[y] & 1 << x;
return is_flagged;
}
-bool Minesweeper::IsMine(int32_t x, int32_t y) {
- bool is_mine = m_IsMineBitmap[y] & 1 << x;
+bool
+Minesweeper::IsMine(int32_t x, int32_t y)
+{
+ bool is_mine = m_is_mine_bitmap[y] & 1 << x;
return is_mine;
}
-V2F32 Minesweeper::ScreenPosToViewPos(V2F32 screen_pos, RenderGroup &render_group) {
+V2F32
+Minesweeper::ScreenPosToViewPos(V2F32 screen_pos, RenderGroup &render_group)
+{
// e.g. [0, 1024] -> [0, 1] -> [0, 4]
// e.g. [0, 768] -> [0, 1] -> [0, 3]
float screen_width = (float)render_group.m_ScreenWidth;
float screen_height = (float)render_group.m_ScreenHeight;
V2F32 view_pos;
- view_pos.x = (screen_pos.x / screen_width) * m_WorldWidth;
- view_pos.y = (screen_pos.y / screen_height) * m_WorldHeight;
+ view_pos.x = (screen_pos.x / screen_width) * m_world_width;
+ view_pos.y = (screen_pos.y / screen_height) * m_world_height;
return view_pos;
}
-void Minesweeper::DrawPauseMenu(RenderGroup &render_group) {
+void
+Minesweeper::DrawPauseMenu()
+{
ImGui::Begin("MinesweeperPause");
if (ImGui::Button("Resume")) {
- m_RunState = MinesweeperRunState::Resume;
+ m_run_state = resume;
+ }
+ if (ImGui::Button("Restart")) {
+ m_run_state = start_menu;
}
if (ImGui::Button("Exit")) {
- m_RunState = MinesweeperRunState::Exit;
+ m_run_state = exit;
}
ImGui::End();
}
-void Minesweeper::DrawGameOverMenu(RenderGroup &render_group) {
- ImGui::Begin("MinesweeperGameOver");
- ImGui::Text("Score = ???");
- if (ImGui::Button("Restart")) {
- m_RunState = MinesweeperRunState::Restart;
+void
+Minesweeper::DrawStartMenu()
+{
+ ImGui::Begin("MinesweeperStartMenu");
+ if (ImGui::RadioButton("beginner", m_difficulty == beginner ? true : false)) {
+ m_difficulty = beginner;
+ }
+ if (ImGui::RadioButton("intermediate", m_difficulty == intermediate ? true : false)) {
+ m_difficulty = intermediate;
+ }
+ if (ImGui::RadioButton("expert", m_difficulty == expert ? true : false)) {
+ m_difficulty = expert;
+ }
+ if (ImGui::Button("Start")) {
+ Reset(m_difficulty);
+ m_run_state = resume;
}
if (ImGui::Button("Exit")) {
- m_RunState = MinesweeperRunState::Exit;
+ m_run_state = exit;
}
ImGui::End();
}
-void Minesweeper::DrawBoard(RenderGroup &render_group) {
- Color covered_cell_color = {0.4f, 0.4f, 0.4f};
- Color uncovered_cell_color = {0.2f, 0.2f, 0.2f};
- Color flag_color = {0.6f, 0.3f, 03.f};
- Color mine_color = {0.8f, 0.2f, 0.2f};
+void
+Minesweeper::DrawGameOverMenu()
+{
+ ImGui::Begin("MinesweeperGameOverMenu");
+ if (ImGui::Button("Play Again")) {
+ m_run_state = start_menu;
+ }
+ if (ImGui::Button("Exit")) {
+ m_run_state = exit;
+ }
+ ImGui::End();
+}
- V2F32 flag_draw_size = {m_CellInnerViewSize.x * 0.5f, m_CellInnerViewSize.y * 0.5f};
- V2F32 flag_draw_offset = {
- (m_CellInnerViewSize.x - flag_draw_size.x) / 2,
- (m_CellInnerViewSize.y - flag_draw_size.y) / 2
+void
+Minesweeper::DrawBoard()
+{
+ Color covered_cell_color {0.6f, 0.6f, 0.6f};
+ Color uncovered_cell_color {0.4f, 0.4f, 0.4f};
+ Color mine_color {0.8f, 0.2f, 0.2f};
+
+ Color flag_color {0.6f, 0.3f, 03.f};
+ V2F32 flag_size = {m_cell_inner_size.x * 0.5f, m_cell_inner_size.y * 0.5f};
+ V2F32 flag_offset = {
+ (m_cell_inner_size.x - flag_size.x) / 2,
+ (m_cell_inner_size.y - flag_size.y) / 2
};
- // Temporary: Drawing Glyph Test
- render_group.PushBitmap(
- {100.0f, 100.0f, 10.0f},
- m_DigitGlyphs[1].bitmap.width,
- m_DigitGlyphs[1].bitmap.height,
- m_DigitGlyphs[1].bitmap.pixels.get());
-
-
- for (int32_t y = 0; y < m_MapHeight; y++) {
- for (int32_t x = 0; x < m_MapWidth; x++) {
- V2F32 world_pos = {
- m_MapViewPos.x + (float)x * m_CellOuterViewSize.x,
- m_MapViewPos.y + (float)y * m_CellOuterViewSize.y,
+ for (int32_t y = 0; y < m_grid_height; y++) {
+ for (int32_t x = 0; x < m_grid_width; x++) {
+ V2F32 cell_pos = {
+ m_grid_pos.x + (float)x * m_cell_outer_size.x,
+ m_grid_pos.y + (float)y * m_cell_outer_size.y,
+ };
+ RectF32 cell_rect = {
+ cell_pos.x, cell_pos.y,
+ cell_pos.x + m_cell_inner_size.x, cell_pos.y + m_cell_inner_size.y
};
+
bool is_covered = IsCovered(x, y);
bool is_flagged = IsFlagged(x, y);
bool is_mine = IsMine(x, y);
if (is_covered) {
- RectF32 cell_world_rect = {
- world_pos.x, world_pos.y,
- world_pos.x + m_CellInnerViewSize.x, world_pos.y + m_CellInnerViewSize.y
- };
- render_group.PushRectangle(cell_world_rect, 0.0f, covered_cell_color);
+ m_render_group.PushRectangle(cell_rect, 0.0f, covered_cell_color);
if (is_flagged) {
- V3F32 flag_world_pos = {
- world_pos.x + flag_draw_offset.x,
- world_pos.y + flag_draw_offset.y,
+ V3F32 flag_pos = {
+ cell_pos.x + flag_offset.x,
+ cell_pos.y + flag_offset.y,
1.0f
};
- RectF32 flag_world_rect = {
- flag_world_pos.x,
- flag_world_pos.y,
- flag_world_pos.x + m_CellInnerViewSize.x,
- flag_world_pos.y + m_CellInnerViewSize.y,
+ RectF32 flag_rect = {
+ flag_pos.x,
+ flag_pos.y,
+ flag_pos.x + flag_size.x,
+ flag_pos.y + flag_size.y,
};
- render_group.PushRectangle(flag_world_rect, flag_world_pos.z, flag_color);
+ m_render_group.PushRectangle(flag_rect, flag_pos.z, flag_color);
}
}
else {
- V3F32 mine_world_pos = {
- world_pos.x,
- world_pos.y,
- 1.0f
- };
if (is_mine) {
- RectF32 mine_world_rect = {
- mine_world_pos.x,
- mine_world_pos.y,
- mine_world_pos.x + m_CellInnerViewSize.x,
- mine_world_pos.y + m_CellInnerViewSize.y,
+ V3F32 mine_pos = {
+ cell_pos.x,
+ cell_pos.y,
+ 1.0f
+ };
+ RectF32 mine_rect = {
+ mine_pos.x,
+ mine_pos.y,
+ mine_pos.x + m_cell_inner_size.x,
+ mine_pos.y + m_cell_inner_size.y,
};
- render_group.PushRectangle(mine_world_rect, mine_world_pos.z, mine_color);
+ m_render_group.PushRectangle(mine_rect, mine_pos.z, mine_color);
}
else {
- RectF32 mine_world_rect = {
- mine_world_pos.x,
- mine_world_pos.y,
- mine_world_pos.x + m_CellInnerViewSize.x,
- mine_world_pos.y + m_CellInnerViewSize.y,
+ m_render_group.PushRectangle(cell_rect, 0.0f, uncovered_cell_color);
+
+ // Todo: Figure out how to scale this properly.
+ // 256.0f is a random number that just works for now.
+ V3F32 mine_count_pos = {
+ 256.0f * cell_pos.x,
+ 256.0f * cell_pos.y,
+ 256.0f
};
- render_group.PushRectangle(mine_world_rect, mine_world_pos.z, uncovered_cell_color);
+ size_t mine_count_val = (size_t)m_adjacent_mine_counts[y*m_grid_width + x];
+ m_render_group.PushBitmap(
+ mine_count_pos,
+ m_digit_glyphs[mine_count_val].bitmap.width,
+ m_digit_glyphs[mine_count_val].bitmap.height,
+ m_digit_glyphs[mine_count_val].bitmap.pixels.get());
}
}
}
diff --git a/src/games/minesweeper/Minesweeper.hpp b/src/games/minesweeper/Minesweeper.hpp
index b081806..b89743e 100644
--- a/src/games/minesweeper/Minesweeper.hpp
+++ b/src/games/minesweeper/Minesweeper.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include "imgui.h"
+#include "renderer/RenderGroup.hpp"
#include <games/Game.hpp>
#include <common/Font.hpp>
@@ -18,73 +20,86 @@ namespace std {
}
-enum class MinesweeperRunState {
- Resume,
- Pause,
- GameOver,
- Restart,
- Exit
-};
-
class Minesweeper : public Game {
- public:
- Minesweeper();
- ~Minesweeper() = default;
+public:
+ enum RunState {
+ start_menu,
+ pause,
+ resume,
+ game_over,
+ exit
+ };
+
+ enum Difficulty {
+ beginner,
+ intermediate,
+ expert
+ };
+
+public:
+ Minesweeper(RenderGroup& render_group);
+ ~Minesweeper() = default;
+
+ bool Update(std::vector<SDL_Event>& events) override;
+
+ void ProcessEventDuringPause(SDL_Event& event);
+ void ProcessEventDuringResume(SDL_Event& event);
- bool Update(std::vector<SDL_Event> &events, RenderGroup &render_group) override;
- void ProcessEventDuringPause(SDL_Event &event, RenderGroup &render_group);
- void ProcessEventDuringResume(SDL_Event &event, RenderGroup &render_group);
+private:
+ void Reset(Difficulty Difficulty);
+ void InitIsMineBitmap(int32_t mine_count);
+ void InitAdjacentMineCounters();
+ void UncoverMines();
+ void Uncover(int32_t x, int32_t y);
+ void ToggleFlag(int32_t x, int32_t y);
- private:
- void Reinit();
- void InitIsMineBitmap(int32_t mine_count);
- void InitAdjacentMineCounters();
+ bool IsCovered(int32_t x, int32_t y);
+ bool IsFlagged(int32_t x, int32_t y);
+ bool IsMine(int32_t x, int32_t y);
- void Uncover(int32_t x, int32_t y);
- void ToggleFlag(int32_t x, int32_t y);
- bool IsCovered(int32_t x, int32_t y);
- bool IsFlagged(int32_t x, int32_t y);
- bool IsMine(int32_t x, int32_t y);
+ V2F32 ScreenPosToViewPos(V2F32 screen_pos, RenderGroup& render_group);
- V2F32 ScreenPosToViewPos(V2F32 screen_pos, RenderGroup &render_group);
+private:
+ void DrawBoard();
+ void DrawStartMenu();
+ void DrawPauseMenu();
+ void DrawGameOverMenu();
- private:
- void DrawPauseMenu(RenderGroup &render_group);
- void DrawGameOverMenu(RenderGroup &render_group);
- void DrawBoard(RenderGroup &render_group);
+private:
+ static constexpr int32_t MAX_MAP_HEIGHT = 32;
+ static constexpr int32_t MAX_MAP_WIDTH = 32;
+ static constexpr std::string_view s_FontFilepath = "./fonts/dejavu_ttf/DejaVuSans.ttf";
- private:
- static constexpr int32_t MAX_MAP_HEIGHT = 32;
- static constexpr int32_t MAX_MAP_WIDTH = 32;
- static constexpr std::string_view s_FontFilepath = "./fonts/dejavu_ttf/DejaVuSans.ttf";
+private:
+ RenderGroup& m_render_group;
- private:
- MinesweeperRunState m_RunState = MinesweeperRunState::Resume;
+ RunState m_run_state = start_menu;
+ Difficulty m_difficulty = beginner;
- float m_WorldWidth = 4.0f;
- float m_WorldHeight = 3.0f;
+ float m_world_width = 4.0f;
+ float m_world_height = 3.0f;
- int32_t m_MapWidth = 16;
- int32_t m_MapHeight = 16;
+ int32_t m_grid_width;
+ int32_t m_grid_height;
- V2F32 m_MapViewPos;
- V2F32 m_CellOuterViewSize;
- V2F32 m_CellInnerViewSize;
+ V2F32 m_grid_pos;
+ V2F32 m_cell_outer_size;
+ V2F32 m_cell_inner_size;
- uint32_t m_IsCoveredBitmap[MAX_MAP_HEIGHT] {};
- uint32_t m_IsFlaggedBitmap[MAX_MAP_HEIGHT] {};
- uint32_t m_IsMineBitmap[MAX_MAP_HEIGHT] {};
- int32_t m_AdjacentMineCounters[MAX_MAP_WIDTH * MAX_MAP_HEIGHT] {};
+ uint32_t m_is_covered_bitmap[MAX_MAP_HEIGHT] {};
+ uint32_t m_is_flagged_bitmap[MAX_MAP_HEIGHT] {};
+ uint32_t m_is_mine_bitmap[MAX_MAP_HEIGHT] {};
+ int32_t m_adjacent_mine_counts[MAX_MAP_WIDTH * MAX_MAP_HEIGHT] {};
- Font m_Font;
- std::array<Glyph, 9> m_DigitGlyphs;
+ Font m_font;
+ std::array<Glyph, 9> m_digit_glyphs;
};
diff --git a/src/games/snake/Snake.cpp b/src/games/snake/Snake.cpp
index 6a95521..aa8cedf 100644
--- a/src/games/snake/Snake.cpp
+++ b/src/games/snake/Snake.cpp
@@ -2,7 +2,9 @@
#include <imgui.h>
-Snake::Snake () {
+Snake::Snake(RenderGroup& render_group)
+ : m_RenderGroup{render_group}
+{
m_IsPaused = false;
m_IsRunning = true;
@@ -33,9 +35,9 @@ Snake::Snake () {
m_Dist = std::uniform_int_distribution<int32_t>(0, m_MapWidth * m_MapHeight - 3);
SpawnFood();
- }
+}
-bool Snake::Update(std::vector<SDL_Event> &events, RenderGroup &render_group) {
+bool Snake::Update(std::vector<SDL_Event> &events) {
uint64_t milliseconds_since_t0 = SDL_GetTicks();
uint64_t milliseconds_since_t0_last = m_LastMillisecondsSinceT0;
uint64_t dt_in_milliseconds = milliseconds_since_t0 - milliseconds_since_t0_last;
@@ -44,8 +46,8 @@ bool Snake::Update(std::vector<SDL_Event> &events, RenderGroup &render_group) {
Color clear_color = {0.3f, 0.3f, 0.3f, 1.0f};
- render_group.SetCameraSize(4.0f, 3.0f);
- render_group.Clear(clear_color);
+ m_RenderGroup.SetCameraSize(4.0f, 3.0f);
+ m_RenderGroup.Clear(clear_color);
for (SDL_Event &event : events) {
@@ -66,7 +68,7 @@ bool Snake::Update(std::vector<SDL_Event> &events, RenderGroup &render_group) {
}
- Draw(render_group);
+ Draw();
DoImgui();
return m_IsRunning;
@@ -249,7 +251,7 @@ void Snake::SpawnFood() {
m_FoodPosition = {bit0_x, bit0_y};
}
-void Snake::Draw(RenderGroup &render_group) {
+void Snake::Draw() {
float world_width = 4.0f;
float world_height = 3.0f;
@@ -275,7 +277,7 @@ void Snake::Draw(RenderGroup &render_group) {
map_world_pos.y + map_world_dim.y
};
Color bg_color = {0.0f, 0.0f, 0.0f, 1.0f};
- render_group.PushRectangle(map_world_rect, map_world_pos.z, bg_color);
+ m_RenderGroup.PushRectangle(map_world_rect, map_world_pos.z, bg_color);
/* draw snake */
@@ -304,7 +306,7 @@ void Snake::Draw(RenderGroup &render_group) {
};
Color color = {0.3f, 0.3f, 0.3f, 1.0f};
- render_group.PushRectangle(world_rect, world_pos.z, color);
+ m_RenderGroup.PushRectangle(world_rect, world_pos.z, color);
tail++;
}
tail = 0;
@@ -332,7 +334,7 @@ void Snake::Draw(RenderGroup &render_group) {
};
Color color = {0.3f, 0.3f, 0.3f, 1.0f};
- render_group.PushRectangle(world_rect, world_pos.z, color);
+ m_RenderGroup.PushRectangle(world_rect, world_pos.z, color);
tail++;
}
@@ -351,7 +353,7 @@ void Snake::Draw(RenderGroup &render_group) {
pos.y + dim.y
};
Color color = {0.3f, 0.6f, 0.4f, 1.0f};
- render_group.PushRectangle(rect, pos.z, color);
+ m_RenderGroup.PushRectangle(rect, pos.z, color);
}
void Snake::DoImgui() {
diff --git a/src/games/snake/Snake.hpp b/src/games/snake/Snake.hpp
index f04ad16..ef1fd34 100644
--- a/src/games/snake/Snake.hpp
+++ b/src/games/snake/Snake.hpp
@@ -17,8 +17,8 @@ public:
public:
- Snake();
- bool Update(std::vector<SDL_Event> &events, RenderGroup &render_group) override;
+ Snake(RenderGroup& render_group);
+ bool Update(std::vector<SDL_Event> &events) override;
private:
@@ -28,7 +28,7 @@ private:
void MaybeMoveSnake(float dt_in_seconds);
void SpawnFood();
- void Draw(RenderGroup &render_group);
+ void Draw();
void DoImgui();
@@ -37,6 +37,8 @@ private:
static constexpr int32_t MAX_MAP_WIDTH = 16;
static constexpr int32_t MAX_MAP_HEIGHT = 16;
+ RenderGroup& m_RenderGroup;
+
bool m_IsPaused;
bool m_IsRunning;
diff --git a/src/games/tetris/Tetris.cpp b/src/games/tetris/Tetris.cpp
index f2a7384..8f07df0 100644
--- a/src/games/tetris/Tetris.cpp
+++ b/src/games/tetris/Tetris.cpp
@@ -9,7 +9,8 @@
// Todo: change to new font scaling api in imgui first
// Todo: test text with hardcoded gap + dummy to ensure it gets placed as expected
-Tetris::Tetris() :
+Tetris::Tetris(RenderGroup& m_RenderGroup) :
+ m_RenderGroup(m_RenderGroup),
m_ActiveTetromino(m_Board),
m_NextTetromino(m_Board)
{
@@ -34,10 +35,10 @@ void Tetris::Restart() {
m_SoftdropCounter = 0;
}
-bool Tetris::Update(std::vector<SDL_Event> &events, RenderGroup &render_group) {
+bool Tetris::Update(std::vector<SDL_Event> &events) {
Color clear_color = {0.2f, 0.2f, 0.2f, 1.0f};
- render_group.SetCameraSize(4.0f, 3.0f);
- render_group.Clear(clear_color);
+ m_RenderGroup.SetCameraSize(4.0f, 3.0f);
+ m_RenderGroup.Clear(clear_color);
if (m_RunningState == TetrisRunningState::Restart) {
Restart();
@@ -71,7 +72,7 @@ bool Tetris::Update(std::vector<SDL_Event> &events, RenderGroup &render_group) {
}
}
- Draw(render_group);
+ Draw();
bool keep_running = m_RunningState != TetrisRunningState::Exit;
@@ -210,26 +211,26 @@ void Tetris::HandleGameOver() {
}
}
-void Tetris::Draw(RenderGroup &render_group) {
- m_Board.Draw(m_Level, render_group);
- m_ActiveTetromino.Draw(render_group);
+void Tetris::Draw() {
+ m_Board.Draw(m_Level, m_RenderGroup);
+ m_ActiveTetromino.Draw(m_RenderGroup);
- DrawNextTetromino(render_group);
- DrawStatistics(render_group);
- DrawLineCounter(render_group);
- DrawLevel(render_group);
- DrawScore(render_group);
+ DrawNextTetromino();
+ DrawStatistics();
+ DrawLineCounter();
+ DrawLevel();
+ DrawScore();
// Todo: Use transparency
if (m_RunningState == TetrisRunningState::Pause) {
- DrawPauseMenu(render_group);
+ DrawPauseMenu();
}
else if (m_RunningState == TetrisRunningState::GameOver) {
- DrawGameOverMenu(render_group);
+ DrawGameOverMenu();
}
}
-void Tetris::DrawPauseMenu(RenderGroup &render_group) {
+void Tetris::DrawPauseMenu() {
ImGui::Begin("TetrisPause", nullptr, s_MenuImGuiWindowFlags);
if (ImGui::Button("Resume")) {
m_RunningState = TetrisRunningState::Resume;
@@ -243,7 +244,7 @@ void Tetris::DrawPauseMenu(RenderGroup &render_group) {
ImGui::End();
}
-void Tetris::DrawGameOverMenu(RenderGroup &render_group) {
+void Tetris::DrawGameOverMenu() {
ImGui::Begin("TetrisGameOver", nullptr, s_MenuImGuiWindowFlags);
ImGui::Text("Score = %d", m_Score);
ImGui::Text("HighScore = %d", m_HighScore);
@@ -256,9 +257,9 @@ void Tetris::DrawGameOverMenu(RenderGroup &render_group) {
ImGui::End();
}
-void Tetris::DrawLineCounter(RenderGroup &render_group) {
+void Tetris::DrawLineCounter() {
V2F32 view_pos = {0.5f, 2.6f};
- ImVec2 screen_pos = render_group.ViewPosToScreenPosImGui(view_pos);
+ ImVec2 screen_pos = m_RenderGroup.ViewPosToScreenPosImGui(view_pos);
ImGui::SetNextWindowPos(screen_pos);
ImGui::Begin("TetrisLines", nullptr, s_DefaultImGuiWindowFlags);
@@ -266,38 +267,38 @@ void Tetris::DrawLineCounter(RenderGroup &render_group) {
ImGui::End();
}
-void Tetris::DrawStatistics(RenderGroup &render_group) {
+void Tetris::DrawStatistics() {
V2F32 view_tetrominoes_pos = {0.4f, 1.8f};
V2F32 view_advance = {0.0f, 0.2f};
V2F32 view_text_title_pos = view_tetrominoes_pos + V2F32(0.02f, 0.4f);
- ImVec2 screen_text_title_pos = render_group.ViewPosToScreenPosImGui(view_text_title_pos);
+ ImVec2 screen_text_title_pos = m_RenderGroup.ViewPosToScreenPosImGui(view_text_title_pos);
V2F32 view_text_pos = view_tetrominoes_pos + V2F32(0.4f, 0.16f);
V2F32 view_text_gap = {0.0f, 0.124f};
- ImVec2 screen_text_pos = render_group.ViewPosToScreenPosImGui(view_text_pos);
- ImVec2 screen_text_gap = render_group.ViewSizeToScreenSizeImGui(view_text_gap);
+ ImVec2 screen_text_pos = m_RenderGroup.ViewPosToScreenPosImGui(view_text_pos);
+ ImVec2 screen_text_gap = m_RenderGroup.ViewSizeToScreenSizeImGui(view_text_gap);
- Tetromino::Draw(Tetromino::t_piece, 0, view_tetrominoes_pos, 0.5f, render_group);
+ Tetromino::Draw(Tetromino::t_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup);
view_tetrominoes_pos.y -= view_advance.y;
- Tetromino::Draw(Tetromino::j_piece, 0, view_tetrominoes_pos, 0.5f, render_group);
+ Tetromino::Draw(Tetromino::j_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup);
view_tetrominoes_pos.y -= view_advance.y;
- Tetromino::Draw(Tetromino::z_piece, 0, view_tetrominoes_pos, 0.5f, render_group);
+ Tetromino::Draw(Tetromino::z_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup);
view_tetrominoes_pos.y -= view_advance.y;
- Tetromino::Draw(Tetromino::o_piece, 0, view_tetrominoes_pos, 0.5f, render_group);
+ Tetromino::Draw(Tetromino::o_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup);
view_tetrominoes_pos.y -= view_advance.y;
- Tetromino::Draw(Tetromino::s_piece, 0, view_tetrominoes_pos, 0.5f, render_group);
+ Tetromino::Draw(Tetromino::s_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup);
view_tetrominoes_pos.y -= view_advance.y;
- Tetromino::Draw(Tetromino::l_piece, 0, view_tetrominoes_pos, 0.5f, render_group);
+ Tetromino::Draw(Tetromino::l_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup);
view_tetrominoes_pos.y -= view_advance.y;
- Tetromino::Draw(Tetromino::i_piece, 0, view_tetrominoes_pos, 0.5f, render_group);
+ Tetromino::Draw(Tetromino::i_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup);
view_tetrominoes_pos.y -= view_advance.y;
@@ -328,9 +329,9 @@ void Tetris::DrawStatistics(RenderGroup &render_group) {
ImGui::End();
}
-void Tetris::DrawScore(RenderGroup &render_group) {
+void Tetris::DrawScore() {
V2F32 view_pos = {3.0f, 2.2f};
- ImVec2 screen_pos = render_group.ViewPosToScreenPosImGui(view_pos);
+ ImVec2 screen_pos = m_RenderGroup.ViewPosToScreenPosImGui(view_pos);
ImGui::SetNextWindowPos(screen_pos);
ImGui::Begin("TetrisScore", nullptr, s_DefaultImGuiWindowFlags);
@@ -339,9 +340,9 @@ void Tetris::DrawScore(RenderGroup &render_group) {
ImGui::End();
}
-void Tetris::DrawNextTetromino(RenderGroup &render_group) {
+void Tetris::DrawNextTetromino() {
V2F32 text_view_pos = {3.0f, 1.8f};
- ImVec2 text_screen_pos = render_group.ViewPosToScreenPosImGui(text_view_pos);
+ ImVec2 text_screen_pos = m_RenderGroup.ViewPosToScreenPosImGui(text_view_pos);
ImGui::SetNextWindowPos(text_screen_pos);
ImGui::Begin("TetrisNextTetromino", nullptr, s_DefaultImGuiWindowFlags);
@@ -350,12 +351,12 @@ void Tetris::DrawNextTetromino(RenderGroup &render_group) {
V2F32 tetromino_view_pos = {3.0, 1.4f};
- Tetromino::Draw(m_NextTetromino.GetId(), 0, tetromino_view_pos, 0.5f, render_group);
+ Tetromino::Draw(m_NextTetromino.GetId(), 0, tetromino_view_pos, 0.5f, m_RenderGroup);
}
-void Tetris::DrawLevel(RenderGroup &render_group) {
+void Tetris::DrawLevel() {
V2F32 view_pos = {3.0f, 1.2f};
- ImVec2 screen_pos = render_group.ViewPosToScreenPosImGui(view_pos);
+ ImVec2 screen_pos = m_RenderGroup.ViewPosToScreenPosImGui(view_pos);
ImGui::SetNextWindowPos(screen_pos);
ImGui::Begin("TetrisLevel", nullptr, s_DefaultImGuiWindowFlags);
diff --git a/src/games/tetris/Tetris.hpp b/src/games/tetris/Tetris.hpp
index 42981db..f4a8caf 100644
--- a/src/games/tetris/Tetris.hpp
+++ b/src/games/tetris/Tetris.hpp
@@ -17,8 +17,8 @@ enum class TetrisRunningState {
class Tetris : public Game {
public:
- Tetris();
- bool Update(std::vector<SDL_Event> &events, RenderGroup& render_group) override;
+ Tetris(RenderGroup& render_group);
+ bool Update(std::vector<SDL_Event> &events) override;
void HandleTetrominoPlacement();
private:
@@ -29,15 +29,15 @@ private:
uint32_t GetHarddropCount(float dt);
void HandleGameOver();
- void Draw(RenderGroup &render_group);
- void DrawLineCounter(RenderGroup &render_group);
- void DrawStatistics(RenderGroup &render_group);
- void DrawScore(RenderGroup &render_group);
- void DrawNextTetromino(RenderGroup &render_group);
- void DrawLevel(RenderGroup &render_group);
+ void Draw();
+ void DrawLineCounter();
+ void DrawStatistics();
+ void DrawScore();
+ void DrawNextTetromino();
+ void DrawLevel();
- void DrawPauseMenu(RenderGroup &render_group);
- void DrawGameOverMenu(RenderGroup &render_group);
+ void DrawPauseMenu();
+ void DrawGameOverMenu();
private:
static constexpr ImGuiWindowFlags s_MenuImGuiWindowFlags = ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_AlwaysAutoResize;
@@ -45,6 +45,7 @@ private:
private:
+ RenderGroup& m_RenderGroup;
TetrisRunningState m_RunningState = TetrisRunningState::Resume;
float m_DtInSecondsRemaining = 0.0f;
diff --git a/src/main.cpp b/src/main.cpp
index 0cf89d7..91192b4 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -154,7 +154,7 @@ main(int argc, char **argv)
if (game) {
- bool keep_game_running = game->Update(game_events, render_group);
+ bool keep_game_running = game->Update(game_events);
if (!keep_game_running) {
game.reset();
}
@@ -162,7 +162,7 @@ main(int argc, char **argv)
else {
Game::GameType type = do_menu(render_group);
if (type != Game::NO_GAME) {
- game = Game::Select(type);
+ game = Game::Select(type, render_group);
}
}
game_events.clear();