DungeonCrawl
Loading...
Searching...
No Matches
map_generator.c
Go to the documentation of this file.
1
5#include "map_generator.h"
6
7#include "../logging/logger.h"
8#include "map.h"
9#include "map_mode.h"
10#include "map_populator.h"
11
12#include <stdint.h>
13#include <stdlib.h>
14#include <time.h>
15
16// map array to store the maze
17int visited[WIDTH][HEIGHT];
18
19int exit_edge = 0;
20int exit_x = 0;
21int exit_y = 0;
22
23
32void shuffle(vector2d_t* dir, int n) {
33 for (int i = n - 1; i > 0; i--) {
34 int j = rand() % (i + 1);
35 vector2d_t tmp = dir[j];
36 dir[j] = dir[i];
37 dir[i] = tmp;
38 }
39}
40
47int is_in_bounds(int x, int y) {
48 return x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT;
49}
50
57int is_valid_cell(int x, int y) {
58 return is_in_bounds(x, y) && !visited[x][y];
59}
60
68int validate_exit_position(int exit_e, int x, int y) {
69 switch (exit_e) {
70 case TOP:
71 return map[x][y + 1] == FLOOR;
72 case BOTTOM:
73 return map[x][y - 1] == FLOOR;
74 case LEFT:
75 return map[x + 1][y] == FLOOR;
76 case RIGHT:
77 return map[x - 1][y] == FLOOR;
78 default:
79 log_msg(ERROR, "map_generator", "Invalid exit edge: %d", exit_e);
80 return 0;
81 }
82}
83
89void carve_passages(int x, int y) {
90 visited[x][y] = 1;
91 map[x][y] = FLOOR;
92
93 // Create a copy of directions and shuffle them
94 vector2d_t shuffled_dirs[4];
95 for (int i = 0; i < 4; i++) {
96 shuffled_dirs[i] = directions[i];
97 }
98 shuffle(shuffled_dirs, 4);
99
100 // Try each direction in random order
101 for (int i = 0; i < 4; i++) {
102 int nx = x + shuffled_dirs[i].dx * 2;// Move two cells in the direction
103 int ny = y + shuffled_dirs[i].dy * 2;
104
105 if (is_valid_cell(nx, ny)) {
106 // Carve passage by setting the cell between current and next to FLOOR
107 int wall_x = x + shuffled_dirs[i].dx;
108 int wall_y = y + shuffled_dirs[i].dy;
109
110 // make the wall a floor and continue the path
111 map[wall_x][wall_y] = FLOOR;
112 carve_passages(nx, ny);
113 }
114 }
115}
116
124int check_neighboring_floors(int x, int y, int neighbor_directions[4]) {
125 int count = 0;
126 for (int i = 0; i < 4; i++) {
127 int dx = x + directions[i].dx;
128 int dy = y + directions[i].dy;
129
130 if (map[dx][dy] == FLOOR) {
131 count++;
132 neighbor_directions[i] = 1;
133 }
134 }
135 return count;
136}
137
142void add_loops(int num_loops) {
143 int count = 0;
144 int max_attempts = num_loops * 10;// Limit the number of attempts
145
146 while (count < num_loops && max_attempts > 0) {
147 // Pick a random cell
148 int x = 1 + rand() % (WIDTH - 2);
149 int y = 1 + rand() % (HEIGHT - 2);
150
151 // If the wall has exactly 2 opposing floor neighbors, knock it down to create a loop
152 if (map[x][y] == WALL) {
153 int neighbor_directions[] = {0, 0, 0, 0};
154
155 int floor_count = check_neighboring_floors(x, y, neighbor_directions);
156
157 // check if the wall has exactly 2 opposing floor neighbors
158 if ((floor_count == 2) &&
159 ((neighbor_directions[TOP] && neighbor_directions[BOTTOM]) ||
160 (neighbor_directions[LEFT] && neighbor_directions[RIGHT]))) {
161 map[x][y] = FLOOR;
162 count++;
163 }
164 }
165
166 max_attempts--;
167 }
168}
169
174void place_exit(int start_edge) {
175 // get a random exit edge that is different from the start edge
176 exit_edge = start_edge;
177 while (exit_edge == start_edge) {
178 exit_edge = rand() % 4;
179 }
180
181 do {
182 switch (exit_edge) {
183 case TOP:
184 exit_x = 1 + 2 * (rand() % ((WIDTH - 2) / 2));
185 exit_y = 0;
186 break;
187 case BOTTOM:
188 exit_x = 1 + 2 * (rand() % ((WIDTH - 2) / 2));
189 exit_y = HEIGHT - 1;
190 break;
191 case LEFT:
192 exit_x = 0;
193 exit_y = 1 + 2 * (rand() % ((HEIGHT - 2) / 2));
194 break;
195 case RIGHT:
196 exit_x = WIDTH - 1;
197 exit_y = 1 + 2 * (rand() % ((HEIGHT - 2) / 2));
198 break;
199 default:
200 log_msg(ERROR, "map_generator", "Invalid exit edge: %d", exit_edge);
201 return;
202 }
203 } while (!validate_exit_position(exit_edge, exit_x, exit_y));
204
205 map[exit_x][exit_y] = EXIT_DOOR;
206}
207
208
213 for (int y = 0; y < HEIGHT; y++) {
214 for (int x = 0; x < WIDTH; x++) {
215 map[x][y] = WALL;
216 revealed_map[x][y] = HIDDEN;
217 visited[x][y] = 0;
218 }
219 }
220}
221
228void generate_maze(int start_x, int start_y) {
229 // Make sure start position is valid for dfs (odd coordinates)
230 // relevant if we want to implement the starting position differently
231 if (start_x % 2 == 0) {
232 start_x++;
233 }
234 if (start_y % 2 == 0) {
235 start_y++;
236 }
237
238 // Generate the map using recursive backtracking
239 carve_passages(start_x, start_y);
240
241 // Add some loops to the map
242 int num_loops = (WIDTH * HEIGHT) / 100 + 1;// Use fewer loops to prevent overflow
243 add_loops(num_loops);
244}
245
252void set_start_position(int start_edge, int* start_x, int* start_y) {
253 switch (start_edge) {
254 case TOP:
255 *start_x = 3 + 2 * (rand() % ((WIDTH - 5) / 2));
256 *start_y = 1;
257 map[*start_x][0] = START_DOOR;
258 break;
259 case RIGHT:
260 *start_x = WIDTH - 2;
261 *start_y = 3 + 2 * (rand() % ((HEIGHT - 5) / 2));
262 map[WIDTH - 1][*start_y] = START_DOOR;
263 break;
264 case BOTTOM:
265 *start_x = 3 + 2 * (rand() % ((WIDTH - 5) / 2));
266 *start_y = HEIGHT - 2;
267 map[*start_x][HEIGHT - 1] = START_DOOR;
268 break;
269 case LEFT:
270 *start_x = 1;
271 *start_y = 3 + 2 * (rand() % ((HEIGHT - 5) / 2));
272 map[0][*start_y] = START_DOOR;
273 break;
274 default:
275 log_msg(ERROR, "map_generator", "Invalid start edge: %d", start_edge);
276 break;
277 }
278}
279
285void make_exit_into_start(int* start_edge, int* start_x, int* start_y) {
286 switch (exit_edge) {
287 case TOP:
288 *start_edge = BOTTOM;
289 *start_x = exit_x;
290 *start_y = HEIGHT - 2;
291 map[*start_x][HEIGHT - 1] = START_DOOR;
292 break;
293 case RIGHT:
294 *start_edge = LEFT;
295 *start_x = 1;
296 *start_y = exit_y;
297 map[0][*start_y] = START_DOOR;
298 break;
299 case BOTTOM:
300 *start_edge = TOP;
301 *start_x = exit_x;
302 *start_y = 1;
303 map[*start_x][0] = START_DOOR;
304 break;
305 case LEFT:
306 *start_edge = RIGHT;
307 *start_x = WIDTH - 2;
308 *start_y = exit_y;
309 map[WIDTH - 1][*start_y] = START_DOOR;
310 break;
311 default:
312 log_msg(ERROR, "map_generator", "Invalid exit edge: %d", exit_edge);
313 break;
314 }
315}
316
318 // Better random seed using a combination of time and process info
319 unsigned int seed = (unsigned int) time(NULL);
320 // XOR with address of a stack variable to add more entropy
321 int stack_var;
322 seed ^= (uintptr_t) &stack_var;
323 srand(seed);
324
325 // Initialize the map with walls
327
328 int start_x = 0;
329 int start_y = 0;
330 int start_edge = 0;
331 if (!exit_x && !exit_y) {
332 // No exit yet, so this is the first time generating the map
333 // Generate map with a random start position (at the start_edge)
334 // Start position must be an odd coordinate (for dfs) and should be at least 3 cells away from other edges (not start_edge)
335 start_edge = rand() % 4;
336 set_start_position(start_edge, &start_x, &start_y);
337 } else {
338 make_exit_into_start(&start_edge, &start_x, &start_y);
339 }
340
341 generate_maze(start_x, start_y);
342
343 place_exit(start_edge);
344
345 populate_map();
346
347 set_player_start_pos(start_x, start_y);
348}
void log_msg(const log_level_t level, const char *module, const char *format,...)
Logs a formatted message with a specified log level and module.
Definition logger.c:246
Header file for logging functionality of the game.
Exposes common types and constants for the games map.
int validate_exit_position(int exit_e, int x, int y)
Check if the exit position is adjacent to a floor cell.
int is_in_bounds(int x, int y)
Check if cell is within bounds of the map.
int is_valid_cell(int x, int y)
Check if a cell is a valid cell to visit (in bounds and not visited)
void add_loops(int num_loops)
Add loops to the map by knocking down some walls.
int check_neighboring_floors(int x, int y, int neighbor_directions[4])
Count neighboring floor cells.
void initialize_map()
Initialize the map with walls.
void shuffle(vector2d_t *dir, int n)
Shuffle array using Fisher-Yates algorithm.
void place_exit(int start_edge)
Place the exit on a random edge of the map, ensuring there's a path to it.
void carve_passages(int x, int y)
Recursive backtracking algorithm to generate map (based on dfs)
void make_exit_into_start(int *start_edge, int *start_x, int *start_y)
Set the start position on the map based on the previous exit position.
void generate_maze(int start_x, int start_y)
Generate a new maze using recursive backtracking.
void generate_map()
Generate the map and populate it with keys, enemies, and the exit.
void set_start_position(int start_edge, int *start_x, int *start_y)
Set the start position on the map.
Exposes functions for generating the game map.
void set_player_start_pos(const int player_x, const int player_y)
Sets the starting position of the player.
Definition map_mode.c:26
Defines and manages functions for map exploration, player movement, and map interactions in map mode.
void populate_map()
@breif Populates the map with a key, enemies, and fountains
Exposes functions for populating the generated map with a key, enemies and fountains.
2-dimensional vector struct
Definition common.h:164