(add): finish version 1.0.0

This commit is contained in:
VisualGMQ 2023-06-24 18:38:24 +08:00
commit 3f8aab27ba
22 changed files with 785 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.vscode
cmake-build

18
CMakeLists.txt Normal file
View File

@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.10)
project(
MineSweep
VERSION 1.0.0
LANGUAGES CXX
)
include(cmake/utility.cmake)
include(cmake/FindSDL2.cmake)
include(cmake/copydll.cmake)
aux_source_directory(src SRC)
add_executable(mine-sweep ${SRC})
target_include_directories(mine-sweep PRIVATE include)
target_link_libraries(mine-sweep PRIVATE SDL2)
target_compile_features(mine-sweep PRIVATE cxx_std_17)
CopyDLL(mine-sweep)

20
ReadMe.md Normal file
View File

@ -0,0 +1,20 @@
使用SDL2实现的扫雷游戏
## 操控
鼠标左键翻开格子,右键插旗子。
显示出失败/胜利图标后再次点击鼠标可以重开一局。
## 截图
![snapshot](./snapshot/snapshot.png)
## 编译
Windows下使用CMake可以进行编译
```bash
cmake -S . -B cmake-build -DSDL2_ROOT=<your SDL path>
cmake --build cmake-build
```

72
cmake/FindSDL2.cmake Normal file
View File

@ -0,0 +1,72 @@
if (NOT TARGET SDL2)
if (NOT SDL2_ROOT)
set(SDL2_ROOT "" CACHE PATH "SDL2 root directory")
endif()
if (WIN32) # Windows
IsMSVCBackend(is_msvc_backend)
IsMinGWBackend(is_mingw_backend)
IsX64Compiler(is_x64_compiler)
if (${is_msvc_backend}) # use MSVC
if(${is_x64_compiler})
set(SDL_LIB_DIR "${SDL2_ROOT}/lib/x64")
set(SDL2_DYNAMIC_LIB_DIR "${SDL2_ROOT}/lib/x64" CACHE PATH "SDL2.dll directory" FORCE)
else()
set(SDL_LIB_DIR "${SDL2_ROOT}/lib/x86")
set(SDL2_DYNAMIC_LIB_DIR "${SDL2_ROOT}/lib/x86" CACHE PATH "SDL2.dll directory" FROCE)
endif()
set(LIB_PATH "${SDL_LIB_DIR}/SDL2.lib")
set(DYNAMIC_LIB_PATH "${SDL2_DYNAMIC_LIB_DIR}/SDL2.dll")
set(MAIN_LIB_PATH "${SDL_LIB_DIR}/SDL2main.lib")
set(SDL_INCLUDE_DIR "${SDL2_ROOT}/include")
mark_as_advanced(SDL2_DYNAMIC_LIB_DIR)
add_library(SDL2::SDL2 SHARED IMPORTED GLOBAL)
set_target_properties(
SDL2::SDL2
PROPERTIES
IMPORTED_LOCATION ${DYNAMIC_LIB_PATH}
IMPORTED_IMPLIB ${LIB_PATH}
INTERFACE_INCLUDE_DIRECTORIES ${SDL_INCLUDE_DIR}
)
add_library(SDL2::SDL2main SHARED IMPORTED GLOBAL)
set_target_properties(
SDL2::SDL2main
PROPERTIES
IMPORTED_LOCATION ${DYNAMIC_LIB_PATH}
IMPORTED_IMPLIB ${MAIN_LIB_PATH}
INTERFACE_INCLUDE_DIRECTORIES ${SDL_INCLUDE_DIR}
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
)
add_library(SDL2 INTERFACE IMPORTED GLOBAL)
target_link_libraries(SDL2 INTERFACE SDL2::SDL2main SDL2::SDL2)
elseif (${is_mingw_backend}) # use MINGW
if(${is_x64_compiler})
set(SDL_INCLUDE_DIR "${SDL2_ROOT}/x86_64-w64-mingw32/include/SDL2")
set(SDL_LIB_DIR "${SDL2_ROOT}/x86_64-w64-mingw32/lib")
set(SDL2_DYNAMIC_LIB_DIR "${SDL2_ROOT}/x86_64-w64-mingw32/bin" CACHE PATH "SDL2.dll directory" FORCE)
else()
set(SDL_INCLUDE_DIR "${SDL2_ROOT}/i686-w64-mingw32/include/SDL2")
set(SDL_LIB_DIR "${SDL2_ROOT}/i686-w64-mingw32/lib")
set(SDL2_DYNAMIC_LIB_DIR "${SDL2_ROOT}/i686-w64-mingw32/bin" CACHE PATH "SDL2.dll directory" FORCE)
endif()
mark_as_advanced(SDL2_DYNAMIC_LIB_DIR)
add_library(SDL2 INTERFACE)
target_link_directories(SDL2 INTERFACE ${SDL_LIB_DIR})
target_link_libraries(SDL2 INTERFACE "-lmingw32 -lSDL2main -lSDL2 -mwindows")
target_include_directories(SDL2 INTERFACE ${SDL_INCLUDE_DIR})
else()
message(FATAL_ERROR "your compiler don't support, please use MSVC, Clang++ or MinGW")
endif()
else() # Linux, MacOSX
find_package(SDL2 QUIET)
if (SDL2_FOUND)
add_library(SDL2 ALIAS SDL2::SDL2)
else()
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 sdl2 REQUIRED IMPORTED_TARGET)
add_library(SDL2 ALIAS PkgConfig::SDL2)
endif()
endif()
endif()

