#include "conversion.hpp" #include "app/app.hpp" #include "engine/vector2.hpp" #include "random_generator.hpp" #include #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(int arg, const std::string &error) { std::cout << "Error: Invalid option argument for -" << arg << ". " << error << std::endl; exit(EXIT_FAILURE); } /** * Parses a unsigned integer command-line argument. * * @param num_dst A pointer to a place to store the result value * @param arg The command-line argument character * @param check_zero Whether or not to make sure that the result is not zero */ void parse_uint_arg(unsigned int *num_dst, int arg, bool check_zero = false) { try { *num_dst = str_to_uint(std::string(optarg)); if (check_zero && *num_dst == 0) { throw "It should not be 0"; } } catch (const char *error) { optarg_error(arg, std::string(error)); } } } // namespace const 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; BoundsOpts 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; int arg = 0; while ((arg = getopt_long(argc, argv, "w:h:W:s:x:y:", options.data(), nullptr)) != -1) { switch (arg) { case 'w': { app_options.maze_bounds->width( static_cast(std::stoul(optarg, nullptr, NUMBER_BASE))); break; } case 'h': { app_options.maze_bounds->height( static_cast(std::stoul(optarg, nullptr, NUMBER_BASE))); break; } case 'x': { start_x = std::make_unique(); parse_uint_arg(start_x.get(), arg); break; } case 'y': { start_y = std::make_unique(); parse_uint_arg(start_y.get(), arg); break; } case 'W': { app_options.wall = optarg; break; } case 's': { unsigned int seed = 0; parse_uint_arg(&seed, 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)); } Vector2Opts 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(); }