From b0c8fc83fe1de761390542a185d141babe180703 Mon Sep 17 00:00:00 2001 From: rattatwinko Date: Sat, 10 May 2025 22:03:07 +0200 Subject: [PATCH] initial --- CMakeLists.txt | 30 +++++++ src/game.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++ src/game.h | 33 ++++++++ src/main.c | 81 +++++++++++++++++++ 4 files changed, 351 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 src/game.c create mode 100644 src/game.h create mode 100644 src/main.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2883ddf --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.10) +project(minesweeper C) + +set(CMAKE_C_STANDARD 99) + +# Find SDL2 and SDL2_ttf using pkg-config +find_package(PkgConfig REQUIRED) +pkg_check_modules(SDL2 REQUIRED sdl2) +pkg_check_modules(SDL2TTF REQUIRED SDL2_ttf) + +include_directories( + ${SDL2_INCLUDE_DIRS} + ${SDL2TTF_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/src +) + +link_directories( + ${SDL2_LIBRARY_DIRS} + ${SDL2TTF_LIBRARY_DIRS} +) + +add_executable(minesweeper + src/main.c + src/game.c +) + +target_link_libraries(minesweeper + ${SDL2_LIBRARIES} + ${SDL2TTF_LIBRARIES} +) diff --git a/src/game.c b/src/game.c new file mode 100644 index 0000000..c71a0db --- /dev/null +++ b/src/game.c @@ -0,0 +1,207 @@ +#include "game.h" +#include +#include +#include +#include + +extern SDL_Renderer* renderer; +extern TTF_Font* font; + +extern SDL_Renderer* renderer; + +int game_over = 0; +const char* texture_blank = +"...." +". ." +". ." +"...."; + +const char* texture_flag = +".!!." +".!!." +".!!." +".!!."; + +const char* texture_mine = +"****" +"****" +"****" +"****"; + +const char* number_textures[9] = { + NULL, // index 0 not used + ".1.." + ".1.." + ".1.." + ".1..", + + ".22." + ". ." + ".22." + ". .", + + ".33." + ". ." + ".33." + ". .", + + ".44." + ". ." + ".44." + ". .", + + ".55." + ". ." + ".55." + ". .", + + ".66." + ". ." + ".66." + ". .", + + ".77." + ". ." + ".77." + ". .", + + ".88." + ". ." + ".88." + ". .", +}; + +void place_mines() { + int placed = 0; + while (placed < NUM_MINES) { + int x = rand() % GRID_WIDTH; + int y = rand() % GRID_HEIGHT; + if (!grid[x][y].has_mine) { + grid[x][y].has_mine = 1; + placed++; + } + } +} + +void calculate_neighbors() { + for (int x = 0; x < GRID_WIDTH; x++) { + for (int y = 0; y < GRID_HEIGHT; y++) { + if (grid[x][y].has_mine) continue; + int count = 0; + for (int dx = -1; dx <= 1; dx++) { + for (int dy = -1; dy <= 1; dy++) { + int nx = x + dx, ny = y + dy; + if (nx >= 0 && nx < GRID_WIDTH && ny >= 0 && ny < GRID_HEIGHT) + count += grid[nx][ny].has_mine; + } + } + grid[x][y].neighbor_mines = count; + } + } +} + +void init_game() { + srand((unsigned int)time(NULL)); + for (int x = 0; x < GRID_WIDTH; x++) + for (int y = 0; y < GRID_HEIGHT; y++) + grid[x][y] = (Cell){0}; + place_mines(); + calculate_neighbors(); + game_over = 0; +} + +void reveal_cell(int x, int y) { + if (x < 0 || x >= GRID_WIDTH || y < 0 || y >= GRID_HEIGHT) return; + if (grid[x][y].revealed || grid[x][y].flagged) return; + grid[x][y].revealed = 1; + + if (grid[x][y].has_mine) { + game_over = 1; + return; + } + + if (grid[x][y].neighbor_mines == 0) { + for (int dx = -1; dx <= 1; dx++) + for (int dy = -1; dy <= 1; dy++) + if (dx || dy) + reveal_cell(x + dx, y + dy); + } +} + +void toggle_flag(int x, int y) { + if (!grid[x][y].revealed) + grid[x][y].flagged = !grid[x][y].flagged; +} + +int is_game_over() { + return game_over; +} + +void draw_text(const char* text, int x, int y, SDL_Color color) { + SDL_Surface* surface = TTF_RenderText_Blended(font, text, color); + SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); + SDL_Rect dst = { x, y, surface->w, surface->h }; + SDL_RenderCopy(renderer, texture, NULL, &dst); + SDL_DestroyTexture(texture); + SDL_FreeSurface(surface); +} + +void draw_texture(const char* pattern, int x, int y, SDL_Color fg, SDL_Color bg) { + for (int row = 0; row < 4; ++row) { + for (int col = 0; col < 4; ++col) { + char pixel = pattern[row * 4 + col]; + SDL_Rect rect = { + x + col * (CELL_SIZE / 4), + y + row * (CELL_SIZE / 4), + CELL_SIZE / 4, + CELL_SIZE / 4 + }; + SDL_SetRenderDrawColor(renderer, + pixel == '.' ? bg.r : fg.r, + pixel == '.' ? bg.g : fg.g, + pixel == '.' ? bg.b : fg.b, + 255); + SDL_RenderFillRect(renderer, &rect); + } + } +} + +void draw_game() { + SDL_SetRenderDrawColor(renderer, 50, 50, 50, 255); + SDL_RenderClear(renderer); + + for (int x = 0; x < GRID_WIDTH; x++) { + for (int y = 0; y < GRID_HEIGHT; y++) { + SDL_Rect rect = { x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE }; + Cell* c = &grid[x][y]; + + // Background + if (c->revealed) { + SDL_SetRenderDrawColor(renderer, 200, 200, 200, 255); + } else { + SDL_SetRenderDrawColor(renderer, 100, 100, 100, 255); + } + SDL_RenderFillRect(renderer, &rect); + + // Border + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderDrawRect(renderer, &rect); + + // Content + if (c->revealed) { + if (c->has_mine) { + draw_text("*", rect.x + 8, rect.y + 4, (SDL_Color){255, 0, 0}); + } else if (c->neighbor_mines > 0) { + char buffer[2]; + snprintf(buffer, 2, "%d", c->neighbor_mines); + draw_text(buffer, rect.x + 10, rect.y + 4, (SDL_Color){0, 0, 255}); + } + } else if (c->flagged) { + draw_text("F", rect.x + 10, rect.y + 4, (SDL_Color){255, 0, 0}); + } + } + } + + SDL_RenderPresent(renderer); +} + diff --git a/src/game.h b/src/game.h new file mode 100644 index 0000000..3963220 --- /dev/null +++ b/src/game.h @@ -0,0 +1,33 @@ +#ifndef GAME_H +#define GAME_H + +#include +#include + +// Game grid dimensions +#define GRID_WIDTH 10 +#define GRID_HEIGHT 10 +#define NUM_MINES 10 +#define CELL_SIZE 32 + +// Cell structure +typedef struct { + int revealed; + int has_mine; + int flagged; + int neighbor_mines; +} Cell; + +// Extern declarations +extern SDL_Renderer* renderer; +extern TTF_Font* font; +extern Cell grid[GRID_WIDTH][GRID_HEIGHT]; + +// Function declarations +void init_game(); +void reveal_cell(int x, int y); +void toggle_flag(int x, int y); +void draw_game(); +void draw_text(const char* text, int x, int y, SDL_Color color); + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..07dbf76 --- /dev/null +++ b/src/main.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include "game.h" + +// Global SDL variables +SDL_Renderer* renderer = NULL; +TTF_Font* font = NULL; +Cell grid[GRID_WIDTH][GRID_HEIGHT]; + +int main(int argc, char* argv[]) { + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + fprintf(stderr, "SDL_Init Error: %s\n", SDL_GetError()); + return 1; + } + + if (TTF_Init() != 0) { + fprintf(stderr, "TTF_Init Error: %s\n", TTF_GetError()); + SDL_Quit(); + return 1; + } + + font = TTF_OpenFont("/usr/share/fonts/dejavu-sans-fonts/DejaVuSans.ttf", 16); // Change path if needed + if (!font) { + fprintf(stderr, "Failed to load font: %s\n", TTF_GetError()); + TTF_Quit(); + SDL_Quit(); + return 1; + } + + SDL_Window* window = SDL_CreateWindow("Minesweeper", + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + GRID_WIDTH * CELL_SIZE, GRID_HEIGHT * CELL_SIZE, SDL_WINDOW_SHOWN); + + if (!window) { + fprintf(stderr, "Window creation failed: %s\n", SDL_GetError()); + TTF_Quit(); + SDL_Quit(); + return 1; + } + + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + if (!renderer) { + fprintf(stderr, "Renderer creation failed: %s\n", SDL_GetError()); + SDL_DestroyWindow(window); + TTF_Quit(); + SDL_Quit(); + return 1; + } + + init_game(); + + int running = 1; + SDL_Event event; + while (running) { + while (SDL_PollEvent(&event)) { + if (event.type == SDL_QUIT) { + running = 0; + } else if (event.type == SDL_MOUSEBUTTONDOWN) { + int x = event.button.x / CELL_SIZE; + int y = event.button.y / CELL_SIZE; + + if (event.button.button == SDL_BUTTON_LEFT) { + reveal_cell(x, y); + } else if (event.button.button == SDL_BUTTON_RIGHT) { + toggle_flag(x, y); + } + } + } + + draw_game(); + SDL_Delay(16); // Cap to ~60 FPS + } + + TTF_CloseFont(font); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + TTF_Quit(); + SDL_Quit(); + return 0; +}