20
cmake/copydll.cmake Normal file
View File

@ -0,0 +1,20 @@
macro(CopyDLL target_name)
if (WIN32)
add_custom_command(
TARGET ${target_name} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $CACHE{SDL2_DYNAMIC_LIB_DIR}/SDL2.dll $<TARGET_FILE_DIR:${target_name}>)
endif()
endmacro(CopyDLL)
macro(CopyResForWASM target_name)
add_custom_command(
TARGET ${target_name} PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/resources ${CMAKE_BINARY_DIR}/resources)
endmacro()
macro(CopyDefScript target_name)
add_custom_command(
TARGET ${target_name} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/luabind/defs.lua $<TARGET_FILE_DIR:${target_name}>
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/luabind/defs.lua ${CMAKE_SOURCE_DIR}/resources/script)
endmacro()

81
cmake/fetch_sdl.cmake Normal file
View File

@ -0,0 +1,81 @@
function(FetchSDL_MSVC)
include(FetchContent)
message(STATUS "fetching SDL2 ...")
FetchContent_Declare(
SDL2
URL https://github.com/libsdl-org/SDL/releases/download/release-2.24.2/SDL2-devel-2.24.2-VC.zip
)
message(STATUS "fetching SDL2_image ...")
FetchContent_Declare(
SDL2_image
URL https://github.com/libsdl-org/SDL_image/releases/download/release-2.6.2/SDL2_image-devel-2.6.2-VC.zip
)
message(STATUS "fetching SDL2_ttf ...")
FetchContent_Declare(
SDL2_ttf
URL https://github.com/libsdl-org/SDL_ttf/releases/download/release-2.20.1/SDL2_ttf-devel-2.20.1-VC.zip
)
message(STATUS "fetching SDL2_mixer ...")
FetchContent_Declare(
SDL2_mixer
URL https://github.com/libsdl-org/SDL_mixer/releases/download/release-2.6.2/SDL2_mixer-devel-2.6.2-VC.zip
)
FetchContent_MakeAvailable(SDL2 SDL2_image SDL2_ttf SDL2_mixer)
set(SDL2_ROOT ${sdl2_SOURCE_DIR} CACHE PATH "SDL2 root directory")
set(SDL2_MIXER_ROOT ${sdl2_mixer_SOURCE_DIR} CACHE PATH "SDL2_mixer root directory")
set(SDL2_TTF_ROOT ${sdl2_ttf_SOURCE_DIR} CACHE PATH "SDL2_ttf root directory")
set(SDL2_IMAGE_ROOT ${sdl2_image_SOURCE_DIR} CACHE PATH "SDL2_image root directory")
endfunction(FetchSDL_MSVC)
function(FetchSDL_MINGW)
include(FetchContent)
message(STATUS "fetching SDL2 ...")
FetchContent_Declare(
SDL2
URL https://github.com/libsdl-org/SDL/releases/download/release-2.26.2/SDL2-devel-2.26.2-mingw.zip
)
message(STATUS "fetching SDL2_image ...")
FetchContent_Declare(
SDL2_image
URL https://github.com/libsdl-org/SDL_image/releases/download/release-2.6.2/SDL2_image-devel-2.6.2-mingw.zip
)
message(STATUS "fetching SDL2_ttf ...")
FetchContent_Declare(
SDL2_ttf
URL https://github.com/libsdl-org/SDL_ttf/releases/download/release-2.20.1/SDL2_ttf-devel-2.20.1-mingw.zip
)
message(STATUS "fetching SDL2_mixer ...")
FetchContent_Declare(
SDL2_mixer
URL https://github.com/libsdl-org/SDL_mixer/releases/download/release-2.6.2/SDL2_mixer-devel-2.6.2-mingw.zip
)
FetchContent_MakeAvailable(SDL2 SDL2_image SDL2_ttf SDL2_mixer)
set(SDL2_ROOT ${sdl2_SOURCE_DIR} CACHE PATH "SDL2 root directory")
set(SDL2_MIXER_ROOT ${sdl2_mixer_SOURCE_DIR} CACHE PATH "SDL2_mixer root directory")
set(SDL2_TTF_ROOT ${sdl2_ttf_SOURCE_DIR} CACHE PATH "SDL2_ttf root directory")
set(SDL2_IMAGE_ROOT ${sdl2_image_SOURCE_DIR} CACHE PATH "SDL2_image root directory")
endfunction(FetchSDL_MINGW)
function(FetchSDL)
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC" OR
(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND
CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES "MSVC")) # use MSVC
FetchSDL_MSVC()
elseif (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR
(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND
CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES "GNU")) # use MINGW
FetchSDL_MINGW()
endif()
endfunction()

