#include "app/app.hpp" #include "conversion.hpp" #include "engine/vector2.hpp" #include "random_generator.hpp" #include #include #include #include #include constexpr unsigned int DEFAULT_MAZE_WIDTH = 40U; constexpr unsigned int DEFAULT_MAZE_HEIGHT = 20U; constexpr std::string_view DEFAULT_MAZE_WALL = "█"; namespace { void optarg_error(char arg, const std::string_view &error) { std::cout << "Error: Invalid option argument for -" << arg << ". " << error << std::endl; exit(EXIT_FAILURE); } /** * Returns the current optarg as a string view. */ std::string_view get_str_optarg() { return std::string_view(optarg); } /** * Returns the current optarg as a unsigned integer. * * @param arg The current command-line argument character * @param check_zero Whether or not to make sure that the result is not zero */ unsigned int get_uint_optarg(char arg, bool check_zero = false) { unsigned int num = 0; try { num = str_to_uint(get_str_optarg()); if (check_zero && num == 0) { throw "It should not be 0"; } } catch (const char *error) { optarg_error(arg, std::string_view(error)); } return num; } } // namespace constexpr std::array options = { option({"width", required_argument, nullptr, 'w'}), option({"height", required_argument, nullptr, 'h'}), option({"wall", required_argument, nullptr, 'W'}), option({"seed", required_argument, nullptr, 's'}), option({"start-x", required_argument, nullptr, 'x'}), option({"start-y", required_argument, nullptr, 'y'}), option({"help", no_argument, nullptr, 0}), option({nullptr, 0, nullptr, 0})}; int main(int argc, char *argv[]) { auto args = std::vector(argv, argv + argc); AppOptions app_options; BoundsOptions default_maze_bounds = {DEFAULT_MAZE_WIDTH, DEFAULT_MAZE_HEIGHT}; app_options.maze_bounds = std::make_shared(default_maze_bounds); app_options.wall = DEFAULT_MAZE_WALL; std::unique_ptr start_x = nullptr; std::unique_ptr start_y = nullptr; char arg = 0; while ((arg = static_cast( getopt_long(argc, argv, "w:h:W:s:x:y:", options.data(), nullptr))) != -1) { switch (arg) { case 'w': { app_options.maze_bounds->width(get_uint_optarg(arg, true)); break; } case 'h': { app_options.maze_bounds->height(get_uint_optarg(arg, true)); break; } case 'x': { start_x = std::make_unique(get_uint_optarg(arg)); break; } case 'y': { start_y = std::make_unique(get_uint_optarg(arg)); break; } case 'W': { app_options.wall = get_str_optarg(); break; } case 's': { auto seed = get_uint_optarg(arg, true); app_options.random_gen = std::make_shared(seed); break; } case 0: { std::cout << "Usage: " << args[0] << " [OPTION]...\n\n" "Options:\n" " -w, --width WIDTH The width of the maze (Default: " << DEFAULT_MAZE_WIDTH << ")\n" " -h, --height HEIGHT The height of the maze (Default: " << DEFAULT_MAZE_HEIGHT << ")\n" " -x, --start-x X The x coordinate for the start " "position " "(Default: random)\n" " -y, --start-y Y The y coordinate for the start " "position " "(Default: random)\n" " -W, --wall WALL Single character used as maze walls " "(Default: '" << DEFAULT_MAZE_WALL << "')\n" " -s, --seed SEED The randomization seed used for maze " "generation\n" " --help Displays usage information" << std::endl; return EXIT_SUCCESS; } case '?': { std::cout << "\nTry '" << args[0] << " --help' for more information" << std::endl; return EXIT_FAILURE; } default: abort(); } } if (app_options.random_gen == nullptr) { app_options.random_gen = std::make_shared(); } if (start_x == nullptr) { start_x = std::make_unique( app_options.random_gen->in_range(0, app_options.maze_bounds->width() - 1U)); } if (start_y == nullptr) { start_y = std::make_unique( app_options.random_gen->in_range(0, app_options.maze_bounds->height() - 1U)); } Vector2Options start_coords = {.x = *start_x, .y = *start_y}; app_options.start_coords = std::make_shared(start_coords); try { app_options.maze_bounds->verify_coords(*app_options.start_coords); } catch (const char *error) { std::cout << "Error: " << error << std::endl; return EXIT_FAILURE; } auto app = App(app_options); app.run(); }