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 }