37
cmake/utility.cmake Normal file
View File

@ -0,0 +1,37 @@
function(IsMSVCBackend RETURN_VALUE)
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC" OR
(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND
CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES "MSVC"))
set(${RETURN_VALUE} 1 PARENT_SCOPE)
else()
set(${RETURN_VALUE} 0 PARENT_SCOPE)
endif()
endfunction()
function(IsMinGWBackend RETURN_VALUE)
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR
(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND
CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES "GNU" AND WIN32)) # use MINGW
set(${RETURN_VALUE} 1 PARENT_SCOPE)
else()
set(${RETURN_VALUE} 0 PARENT_SCOPE)
endif()
endfunction()
function(IsGNUBackend RETURN_VALUE)
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR
(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND
CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES "GNU" AND (UNIX OR APPLE))) # use GNU G++
set(${RETURN_VALUE} 1 PARENT_SCOPE)
else()
set(${RETURN_VALUE} 0 PARENT_SCOPE)
endif()
endfunction()
function(IsX64Compiler RETURN_VALUE)
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
set(${RETURN_VALUE} 1 PARENT_SCOPE)
else()
set(${RETURN_VALUE} 0 PARENT_SCOPE)
endif()
endfunction()

84
include/context.hpp Normal file
View File

@ -0,0 +1,84 @@
#pragma once
#include "window.hpp"
#include "renderer.hpp"
#include "matrix.hpp"
struct Tile {
enum Type {
Bomb = -1,
} type = static_cast<Type>(0);
bool isVisiable = false;
bool isFlaged = false;
};
constexpr int TileLen = 32;
constexpr int WindowWidth = TileLen * 20;
constexpr int WindowHeight = TileLen * 20;
constexpr int MineCount = 50;
using Map = Matrix<Tile>;
Map CreateRandomMap(int bombCount, int w, int h);
inline auto TextureDestroy = [](SDL_Texture* texture) {
SDL_DestroyTexture(texture);
};
struct Context final {
enum GameState {
Gaming,
Win,
Explode,
};
GameState state = Gaming;
std::unique_ptr<SDL_Texture, decltype(TextureDestroy)> numberImage;
std::unique_ptr<SDL_Texture, decltype(TextureDestroy)> mineImage;
std::unique_ptr<SDL_Texture, decltype(TextureDestroy)> flagImage;
std::unique_ptr<SDL_Texture, decltype(TextureDestroy)> gameoverImage;
std::unique_ptr<SDL_Texture, decltype(TextureDestroy)> winImage;
Window window;
Renderer renderer;
Map map;
bool debugMode = false;
int nakkedCount = 0;
int mineCount = 0;
static void Init() {
if (!instance_) {
Window window("mine-sweep", WindowWidth, WindowHeight);
Renderer renderer(window);
instance_ = std::unique_ptr<Context>(new Context(
std::move(window),
std::move(renderer),
CreateRandomMap(MineCount, WindowWidth / TileLen, WindowHeight / TileLen),
MineCount
));
}
}
static void Quit() {
instance_.reset();
}
static Context& Inst() {
SDL_assert(instance_);
return *instance_;
}
void DrawMap();
void HandleEvent(SDL_Event&);
private:
static std::unique_ptr<Context> instance_;
Context(Window&& window, Renderer&& renderer, Map&& map, int mineCount);
void drawOneTile(int x, int y, const Tile& tile);
void handleMouseLeftBtnDown(const SDL_MouseButtonEvent&);
void handleMouseRightBtnDown(const SDL_MouseButtonEvent&);
void handleKeyDown(const SDL_KeyboardEvent&);
};

