// omarek | 2023-12-18

#include <stdio.h>
#include <stdlib.h>

// expecting odd nums
#define WIDTH 31
#define HEIGHT 31

#define WALL_CHAR 'X' // suggested: X @ % # O
#define ALT_WALL_CHAR '\0' // '\0' means no altering
#define VOID_CHAR ' ' // suggested: . <whitespace>

// maze is GLOBAL
char maze[WIDTH*HEIGHT];

// returns rand number in range [0--max]
int get_rand_num(int max) {
    return rand() % (max + 1);
}

int get_rand_odd_row(void) {
	return 1 + get_rand_num(HEIGHT/2 - 1)*2;
}

void shuffle_array(int* a, int len) {
	int swap_index = 0;
	for (int i = 0; i < len; i++)
	{
		swap_index = get_rand_num(len-1);
		int tmp = a[swap_index];
		a[swap_index] = a[i];
		a[i] = tmp;
	}
}

// prints out maze
void print_maze(void) {
	int i_maze = 0;
	printf("\n");
	for (int i_row = 0; i_row < HEIGHT; i_row++)
	{
		for (int i_col = 0; i_col < WIDTH; i_col++)
		{
			printf("%c", maze[i_maze]);
			i_maze++;
		}
		printf("\n");
	}
	printf("\n");
}

// fills whole maze with argument
void fill_maze(char c) {
	int limit = WIDTH*HEIGHT;
	for (int i = 0; i < limit; i++)
		maze[i] = c;
}

// fills 'walls' with argument
void walls_maze(char c) {
	int limit = WIDTH*HEIGHT;
	// top
	for (int i = 0; i < WIDTH; i++)
		maze[i] = c;
	// bottom
	for (int i = 0; i < limit; i += WIDTH)
		maze[i] = c;
	// left
	for (int i = WIDTH-1; i < limit; i += WIDTH)
		maze[i] = c;
	// right
	for (int i = WIDTH*(HEIGHT-1); i < limit; i++)
		maze[i] = c;
}

// generates inner walls of maze
// follows intersecs of even coordinations
// spans a line till wall is reached
void generate_inner_walls(void) {
	// array of intersecs
	int intersec_count = (WIDTH-3)*(HEIGHT-3) / 4;
	int intersec_cells[intersec_count];
	int i_intersec_cells = 0;
	// first row & col and last row & col are ommited
	for (int row = 2; row < HEIGHT-1; row += 2) {
		for (int col = 2; col < WIDTH-1; col += 2) {
			intersec_cells[i_intersec_cells] = row*WIDTH + col;
			i_intersec_cells++;
		}
	}

	// shuffle intersecs
	shuffle_array(intersec_cells, intersec_count);

	// spaning lines
	for (int i = 0; i < intersec_count; i++)
	{
		int turtle_pos = intersec_cells[i];
		// some line already spaning through?
		if (maze[turtle_pos] == WALL_CHAR)
			continue;

		// generate direction and step is based on it
		// 0 = top, 1 = right, 2 = bottom, 3 = left
		int generated_dir = get_rand_num(3);
		int step = -WIDTH;                         // top (default)
		if      (generated_dir == 1) step = 1;     // right
		else if (generated_dir == 2) step = WIDTH; // bottom
		else if (generated_dir == 3) step = -1;    // left

		do
		{
			// draw
			maze[turtle_pos] = WALL_CHAR;
			// step
			turtle_pos += step;
		} while (maze[turtle_pos] != WALL_CHAR);
	}

	// poke entrance and exit
	maze[get_rand_odd_row()*WIDTH] = VOID_CHAR; // 1st col
	maze[get_rand_odd_row()*WIDTH + WIDTH - 1] = VOID_CHAR; // last col
}

// adds special look to maze
void alter_maze(void) {
	int limit = WIDTH*HEIGHT;
	for (int i = 0; i < limit; i += 2)
	{
		if (maze[i] == WALL_CHAR)
			maze[i] = ALT_WALL_CHAR;
	}
}

// generates maze
void generate_maze(void) {
	fill_maze(VOID_CHAR);
	walls_maze(WALL_CHAR);
	generate_inner_walls();
}

int main(int argc, char* argv[]) {
	// seed
	int seed = 1; //default
	if (argc >= 2) seed = atoi(argv[1]); // take seed from input
	if (seed == 0) seed = 1; // on invalid input 1
	if (seed < 0) seed = -seed; // uint fix

	printf("\nmaze %dx%d#%d\n", WIDTH, HEIGHT, seed);
	if (argc < 2)
		printf("(takes seed as an argument: ./maze-generator [seed])\n");
	srand(seed);

	generate_maze();
	if (ALT_WALL_CHAR != 0)
		alter_maze();
	print_maze();
	return 0;
}
