From 1f0dd604952e39d030367b2bbf45b69f8c63cc5b Mon Sep 17 00:00:00 2001 From: Kirill Petrashin Date: Sat, 28 Mar 2026 19:33:49 +0300 Subject: Allow resizing the map + some other stuff --- main.c | 51 +++++++++++++++++++++++++++++++++++++++------------ map.c | 13 ++++++++----- map.h | 6 +++--- path.c | 42 +++++++++++++++++++++++++++++++++++------- path.h | 9 +++++++-- stack.c | 2 ++ 6 files changed, 94 insertions(+), 29 deletions(-) diff --git a/main.c b/main.c index aeceedc..442e6eb 100644 --- a/main.c +++ b/main.c @@ -14,14 +14,14 @@ #include "path.h" /* So, TODO for now: - - Keybinds to increase map size + - Generate an image?? - Allow maps to have costs - Implement greedy-best-first algorithm (4 and 8 dir) - Implement Dijkstra algorithm (4 and 8 dir) - Implement the A* algorithm (4 and 8 dir) - MORE MAPS FOR THE MAP PEOPLE - Implement controls (to change maps, move start/goal, etc.) - - Clean up unused `#include`s + - Clean up unused `#include`s - mouse controls to edit map, drag start and end around */ void sigint_handler(int sig) { @@ -37,7 +37,7 @@ void initialize_colors(void) { init_pair(EMPTY_COLOR, COLOR_BLACK, -1); init_pair(VISITED_COLOR, COLOR_GREEN, COLOR_GREEN); init_pair(GOAL_COLOR, -1, COLOR_RED); - init_pair(WALL_COLOR, COLOR_WHITE, COLOR_WHITE); + init_pair(WALL_COLOR, COLOR_WHITE, COLOR_WHITE); init_pair(START_COLOR, -1, COLOR_RED); init_pair(PATH_COLOR, COLOR_RED, COLOR_RED); init_pair(FRONTIER_COLOR, COLOR_BLUE, COLOR_BLUE); @@ -65,7 +65,7 @@ int main(int argc, char **argv) { int dirs = 8; srand(time(NULL)); - + /* Handle args. * Currently supported: * -a to animate the pathfinding (get input after each step) @@ -74,6 +74,7 @@ int main(int argc, char **argv) { * -4 for four directions * -8 for eight directions */ /* TODO: Argument to choose the algorithm */ + /* TODO: Argument to set seed */ int opt; while ((opt = getopt(argc, argv, ":am:f:48")) != -1) { switch (opt) { @@ -81,7 +82,7 @@ int main(int argc, char **argv) { case 'm': if (sscanf(optarg, "%zux%zu", &mwidth, &mheight) != 2) error("Wrong maze size string in argument\n"); is_maze = 1; - map = rbt_maze_map(mwidth, mheight, (unsigned int) time(NULL)); + map = rbt_maze_map(mwidth, mheight, rand()); start_pos.x = 0; start_pos.y = 0; height = mheight * 2 - 1; @@ -122,7 +123,7 @@ int main(int argc, char **argv) { draw_map(map, width, height, offset_x, offset_y, start_pos, end_pos, NULL, NULL, NULL, NULL); //print_map_out(map, width, height); - char visited[height][width]; + char **visited = visited_new(width, height); Path path = NULL; path = breadth_first_search_path(dirs, map, width, height, start_pos, end_pos, visited, anim); @@ -130,13 +131,39 @@ int main(int argc, char **argv) { draw_map(map, width, height, offset_x, offset_y, start_pos, end_pos, NULL, path, visited, NULL); char c = getch(); switch (c) { - case 'h': offset_x -= 2; break; - case 'l': offset_x += 2; break; - case 'j': offset_y += 1; break; - case 'k': offset_y -= 1; break; + /* FIXME: scroll the view not the content or whatever */ + case 'h': offset_x += 2; break; + case 'l': offset_x -= 2; break; + case 'j': offset_y -= 1; break; + case 'k': offset_y += 1; break; case 'a': anim = !anim; break; - case 'n': - if (is_maze) { + case 'y': + case 'o': + case 'u': + case 'i': + if(is_maze) { + switch (c) { + case 'y': mwidth -= 1; break; + case 'o': mwidth += 1; break; + case 'u': mheight += 1; break; + case 'i': mheight -= 1; break; + } + map_free(map, height); + visited_free(visited, height); + path_free(path, height); + + height = mheight * 2 - 1; + width = mwidth * 2 - 1; + end_pos.x = width - 1; + end_pos.y = height - 1; + + map = rbt_maze_map(mwidth, mheight, rand()); + visited = visited_new(width, height); + path = breadth_first_search_path(dirs, map, width, height, start_pos, end_pos, visited, anim); + } + break; + case 'n': + if (is_maze) { map_free(map, height); map = rbt_maze_map(mwidth, mheight, rand()); path_free(path, height); diff --git a/map.c b/map.c index d527cec..f26d4b1 100644 --- a/map.c +++ b/map.c @@ -27,7 +27,7 @@ Map empty_map(size_t width, size_t height) { /* When we allow maps to have costs, these two neighbours functions will have to use the instead of COST_* defines */ /* TODO: maybe merge them, add `dirs` parameter, like we do in path.c? */ unsigned int neighbours_4dir(Position neighbour_array[4], size_t cost_array[4], Position pos, size_t width, size_t height, \ - char visited[height][width]) { + char **visited) { size_t cur = 0; if (pos.x > 0 && !visited[pos.y][pos.x - 1]) { neighbour_array[cur].x = pos.x - 1; @@ -58,7 +58,7 @@ unsigned int neighbours_4dir(Position neighbour_array[4], size_t cost_array[4], } unsigned int neighbours_8dir(Position neighbour_array[8], size_t cost_array[8], Position pos, size_t width, size_t height, \ - char visited[height][width]) { + char **visited) { size_t cur = 0; if (pos.x > 0 && !visited[pos.y][pos.x - 1]) { neighbour_array[cur].x = pos.x - 1; @@ -133,8 +133,8 @@ Map rbt_maze_map(size_t width, size_t height, unsigned int seed) { Position beginning_cell = {width - 1, height - 1}; - char visited[height][width]; /* 1 if visited, 0 if not */ - memset(visited, 0, sizeof(char) * width * height); + char **visited = visited_new(width, height); + visited_clear(visited, width, height); PositionStack ps = ps_new(); if (ps_push(&ps, beginning_cell) == -1) error("Is the STACK_SIZE zero?"); @@ -157,6 +157,7 @@ Map rbt_maze_map(size_t width, size_t height, unsigned int seed) { } } while (ps_peek(ps).x != beginning_cell.x || ps_peek(ps).y != beginning_cell.y) ; + visited_free(visited, height); return map; } @@ -208,7 +209,7 @@ Map file_plaintext_map(char *filename, size_t *width, size_t *height, Position * /* TODO: so many fucking arguments lmao. Break it down into several functions? */ /* TODO: draw the start and goal with no background, allowing us to see the frontier/path/whatever's underneath. I think attr_get() will be useful */ -void draw_map(Map map, size_t width, size_t height, int offset_x, int offset_y, Position start, Position goal, Position *cursor, Path path, char visited[height][width], PositionPQ *frontier) { +void draw_map(Map map, size_t width, size_t height, int offset_x, int offset_y, Position start, Position goal, Position *cursor, Path path, char **visited, PositionPQ *frontier) { /* I think it flickers less when we do that */ wnoutrefresh(stdscr); @@ -223,6 +224,8 @@ void draw_map(Map map, size_t width, size_t height, int offset_x, int offset_y, mvaddch(i + offset_y, DRAW_MAP_OFFSET_X - 1 + offset_x - 2, ' '); mvaddch(i + offset_y, DRAW_MAP_OFFSET_X + width * 2 + offset_x + 2, ' '); mvaddch(i + offset_y, DRAW_MAP_OFFSET_X + width * 2 + 1 + offset_x + 2, ' '); + mvaddch(i + offset_y, DRAW_MAP_OFFSET_X + width * 2 + 2 + offset_x + 2, ' '); + mvaddch(i + offset_y, DRAW_MAP_OFFSET_X + width * 2 + 3 + offset_x + 2, ' '); } /* Draw the borders */ diff --git a/map.h b/map.h index e981513..c58b3fa 100644 --- a/map.h +++ b/map.h @@ -11,12 +11,12 @@ Map empty_map(size_t width, size_t height); /* Stores all the existing 4dir neighbours of pos in neighbour_array and returns their amount */ unsigned int neighbours_4dir(Position neighbour_array[4], size_t cost_array[4], Position pos, size_t width, size_t height, \ - char visited[height][width]); + char **visited); /* Stores all the existing 8dir neighbours of pos in neighbour_array and returns their amount. * Additionaly stores costs into cost_array if it's not NULL. * The cost of goint orthogonally is 10, diagonaly is 14 (sqrt(2) * 10) */ unsigned int neighbours_8dir(Position neighbour_array[8], size_t cost_array[8], Position pos, size_t width, size_t height, \ - char visited[height][width]); + char **visited); /* https://en.wikipedia.org/wiki/Maze_generation_algorithm#Randomized_depth-first_search * WARNING: width and height are not the width and height of the returned map! @@ -41,7 +41,7 @@ Map file_plaintext_map(char *filename, size_t *width, size_t *height, Position * /* Draw the map. Bet you didn't expect that. * path could be NULL to draw a map with no path. So can cursor, frontier and visited */ -void draw_map(Map map, size_t width, size_t height, int offset_x, int offset_y, Position start, Position goal, Position *cursor, Path path, char visited[height][width], PositionPQ *frontier); +void draw_map(Map map, size_t width, size_t height, int offset_x, int offset_y, Position start, Position goal, Position *cursor, Path path, char **visited, PositionPQ *frontier); /* Frees all the memory reserved for the map */ void map_free(Map map, size_t height); diff --git a/path.c b/path.c index 60f1482..64b2d95 100644 --- a/path.c +++ b/path.c @@ -10,7 +10,7 @@ #include "error.h" /* TODO: somehow get offsets back to main */ -int anim(Map map, size_t width, size_t height, Position start, Position end, Position *cur, char visited[height][width], PositionPQ *frontier) { +int anim(Map map, size_t width, size_t height, Position start, Position end, Position *cur, char **visited, PositionPQ *frontier) { static int offset_y = 0, offset_x = 0; while (1) { draw_map(map, width, height, offset_x, offset_y, start, end, cur, NULL, visited, frontier); @@ -27,9 +27,9 @@ int anim(Map map, size_t width, size_t height, Position start, Position end, Pos return 0; } /* BLOODY FUCK IT WORKS */ -Path breadth_first_search_path(int dirs, Map map, size_t width, size_t height, Position start, Position end, char visited[height][width], char should_anim) { +Path breadth_first_search_path(int dirs, Map map, size_t width, size_t height, Position start, Position end, char **visited, char should_anim) { /* The function to use to find neighbours */ - unsigned int (*neighbours)(Position[], size_t[], Position, size_t, size_t, char[height][width]) = NULL; + unsigned int (*neighbours)(Position[], size_t[], Position, size_t, size_t, char**) = NULL; switch (dirs) { case 4: neighbours = &neighbours_4dir; break; case 8: neighbours = &neighbours_8dir; break; @@ -47,7 +47,7 @@ Path breadth_first_search_path(int dirs, Map map, size_t width, size_t height, P PositionPQ *frontier = ppq_new(start, 0); - memset(visited, 0, height * width * sizeof(char)); + visited_clear(visited, width, height); while (frontier != NULL) { Position cur = ppq_pop(&frontier); @@ -91,10 +91,10 @@ Path breadth_first_search_path(int dirs, Map map, size_t width, size_t height, P } /* FIXME: THIS IS NOT YET DIJKSTRA!!! don't use for now (WIP) */ -Path dijkstra_path(int dirs, Map map, size_t width, size_t height, Position start, Position end, char visited[height][width], char should_anim) { +Path dijkstra_path(int dirs, Map map, size_t width, size_t height, Position start, Position end, char **visited, char should_anim) { return NULL; /* The function to use to find neighbours */ - unsigned int (*neighbours)(Position[], size_t[], Position, size_t, size_t, char[height][width]) = NULL; + unsigned int (*neighbours)(Position[], size_t[], Position, size_t, size_t, char**) = NULL; switch (dirs) { case 4: neighbours = &neighbours_4dir; break; case 8: neighbours = &neighbours_8dir; break; @@ -112,7 +112,7 @@ Path dijkstra_path(int dirs, Map map, size_t width, size_t height, Position star PositionPQ *frontier = ppq_new(start, 0); - memset(visited, 0, height * width * sizeof(char)); + visited_clear(visited, width, height); while (frontier != NULL) { Position cur = ppq_pop(&frontier); @@ -168,3 +168,31 @@ void path_free(Path path, size_t height) { } free(path); } + +char **visited_new(size_t width, size_t height) { + char **visited = malloc(sizeof(char*) * height); + if (visited == NULL) return NULL; + + for (size_t row = 0; row < height; row++) { + visited[row] = malloc(sizeof(char) * width); + if (visited[row] == NULL) return NULL; + memset(visited[row], 0x00, width*sizeof(char)); + } + + return visited; +} + +void visited_clear(char **visited, size_t width, size_t height) { + for (size_t row = 0; row < height; row++) { + memset(visited[row], 0x00, sizeof(char) * width); + } +} + +void visited_free(char **visited, size_t height) { + if (visited == NULL) return; + for (size_t row = 0; row < height; row++) { + free(visited[row]); + } + free(visited); + return; +} diff --git a/path.h b/path.h index 330ea3d..f9c9c75 100644 --- a/path.h +++ b/path.h @@ -5,11 +5,16 @@ #include "map.h" /* dirs can be 4 or 8 to disallow or allow diagonal movement */ -Path breadth_first_search_path(int dirs, Map map, size_t width, size_t height, Position start, Position end, char visited[height][width], char should_anim); -Path dijkstra_path(int dirs, Map map, size_t width, size_t height, Position start, Position end, char visited[height][width], char should_anim); +Path breadth_first_search_path(int dirs, Map map, size_t width, size_t height, Position start, Position end, char **visited, char should_anim); +Path dijkstra_path(int dirs, Map map, size_t width, size_t height, Position start, Position end, char **visited, char should_anim); size_t manhattan_distance(Position a, Position b); void path_free(Path path, size_t height); +/* Helper funcs for the visited array */ +char **visited_new(size_t width, size_t height); +void visited_clear(char **visited, size_t width, size_t height); +void visited_free(char **visited, size_t height); + #endif /* ASTAR_H_ */ diff --git a/stack.c b/stack.c index 1edb5c5..f0624c8 100644 --- a/stack.c +++ b/stack.c @@ -18,11 +18,13 @@ int ps_push(PositionStack *ps, Position pos) { } Position ps_pop(PositionStack *ps) { + if (ps->top == 0) error("Stack underflow\n"); ps->top -= 1; return ps->arr[ps->top]; } Position ps_peek(PositionStack ps) { + if (ps.top == 0) error("Stack underflow\n"); return ps.arr[ps.top - 1]; } -- cgit v1.2.3