46
include/matrix.hpp Normal file
View File

@ -0,0 +1,46 @@
#pragma once
template <typename T>
class Matrix final {
public:
Matrix(int w, int h): data_(std::unique_ptr<T[]>(new T[w * h])), w_(w), h_(h) {}
void Fill(const T& value) {
for (int i = 0 ; i < w_ * h_; i++) {
GetByIndex(i) = value;
}
}
T& Get(int x, int y) {
return data_.get()[x + y * w_];
}
const T& Get(int x, int y) const {
return data_.get()[x + y * w_];
}
T& GetByIndex(int idx) {
return data_.get()[idx];
}
int MaxSize() const {
return w_ * h_;
}
int Width() const {
return w_;
}
int Height() const {
return h_;
}
bool IsIn(int x, int y) const {
return x >= 0 && x < w_ && y >= 0 && y < h_;
}
private:
std::unique_ptr<T[]> data_;
int w_;
int h_;
};

6
include/pch.hpp Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include "SDL.h"
#include <string>
#include <memory>
#include <random>

25
include/renderer.hpp Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include "pch.hpp"
#include "window.hpp"
class Renderer final {
public:
friend struct Context;
Renderer(const Window&);
~Renderer();
Renderer(const Renderer&) = delete;
Renderer(Renderer&&);
Renderer& operator=(const Renderer&) = delete;
void SetColor(const SDL_Color&);
void Clear();
void Present();
void DrawRect(const SDL_Rect& rect);
void FillRect(const SDL_Rect& rect);
void DrawLine(const SDL_Point& p1, const SDL_Point& p2);
void DrawTexture(SDL_Texture*, const SDL_Rect&, int x, int y);
private:
SDL_Renderer* renderer_;
};

