DungeonCrawl
Loading...
Searching...
No Matches
map_generator.c File Reference

Implements functionality for generating the game map. More...

#include "map_generator.h"
#include "../logging/logger.h"
#include "map.h"
#include "map_mode.h"
#include "map_populator.h"
#include <stdint.h>
#include <stdlib.h>
#include <time.h>

Go to the source code of this file.

Functions

void shuffle (vector2d_t *dir, int n)
 Shuffle array using Fisher-Yates algorithm.
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)
int validate_exit_position (int exit_e, int x, int y)
 Check if the exit position is adjacent to a floor cell.
void carve_passages (int x, int y)
 Recursive backtracking algorithm to generate map (based on dfs)
int check_neighboring_floors (int x, int y, int neighbor_directions[4])
 Count neighboring floor cells.
void add_loops (int num_loops)
 Add loops to the map by knocking down some walls.
void place_exit (int start_edge)
 Place the exit on a random edge of the map, ensuring there's a path to it.
void initialize_map ()
 Initialize the map with walls.
void generate_maze (int start_x, int start_y)
 Generate a new maze using recursive backtracking.
void set_start_position (int start_edge, int *start_x, int *start_y)
 Set the start position on the map.
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_map ()
 Generate the map and populate it with keys, enemies, and the exit.

Variables

int visited [WIDTH][HEIGHT]
int exit_edge = 0
int exit_x = 0
int exit_y = 0

Detailed Description

Implements functionality for generating the game map.

Definition in file map_generator.c.

Function Documentation

◆ add_loops()

void add_loops ( int num_loops)

Add loops to the map by knocking down some walls.

Parameters
num_loopsnumber of loops to add

Definition at line 142 of file map_generator.c.

142 {
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}
int check_neighboring_floors(int x, int y, int neighbor_directions[4])
Count neighboring floor cells.

◆ carve_passages()

void carve_passages ( int x,
int y )

Recursive backtracking algorithm to generate map (based on dfs)

Parameters
xstarting x coordinate
ystarting y coordinate

Definition at line 89 of file map_generator.c.

89 {
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}
int is_valid_cell(int x, int y)
Check if a cell is a valid cell to visit (in bounds and not visited)
void shuffle(vector2d_t *dir, int n)
Shuffle array using Fisher-Yates algorithm.
void carve_passages(int x, int y)
Recursive backtracking algorithm to generate map (based on dfs)
2-dimensional vector struct
Definition common.h:164

◆ check_neighboring_floors()

int check_neighboring_floors ( int x,
int y,
int neighbor_directions[4] )

Count neighboring floor cells.

Parameters
xx coordinate of the cell
yy coordinate of the cell
neighbor_directionsbool array to store on which sides the neighbors are
Returns
number of neighboring floor cells

Definition at line 124 of file map_generator.c.

124 {
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}

◆ generate_map()

void generate_map ( )

Generate the map and populate it with keys, enemies, and the exit.

Definition at line 317 of file map_generator.c.

317 {
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 initialize_map()
Initialize the map with walls.
void place_exit(int start_edge)
Place the exit on a random edge of the map, ensuring there's a path to it.
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 set_start_position(int start_edge, int *start_x, int *start_y)
Set the start position on the 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
void populate_map()
@breif Populates the map with a key, enemies, and fountains

◆ generate_maze()

void generate_maze ( int start_x,
int start_y )

Generate a new maze using recursive backtracking.

Parameters
start_xStarting x coordinate
start_yStarting y coordinate

Definition at line 228 of file map_generator.c.

228 {
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}
void add_loops(int num_loops)
Add loops to the map by knocking down some walls.

◆ initialize_map()

void initialize_map ( )

Initialize the map with walls.

Definition at line 212 of file map_generator.c.

212 {
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}

◆ is_in_bounds()

int is_in_bounds ( int x,
int y )

Check if cell is within bounds of the map.

Parameters
xx coordinate of the cell
yy coordinate of the cell
Returns
1 if in bounds, 0 otherwise

Definition at line 47 of file map_generator.c.

47 {
48 return x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT;
49}

◆ is_valid_cell()

int is_valid_cell ( int x,
int y )

Check if a cell is a valid cell to visit (in bounds and not visited)

Parameters
xx coordinate of the cell
yy coordinate of the cell
Returns
1 if valid, 0 otherwise

Definition at line 57 of file map_generator.c.

57 {
58 return is_in_bounds(x, y) && !visited[x][y];
59}
int is_in_bounds(int x, int y)
Check if cell is within bounds of the map.

◆ make_exit_into_start()

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.

Parameters
start_xpointer to the x coordinate of the start position
start_ypointer to the y coordinate of the start position

Definition at line 285 of file map_generator.c.

285 {
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}
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

◆ place_exit()

void place_exit ( int start_edge)

Place the exit on a random edge of the map, ensuring there's a path to it.

Parameters
start_edgethe edge from which the player enters the map, the exit cannot be on this edge

Definition at line 174 of file map_generator.c.

174 {
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}
int validate_exit_position(int exit_e, int x, int y)
Check if the exit position is adjacent to a floor cell.

◆ set_start_position()

void set_start_position ( int start_edge,
int * start_x,
int * start_y )

Set the start position on the map.

Parameters
start_edgethe edge from which the player enters the map
start_xpointer to the x coordinate of the start position
start_ypointer to the y coordinate of the start position

Definition at line 252 of file map_generator.c.

252 {
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}

◆ shuffle()

void shuffle ( vector2d_t * dir,
int n )

Shuffle array using Fisher-Yates algorithm.

This function randomly shuffles the elements of the given array of directions.

Parameters
dirArray of directions to be shuffled.
nSize of the array.

Definition at line 32 of file map_generator.c.

32 {
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}

◆ validate_exit_position()

int validate_exit_position ( int exit_e,
int x,
int y )

Check if the exit position is adjacent to a floor cell.

Parameters
exit_ethe edge of the exit (TOP, BOTTOM, LEFT, RIGHT)
xx coordinate of the exit
yy coordinate of the exit
Returns
1 if valid, 0 otherwise

Definition at line 68 of file map_generator.c.

68 {
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}

Variable Documentation

◆ exit_edge

int exit_edge = 0

Definition at line 19 of file map_generator.c.

◆ exit_x

int exit_x = 0

Definition at line 20 of file map_generator.c.

◆ exit_y

int exit_y = 0

Definition at line 21 of file map_generator.c.

◆ visited

int visited[WIDTH][HEIGHT]

Definition at line 17 of file map_generator.c.