#include #include #include #include #include #include #include #include "map.h" #include "structs.h" #include "config.h" #include "error.h" #include "priority_queue.h" #include "path.h" #include "bmp.h" /* So, TODO for now: - Allow maps to have costs - check ppq_insert() order - more info in anim() - save pathfinding to a series of BMPs - less magical values - MORE MAPS FOR THE MAP PEOPLE - Clean up unused `#include`s - Comments */ void sigint_handler(int sig) { (void)sig; /* We know it's a SIGINT */ endwin(); printf("Received SIGINT\n"); exit(1); } void initialize_colors(void) { start_color(); use_default_colors(); 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(START_COLOR, -1, COLOR_RED); init_pair(PATH_COLOR, COLOR_YELLOW, COLOR_RED); init_pair(FRONTIER_COLOR, COLOR_BLUE, COLOR_BLUE); init_pair(CURSOR_COLOR, -1, COLOR_RED); init_pair(WALL_TEXT_COLOR, COLOR_BLACK, COLOR_WHITE); } void init_ncurses(void) { initscr(); /* Initialize the ncurses screen */ cbreak(); /* Process input one char at a time */ curs_set(0); /* Hide the cursor */ noecho(); /* Don't echo characters */ keypad(stdscr, TRUE); initialize_colors(); } int main(int argc, char **argv) { Position start_pos, end_pos; size_t width, height; Map map = NULL; Path (*path_func)(int, Map, size_t, size_t, Position, Position, char **, char) = &astar_path; size_t mwidth = 20; /* Maze width */ size_t mheight = 10; /* Maze height */ char is_maze = 0; char bmp_only = 0; char *bmp_filename = NULL; char anim = 0; int dirs = 8; srand(time(NULL)); /* Handle args. * Currently supported: * -a to animate the pathfinding (get input after each step) * -d for Dijkstra * -m {width}x{height} to give a maze of given size * -f {filename} to load a map from the filename * -b {filename} to just generate a bitmap, no interface * -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, ":adm:f:b:48")) != -1) { switch (opt) { case 'a': anim = 1; break; case 'd': path_func = &dijkstra_path; break; 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, rand()); start_pos.x = 0; start_pos.y = 0; height = mheight * 2 - 1; width = mwidth * 2 - 1; end_pos.x = width - 1; end_pos.y = height - 1; break; case 'f': map = file_plaintext_map(optarg, &width, &height, &start_pos, &end_pos); break; case 'b': bmp_only = 1; bmp_filename = optarg; break; case '4': dirs = 4; break; case '8': dirs = 8; break; case '?': error("Unknown option: %c\n", optopt); case ':': error("Option %c requires an argument\n", optopt); } } if (map == NULL) { /* If no map were given */ is_maze = 1; if (argc == 4) { /* maze size as argv[2] and [3] */ mwidth = atoi(argv[2]); mheight = atoi(argv[3]); } map = rbt_maze_map(mwidth, mheight, rand()); start_pos.x = 0; start_pos.y = 0; height = mheight * 2 - 1; width = mwidth * 2 - 1; end_pos.x = width - 1; end_pos.y = height - 1; } signal(SIGINT, sigint_handler); if (!bmp_only) { init_ncurses(); } char **visited = visited_new(width, height); Path path = NULL; path = path_func(dirs, map, width, height, start_pos, end_pos, visited, anim); if (bmp_only) { map_to_bmp(map, width, height, start_pos, end_pos, path, visited, bmp_filename); printf("Wrote the bmp to %s\n", bmp_filename); exit(0); } while (1) { draw_map(map, width, height, start_pos, end_pos, NULL, path, visited, NULL); int c = getch(); /* TODO: add a keybinding to calculate the path again, or maybe do it on 'a' */ /* TODO: add a keybinding to load a map from a file */ switch (c) { case 'h': map_offset_x += 2; break; case 'l': map_offset_x -= 2; break; case 'j': map_offset_y -= 1; break; case 'k': map_offset_y += 1; break; case 'H': clear(); map_offset_x += 20; break; case 'L': clear(); map_offset_x -= 20; break; case 'J': clear(); map_offset_y -= 10; break; case 'K': clear(); map_offset_y += 10; break; case 'z': clear(); map_offset_x = 2; map_offset_y = 1; break; /* Move to top left corner */ case 'x': clear(); map_offset_x = - width * 2 + COLS - 2; map_offset_y = - height + LINES - 2; break; /* Move to bottom right corner */ case 'g': switch (getch()) { case 's': clear(); map_offset_x = -start_pos.x * 2 + COLS/2; map_offset_y = -start_pos.y + LINES/2; break; case 'e': clear(); map_offset_x = -end_pos.x * 2 + COLS/2; map_offset_y = -end_pos.y + LINES/2; break; } break; case 'a': anim = !anim; break; case 'd': if (path_func == astar_path) { set_message("Dijkstra"); path_func = &dijkstra_path; } else { set_message("A*"); path_func = &astar_path; }; path_free(path, height); path = path_func(dirs, map, width, height, start_pos, end_pos, visited, anim); break; case 'y': case 'o': case 'u': case 'i': if(is_maze) { switch (c) { case 'y': if (mwidth > 5) mwidth -= 1; break; case 'o': mwidth += 1; break; case 'u': mheight += 1; break; case 'i': if (mheight > 2) 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 = path_func(dirs, map, width, height, start_pos, end_pos, visited, anim); } break; case 's': curs_set(2); /* Show the cursor */ echo(); /* Echo characters */ set_message(FILENAME_PROMPT); print_message(height); char filename[FILENAME_BUF_SIZE] = "out.bmp"; mvgetnstr(height + map_offset_y + 1, map_offset_x - 2 + sizeof(FILENAME_PROMPT), filename, FILENAME_BUF_SIZE - 1); map_to_bmp(map, width, height, start_pos, end_pos, path, visited, filename); curs_set(0); /* Hide the cursor */ noecho(); /* Don't echo characters */ getch(); break; case 'n': if (is_maze) { map_free(map, height); map = rbt_maze_map(mwidth, mheight, rand()); path_free(path, height); path = path_func(dirs, map, width, height, start_pos, end_pos, visited, anim); } break; case 'e': is_maze = 0; visited_free(visited, height); path_free(path, height); map_editor(&map, &width, &height, &start_pos, &end_pos); wrefresh(curscr); visited = visited_new(width, height); path = path_func(dirs, map, width, height, start_pos, end_pos, visited, anim); break; case KEY_RESIZE: clear(); break; case 'q': map_free(map, height); path_free(path, height); endwin(); return 0; } } endwin(); return 0; }