18
include/window.hpp Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include "pch.hpp"
class Window final {
public:
friend class Renderer;
Window(const std::string& title, int w, int h);
Window(const Window&) = delete;
Window(Window&&);
Window& operator=(const Window&) = delete;
~Window();
private:
SDL_Window* window_;
};

BIN
resources/flag.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
resources/font.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
resources/gameover.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

BIN
resources/mine.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
resources/win.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

BIN
snapshot/snapshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

262
src/context.cpp Normal file
View File

@ -0,0 +1,262 @@
#include "context.hpp"
constexpr SDL_Color NormalTileColor = {150, 150, 150, 255};
constexpr SDL_Color HoverTileColor = {200, 200, 200, 255};
constexpr SDL_Color BorderTileColor = {0, 0, 0, 255};
constexpr SDL_Color NakedTileColor = {50, 50, 50, 255};
constexpr SDL_Color KeyColor = {118, 66, 138, 255};
std::unique_ptr<Context> Context::instance_ = nullptr;
Map CreateRandomMap(int bombCount, int w, int h) {
SDL_assert(bombCount < w * h);
std::random_device d;
std::mt19937 gen(d());
std::uniform_int_distribution dist1(0, w - 1);
std::uniform_int_distribution dist2(0, h - 1);
Map map(w, h);
constexpr int MaxBombSetupCount = 100;
// generate all mines
while (bombCount > 0) {
bool setupBomb = false;
int setupCount = MaxBombSetupCount;
while (!setupBomb && setupCount > 0) {
int x = dist1(gen);
int y = dist2(gen);
auto& tile = map.Get(x, y);
if (tile.type != Tile::Bomb) {
tile.type = Tile::Bomb;
setupBomb = true;
} else {
setupCount --;
}
}
if (setupCount == 0) {
for (int i = 0; i < map.MaxSize(); i++) {
if (map.GetByIndex(i).type != Tile::Bomb) {
map.GetByIndex(i).type = Tile::Bomb;
break;
}
}
}
bombCount --;
}
// generate nearly mine number
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
auto& tile = map.Get(x, y);
if (tile.type == Tile::Bomb) {
continue;
}
int mineCount = 0;
for (int nx = -1; nx <= 1; nx ++) {
for (int ny = -1; ny <= 1; ny++) {
int detectX = x + nx;
int detectY = y + ny;
if (map.IsIn(detectX, detectY)) {
if (map.Get(detectX, detectY).type == Tile::Bomb) {
mineCount ++;
}
}
}
}
tile.type = static_cast<Tile::Type>(mineCount);
}
}
return map;
}
SDL_Texture* loadTexture(SDL_Renderer* renderer, const std::string& bmpFilename, const SDL_Color& keycolor) {
SDL_Surface* surface = SDL_LoadBMP(bmpFilename.c_str());
SDL_SetColorKey(surface, SDL_TRUE, SDL_MapRGB(surface->format, keycolor.r, keycolor.g, keycolor.b));
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_FreeSurface(surface);
return texture;
}
Context::Context(Window&& window, Renderer&& renderer, Map&& map, int mineCount)
: numberImage(loadTexture(renderer.renderer_, "resources/font.bmp", KeyColor), TextureDestroy),
mineImage(loadTexture(renderer.renderer_,"resources/mine.bmp", KeyColor), TextureDestroy),
flagImage(loadTexture(renderer.renderer_,"resources/flag.bmp", KeyColor), TextureDestroy),
gameoverImage(loadTexture(renderer.renderer_,"resources/gameover.bmp", KeyColor), TextureDestroy),
winImage(loadTexture(renderer.renderer_,"resources/win.bmp", KeyColor), TextureDestroy),
window(std::move(window)),
renderer(std::move(renderer)),
map(std::move(map)),
mineCount(mineCount) {}
void Context::DrawMap() {
for (int y = 0; y < map.Height(); y++) {
for (int x = 0; x < map.Width(); x++) {
const auto& tile = map.Get(x, y);
drawOneTile(x, y, tile);
}
}
if (state == GameState::Explode) {
renderer.DrawTexture(gameoverImage.get(), SDL_Rect{0, 0, 256, 256},
(WindowWidth - 256) / 2,
(WindowHeight - 256) / 2);
}
if (state == GameState::Win) {
renderer.DrawTexture(winImage.get(), SDL_Rect{0, 0, 256, 256},
(WindowWidth - 256) / 2,
(WindowHeight - 256) / 2);
}
}
void Context::drawOneTile(int x, int y, const Tile& tile) {
int tileX = x * TileLen;
int tileY = y * TileLen;
SDL_Rect rect = {tileX, tileY, TileLen, TileLen};
SDL_Point mousePos;
SDL_GetMouseState(&mousePos.x, &mousePos.y);
if (SDL_PointInRect(&mousePos, &rect)) {
renderer.SetColor(HoverTileColor);
} else {
renderer.SetColor(NormalTileColor);
}
if (tile.isVisiable && static_cast<int>(tile.type) >= 0) {
renderer.SetColor(NakedTileColor);
}
renderer.FillRect(rect);
renderer.SetColor(BorderTileColor);
renderer.DrawRect(rect);
if (tile.isVisiable) {
if (tile.type == Tile::Bomb) {
renderer.DrawTexture(mineImage.get(), SDL_Rect{0, 0, 32, 32}, tileX, tileY);
} else {
int mineCount = tile.type;
if (mineCount > 0) {
renderer.DrawTexture(
numberImage.get(),
SDL_Rect{309 / 8 * (mineCount - 1), 0, 32, 32},
tileX, tileY);
}
}
} else {
if (tile.isFlaged) {
renderer.DrawTexture(flagImage.get(), SDL_Rect{0, 0, 32, 32}, tileX, tileY);
}
}
if (debugMode) {
if (tile.type == Tile::Bomb) {
renderer.DrawTexture(mineImage.get(), SDL_Rect{0, 0, 32, 32}, tileX, tileY);
} else {
int mineCount = tile.type;
if (mineCount > 0) {
renderer.DrawTexture(
numberImage.get(),
SDL_Rect{309 / 8 * (mineCount - 1), 0, 32, 32},
tileX, tileY);
}
}
}
}
void Context::HandleEvent(SDL_Event& e) {
if (!state == GameState::Gaming) {
if (e.type == SDL_MOUSEBUTTONDOWN) {
map = CreateRandomMap(MineCount, WindowWidth / TileLen, WindowHeight / TileLen);
mineCount = MineCount;
nakkedCount = 0;
state = GameState::Gaming;
}
return;
}
if (e.type == SDL_MOUSEBUTTONDOWN) {
if (e.button.button == SDL_BUTTON_LEFT) {
handleMouseLeftBtnDown(e.button);
}
if (e.button.button == SDL_BUTTON_RIGHT) {
handleMouseRightBtnDown(e.button);
}
}
if (e.type == SDL_KEYDOWN) {
handleKeyDown(e.key);
}
}
inline SDL_Point calcTileCoord(int mouseX, int mouseY) {
return {mouseX / TileLen, mouseY / TileLen};
}
void floodFill(Context& ctx, Map& map, int x, int y) {
if (!map.IsIn(x, y)) {
return;
}
auto& tile = map.Get(x, y);
if (!tile.isVisiable && !tile.isFlaged && tile.type != Tile::Bomb) {
tile.isVisiable = true;
ctx.nakkedCount ++;
if (static_cast<int>(tile.type) == 0) {
floodFill(ctx, map, x - 1, y);
floodFill(ctx, map, x + 1, y);
floodFill(ctx, map, x, y - 1);
floodFill(ctx, map, x, y + 1);
}
}
}
void Context::handleMouseLeftBtnDown(const SDL_MouseButtonEvent& e) {
auto tileCoord = calcTileCoord(e.x, e.y);
if (!map.IsIn(tileCoord.x, tileCoord.y)) {
return;
}
auto& tile = map.Get(tileCoord.x, tileCoord.y);
if (tile.isVisiable || tile.isFlaged) {
return;
}
if (!tile.isVisiable && tile.type == Tile::Bomb) {
state = GameState::Explode;
for (int i = 0; i < map.MaxSize(); i++) {
auto& tile = map.GetByIndex(i);
tile.isVisiable = true;
tile.isFlaged = false;
}
return;
}
floodFill(*this, map, tileCoord.x, tileCoord.y);
if (nakkedCount == map.MaxSize() - mineCount) {
state = GameState::Win;
}
}
void Context::handleMouseRightBtnDown(const SDL_MouseButtonEvent& e) {
auto tileCoord = calcTileCoord(e.x, e.y);
if (!map.IsIn(tileCoord.x, tileCoord.y)) {
return;
}
auto& tile = map.Get(tileCoord.x, tileCoord.y);
if (!tile.isVisiable) {
tile.isFlaged = !tile.isFlaged;
}
}
void Context::handleKeyDown(const SDL_KeyboardEvent& e) {
if (e.keysym.scancode == SDL_SCANCODE_G) {
debugMode = !debugMode;
}
}

