aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile83
-rw-r--r--README.md58
-rwxr-xr-xcount.sh2
-rwxr-xr-xdebug.sh2
-rw-r--r--doc/maze.1102
-rw-r--r--files/2.txt7
-rw-r--r--files/asd.txt23
-rw-r--r--src/app.h15
-rw-r--r--src/args.c27
-rw-r--r--src/args.h21
-rw-r--r--src/color.c21
-rw-r--r--src/color.h16
-rw-r--r--src/config.c16
-rw-r--r--src/config.h54
-rw-r--r--src/defaults.h48
-rw-r--r--src/file.c95
-rw-r--r--src/file.h10
-rw-r--r--src/game/commands.c61
-rw-r--r--src/game/commands.h24
-rw-r--r--src/game/entry.c152
-rw-r--r--src/game/entry.h9
-rw-r--r--src/game/game.c109
-rw-r--r--src/game/game.h67
-rw-r--r--src/game/operations.c144
-rw-r--r--src/game/operations.h44
-rw-r--r--src/main.c44
-rw-r--r--src/maze.h32
-rw-r--r--src/maze_generator.c113
-rw-r--r--src/maze_generator.h8
-rw-r--r--src/maze_solver.c78
-rw-r--r--src/maze_solver.h8
-rw-r--r--src/menu.c184
-rw-r--r--src/menu.h8
-rw-r--r--src/utilities.c56
-rw-r--r--src/utilities.h14
-rw-r--r--tests/test.c17
37 files changed, 1773 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e660fd9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+bin/
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..8102aec
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,83 @@
+CC = gcc
+CFLAGS = -Isrc $$(ncursesw5-config --cflags)
+LIBS = $$(ncursesw5-config --libs)
+#LIBS = -lncursesw
+
+PREFIX = /usr/local
+MANPREFIX = $(PREFIX)/man
+
+all: bin/maze
+
+debug: CFLAGS += -Wall -DEBUG -g
+debug: bin/maze
+
+app_sources = $(wildcard src/*.c src/game/*.c)
+app_objects = $(patsubst src/%.c, bin/%.o, $(app_sources))
+
+bin/maze: $(app_objects)
+ $(CC) $(CFLAGS) $^ -o $@ $(LIBS)
+
+bin/main.o: src/main.c bin
+ $(CC) $(CFLAGS) -c $< -o $@
+
+bin/args.o: src/args.c bin
+ $(CC) $(CFLAGS) -c $< -o $@
+
+bin/config.o: src/config.c bin
+ $(CC) $(CFLAGS) -c $< -o $@
+
+bin/color.o: src/color.c bin
+ $(CC) $(CFLAGS) -c $< -o $@
+
+bin/menu.o: src/menu.c bin
+ $(CC) $(CFLAGS) -c $< -o $@
+
+bin/game/entry.o: src/game/entry.c bin/game
+ $(CC) $(CFLAGS) -c $< -o $@
+
+bin/game/game.o: src/game/game.c bin/game
+ $(CC) $(CFLAGS) -c $< -o $@
+
+bin/game/operations.o: src/game/operations.c bin/game
+ $(CC) $(CFLAGS) -c $< -o $@
+
+bin/game/commands.o: src/game/commands.c bin/game
+ $(CC) $(CFLAGS) -c $< -o $@
+
+bin/file.o: src/file.c bin
+ $(CC) $(CFLAGS) -c $< -o $@
+
+bin/maze_generator.o: src/maze_generator.c bin
+ $(CC) $(CFLAGS) -c $< -o $@
+
+bin/maze_solver.o: src/maze_solver.c bin
+ $(CC) $(CFLAGS) -c $< -o $@
+
+bin/utilities.o: src/utilities.c bin
+ $(CC) $(CFLAGS) -c $< -o $@
+
+bin:
+ mkdir -p $@
+
+bin/game:
+ mkdir -p $@
+
+clean:
+ rm -rf bin/*
+
+install: bin/maze
+ @echo INSTALL execuatble
+ mkdir -p $(PREFIX)/bin
+ cp $< $(PREFIX)/bin/maze
+ chmod 755 $(PREFIX)/bin/maze
+ @echo INSTALL man page
+ gzip -c doc/maze.1 > $(MANPREFIX)/man1/maze.1.gz
+ chmod 644 $(MANPREFIX)/man1/maze.1.gz
+
+uninstall:
+ @echo REMOVE execuatble
+ rm -f $(PREFIX)/bin/maze
+ @echo REMOVE man page
+ rm -f $(MANPREFIX)/man1/maze.1.gz
+
+.PHONY: clean install uninstall
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3bc8150
--- /dev/null
+++ b/README.md
@@ -0,0 +1,58 @@
+**Maze**
+
+maze is a command line maze game.
+
+Features
+--------
+
+- [x] Load/save maze from/to text file.
+- [x] Generate random maze.
+- [x] Solve maze.
+- [x] Help you solve maze by one move at once.
+- [x] Configurable key bindings in defaults.h.
+
+Dependencies
+------------
+
+* ncurses
+
+On ubuntu, you can install the dependencies by running the following command with root privilages:
+
+ # apt install libncurses-dev
+
+Installation
+------------
+
+Currently, this program is not available in any package manager, so you have to build it from source. You can run this command that will build and install the program on your system:
+
+ # make install
+
+By default, maze is installed using the prefix "/usr/local", so the full path of the executable will be "/usr/local/bin/maze".
+
+You can install maze into a directory of your choice by changing the second command to:
+
+ # make PREFIX="/your/dir" install
+
+In order to remove the program from your system, run the following command:
+
+ # make uninstall
+
+Please note that these commands require root privileges.
+
+If you don't want to install, just run it, you can do it in this way:
+
+ $ make
+ $ ./bin/maze
+
+Usage
+-----
+
+Please see the man page for more information.
+
+You can see the man page by runing the following command in the project directiory:
+
+ $ man doc/maze.1
+
+Or you can do this, if you installed the program:
+
+ $ man maze
diff --git a/count.sh b/count.sh
new file mode 100755
index 0000000..96700ff
--- /dev/null
+++ b/count.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+find src -name '*.c' | xargs wc -l
diff --git a/debug.sh b/debug.sh
new file mode 100755
index 0000000..0819364
--- /dev/null
+++ b/debug.sh
@@ -0,0 +1,2 @@
+make clean
+make debug \ No newline at end of file
diff --git a/doc/maze.1 b/doc/maze.1
new file mode 100644
index 0000000..e7700bd
--- /dev/null
+++ b/doc/maze.1
@@ -0,0 +1,102 @@
+.TH MAZE 6
+.SH NAME
+maze - command line maze game
+.SH SYNOPSIS
+.B maze
+.RB [ \-i
+.IR INPUT_FILE ]
+.RB [ \-o
+.IR OUTPUT_FILE ]
+.RB [ \-log
+.IR PATH ]
+.RB [ \-h ]
+.RB [ \-g ]
+.RB [ \-s ]
+.SH DESCRIPTION
+maze is a command line maze game.
+
+It can generates random mazes or read them from file.
+.SH OPTIONS
+.TP
+.BI "\-i " INPUT_FILE
+Load maze from
+.IR INPUT_FILE
+file.
+.TP
+.BI "\-o " OUTPUT_FILE
+Save maze to
+.IR OUTPUT_FILE
+file.
+.TP
+.BI "\-log " PATH
+Enable logging and set the path of the log file to
+.IR PATH .
+.TP
+.B \-h
+Hide the menu.
+.TP
+.B \-g
+Generate random maze.
+.TP
+.B \-s
+Start the game.
+
+.SH KEY BINDINGS
+In the game you can use the following key combinations for controlling the enviroment.
+
+.TP
+.B k, Up
+Move the line up.
+.TP
+.B j, Down
+Move the line down.
+.TP
+.B h, Left
+Move the line left.
+.TP
+.B l, Right
+Move the line right.
+.TP
+.B Ctrl-k, Ctrl-Up
+Move the maze up.
+.TP
+.B Ctrl-j, Ctrl-Down
+Move the maze down.
+.TP
+.B Ctrl-h, Ctrl-Left
+Move the maze left.
+.TP
+.B Ctrl-l, Ctrl-Right
+Move the maze right.
+.TP
+.B :
+Enter the
+.B maze
+command prompt.
+.TP
+.B n
+Move the line by one into the direction of exit.
+.TP
+.B s
+Solve the maze.
+.TP
+.B p
+Show/hide path.
+.TP
+.B r
+Generate new maze with the same size of the previous maze.
+.TP
+.B m
+Go to menu.
+.TP
+.B q
+Quit from the program.
+
+.SH COMMANDS
+This section contains the commands, you can run if you enter to the
+.B maze
+command prompt.
+
+.SH AUTHOR
+Sopár Adrián <\fIadrian.sopar@protonmail.com\fR>
+
diff --git a/files/2.txt b/files/2.txt
new file mode 100644
index 0000000..ebf08df
--- /dev/null
+++ b/files/2.txt
@@ -0,0 +1,7 @@
+start 1 1
+end 3 3
+11111
+10001
+11101
+10001
+11111
diff --git a/files/asd.txt b/files/asd.txt
new file mode 100644
index 0000000..9811bb3
--- /dev/null
+++ b/files/asd.txt
@@ -0,0 +1,23 @@
+start=(1,1)
+end=(39,19)
+11111111111111111111111111111111111111111
+10000000000000000000100000100000000010001
+11111111111111111110101111101111101011101
+10000000000000100010100000100000101000001
+11111110111111101010111010101111101111101
+10000010001000001010001010001000001010001
+10111011101011111011101011111011111010111
+10100000001000100000101000001000100010001
+10111111111110101111101111111110101111101
+10000000000010100000001000000000100000001
+10111111111010111111111011111111101111111
+10000000001000101000000010001000001000001
+11111111101111101010111110101011111011101
+10000000001000000010001010100010100000101
+10111111111011111111101010111110101110101
+10001000001010000010101000100010001000101
+11101010101010111010101011101011111011101
+10001010101000100000101010001000100010001
+10111110101111111111101010111110101110111
+10000000100000000000001000100000001000001
+11111111111111111111111111111111111111111
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 <string.h>
+
+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 <stdbool.h>
+
+#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 <ncurses.h>
+
+#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 <ncurses.h>
+
+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 <string.h>
+
+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 <stdlib.h>
+#include <stdbool.h>
+#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 <ncurses.h>
+#include <stdio.h>
+
+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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#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 <string.h>
+
+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 <stdlib.h>
+#include <string.h>
+#include <ncurses.h>
+#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 <string.h>
+
+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 <stdlib.h>
+#include <stdbool.h>
+#include <ncurses.h>
+#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 <string.h>
+#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 <ncurses.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <time.h>
+#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 <stdlib.h>
+#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 <string.h>
+#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 <stdlib.h>
+#include <ncurses.h>
+#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 <string.h>
+#include <time.h>
+
+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 <stdlib.h>
+
+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
diff --git a/tests/test.c b/tests/test.c
new file mode 100644
index 0000000..f72fd23
--- /dev/null
+++ b/tests/test.c
@@ -0,0 +1,17 @@
+#include <criterion/criterion.h>
+#include <criterion/theories.h>
+
+#define A \
+ { \
+ DataPoints(int, 0, 1, 2) \
+ }
+
+TheoryDataPoints(maze, generate) = A;
+TheoryDataPoints(maze, solve) = A;
+
+Theory((int seed), maze, generate)
+{
+ srand(seed);
+}
+
+Theory((int seed), maze, solve) {} \ No newline at end of file