From 74ea6dc86646cee9915292d73d8c7afef01ef3e0 Mon Sep 17 00:00:00 2001 From: Sopár Adrián Date: Thu, 20 Jun 2024 09:28:14 +0200 Subject: First commit. This is mostly the state of the project as I left it around the end of 2019. --- src/app.h | 15 ++++ src/args.c | 27 ++++++++ src/args.h | 21 ++++++ src/color.c | 21 ++++++ src/color.h | 16 +++++ src/config.c | 16 +++++ src/config.h | 54 +++++++++++++++ src/defaults.h | 48 +++++++++++++ src/file.c | 95 ++++++++++++++++++++++++++ src/file.h | 10 +++ src/game/commands.c | 61 +++++++++++++++++ src/game/commands.h | 24 +++++++ src/game/entry.c | 152 +++++++++++++++++++++++++++++++++++++++++ src/game/entry.h | 9 +++ src/game/game.c | 109 ++++++++++++++++++++++++++++++ src/game/game.h | 67 ++++++++++++++++++ src/game/operations.c | 144 +++++++++++++++++++++++++++++++++++++++ src/game/operations.h | 44 ++++++++++++ src/main.c | 44 ++++++++++++ src/maze.h | 32 +++++++++ src/maze_generator.c | 113 +++++++++++++++++++++++++++++++ src/maze_generator.h | 8 +++ src/maze_solver.c | 78 +++++++++++++++++++++ src/maze_solver.h | 8 +++ src/menu.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/menu.h | 8 +++ src/utilities.c | 56 +++++++++++++++ src/utilities.h | 14 ++++ 28 files changed, 1478 insertions(+) create mode 100644 src/app.h create mode 100644 src/args.c create mode 100644 src/args.h create mode 100644 src/color.c create mode 100644 src/color.h create mode 100644 src/config.c create mode 100644 src/config.h create mode 100644 src/defaults.h create mode 100644 src/file.c create mode 100644 src/file.h create mode 100644 src/game/commands.c create mode 100644 src/game/commands.h create mode 100644 src/game/entry.c create mode 100644 src/game/entry.h create mode 100644 src/game/game.c create mode 100644 src/game/game.h create mode 100644 src/game/operations.c create mode 100644 src/game/operations.h create mode 100644 src/main.c create mode 100644 src/maze.h create mode 100644 src/maze_generator.c create mode 100644 src/maze_generator.h create mode 100644 src/maze_solver.c create mode 100644 src/maze_solver.h create mode 100644 src/menu.c create mode 100644 src/menu.h create mode 100644 src/utilities.c create mode 100644 src/utilities.h (limited to 'src') diff --git a/src/app.h b/src/app.h new file mode 100644 index 0000000..cc45fbc --- /dev/null +++ b/src/app.h @@ -0,0 +1,15 @@ +#ifndef H_MAZE_APP +#define H_MAZE_APP + +#include "maze.h" +#include "config.h" +#include "args.h" + +struct app +{ + struct maze *maze; + struct args *args; + struct conf *conf; +}; + +#endif \ No newline at end of file diff --git a/src/args.c b/src/args.c new file mode 100644 index 0000000..67e7b81 --- /dev/null +++ b/src/args.c @@ -0,0 +1,27 @@ +#include "args.h" +#include + +void load_args(int argc, char **argv, struct args *args) +{ + args->maze_source = MS_UNDEFINIED; + args->input_file_name[0] = 0; + args->output_file_name[0] = 0; + args->start = false; + args->hide_menu = false; + for (int i = 1; i < argc; i++) + { + if (!strcmp(argv[i], "-g")) + args->maze_source = MS_RANDOM; + else if (!strcmp(argv[i], "-s")) + args->start = true; + else if (!strcmp(argv[i], "-h")) + args->hide_menu = true; + else if (i + 1 < argc) + { + if (!strcmp(argv[i], "-i")) + strcpy(args->input_file_name, argv[i + 1]); + else if (!strcmp(argv[i], "-o")) + strcpy(args->output_file_name, argv[i + 1]); + } + } +} \ No newline at end of file diff --git a/src/args.h b/src/args.h new file mode 100644 index 0000000..31e42f4 --- /dev/null +++ b/src/args.h @@ -0,0 +1,21 @@ +#ifndef MAZE_ARGS_H +#define MAZE_ARGS_H + +#include + +#define MS_UNDEFINIED 0 +#define MS_FROM_FILE 1 +#define MS_RANDOM 2 + +struct args +{ + int maze_source; + char input_file_name[256]; + char output_file_name[256]; + bool start; + bool hide_menu; +}; + +void load_args(int argc, char **argv, struct args *args); + +#endif \ No newline at end of file diff --git a/src/color.c b/src/color.c new file mode 100644 index 0000000..b2cc725 --- /dev/null +++ b/src/color.c @@ -0,0 +1,21 @@ +#include "color.h" +#include + +#define COLOR3_COUNT 8 + +void maze_color_init() +{ + start_color(); + short colors[COLOR3_COUNT] = {COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN, COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE}; + for (short f = 0; f < COLOR3_COUNT; f++) + for (short b = 0; b < COLOR3_COUNT; b++) + init_pair(get_color_code(f, b), f, b); +} + +short get_color_code(short foreground, short background) +{ + short code = foreground; + code |= background << 3; + code++; + return code; +} \ No newline at end of file diff --git a/src/color.h b/src/color.h new file mode 100644 index 0000000..ce1e3d3 --- /dev/null +++ b/src/color.h @@ -0,0 +1,16 @@ +#ifndef H_MAZE_COLOR +#define H_MAZE_COLOR + +#include + +short get_color_code(short foreground, short background); + +#define CI_DEFAULT get_color_code(COLOR_WHITE,COLOR_BLACK) +#define CI_LINE_INFO get_color_code(COLOR_BLACK,COLOR_CYAN) +#define CI_LINE_SUCCESS get_color_code(COLOR_BLACK,COLOR_GREEN) +#define CI_LINE_CMD get_color_code(COLOR_BLACK,COLOR_YELLOW) +#define CI_LINE_ERROR get_color_code(COLOR_BLACK,COLOR_RED) + +void maze_color_init(); + +#endif \ No newline at end of file diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..98f806a --- /dev/null +++ b/src/config.c @@ -0,0 +1,16 @@ +#include "config.h" +#include "defaults.h" +#include + +struct conf *load_default_conf() +{ + struct conf *conf = malloc(sizeof(struct conf)); + //Bindings. + struct binding bindings[] = DEFAULT_BINDINGS; + conf->bindings = malloc(sizeof(bindings)); + memcpy(conf->bindings, bindings, sizeof(bindings)); + conf->count = sizeof(bindings) / sizeof(struct binding); + //Game blocks. + set_blocks(&(conf->blocks)); + return conf; +} \ No newline at end of file diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..6ee5a47 --- /dev/null +++ b/src/config.h @@ -0,0 +1,54 @@ +#ifndef H_MAZE_CONFIG +#define H_MAZE_CONFIG + +#include +#include +#include "game/operations.h" + +struct binding +{ + wchar_t c; + char cname[5]; + enum op_res (*operation)(struct game_state *, int); + int param; +}; + +struct block +{ + wchar_t chr; + bool bold; + short color; +}; + +struct game_blocks +{ + struct block wall; + struct block road; + struct block path; + struct block solve; + struct block player; + struct block start; + struct block target; +}; + +struct conf +{ + struct binding *bindings; + struct game_blocks blocks; + size_t count; +}; + +static inline struct block get_block(wchar_t chr, bool bold, short color) +{ + struct block result; + result.chr = chr; + result.bold = bold; + result.color = color; + return result; +} + +struct conf *load_default_conf(); + +void destroy_conf(struct conf *); + +#endif \ No newline at end of file diff --git a/src/defaults.h b/src/defaults.h new file mode 100644 index 0000000..d4fdc1b --- /dev/null +++ b/src/defaults.h @@ -0,0 +1,48 @@ +#ifndef H_MAZE_DEFAULTS +#define H_MAZE_DEFAULTS + +#include "config.h" +#include "color.h" +#include +#include + +static inline void set_blocks(struct game_blocks *game_blocks) +{ + game_blocks->wall = get_block(' ', false, get_color_code(COLOR_BLACK, COLOR_WHITE)); + game_blocks->road = get_block(' ', false, get_color_code(COLOR_WHITE, COLOR_BLACK)); + game_blocks->path = get_block(0x2022, false, get_color_code(COLOR_YELLOW, COLOR_BLACK)); + game_blocks->solve = get_block(0x2022, false, get_color_code(COLOR_BLUE, COLOR_BLACK)); + game_blocks->player = get_block(0x25CF, false, get_color_code(COLOR_CYAN, COLOR_BLACK)); + game_blocks->start = get_block('S', true, get_color_code(COLOR_GREEN, COLOR_BLACK)); + game_blocks->target = get_block('T', true, get_color_code(COLOR_RED, COLOR_BLACK)); +} + +#define DEFAULT_BINDINGS \ + { \ + {KEY_UP, "", move_player, MOVE_UP}, \ + {KEY_DOWN, "", move_player, MOVE_DOWN}, \ + {KEY_LEFT, "", move_player, MOVE_LEFT}, \ + {KEY_RIGHT, "", move_player, MOVE_RIGHT}, \ + {'k', "", move_player, MOVE_UP}, \ + {'j', "", move_player, MOVE_DOWN}, \ + {'h', "", move_player, MOVE_LEFT}, \ + {'l', "", move_player, MOVE_RIGHT}, \ + {0, "kUP5", move_maze, MOVE_UP}, \ + {0, "kDN5", move_maze, MOVE_DOWN}, \ + {0, "kLFT5", move_maze, MOVE_LEFT}, \ + {0, "kRIT5", move_maze, MOVE_RIGHT}, \ + {0, "^K", move_maze, MOVE_UP}, \ + {0, "^J", move_maze, MOVE_DOWN}, \ + {0, "^H", move_maze, MOVE_LEFT}, \ + {0, "^L", move_maze, MOVE_RIGHT}, \ + {'0', "", move_maze, MOVE_BEGINNING}, \ + {'p', "", turn_display_switch, DISP_PATH}, \ + {'q', "", quit, GR_QUIT}, \ + {'r', "", new_random, 0}, \ + {'c', "", center, 0}, \ + {'n', "", help_by_n_move, 1}, \ + {'s', "", solve, 0}, \ + {':', "", start_command_prompt, ':'}, \ + } + +#endif \ No newline at end of file diff --git a/src/file.c b/src/file.c new file mode 100644 index 0000000..511b580 --- /dev/null +++ b/src/file.c @@ -0,0 +1,95 @@ +#include "file.h" +#include +#include +#include +#include "utilities.h" + +struct data +{ + int x; + int y; + int value; +}; + +static int **list_to_map(struct data *values, int count, int width, int height) +{ + int **map = malloc(sizeof_2d(width, height, sizeof(int))); + init2d((void **)map, width, height, sizeof(int)); + for (int i = 0; i < count; i++) + map[values[i].x][values[i].y] = values[i].value; + return map; +} + +static void get_numbers(char *line, int *left_int, int *right_int) +{ + char *left, *right; + if (find_lr_ints(line, &left, &right)) + { + *left_int = strtol(left, NULL, 10); + *right_int = strtol(right, NULL, 10); + } +} + +struct maze *maze_from_file(char *path) +{ + FILE *file = fopen(path, "r"); + if (file == NULL) + return NULL; + struct maze *maze = malloc(sizeof(struct maze)); + struct data values[50 * 50]; + char buffer[256]; + int i = 0, y = 0, max_width = 0; + while (fgets(buffer, 256, file)) + { + int length = strlen(buffer); + if (length > 0) + { + if (length > 5 && !strncmp(buffer, "start", 5)) + { + get_numbers(buffer + 5, &(maze->starting_point.x), &(maze->starting_point.y)); + } + else if (length > 3 && !strncmp(buffer, "end", 3)) + { + get_numbers(buffer + 3, &(maze->end_point.x), &(maze->end_point.y)); + } + else + { + int x; + for (x = 0; buffer[x] != 10; x++) + { + values[i].x = x; + values[i].y = y; + values[i].value = buffer[x] == '0' ? 0 : 1; + i++; + } + if (x > max_width) + max_width = x; + y++; + } + } + } + fclose(file); + maze->width = max_width; + maze->height = y; + maze->map = list_to_map(values, i, maze->width, maze->height); + return maze; +} + +int maze_to_file(char *path, struct maze *maze) +{ + FILE *file = fopen(path, "w"); + if (file == NULL) + return -1; + fprintf(file, "start=(%d,%d)\n", maze->starting_point.x, maze->starting_point.y); + fprintf(file, "end=(%d,%d)\n", maze->end_point.x, maze->end_point.y); + for (int y = 0; y < maze->height; y++) + { + for (int x = 0; x < maze->width; x++) + { + fprintf(file, "%d", maze->map[x][y]); + } + fprintf(file, "\n"); + } + fclose(file); + return 0; +} \ No newline at end of file diff --git a/src/file.h b/src/file.h new file mode 100644 index 0000000..5e5268d --- /dev/null +++ b/src/file.h @@ -0,0 +1,10 @@ +#ifndef MAZE_FILE_H +#define MAZE_FILE_H + +#include "maze.h" + +struct maze *maze_from_file(char *path); + +int maze_to_file(char *path, struct maze *maze); + +#endif \ No newline at end of file diff --git a/src/game/commands.c b/src/game/commands.c new file mode 100644 index 0000000..7e0e865 --- /dev/null +++ b/src/game/commands.c @@ -0,0 +1,61 @@ +#include "commands.h" +#include + +static struct cmd_res command_switch(struct game_state *state, int argc, char **argv); + +struct cmd_res run_command(struct game_state *state, char *command) +{ + int argc = 0; + char *argv[MAX_ARGS_COUNT]; + char *arg = command; + char *space; + int arg_length; + while ((space = strchr(arg, ' ')) != NULL) + { + if (*arg != ' ') + { + arg_length = space - arg; + argv[argc] = malloc(arg_length + 1); + strncpy(argv[argc], arg, arg_length); + argv[argc][arg_length] = 0; + arg = space + 1; + argc++; + } + } + //Last arg + arg_length = strlen(arg); + argv[argc] = malloc(arg_length + 1); + strncpy(argv[argc], arg, arg_length); + argv[argc][arg_length] = 0; + argc++; + + struct cmd_res response = command_switch(state, argc, argv); + for (int i = 0; i < argc; i++) + free(argv[i]); + return response; +} + +static struct cmd_res nothing(struct game_state *state, int argc, char **argv) +{ + struct cmd_res response; + response.type = CRT_FAIL; + strncpy(response.text, "There is no command you entered!", MAX_CR_TEXT_LENGTH); + return response; +} + +static struct cmd_res quit(struct game_state *state, int argc, char **argv) +{ + state->result = GR_QUIT; + struct cmd_res response; + response.type = CRT_QUIT; + *(response.text) = 0; + return response; +} + +static struct cmd_res command_switch(struct game_state *state, int argc, char **argv) +{ + if (!strcmp("", *argv)) + return nothing(state, argc, argv); + else if (!strcmp("q", *argv)) + return quit(state, argc, argv); +} \ No newline at end of file diff --git a/src/game/commands.h b/src/game/commands.h new file mode 100644 index 0000000..8e2cc5f --- /dev/null +++ b/src/game/commands.h @@ -0,0 +1,24 @@ +#ifndef H_MAZE_GAME_COMMANDS +#define H_MAZE_GAME_COMMANDS + +#include "game.h" + +enum cmd_res_type +{ + CRT_SUCCESS, + CRT_FAIL, + CRT_QUIT +}; + +#define MAX_CR_TEXT_LENGTH 256 +#define MAX_ARGS_COUNT 100 + +struct cmd_res +{ + enum cmd_res_type type; + char text[MAX_CR_TEXT_LENGTH]; +}; + +struct cmd_res run_command(struct game_state *state, char *command); + +#endif \ No newline at end of file diff --git a/src/game/entry.c b/src/game/entry.c new file mode 100644 index 0000000..50cf78c --- /dev/null +++ b/src/game/entry.c @@ -0,0 +1,152 @@ +#include "entry.h" +#include "game.h" +#include +#include +#include +#include "utilities.h" +#include "color.h" +#include "maze_solver.h" + +static inline int mvaddwch(int y, int x, wchar_t wch) +{ + wchar_t arr[2]; + arr[0] = wch; + arr[1] = 0; + return mvaddwstr(y, x, arr); +} + +static inline int addwch(wchar_t wch) +{ + wchar_t arr[2]; + arr[0] = wch; + arr[1] = 0; + return addwstr(arr); +} + +static void darw_block(struct block *block) +{ + attron(COLOR_PAIR(block->color)); + if (block->bold) + attron(A_BOLD); + addwch(block->chr); + if (block->bold) + attroff(A_BOLD); +} + +static void mv_draw_block(int y, int x, struct block *block) +{ + int width = getmaxx(stdscr); + int height = getmaxy(stdscr) - 1; + if (x >= 0 && x < width && y >= 0 && y < height) + { + move(y, x); + darw_block(block); + } +} + +void draw_maze(struct maze_display *maze_draw, struct game_blocks *blocks) +{ + int width = getmaxx(stdscr); + int height = getmaxy(stdscr) - 1; + enum signs path_sign; + for (int y = 0; y < maze_draw->maze->height; y++) + { + int real_y = maze_draw->maze_pos.y + y; + if (real_y >= 0 && real_y < height) + { + if (maze_draw->maze_pos.x >= 0) + move(real_y, maze_draw->maze_pos.x); + for (int x = 0; x < maze_draw->maze->width && y < width; x++) + { + int real_x = maze_draw->maze_pos.x + x; + if (real_x == 0 && x > 0) + { + move(real_y, 0); + } + if (real_x >= 0 && real_x < width) + { + path_sign = maze_draw->signs[x][y]; + if (path_sign == RS_NONE || (path_sign == RS_PATH && !maze_draw->display_player_path)) //Maze + { + if (maze_draw->maze->map[x][y]) + darw_block(&(blocks->wall)); + else + darw_block(&(blocks->road)); + } + else + { + if (path_sign == RS_PATH) + darw_block(&(blocks->path)); + else if (path_sign == RS_SOLVE) + darw_block(&(blocks->solve)); + } + } + } + } + } + //Player + mv_draw_block( + maze_draw->maze_pos.y + maze_draw->player_pos.y, + maze_draw->maze_pos.x + maze_draw->player_pos.x, &(blocks->player)); + mv_draw_block( + maze_draw->maze_pos.y + maze_draw->maze->starting_point.y, + maze_draw->maze_pos.x + maze_draw->maze->starting_point.x, &(blocks->start)); + mv_draw_block( + maze_draw->maze_pos.y + maze_draw->maze->end_point.y, + maze_draw->maze_pos.x + maze_draw->maze->end_point.x, &(blocks->target)); +} + +static void own_clear() +{ + attron(COLOR_PAIR(CI_DEFAULT)); + int width = getmaxx(stdscr); + int height = getmaxy(stdscr) - 1; + for (int y = 0; y < height; y++) + for (int x = 0; x < width; x++) + mvaddch(y, x, ' '); +} + +enum game_res game(struct app *app) +{ + curs_set(0); + noecho(); + struct game_state *state = create_game_state(app->maze); + + while (1) + { + if (state->display->player_pos.x != state->new_player_pos.x || + state->display->player_pos.y != state->new_player_pos.y) //Does player moved? + { + state->display->signs[state->display->player_pos.x][state->display->player_pos.y] = RS_PATH; + state->display->player_pos = state->new_player_pos; + if (state->center) + state->new_maze_pos = get_player_center(state->display->player_pos); + if (state->display->player_pos.x == state->display->maze->end_point.x && + state->display->player_pos.y == state->display->maze->end_point.y) //Win? + state->win = true; + } + state->display->maze_pos = state->new_maze_pos; + //Draw! + own_clear(); + draw_maze(state->display, &(app->conf->blocks)); + if (state->win) + draw_line_win(); + else + draw_line_info(state->steps_taken, solve_maze(app->maze, state->display->player_pos, NULL)); + wrefresh(stdscr); + //Get user input! + wchar_t c = wgetch(stdscr); + struct binding *last_binding = &(app->conf->bindings[app->conf->count - 1]); + for (struct binding *binding = app->conf->bindings; binding <= last_binding; binding++) + { + if ((*(binding->cname) != 0 && strcmp(keyname(c), binding->cname) == 0) || c == binding->c) + { + if (binding->operation(state, binding->param) == OPR_QUIT) + { + return state->result; + } + } + } + } + return GR_QUIT; +} diff --git a/src/game/entry.h b/src/game/entry.h new file mode 100644 index 0000000..655f719 --- /dev/null +++ b/src/game/entry.h @@ -0,0 +1,9 @@ +#ifndef H_MAZE_GAME_ENTRY +#define H_MAZE_GAME_ENTRY + +#include "app.h" +#include "game.h" + +enum game_res game(struct app *app); + +#endif \ No newline at end of file diff --git a/src/game/game.c b/src/game/game.c new file mode 100644 index 0000000..a9ac239 --- /dev/null +++ b/src/game/game.c @@ -0,0 +1,109 @@ +#include "game.h" +#include "color.h" +#include "utilities.h" +#include + +void draw_line(int color, char *text, size_t length) +{ + int width = getmaxx(stdscr); + int height = getmaxy(stdscr) - 1; + char footer[width + 1]; + memset(footer, ' ', width); + footer[width] = 0; + //Display: + attron(COLOR_PAIR(color)); + mvprintw(height, 0, footer); + strncpy(footer, text, length); + mvprintw(height, 0, footer); +} + +void draw_line_info(int steps_taken, int steps_remaining) +{ + char buffer[getmaxx(stdscr) + 1]; + int length = sprintf(buffer, "%d/%d", steps_taken, steps_remaining); + draw_line(CI_LINE_INFO, buffer, length); +} + +void draw_line_win() +{ + char buffer[getmaxx(stdscr) + 1]; + int length = sprintf(buffer, "\u2714 Win! \u263A"); + draw_line(CI_LINE_SUCCESS, buffer, length); +} + +int get_command(char prompt, char *command) +{ + int width = getmaxx(stdscr); + int height = getmaxy(stdscr) - 1; + + char line[width + 1]; + memset(line, ' ', width); + line[width] = 0; + attron(COLOR_PAIR(CI_LINE_CMD)); + curs_set(1); + + char *current = command; + *(current++) = prompt; + *current = 0; + while (1) + { + mvaddstr(height, 0, line); + mvaddstr(height, 0, command); + refresh(); + wchar_t c = wgetch(stdscr); + if (c == KEY_BACKSPACE) + { + current--; + *current = 0; + if (current == command) + { + curs_set(0); + return 0; + } + } + else if (c == 10) + { + curs_set(0); + return 1; + } + else + { + *(current++) = c; + *current = 0; + } + } +} + +struct game_state *create_game_state(struct maze *maze) +{ + struct maze_display *display = malloc(sizeof(struct maze_display)); + + display->maze_pos.x = 0; + display->maze_pos.y = 0; + display->maze = maze; + display->player_pos = maze->starting_point; + display->display_player_path = true; + //Signs + int signs_length = sizeof_2d(maze->width, maze->height, sizeof(int)); + display->signs = malloc(signs_length); + memset(display->signs, (int)RS_NONE, signs_length); + init2d((void **)display->signs, maze->width, maze->height, sizeof(int)); + display->signs[display->player_pos.x][display->player_pos.y] = RS_PATH; + + struct game_state *state = malloc(sizeof(struct game_state)); + state->display = display; + + state->center = false; + state->win = false; + state->steps_taken = 0; + state->new_maze_pos = display->maze_pos; + state->new_player_pos = display->player_pos; + return state; +} + +struct array +{ + int width; + int height; + int *arr; +}; diff --git a/src/game/game.h b/src/game/game.h new file mode 100644 index 0000000..4b0de07 --- /dev/null +++ b/src/game/game.h @@ -0,0 +1,67 @@ +#ifndef H_MAZE_GAME_GAME +#define H_MAZE_GAME_GAME + +#include +#include +#include +#include "maze.h" + +enum game_res +{ + GR_MENU, + GR_QUIT +}; + +enum signs +{ + RS_NONE, + RS_SOLVE, + RS_PATH +}; + +struct maze_display +{ + struct point maze_pos; + struct point player_pos; + struct maze *maze; + enum signs **signs; + bool display_player_path; +}; + +struct game_state +{ + struct maze_display *display; + struct point new_maze_pos; + struct point new_player_pos; + int steps_taken; + bool center; + bool win; + char cmd_history[100][256]; + size_t cmd_count; + enum game_res result; +}; + +struct game_state *create_game_state(struct maze *maze); + +void draw_line_win(); + +void draw_line_info(int steps_taken, int steps_remaining); + +int get_command(char prompt, char *command); + +static inline int is_movable(struct maze_display *display, int x, int y) +{ + return display->maze->map[display->player_pos.x + x][display->player_pos.y + y] == 0; +} + +static inline struct point get_player_center(struct point player_pos) +{ + int width = getmaxx(stdscr); + int height = getmaxy(stdscr) - 1; + struct point maze_pos; + maze_pos.x = (width / 2) - player_pos.x; + maze_pos.y = (height / 2) - player_pos.y; + return maze_pos; +} + +#endif \ No newline at end of file diff --git a/src/game/operations.c b/src/game/operations.c new file mode 100644 index 0000000..aa9424f --- /dev/null +++ b/src/game/operations.c @@ -0,0 +1,144 @@ +#include "operations.h" +#include +#include "maze_generator.h" +#include "maze_solver.h" +#include "commands.h" + +enum op_res move_player(struct game_state *state, int data) +{ + if (state->win) + return OPR_NONE; + enum move move = (enum move)data; + if (move == MOVE_UP && is_movable(state->display, 0, -1)) + { + state->new_player_pos.y--; + state->steps_taken++; + } + else if (move == MOVE_DOWN && is_movable(state->display, 0, 1)) + { + state->new_player_pos.y++; + state->steps_taken++; + } + else if (move == MOVE_LEFT && is_movable(state->display, -1, 0)) + { + state->new_player_pos.x--; + state->steps_taken++; + } + else if (move == MOVE_RIGHT && is_movable(state->display, 1, 0)) + { + state->new_player_pos.x++; + state->steps_taken++; + } + return OPR_NONE; +} + +enum op_res move_maze(struct game_state *state, int data) +{ + switch ((enum move)data) + { + case MOVE_UP: + state->new_maze_pos.y--; + break; + case MOVE_DOWN: + state->new_maze_pos.y++; + break; + case MOVE_LEFT: + state->new_maze_pos.x--; + break; + case MOVE_RIGHT: + state->new_maze_pos.x++; + break; + case MOVE_BEGINNING: + { + state->new_maze_pos.x = 0; + state->new_maze_pos.y = 0; + } + break; + } + return OPR_NONE; +} + +enum op_res quit(struct game_state *state, int data) +{ + state->result = data; + return OPR_QUIT; +} + +enum op_res turn_display_switch(struct game_state *state, int data) +{ + switch ((enum display)data) + { + case DISP_PATH: + state->display->display_player_path = !(state->display->display_player_path); + break; + } + return OPR_NONE; +} + +enum op_res new_random(struct game_state *state, int data) +{ + struct maze_display *display = state->display; + generate_maze(display->maze); + display->player_pos = display->maze->starting_point; + state->new_player_pos = display->player_pos; + for (int x = 0; x < display->maze->width; x++) + { + for (int y = 0; y < display->maze->height; y++) + { + display->signs[x][y] = RS_NONE; + } + } + state->win = false; + state->steps_taken = 0; + return OPR_NONE; +} + +enum op_res center(struct game_state *state, int data) +{ + state->center = !state->center; + if (state->center) + state->new_maze_pos = get_player_center(state->display->player_pos); + return OPR_NONE; +} + +enum op_res help_by_n_move(struct game_state *state, int data) +{ + struct point path[3000]; + int count = solve_maze(state->display->maze, state->display->player_pos, path); + if (count > 0) + { + state->new_player_pos = path[1]; + state->steps_taken++; + } + return OPR_NONE; +} + +enum op_res solve(struct game_state *state, int data) +{ + struct point path[3000]; + int count = solve_maze(state->display->maze, state->display->player_pos, path); + for (int i = 0; i < count; i++) + { + state->display->signs[path[i].x][path[i].y] = RS_SOLVE; + } + return OPR_NONE; +} + +enum op_res start_command_prompt(struct game_state *state, int data) +{ + char command[256]; + if (get_command((char)data, command)) + { + struct cmd_res response = run_command(state, command + 1); + switch (response.type) + { + case CRT_SUCCESS: + break; + case CRT_FAIL: + break; + case CRT_QUIT: + return OPR_QUIT; + } + } + return OPR_NONE; +} \ No newline at end of file diff --git a/src/game/operations.h b/src/game/operations.h new file mode 100644 index 0000000..2f2af47 --- /dev/null +++ b/src/game/operations.h @@ -0,0 +1,44 @@ +#ifndef H_MAZE_GAME_OPERATION +#define H_MAZE_GAME_OPERATION + +#include "game.h" + +enum op_res +{ + OPR_NONE, + OPR_QUIT +}; + +enum move +{ + MOVE_UP, + MOVE_DOWN, + MOVE_LEFT, + MOVE_RIGHT, + MOVE_BEGINNING +}; + +enum display +{ + DISP_PATH +}; + +enum op_res move_player(struct game_state *state, int data); + +enum op_res move_maze(struct game_state *state, int data); + +enum op_res quit(struct game_state *state, int data); + +enum op_res turn_display_switch(struct game_state *state, int data); + +enum op_res new_random(struct game_state *state, int data); + +enum op_res center(struct game_state *state, int data); + +enum op_res help_by_n_move(struct game_state *state, int data); + +enum op_res solve(struct game_state *state, int data); + +enum op_res start_command_prompt(struct game_state *state, int data); + +#endif \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..20ff774 --- /dev/null +++ b/src/main.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include "app.h" +#include "color.h" +#include "menu.h" + +void handle(struct app *app) +{ +} + +int main(int argc, char **argv) +{ + srand(time(NULL)); + //Setup ncurses + setlocale(LC_ALL, ""); + initscr(); + raw(); + keypad(stdscr, TRUE); + maze_color_init(); + //Construct app + struct app app; + app.maze = NULL; + //Load args + struct args args; + args.hide_menu = true; + load_args(argc, argv, &args); + app.args = &args; + //Load config + app.conf = load_default_conf(); + //Start main loop + handle(&app); + if (!args.hide_menu) + main_menu_loop(&app); + //Deallocate + if (app.maze != NULL) + { + free(app.maze->map); + free(app.maze); + } + //End + endwin(); +} \ No newline at end of file diff --git a/src/maze.h b/src/maze.h new file mode 100644 index 0000000..5b3581a --- /dev/null +++ b/src/maze.h @@ -0,0 +1,32 @@ +#ifndef MAZE_H +#define MAZE_H + +struct point +{ + int x; + int y; +}; + +struct maze +{ + int width; + int height; + int **map; + struct point starting_point; + struct point end_point; +}; + +static inline struct point relative_point(struct point point, int x, int y) +{ + struct point new_point; + new_point.x = point.x + x; + new_point.y = point.y + y; + return new_point; +} + +static inline int is_valid_maze_size(int size) +{ + return size > 2 && size % 2 != 0; +} + +#endif \ No newline at end of file diff --git a/src/maze_generator.c b/src/maze_generator.c new file mode 100644 index 0000000..3a2e3b2 --- /dev/null +++ b/src/maze_generator.c @@ -0,0 +1,113 @@ +#include +#include "maze_generator.h" +#include "utilities.h" +#include "maze.h" + +static void remove_wall(int **graph, struct point a, struct point b) +{ + int diff_x = a.x - b.x; + int diff_y = a.y - b.y; + if (diff_y == 0) + { + if (diff_x == 1) + graph[b.x][b.y] &= 3; + else if (diff_x == -1) + graph[a.x][a.y] &= 3; + } + else if (diff_x == 0) + { + if (diff_y == 1) + graph[b.x][b.y] &= 5; + else if (diff_y == -1) + graph[a.x][a.y] &= 5; + } +} + +static void generate_maze_graph(int **graph, int width, int height, struct point point) +{ + //I've been here. + graph[point.x][point.y] &= 6; + struct point points[4]; + int index = 0; + //Possibilities: + if (point.x > 0) + points[index++] = relative_point(point, -1, 0); + if (point.x < width - 1) + points[index++] = relative_point(point, 1, 0); + if (point.y > 0) + points[index++] = relative_point(point, 0, -1); + if (point.y < height - 1) + points[index++] = relative_point(point, 0, 1); + //Shuffle: + shuffle(points, index, sizeof(struct point)); + //Examine possibilities: + index--; + struct point new_point; + while (index >= 0) + { + new_point = points[index]; + if ((graph[new_point.x][new_point.y] & 1) == 1) + { + //Remove wall: + remove_wall(graph, point, new_point); + //Go on: + generate_maze_graph(graph, width, height, new_point); + } + index--; + } +} + +int generate_maze(struct maze *maze) +{ + if (!is_valid_maze_size(maze->width) || !is_valid_maze_size(maze->height)) + return -1; + //Init graph: + int graph_width = ((maze->width - 2) + 1) / 2; + int graph_height = ((maze->height - 2) + 1) / 2; + char mem[sizeof_2d(graph_width, graph_height, sizeof(int))]; + init2d((void **)mem, graph_width, graph_height, sizeof(int)); + int **graph = (int **)mem; + for (int x = 0; x < graph_width; x++) + for (int y = 0; y < graph_height; y++) + graph[x][y] = 7; + //Generate graph: + struct point starting_point; + starting_point.x = 0; + starting_point.y = 0; + generate_maze_graph(graph, graph_width, graph_height, starting_point); + //Convert: + for (int x = 0; x < graph_width; x++) + { + for (int y = 0; y < graph_height; y++) + { + int real_x = (2 * x) + 1; + int real_y = (2 * y) + 1; + int is_last_x = x == maze->width - 2; + int is_last_y = y == maze->height - 2; + maze->map[real_x][real_y] = 0; + if (!is_last_x) + maze->map[real_x + 1][real_y] = (graph[x][y] & 4) == 4; + if (!is_last_y) + maze->map[real_x][real_y + 1] = (graph[x][y] & 2) == 2; + if (!(is_last_x || is_last_y)) + maze->map[real_x + 1][real_y + 1] = 1; + } + } + //Set sides: + for (int x = 0; x < maze->width; x++) + { + maze->map[x][0] = 1; + maze->map[x][maze->height - 1] = 1; + } + for (int y = 0; y < maze->height; y++) + { + maze->map[0][y] = 1; + maze->map[maze->width - 1][y] = 1; + } + //Set start and end: + maze->starting_point.x = 1; + maze->starting_point.y = 1; + maze->end_point.x = maze->width - 2; + maze->end_point.y = maze->height - 2; + return 0; +} \ No newline at end of file diff --git a/src/maze_generator.h b/src/maze_generator.h new file mode 100644 index 0000000..2176703 --- /dev/null +++ b/src/maze_generator.h @@ -0,0 +1,8 @@ +#ifndef MAZE_GENERATOR_H +#define MAZE_GENERATOR_H + +#include "maze.h" + +int generate_maze(struct maze *maze); + +#endif \ No newline at end of file diff --git a/src/maze_solver.c b/src/maze_solver.c new file mode 100644 index 0000000..6286d24 --- /dev/null +++ b/src/maze_solver.c @@ -0,0 +1,78 @@ +#include "maze_solver.h" +#include +#include "utilities.h" + +struct solver_data +{ + struct maze *maze; + struct point *first_point; + struct point *last_point; + int count; + //int temp[51][51]; + int **temp; +}; + +static void get_neighbours(struct point point, struct point *neighbours) +{ + neighbours[0].x = point.x + 1; + neighbours[0].y = point.y; + neighbours[1].x = point.x - 1; + neighbours[1].y = point.y; + neighbours[2].x = point.x; + neighbours[2].y = point.y + 1; + neighbours[3].x = point.x; + neighbours[3].y = point.y - 1; +} + +static int solve_maze_rec(struct solver_data *data, struct point point) +{ + if (data->first_point == NULL) + data->count++; + else + *(data->last_point++) = point; + + data->temp[point.x][point.y] = 1; + if (data->maze->end_point.x == point.x && data->maze->end_point.y == point.y) + { + if (data->first_point == NULL) + return data->count; + else + return (data->last_point - data->first_point); + } + struct point neighbours[4]; + get_neighbours(point, neighbours); + for (int i = 0; i < 4; i++) + { + if (neighbours[i].x >= 0 && + neighbours[i].y >= 0 && + neighbours[i].x < data->maze->width && + neighbours[i].y < data->maze->height && + data->maze->map[neighbours[i].x][neighbours[i].y] != 1 && + data->temp[neighbours[i].x][neighbours[i].y] != 1) + { + int value = solve_maze_rec(data, neighbours[i]); + if (value >= 0) + return value; + } + } + if (data->first_point == NULL) + data->count--; + else + data->last_point--; + return -1; +} + +int solve_maze(struct maze *maze, struct point point, struct point *path) +{ + struct solver_data solver_data; + solver_data.maze = maze; + solver_data.first_point = path; + solver_data.last_point = path; + solver_data.count = 0; + int length = sizeof_2d(maze->width, maze->height, sizeof(int)); + char mem[length]; + memset(mem, 0, length); + init2d((void **)mem, maze->width, maze->height, sizeof(int)); + solver_data.temp = (int **)mem; + return solve_maze_rec(&solver_data, point) - 1; +} \ No newline at end of file diff --git a/src/maze_solver.h b/src/maze_solver.h new file mode 100644 index 0000000..8dac12c --- /dev/null +++ b/src/maze_solver.h @@ -0,0 +1,8 @@ +#ifndef MAZE_SOLVER_H +#define MAZE_SOLVER_H + +#include "maze.h" + +int solve_maze(struct maze *maze, struct point point, struct point *path); + +#endif \ No newline at end of file diff --git a/src/menu.c b/src/menu.c new file mode 100644 index 0000000..eca1a20 --- /dev/null +++ b/src/menu.c @@ -0,0 +1,184 @@ +#include "menu.h" +#include +#include +#include "color.h" +#include "game/entry.h" +#include "utilities.h" +#include "maze_generator.h" +#include "file.h" + +#define MO_LOAD_FROM_FILE 1 +#define MO_SAVE_TO_FILE 2 +#define MO_GENERATE 3 +#define MO_START 4 +#define MO_QUIT 5 +#define MO_INVALID_OPTION 6 + +int choose_menu_option() +{ + attron(COLOR_PAIR(CI_DEFAULT)); + clear(); + printw("1. Load from file.\n"); + printw("2. Save to file.\n"); + printw("3. Random maze.\n"); + printw("4. Start the game.\n"); + printw("q. Quit.\n"); + printw("Choose:\n"); + curs_set(1); + echo(); + refresh(); + //char buffer[8]; + //wgetnstr(stdscr, buffer, 8); + switch (getch()) + { + case '1': + return MO_LOAD_FROM_FILE; + break; + case '2': + return MO_SAVE_TO_FILE; + break; + case '3': + return MO_GENERATE; + break; + case '4': + return MO_START; + break; + case 'q': + return MO_QUIT; + break; + } + return MO_INVALID_OPTION; +} + +int get_file_path(char *path) +{ + attron(COLOR_PAIR(CI_DEFAULT)); + clear(); + printw("Enter the path of file:\n"); + curs_set(1); + echo(); + wgetnstr(stdscr, path, 256); + return *path == 0 ? -1 : 1; +} + +int save_file_path(char *path) +{ + return get_file_path(path); +} + +int open_file_path(char *path) +{ + return get_file_path(path); +} + +void get_size(int *width, int *height) +{ + attron(COLOR_PAIR(CI_DEFAULT)); + while (1) + { + clear(); + printw("Enter a size:\n"); + curs_set(1); + echo(); + char buffer[16]; + wgetnstr(stdscr, buffer, 16); + char *left, *right; + if (find_lr_ints(buffer, &left, &right)) + { + char *end; + *width = strtol(left, &end, 10); + printw("\nwidth=%d\n", *width); + if (*end == '%') + { + *width = ((double)*width / 100) * getmaxx(stdscr); + if (!is_valid_maze_size(*width)) + (*width)--; + } + *height = strtol(right, &end, 10); + printw("\nheight=%d\n", *height); + if (*end == '%') + { + *height = ((double)*height / 100) * getmaxy(stdscr); + if (!is_valid_maze_size(*height)) + (*height)--; + } + printw("\nwidth=%d\nheight=%d\n", *width, *height); + } + if (is_valid_maze_size(*width) && is_valid_maze_size(*height)) + return; + printw("\nWrong size number!\n"); + while (getch() != 10) + ; + } +} + +void main_menu_loop(struct app *app) +{ + while (1) + { + switch (choose_menu_option()) + { + case MO_LOAD_FROM_FILE: + { + char path[256]; + if (open_file_path(path)) + { + app->maze = maze_from_file(path); + if (app->maze == NULL) + { + printw("\nFile is not exsist!\n"); + while (getch() != 10) + ; + } + } + } + break; + case MO_SAVE_TO_FILE: + { + char path[256]; + if (save_file_path(path)) + { + maze_to_file(path, app->maze); + } + } + break; + case MO_GENERATE: + { + app->maze = malloc(sizeof(struct maze)); + get_size(&(app->maze->width), &(app->maze->height)); + //maze->width = 41; + //maze->height = 21; + app->maze->map = malloc(sizeof_2d(app->maze->width, app->maze->height, sizeof(int))); + init2d((void **)app->maze->map, app->maze->width, app->maze->height, sizeof(int)); + generate_maze(app->maze); + } + break; + case MO_START: + { + if (app->maze == NULL) + { + printw("\nThere is no maze!\n"); + while (getch() != 10) + ; + } + else if (game(app) == GR_QUIT) + { + return; + } + } + break; + case MO_QUIT: + { + return; + } + case MO_INVALID_OPTION: + { + printw("\nError!\n"); + while (getch() != 10) + ; + } + break; + } + //TODO: Handle. + } +} \ No newline at end of file diff --git a/src/menu.h b/src/menu.h new file mode 100644 index 0000000..83394f5 --- /dev/null +++ b/src/menu.h @@ -0,0 +1,8 @@ +#ifndef H_MAZE_MENU +#define H_MAZE_MENU + +#include "app.h" + +void main_menu_loop(struct app *app); + +#endif \ No newline at end of file diff --git a/src/utilities.c b/src/utilities.c new file mode 100644 index 0000000..1142ec4 --- /dev/null +++ b/src/utilities.c @@ -0,0 +1,56 @@ +#include "utilities.h" +#include +#include + +static unsigned int random_number(unsigned int max) +{ + return rand() / (RAND_MAX / max); +} + +void shuffle(void *base, size_t nitems, size_t size) +{ + char *c_base = base; + char swap[size]; + for (int i = 0; i < nitems; i++) + { + char *a = c_base + (i * size); + char *b = c_base + (random_number(nitems - 1) * size); + memcpy(swap, a, size); + memcpy(a, b, size); + memcpy(b, swap, size); + } +} + +void init2d(void **array, size_t width, size_t height, size_t size) +{ + char **c_array = (char **)array; + char *start = (char *)array; + for (size_t x = 0; x < width; x++) + c_array[x] = start + (width * sizeof(int *)) + (x * height * size); +} + +size_t sizeof_2d(size_t width, size_t height, size_t size) +{ + return (width * sizeof(char *)) + (width * height * size); +} + +/* + * Find left and right ints. + */ +int find_lr_ints(char *text, char **left, char **right) +{ + *left = text; + while (**left != 0 && (**left < '0' || **left > '9')) + (*left)++; + if (**left == 0) + return 0; + + *right = text + strlen(text); + while (**right < '0' || **right > '9') + (*right)--; + + while (**right >= '0' && **right <= '9') + (*right)--; + (*right)++; + return 1; +} \ No newline at end of file diff --git a/src/utilities.h b/src/utilities.h new file mode 100644 index 0000000..6787883 --- /dev/null +++ b/src/utilities.h @@ -0,0 +1,14 @@ +#ifndef UTILITIES_H +#define UTILITIES_H + +#include + +void shuffle(void *base, size_t nitems, size_t size); + +void init2d(void **array, size_t width, size_t height, size_t size); + +size_t sizeof_2d(size_t width, size_t height, size_t size); + +int find_lr_ints(char *text, char **left, char **right); + +#endif \ No newline at end of file -- cgit v1.2.3