36
src/main.cpp Normal file
View File

@ -0,0 +1,36 @@
#include <iostream>
#include "context.hpp"
int main(int argc, char** argv) {
SDL_Init(SDL_INIT_EVERYTHING);
Context::Init();
auto& ctx = Context::Inst();
auto& renderer = ctx.renderer;
SDL_Event event;
bool shouldClose = false;
while(!shouldClose) {
while(SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
shouldClose = true;
}
ctx.HandleEvent(event);
}
renderer.SetColor(SDL_Color{200, 200, 200, 255});
renderer.Clear();
ctx.DrawMap();
renderer.Present();
SDL_Delay(30);
}
Context::Quit();
SDL_Quit();
return 0;
}

43
src/renderer.cpp Normal file
View File

@ -0,0 +1,43 @@
#include "renderer.hpp"
Renderer::Renderer(const Window& window) {
renderer_ = SDL_CreateRenderer(window.window_, -1, 0);
}
void Renderer::SetColor(const SDL_Color& c) {
SDL_SetRenderDrawColor(renderer_, c.r, c.g, c.b, c.a);
}
Renderer::Renderer(Renderer&& oth) {
renderer_ = oth.renderer_;
oth.renderer_ = nullptr;
}
void Renderer::Clear() {
SDL_RenderClear(renderer_);
}
void Renderer::Present() {
SDL_RenderPresent(renderer_);
}
Renderer::~Renderer() {
SDL_DestroyRenderer(renderer_);
}
void Renderer::DrawRect(const SDL_Rect& rect) {
SDL_RenderDrawRect(renderer_, &rect);
}
void Renderer::FillRect(const SDL_Rect& rect) {
SDL_RenderFillRect(renderer_, &rect);
}
void Renderer::DrawLine(const SDL_Point& p1, const SDL_Point& p2) {
SDL_RenderDrawLine(renderer_, p1.x, p1.y, p2.x, p2.y);
}
void Renderer::DrawTexture(SDL_Texture* texture, const SDL_Rect& rect, int x, int y) {
SDL_Rect dst = {x, y, rect.w, rect.h};
SDL_RenderCopy(renderer_, texture, &rect, &dst);
}

15
src/window.cpp Normal file
View File

@ -0,0 +1,15 @@
#include "window.hpp"
Window::Window(const std::string& title, int w, int h) {
window_ = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, w, h, SDL_WINDOW_SHOWN);
}
Window::Window(Window&& oth) {
window_ = oth.window_;
oth.window_ = nullptr;
}
Window::~Window() {
SDL_DestroyWindow(window_);
}