diff --git a/CMakeLists.txt b/CMakeLists.txt index 06b2797..ffc759b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,10 +20,11 @@ endif() set(CMAKE_C_STANDARD 11) # Requires C11 standard +add_library(stack stack.c) add_library(maze_gen maze.c) add_executable(ray_maze main.c) -target_link_libraries(ray_maze raylib maze_gen) +target_link_libraries(ray_maze raylib maze_gen stack) # Checks if OSX and links appropriate frameworks (only required on MacOS) if (APPLE) diff --git a/main.c b/main.c index edd8ffa..4e2395c 100644 --- a/main.c +++ b/main.c @@ -44,23 +44,36 @@ int main(void) { // Generate maze; const int sideLength = 30; int *box_data = (int *) calloc(sideLength*sideLength, sizeof(int)); - maze_t box_maze = {box_data,sideLength,sideLength}; - carve_maze(&box_maze); + maze_t m = {box_data, sideLength, sideLength}; + fill_maze(&m); + carve_maze(&m); + unsigned int frames = 0; + size_t initial_cap = (m.width * m.height) / 4; + arr_stack_t *stack = new_stack(sizeof(pos_t), initial_cap); + pos_t current = {0, 0}; while (!WindowShouldClose()) { BeginDrawing(); { + if (frames % 2 == 0 && stack->len > 0) { + carve_step(&m, stack, ¤t); + } ClearBackground(RAYWHITE); DrawText("RayMaze!", screen_width - 30, 30, 20, LIGHTGRAY); - draw_maze(&box_maze, 10, 10, 9); + draw_maze(&m, 10, 10, 9); + DrawRectangle(10 + (9 * current.ox) + 3, 10 + (9 * current.oy) + 3, 3, 3, MAGENTA); if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) { - carve_maze(&box_maze); + current = (pos_t) {0, 0}; + fill_maze(&m); + push(stack, ¤t); } + frames++; } EndDrawing(); } CloseWindow(); free(box_data); + free_stack(stack); return 0; } diff --git a/maze.c b/maze.c index d4e9249..194ea10 100644 --- a/maze.c +++ b/maze.c @@ -13,6 +13,7 @@ Credit to Jamis Buck for teaching me this wonderful algorithm. #include #include #include "maze.h" +#include "stack.h" // Fill a maze with walls, i.e. NONE values void fill_maze(struct maze *m) { @@ -24,49 +25,39 @@ void fill_maze(struct maze *m) { } // get a random neighbour! -struct neighbour get_neighbour(int ox, int oy, struct maze *m) { - // check out-of-bounds - bool can_N, can_E, can_S, can_W; - can_N = (oy - 1) >= 0; - can_E = (ox + 1) < m->width; - can_S = (oy + 1) < m->height; - can_W = (ox - 1) >= 0; - - struct neighbour neighbours[4]; +neighbour_t get_neighbour(int ox, int oy, struct maze *m) { + typedef struct { + int direction; + char offset_x; + char offset_y; + bool isNotOOB; + } direction_t; + + direction_t directions[4] = { + N, 0, -1, (oy - 1) >= 0, + E, 1, 0, (ox + 1) < m->width, + S, 0, 1, (oy + 1) < m->height, + W, -1, 0, (ox - 1) >= 0, + }; + + neighbour_t neighbours[4]; int count = 0; - // North: (0, -1) - if (can_N) { - int *data = &m->data[(m->width * (oy - 1)) + (ox)]; - if (*data == NONE) { - neighbours[count] = (struct neighbour) {0, -1, N, data}; - count++; - } - } - // East: (+1, 0) - if (can_E) { - int *data = &m->data[(m->width * (oy)) + (ox + 1)]; - if (*data == NONE) { - neighbours[count] = (struct neighbour) {1, 0, E, data}; - count++; - } - } - // South: (0, +1) - if (can_S) { - int *data = &m->data[(m->width * (oy + 1)) + (ox)]; - if (*data == NONE) { - neighbours[count] = (struct neighbour) {0, 1, S, data}; - count++; - } - } - // West: (-1, 0) - if (can_W) { - int *data = &m->data[(m->width * (oy)) + (ox - 1)]; - if (*data == NONE) { - neighbours[count] = (struct neighbour) {-1, 0, W, data}; - count++; + for (int i = 0; i < 4; i++) { + const direction_t current = directions[i]; + if (current.isNotOOB) { + int *data = &m->data[(m->width * (oy + current.offset_y)) + (ox + current.offset_x)]; + if (*data == NONE) { + neighbours[count] = (neighbour_t) { + current.offset_x, + current.offset_y, + current.direction, + data + }; + count++; + } } } - struct neighbour out = {.ox=0, .oy=0}; + neighbour_t out = {.ox=0, .oy=0}; // No valid neighbours. if (count == 0) { // Can't keep going, return! @@ -77,8 +68,7 @@ struct neighbour get_neighbour(int ox, int oy, struct maze *m) { // Honestly, I've stopped caring. // PRO TIP: MAKE SURE YOU DON'T INDEX BY NEGATIVE VALUES. // THIS BUG TOOK AWAY VALUABLE HOURS OF MY LIFE. - int rand = arc4random(); - out = neighbours[rand % count]; + out = neighbours[arc4random() % count]; return out; } @@ -99,6 +89,10 @@ static const int opposite[N+1] = { S }; +typedef struct {int ox; int oy;} pos_t; + +void carve_step(struct maze *m, arr_stack_t *stack, pos_t *current); + void carve(int ox, int oy, struct maze *m) { int *current = &m->data[(m->width * oy) + ox]; while (true) { @@ -122,10 +116,61 @@ void carve(int ox, int oy, struct maze *m) { } } + +void carve_iter(int ox, int oy, struct maze *m) { + size_t initial_cap = (m->width * m->height) / 4; + arr_stack_t *stack = new_stack(sizeof(pos_t), initial_cap); + pos_t current = {ox, oy}; + push(stack, ¤t); + while (stack->len) { + carve_step(m, stack, ¤t); + } + free_stack(stack); +} + +void carve_step(struct maze *m, arr_stack_t *stack, pos_t *current) { + int *cell = &m->data[(m->width * (*current).oy) + (*current).ox]; + neighbour_t nb = get_neighbour((*current).ox, (*current).oy, m); + // no neighbours here, move on + if (nb.ox != 0 || nb.oy != 0) { + // Check if there's any path to the neighbour + // If not: + if (!(*cell & nb.direction) && *nb.data == NONE) { + *nb.data |= opposite[nb.direction]; // carved! + // carve self! + *cell |= nb.direction; + // recur! + push(stack, &((pos_t) {current->ox + nb.ox, current->oy + nb.oy})); + peek(stack, current); + } + } else { + pop(stack, current); + } +} +void carve_step_fishbone(struct maze *m, arr_stack_t *stack, pos_t *current) { + int *cell = &m->data[(m->width * (*current).oy) + (*current).ox]; + neighbour_t nb = get_neighbour((*current).ox, (*current).oy, m); + // no neighbours here, move on + if (nb.ox != 0 || nb.oy != 0) { + // Check if there's any path to the neighbour + // If not: + if (!(*cell & nb.direction) && *nb.data == NONE) { + *nb.data |= opposite[nb.direction]; // carved! + // carve self! + *cell |= nb.direction; + // recur! + push(stack, &((pos_t) {current->ox + nb.ox, current->oy + nb.oy})); +// peek(stack, current); + } + } else { + pop(stack, current); + } +} + void carve_maze(maze_t *m) { // Fill the maze, we'll be carving paths out. fill_maze(m); - carve(0, 0, m); + carve_iter(0, 0, m); } //int main(int argc, char **argv) { diff --git a/stack.c b/stack.c new file mode 100644 index 0000000..5bab4f9 --- /dev/null +++ b/stack.c @@ -0,0 +1,59 @@ +// +// Created by Nokko on 2022-04-09. +// + +#include +#include + +typedef struct { + size_t len; // # elements + size_t cap; // capacity + size_t element_size; + void *data; +} arr_stack_t; + +arr_stack_t *new_stack(size_t element_size, size_t initial_cap) { + arr_stack_t *stack = (arr_stack_t *) calloc(1,sizeof (arr_stack_t)); + if (stack == NULL) return NULL; + stack->data = malloc(element_size * initial_cap); + if (stack->data == NULL) { + free(stack); + return NULL; + } + stack->element_size = element_size; + stack->cap = initial_cap; + return stack; +} + +void free_stack(arr_stack_t *stack) { + free(stack->data); + free(stack); +} + +void realloc_stack(arr_stack_t *stack) { + size_t new_cap = (stack->element_size * stack->len) * 2; + void *new_data = realloc(stack->data, new_cap); + stack->data = new_data; + stack->cap = new_cap; +} + +size_t push(arr_stack_t *stack, void *data) { + stack->len += 1; + if (stack->len > stack->cap) { + realloc_stack(stack); + } + memcpy(stack->data + (stack->len - 1) * stack->element_size, data, stack->element_size); + return stack->len; +} + +size_t peek(arr_stack_t *stack, void *dest) { + if ((stack->len - 1) < 0) return 0; + memcpy(dest, stack->data + (stack->len - 1) * stack->element_size, stack->element_size); + return stack->len; +} + +size_t pop(arr_stack_t *stack, void *dest) { + if ((stack->len - 1) < 0) return 0; + memcpy(dest, stack->data + (--stack->len) * stack->element_size, stack->element_size); + return stack->len; +} \ No newline at end of file diff --git a/stack.h b/stack.h new file mode 100644 index 0000000..3e8e151 --- /dev/null +++ b/stack.h @@ -0,0 +1,28 @@ +// +// Created by Nokko on 2022-04-09. +// + +#include +#ifndef RAY_MAZE_STACK_H +#define RAY_MAZE_STACK_H + +typedef struct { + size_t len; // # elements + size_t cap; // capacity + size_t element_size; + void *data; +} arr_stack_t; + +arr_stack_t *new_stack(size_t element_size, size_t initial_cap); + +void free_stack(arr_stack_t *stack); + +void realloc_stack(arr_stack_t *stack); + +size_t push(arr_stack_t *stack, void *data); + +size_t pop(arr_stack_t *stack, void *dest); + +size_t peek(arr_stack_t *stack, void *dest); + +#endif //RAY_MAZE_STACK_H