From e3690eb85a9456cc1f3ccda751ae7d9fdf2d3b03 Mon Sep 17 00:00:00 2001 From: Hampus Date: Tue, 4 Jan 2022 18:55:51 +0100 Subject: refactor: improve even more --- src/grid.c | 55 +++++++++++++++++++ src/grid.h | 59 +++++++++++++++++++++ src/maze.c | 145 +++++++++++++++++++-------------------------------- src/maze.h | 28 ++-------- src/mazerator.c | 118 ++++++++++++++++++++--------------------- src/position.c | 9 ++++ src/position.h | 13 +++++ src/position_stack.c | 7 +-- src/position_stack.h | 12 ++--- src/utils.c | 54 ++++++++++++++++--- src/utils.h | 22 ++++++++ 11 files changed, 330 insertions(+), 192 deletions(-) create mode 100644 src/grid.c create mode 100644 src/grid.h create mode 100644 src/position.c create mode 100644 src/position.h (limited to 'src') diff --git a/src/grid.c b/src/grid.c new file mode 100644 index 0000000..7248087 --- /dev/null +++ b/src/grid.c @@ -0,0 +1,55 @@ +#include "grid.h" +#include "utils.h" +#include + +Grid grid_create(unsigned int width, unsigned int height, char *fill) +{ + unsigned int mem_height = height * sizeof(char **); + unsigned int mem_width = width * sizeof(char *); + + Dimensions dimens = {.width = width, .height = height}; + + Grid grid = {.grid = malloc_s(mem_height), .dimens = dimens}; + + // Fill the grid + for (unsigned int y = 0; y < height; y++) + { + grid.grid[y] = malloc_s(mem_width); + + for (unsigned int x = 0; x < width; x++) + grid.grid[y][x] = fill; + } + + return grid; +} + +char *grid_get(Grid grid, Position pos) +{ + return grid.grid[pos.y][pos.x]; +} + +void grid_set(Grid grid, Position pos, char *value) +{ + grid.grid[pos.y][pos.x] = value; +} + +void grid_print(Grid grid) +{ + for (unsigned int y = 0; y < grid.dimens.height; y++) + { + for (unsigned int x = 0; x < grid.dimens.width; x++) + printf("%s", grid.grid[y][x]); + + printf("\n"); + } +} + +void grid_destroy(Grid grid) +{ + // Deallocate the memory of the grid + for (unsigned int y = 0; y < grid.dimens.height; y++) + free(grid.grid[y]); + + free(grid.grid); +} + diff --git a/src/grid.h b/src/grid.h new file mode 100644 index 0000000..65fa01a --- /dev/null +++ b/src/grid.h @@ -0,0 +1,59 @@ +#ifndef GRID_H +#define GRID_H + +#include "position.h" + +typedef struct Dimensions +{ + unsigned int width; + unsigned int height; +} Dimensions; + +typedef struct Grid +{ + char ***grid; + Dimensions dimens; +} Grid; + +/** + * Returns a grid. + * + * @param width The grid width + * @param height The grid height + * @param fill A string to fill the new grid with + */ +Grid grid_create(unsigned int width, unsigned int height, char *fill); + +/* + * Returns a value from a position in a grid. + * + * @param grid A grid + * @param pos A grid position + */ +char *grid_get(Grid grid, Position pos); + +/* + * Sets the value of a position in a grid. + * + * @param grid A grid + * @param pos A grid position + * @param value A new value + */ +void grid_set(Grid grid, Position pos, char *value); + +/** + * Prints a grid. + * + * @param grid A grid + */ +void grid_print(Grid grid); + +/** + * Destroys a grid. + * + * @param grid A grid + */ +void grid_destroy(Grid grid); + +#endif + diff --git a/src/maze.c b/src/maze.c index ec627b9..2097906 100644 --- a/src/maze.c +++ b/src/maze.c @@ -1,124 +1,89 @@ #include "maze.h" +#include "grid.h" +#include "position.h" #include #include #include -/** - * Returns a filled grid. - * - * @param width The width of the new grid - * @param height The height of the new grid - * @param fill A string to fill the new grid with - */ -char ***create_grid(int width, int height, char *fill) +int is_pos_empty(Grid grid, Position pos) { - char ***grid; - - // Fill the grid - grid = malloc(height * sizeof(char **)); - for (int y = 0; y < height; y++) - { - grid[y] = malloc(width * sizeof(char *)); - - for (int x = 0; x < width; x++) - { - grid[y][x] = fill; - } - } - - return grid; + return strcmp(grid_get(grid, pos), " ") != 0; } -Maze maze_create(Dimensions dimens, char *wall) +void add_neighbour(Position neighbours[], uint8_t *neighbour_cnt, Position neighbour_pos) { - Maze maze; - - maze.dimens = dimens; - - Dimensions full_dimens = {.width = dimens.width * 2 + 1, - .height = dimens.height * 2 + 1}; + neighbours[*neighbour_cnt] = neighbour_pos; + (*neighbour_cnt)++; - maze.full_dimens = full_dimens; - - maze.grid = create_grid(full_dimens.width, full_dimens.height, wall); - - return maze; } -void maze_destroy(Maze *maze) +void get_neighbours(Grid grid, Position pos, Position neighbours[], uint8_t *neighbour_cnt) { - // Deallocate the memory of the maze's grid - for (int y = 0; y < maze->full_dimens.height; y++) + if (pos.y != 1) { - free(maze->grid[y]); - } - free(maze->grid); -} + Position pos_down = position_create(pos.x, pos.y - 2); -void get_neighbours(Maze *maze, Position pos, Position neighbours[3], int *neighbour_cnt) -{ - if (pos.y != 0 && strcmp(maze->grid[pos.y * 2 - 1][pos.x * 2 + 1], " ") != 0) - { - Position down_neighbour_pos = {.x = pos.x, .y = pos.y - 1}; - neighbours[*neighbour_cnt] = down_neighbour_pos; - (*neighbour_cnt)++; + if (is_pos_empty(grid, pos_down)) + add_neighbour(neighbours, neighbour_cnt, pos_down); } - if (pos.y != maze->dimens.height - 1 && - strcmp(maze->grid[(pos.y + 1) * 2 + 1][pos.x * 2 + 1], " ") != 0) + + if (pos.y != grid.dimens.height - 2) { - Position up_neighbour_pos = {.x = pos.x, .y = pos.y + 1}; - neighbours[*neighbour_cnt] = up_neighbour_pos; - (*neighbour_cnt)++; + Position pos_up = position_create(pos.x, pos.y + 2); + + if (is_pos_empty(grid, pos_up)) + add_neighbour(neighbours, neighbour_cnt, pos_up); } - if (pos.x != 0 && strcmp(maze->grid[pos.y * 2 + 1][pos.x * 2 - 1], " ") != 0) + + if (pos.x != 1) { - Position left_neighbour_pos = {.x = pos.x - 1, .y = pos.y}; - neighbours[*neighbour_cnt] = left_neighbour_pos; - (*neighbour_cnt)++; + Position pos_left = position_create(pos.x - 2, pos.y); + + if (is_pos_empty(grid, pos_left)) + add_neighbour(neighbours, neighbour_cnt, pos_left); } - if (pos.x != maze->dimens.width - 1 && - strcmp(maze->grid[pos.y * 2 + 1][(pos.x + 1) * 2 + 1], " ") != 0) + + if (pos.x != grid.dimens.width - 2) { - Position right_neighbour_pos = {.x = pos.x + 1, .y = pos.y}; - neighbours[*neighbour_cnt] = right_neighbour_pos; - (*neighbour_cnt)++; + Position pos_right = position_create(pos.x + 2, pos.y); + + if (is_pos_empty(grid, pos_right)) + add_neighbour(neighbours, neighbour_cnt, pos_right); } } -int is_whole_maze_visited(Maze *maze, int visited_pos_cnt) +unsigned int get_maze_size(Grid grid) { - if (visited_pos_cnt == (maze->dimens.height * maze->dimens.width) - 1) - { - return 1; - } + return ((grid.dimens.width - 1) / 2) * ((grid.dimens.height - 1) / 2); +} - return 0; +int is_whole_maze_visited(Grid grid, unsigned int visited_pos_cnt) +{ + return visited_pos_cnt == get_maze_size(grid) - 1; } -void maze_excavate(Maze *maze, Position start_pos) +void grid_to_maze(Grid grid, Position start_pos) { - PositionStack *path = pos_stack_create(maze->dimens.width * maze->dimens.height); + PositionStack *path = pos_stack_create(get_maze_size(grid)); pos_stack_push(path, start_pos); - int visited_pos_cnt = 0; + unsigned int visited_pos_cnt = 0; while (1) { Position pos = pos_stack_peek(path); - maze->grid[pos.y * 2 + 1][pos.x * 2 + 1] = " "; + grid_set(grid, pos, " "); - Position neighbours[3] = {}; - int neighbour_cnt = 0; + Position neighbours[3]; + uint8_t neighbour_cnt = 0; - get_neighbours(maze, pos, neighbours, &neighbour_cnt); + get_neighbours(grid, pos, neighbours, &neighbour_cnt); if (neighbour_cnt == 0) { - if (is_whole_maze_visited(maze, visited_pos_cnt)) - { + if (is_whole_maze_visited(grid, visited_pos_cnt)) break; - } // Go back a step pos_stack_pop(path); @@ -129,8 +94,15 @@ void maze_excavate(Maze *maze, Position start_pos) Position next_pos = neighbours[rand() % neighbour_cnt]; - maze->grid[pos.y * 2 - (pos.y - next_pos.y) + 1] - [pos.x * 2 - (pos.x - next_pos.x) + 1] = " "; + Position between_pos = position_create(pos.x, pos.y); + + if (next_pos.y != pos.y) + between_pos.y += next_pos.y > pos.y ? 1 : -1; + + if (next_pos.x != pos.x) + between_pos.x += next_pos.x > pos.x ? 1 : -1; + + grid_set(grid, between_pos, " "); pos_stack_push(path, next_pos); } @@ -138,14 +110,3 @@ void maze_excavate(Maze *maze, Position start_pos) pos_stack_destroy(path); } -void maze_print(Maze maze) -{ - for (int y = 0; y < maze.full_dimens.height; y++) - { - for (int x = 0; x < maze.full_dimens.width; x++) - { - printf("%s", maze.grid[y][x]); - } - printf("\n"); - } -} diff --git a/src/maze.h b/src/maze.h index 8c66ff7..1a3937f 100644 --- a/src/maze.h +++ b/src/maze.h @@ -2,34 +2,14 @@ #define MAZE_H #include "position_stack.h" - -typedef struct Dimensions -{ - int width; - int height; -} Dimensions; - -typedef struct Maze -{ - char ***grid; - Dimensions dimens; - Dimensions full_dimens; -} Maze; - -Maze maze_create(Dimensions dimens, char *wall); - -void maze_destroy(Maze *maze); +#include "grid.h" /** - * Excavates a maze. - * - * This is what creates the actual maze. + * Creates a maze from a grid * - * @param maze The maze to excavate + * @param grid A grid * @param start_pos Start position */ -void maze_excavate(Maze *maze, Position start_pos); - -void maze_print(Maze maze); +void grid_to_maze(Grid grid, Position start_pos); #endif diff --git a/src/mazerator.c b/src/mazerator.c index d231173..95c6fb6 100644 --- a/src/mazerator.c +++ b/src/mazerator.c @@ -1,38 +1,19 @@ #include "maze.h" +#include "grid.h" +#include "position.h" #include "utils.h" #include #include #include -void validate_number_optarg(char *optarg, int c) +void optarg_error(char arg, char *error) { - unsigned int is_invalid = 0; - - size_t error_length = 40; - char *error = malloc(error_length + 1); - - if (!is_number(optarg)) - { - is_invalid = 1; - snprintf(error, error_length, "It must be a number"); - } - else if (atoi(optarg) < 1) - { - is_invalid = 1; - snprintf(error, error_length, "It must be greater than 0"); - } - - if (is_invalid == 1) - { - printf("Error: Invalid option argument for -%c. %s\n", c, error); - free(error); - exit(1); - } - - free(error); + printf("Error: Invalid option argument for -%c. %s\n", arg, error); + exit(EXIT_FAILURE); } -void validate_start_coords(int start_x, int start_y, int width, int height) +void validate_start_coords(unsigned int start_x, unsigned int start_y, unsigned int width, + unsigned int height) { char *error_format = "Error: The %s start coordinate is not allowed to be higher than the maze's %s\n"; @@ -40,17 +21,17 @@ void validate_start_coords(int start_x, int start_y, int width, int height) if (start_x > width) { printf(error_format, "x", "width"); - exit(1); + exit(EXIT_FAILURE); } if (start_y > height) { printf(error_format, "y", "height"); - exit(1); + exit(EXIT_FAILURE); } } -void get_seed(int *seed_dst) +void get_seed(unsigned int *seed_dst) { FILE *urandom = fopen("/dev/urandom", "r"); fread(seed_dst, sizeof(*seed_dst), 1, urandom); @@ -68,40 +49,60 @@ const struct option options[] = {{"width", required_argument, NULL, 'w'}, int main(int argc, char *argv[]) { - int maze_width = 40; - int maze_height = 20; - int start_x = 0; - int start_y = 0; + unsigned int maze_width = 40U; + unsigned int maze_height = 20U; + unsigned int start_x = 1U; + unsigned int start_y = 1U; + unsigned int *seed = NULL; + char *wall = "█"; - int seed = -1; - int c; - while ((c = getopt_long(argc, argv, "w:h:W:s:x:y:", options, NULL)) != -1) + int arg; + while ((arg = getopt_long(argc, argv, "w:h:W:s:x:y:", options, NULL)) != -1) { - switch (c) + char *err = NULL; + + switch (arg) { case 'w': - validate_number_optarg(optarg, c); - maze_width = atoi(optarg); + maze_width = str_to_uint(optarg, &err); + + if (err != NULL) + optarg_error(arg, err); + break; case 'h': - validate_number_optarg(optarg, c); - maze_height = atoi(optarg); + maze_height = str_to_uint(optarg, &err); + + if (err != NULL) + optarg_error(arg, err); + break; case 'x': - validate_number_optarg(optarg, c); - start_x = atoi(optarg); + start_x = str_to_uint(optarg, &err); + + if (err != NULL) + optarg_error(arg, err); + break; case 'y': - validate_number_optarg(optarg, c); - start_y = atoi(optarg); + start_y = str_to_uint(optarg, &err); + + if (err != NULL) + optarg_error(arg, err); + break; case 'W': wall = optarg; break; case 's': - validate_number_optarg(optarg, c); - seed = atoi(optarg); + seed = malloc_s(sizeof(unsigned int *)); + + *seed = str_to_uint(optarg, &err); + + if (err != NULL) + optarg_error(arg, err); + break; case 0: printf("Usage: %s [OPTION]...\n\n" @@ -109,9 +110,9 @@ int main(int argc, char *argv[]) " -w, --width WIDTH The width of the maze (Default: 40)\n" " -h, --heigth HEIGHT The heigth of the maze (Default: 20)\n" " -x, --start-x X The x coordinate for the start position " - "(Default: 0)\n" + "(Default: 1)\n" " -y, --start-y Y The y coordinate for the start position " - "(Default: 0)\n" + "(Default: 1)\n" " -W, --wall WALL Single character used as maze walls " "(Default: '█')\n" " -s, --seed SEED The randomization seed used for maze " @@ -127,22 +128,23 @@ int main(int argc, char *argv[]) validate_start_coords(start_x, start_y, maze_width, maze_height); - if (seed == -1) + if (seed == NULL) { - get_seed(&seed); + seed = malloc_s(sizeof(unsigned int *)); + get_seed(seed); } - srand(seed); + srand(*seed); - Dimensions dimens = {.width = maze_width, .height = maze_height}; + free(seed); - Maze maze = maze_create(dimens, wall); + Position start_pos = position_create(start_x, start_y); - Position start_pos = {.x = start_x, .y = start_y}; + Grid grid = grid_create(maze_width * 2 + 1, maze_width * 2 + 1, wall); - maze_excavate(&maze, start_pos); + grid_to_maze(grid, start_pos); - maze_print(maze); + grid_print(grid); - maze_destroy(&maze); + grid_destroy(grid); } diff --git a/src/position.c b/src/position.c new file mode 100644 index 0000000..937edd5 --- /dev/null +++ b/src/position.c @@ -0,0 +1,9 @@ +#include "position.h" + +Position position_create(unsigned int x, unsigned int y) +{ + Position pos = {.x = x, .y = y}; + + return pos; +} + diff --git a/src/position.h b/src/position.h new file mode 100644 index 0000000..d1bc9eb --- /dev/null +++ b/src/position.h @@ -0,0 +1,13 @@ +#ifndef POSITION_H +#define POSITION_H + +typedef struct Position +{ + unsigned int x; + unsigned int y; +} Position; + +Position position_create(unsigned int x, unsigned int y); + +#endif + diff --git a/src/position_stack.c b/src/position_stack.c index 846d933..597ed5a 100644 --- a/src/position_stack.c +++ b/src/position_stack.c @@ -1,4 +1,5 @@ #include "position_stack.h" +#include "utils.h" #include #include @@ -19,13 +20,13 @@ void stack_error(int err) } // Creates a new stack -PositionStack *pos_stack_create(int capacity) +PositionStack *pos_stack_create(unsigned int capacity) { - PositionStack *pos_stack = malloc(sizeof(PositionStack)); + PositionStack *pos_stack = malloc_s(sizeof(PositionStack)); pos_stack->capacity = capacity; pos_stack->top = -1; - pos_stack->items = malloc(sizeof(Position) * capacity); + pos_stack->items = malloc_s(sizeof(Position) * capacity); return pos_stack; } diff --git a/src/position_stack.h b/src/position_stack.h index 83fb98c..674da23 100644 --- a/src/position_stack.h +++ b/src/position_stack.h @@ -1,23 +1,19 @@ #ifndef POSITION_STACK_H #define POSITION_STACK_H +#include "position.h" + #define STACK_ERR_OVERFLOW 0xFFF01 #define STACK_ERR_UNDERFLOW 0xFFF02 -typedef struct Position -{ - int x; - int y; -} Position; - typedef struct PositionStack { - int capacity; + unsigned int capacity; int top; Position *items; } PositionStack; -PositionStack *pos_stack_create(int capacity); +PositionStack *pos_stack_create(unsigned int capacity); void pos_stack_destroy(PositionStack *pos_stack); diff --git a/src/utils.c b/src/utils.c index 93f7c53..69ce55d 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,17 +1,57 @@ #include "utils.h" #include #include +#include +#include -/** - * Returns whether or not a string is a number. - */ int is_number(char *str) { - size_t len = strlen(str); - for (int c = 0; c < len; c++) - { + unsigned int length = strlen(str); + + for (unsigned int c = 0; c < length; c++) if (!isdigit(str[c])) return 0; - } + return 1; } + + +void *malloc_s(unsigned long amount) +{ + void *memory = malloc(amount); + + if (memory == NULL) + { + printf("Error: Memory allocation failed"); + exit(EXIT_FAILURE); + } + + return memory; +} + +unsigned int str_to_uint(char *str, char **err) +{ + if (*str == '-') + { + *err = "Not greater than 0"; + return 0; + } + + char *str_waste; + unsigned long num = strtoul(str, &str_waste, 10); + + if (strlen(str_waste) != 0) + { + *err = "Not a number"; + return 0; + } + + if (num > (unsigned long)UINT_MAX) + { + *err = "Too large"; + return 0; + } + + return (unsigned int)num; +} + diff --git a/src/utils.h b/src/utils.h index a1b69e1..049e583 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,6 +1,28 @@ #ifndef UTILS_H #define UTILS_H +/** + * Returns whether or not a string is a number. + * + * @param str A string + */ int is_number(char *str); +/** + * Safely allocates memory to the heap. + * + * @param amount The amount of memory to allocate + * @returns The allocated memory. + */ +void *malloc_s(unsigned long amount); + +/** + * Converts a string to a unsigned integer. + * + * @param str A string + * @param err A error destination + * @returns A unsigned integer. + */ +unsigned int str_to_uint(char *str, char **err); + #endif -- cgit v1.2.3-18-g5258