minesweeper

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

main.c (6375B)


      1 #include <raylib.h>
      2 #include <stdlib.h>
      3 
      4 // Global variables
      5 #define DEFAULT_BOARD_X 30
      6 #define DEFAULT_BOARD_Y 20
      7 #define DEFAULT_BOMBS 70
      8 static Vector2 squareSize;
      9 static int TilesX = DEFAULT_BOARD_X;
     10 static int TilesY = DEFAULT_BOARD_Y;
     11 static int TotalBombs = DEFAULT_BOMBS;
     12 typedef struct Bombs {
     13   int x, y;
     14 } Bomb;
     15 static Bomb *bombPos; // all bombs position
     16 static bool **shown;  // if a tile is hidden
     17 static bool **flags;  // if a tile is flagged
     18 static bool gameOver = false;
     19 
     20 // function declerations
     21 static void ExplodeAllBombs(void);
     22 static void NewGame(void);
     23 static void UpdateGame(void);
     24 static void DrawGame(void);
     25 static void ClearNeighbours(const int *x, const int *y);
     26 static bool HasNeighbours(const int *x, const int *y);
     27 static void DrawTile(const int *x, const int *y);
     28 
     29 int main(int argc, char *argv[]) {
     30   // Set game area and bomb amount with command line args
     31   switch (argc) {
     32   case 4:
     33     TotalBombs = strtol(argv[3], NULL, 0);
     34   case 3:
     35     TilesY = strtol(argv[2], NULL, 0);
     36     if (TilesY <= 0)
     37       TilesY = DEFAULT_BOARD_Y;
     38   case 2:
     39     TilesX = strtol(argv[1], NULL, 0);
     40     if (TilesX <= 0)
     41       TilesX = DEFAULT_BOARD_X;
     42     if ((argc == 4) && (TotalBombs <= 0 || TotalBombs >= TilesX * TilesY))
     43       TotalBombs = (TilesX * TilesY) / 7;
     44   }
     45   shown = malloc(TilesX * sizeof(bool *));
     46   flags = malloc(TilesX * sizeof(bool *));
     47   shown[0] = malloc(TilesX * TilesY * sizeof(bool));
     48   flags[0] = malloc(TilesX * TilesY * sizeof(bool));
     49   for (int i = 1; i < TilesX; i++) {
     50     shown[i] = shown[0] + i * TilesY;
     51     flags[i] = flags[0] + i * TilesY;
     52   }
     53   bombPos = malloc(TotalBombs * sizeof(Bomb));
     54   if (bombPos == NULL || shown == NULL || shown[0] == NULL || flags == NULL ||
     55       flags[0] == NULL)
     56     return 255; // memory allocation failed
     57 
     58   SetConfigFlags(FLAG_WINDOW_RESIZABLE);
     59   SetConfigFlags(FLAG_VSYNC_HINT);
     60   InitWindow(800, 800, "Minesweeper in C99");
     61   NewGame();
     62 
     63   while (!WindowShouldClose()) { // game loop
     64     UpdateGame();
     65     DrawGame();
     66   }
     67 
     68   // close down and free memory
     69   free(bombPos);
     70   free(shown[0]);
     71   free(flags[0]);
     72   free(shown);
     73   free(flags);
     74   CloseWindow();
     75   return 0;
     76 }
     77 
     78 void NewGame(void) {
     79   // reset board
     80   for (int i = 0; i < TotalBombs; i++) {
     81     bombPos[i].x = 0;
     82     bombPos[i].y = 0;
     83   }
     84   for (int x = 0; x < TilesX; x++)
     85     for (int y = 0; y < TilesY; y++) {
     86       shown[x][y] = false;
     87       flags[x][y] = false;
     88     }
     89   gameOver = false;
     90 
     91   // place bombs (with no duplicates)
     92   for (int i = 0; i < TotalBombs; i++) {
     93     bombPos[i].x = rand() % TilesX;
     94     bombPos[i].y = rand() % TilesY;
     95     for (int j = 0; j < TotalBombs; j++)
     96       if ((j != i) &&
     97           (bombPos[i].x == bombPos[j].x && bombPos[i].y == bombPos[j].y))
     98         i--; // repeat bomb
     99   }
    100 }
    101 
    102 void ExplodeAllBombs(void) {
    103   for (int i = 0; i < TotalBombs; i++)
    104     shown[bombPos[i].x][bombPos[i].y] = true;
    105   gameOver = true;
    106 }
    107 
    108 void UpdateGame(void) {
    109   const Vector2 mouse = GetMousePosition();
    110   const int mouseX = mouse.x / squareSize.x;
    111   const int mouseY = mouse.y / squareSize.y;
    112   if (gameOver) {
    113     if (IsKeyPressed(KEY_SPACE)) {
    114       gameOver = false;
    115       NewGame();
    116     }
    117   } else if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) &&
    118              !flags[mouseX][mouseY]) {
    119     shown[mouseX][mouseY] = true;
    120     if (!HasNeighbours(&mouseX, &mouseY))
    121       ClearNeighbours(&mouseX, &mouseY);
    122     else
    123       for (int i = 0; i < TotalBombs; i++)
    124         if (bombPos[i].x == mouseX && bombPos[i].y == mouseY) {
    125           ExplodeAllBombs();
    126           break;
    127         }
    128 
    129     // calulate if game has ended
    130     int count = 0;
    131     for (int x = 0; x < TilesX; x++)
    132       for (int y = 0; y < TilesY; y++)
    133         if (!shown[x][y])
    134           count++;
    135     if (count == TotalBombs)
    136       gameOver = true;
    137 
    138   } else if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON))
    139     if (!shown[mouseX][mouseY])
    140       flags[mouseX][mouseY] = !flags[mouseX][mouseY];
    141 }
    142 
    143 void ClearNeighbours(const int *x, const int *y) {
    144   for (int nX = -1; nX <= 1; nX++) {
    145     const int xPos = *x + nX;
    146     if (xPos < 0 || xPos >= TilesX)
    147       continue;
    148     for (int nY = -1; nY <= 1; nY++) {
    149       const int yPos = *y + nY;
    150       if (yPos < 0 || yPos >= TilesY || shown[xPos][yPos] || flags[xPos][yPos])
    151         continue;
    152       shown[xPos][yPos] = true;
    153       if (!HasNeighbours(&xPos, &yPos))
    154         ClearNeighbours(&xPos, &yPos); // recurse if tile has no neighbours
    155     }
    156   }
    157 }
    158 
    159 bool HasNeighbours(const int *x, const int *y) {
    160   for (int i = 0; i < TotalBombs; i++)
    161     if ((bombPos[i].x == *x - 1 || bombPos[i].x == *x + 1 ||
    162          bombPos[i].x == *x) &&
    163         (bombPos[i].y == *y - 1 || bombPos[i].y == *y + 1 ||
    164          bombPos[i].y == *y))
    165       return true;
    166   return false;
    167 }
    168 
    169 void DrawGame(void) {
    170   squareSize.x = (float)GetScreenWidth() / (float)TilesX;
    171   squareSize.y = (float)GetScreenHeight() / (float)TilesY;
    172   ClearBackground(GRAY);
    173   for (int x = 0; x < TilesX; x++)
    174     for (int y = 0; y < TilesY; y++)
    175       if (shown[x][y])
    176         DrawTile(&x, &y);
    177       else if (flags[x][y])
    178         DrawText("?", (x + 0.35f) * squareSize.x, (y + 0.2f) * squareSize.y,
    179                  squareSize.y * 0.6f, GREEN);
    180 
    181   if (gameOver) {
    182     const float xsize = TilesX * 0.2f * squareSize.x;
    183     const float ysize = TilesY * 0.2f * squareSize.y;
    184     const float fsize = TilesX * 0.1f * squareSize.x;
    185     DrawText("GAME OVER!", xsize, ysize, fsize, GOLD);
    186     DrawText("(press SPACE to replay)", xsize, (TilesY * 0.5f) * squareSize.y,
    187              fsize * 0.5f, GOLD);
    188   }
    189   EndDrawing();
    190 }
    191 
    192 void DrawTile(const int *x, const int *y) {
    193   const float xsize = (*x + 0.35f) * squareSize.x;
    194   const float ysize = (*y + 0.2f) * squareSize.y;
    195   const float fsize = 0.55f * squareSize.y;
    196   // find naboring bombs
    197   int nearby = 0;
    198   for (int i = 0; i < TotalBombs; i++) {
    199     if ((bombPos[i].x == *x - 1 || bombPos[i].x == *x + 1 ||
    200          bombPos[i].x == *x) &&
    201         (bombPos[i].y == *y - 1 || bombPos[i].y == *y + 1 ||
    202          bombPos[i].y == *y))
    203       nearby++;
    204 
    205     if (bombPos[i].x == *x && bombPos[i].y == *y) {
    206       DrawText("x", xsize, ysize, fsize, RED);
    207       return;
    208     }
    209   }
    210   const Vector2 rectpos = {squareSize.x * (float)*x, squareSize.y * (float)*y};
    211   DrawRectangleV(rectpos, squareSize, RAYWHITE);
    212 
    213   if (nearby == 0)
    214     return;
    215   const char c[] = {nearby + '0', '\0'};
    216   DrawText(c, xsize, ysize, fsize, BLACK);
    217 }