mazegen.c (4872B)
1 /* See LICENSE file for copyright and license details. 2 * 3 * Maze generator - Implements the Deep-First Search algorithm. 4 */ 5 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <time.h> 10 #include <unistd.h> 11 12 #define N (1<<3) /* North */ 13 #define S (1<<2) /* South */ 14 #define W (1<<1) /* West */ 15 #define E (0x01) /* East */ 16 #define A (N|S|W|E) /* All the above */ 17 18 #define BORDERS 12 /* Unused */ 19 #define WALLS 8 20 #define SOLUTION 4 /* Unused */ 21 #define BACKTICK 0 /* Unused */ 22 23 #define ROWS 20 24 #define COLS 20 25 26 #define ISSET(C, W) (W == (C & W)) 27 28 /* globals */ 29 int rows, cols; 30 31 /* function declarations */ 32 void maze_gen(short int *grid, int TotalCells); 33 void maze_open(short int *grid); 34 int nextcell(short int *grid, int cell); 35 void pulldown(short int *grid, int CurrentCell, int cell); 36 void push(int *stack, int *top, int cell); 37 int pop(int *stack, int *top); 38 void maze_show(short int *grid); 39 40 /* function implementations */ 41 int 42 main(int argc, char *argv[]) { 43 short int *grid; 44 int c; 45 46 rows = ROWS; 47 cols = COLS; 48 49 while((c = getopt(argc, argv, "c:r:h")) != -1) { 50 switch(c) { 51 case 'c': 52 cols = atoi(optarg); 53 break; 54 case 'r': 55 rows = atoi(optarg); 56 break; 57 case 'h': 58 printf( 59 "Usage: %s -<hV> [-c cols] [-r rows]\n" 60 "\nOptions\n" 61 " -r rows\t: set the rows (default: %d)\n" 62 " -c cols\t: set the columns (default: %d)\n" 63 " -h\t: show this help\n" 64 " -V\t: show the version and exit\n" 65 "\n", 66 argv[0], ROWS, COLS); 67 return 1; 68 break; 69 default: 70 printf("Usage: %s -<hV> [-c cols] [-r rows]\n", argv[0]); 71 return 1; 72 } 73 } 74 75 if(rows <= 0|| cols <= 0) { 76 fprintf(stderr, "%s: invalid maze size (columns or rows)\n", argv[0]); 77 return 1; 78 } 79 80 if((grid = malloc(rows * cols * sizeof(short int))) == NULL) { 81 fprintf(stderr, "Sorry, there is not enough memory available.\n"); 82 return 1; 83 } 84 85 srandom(time(NULL)); 86 memset(grid, '\0', rows * cols); 87 88 maze_gen(grid, rows * cols); 89 maze_open(grid); 90 maze_show(grid); 91 92 free(grid); 93 94 return 0; 95 } 96 97 void 98 maze_open(short int *grid) { 99 int i; 100 101 for(i = 0; i < 2; i++) { 102 switch(1 + random() % 4) { 103 case 1: /* north */ 104 grid[random() % cols] ^= N << WALLS; 105 break; 106 case 2: /* south */ 107 grid[cols * (rows - 1) + (random() % cols)] ^= S << WALLS; 108 break; 109 case 3: /* east */ 110 grid[(random() % rows) * cols + (cols - 1)] ^= E << WALLS; 111 break; 112 case 4: /* west */ 113 grid[(random() % rows) * cols] ^= W << WALLS; 114 break; 115 } 116 } 117 } 118 119 void 120 push(int *stack, int *top, int cell) { 121 stack[(*top)++] = cell; 122 } 123 124 int 125 pop(int *stack, int *top) { 126 if(! *top) { 127 printf("The stack is empty.\n"); 128 return 0; 129 } 130 131 return stack[--(*top)]; 132 } 133 134 void 135 maze_gen(short int *grid, int TotalCells) { 136 int CurrentCell = random() / (RAND_MAX / TotalCells + 1); 137 int VisitedCells = 1; 138 int AdCell; /* Adiacent cell */ 139 int *stack, top = 0; 140 int i; 141 142 if(! (stack = malloc(sizeof(int) * TotalCells))) 143 return; 144 145 for(i = 0; i < rows * cols; i++) 146 grid[i] = A << WALLS; 147 148 while(VisitedCells < TotalCells) { 149 if((AdCell = nextcell(grid, CurrentCell)) != -1) { 150 pulldown(grid, CurrentCell, AdCell); 151 152 push(stack, &top, CurrentCell); 153 CurrentCell = AdCell; 154 ++VisitedCells; 155 } 156 else 157 CurrentCell = pop(stack, &top); 158 } 159 } 160 161 int 162 nextcell(short int *grid, int cell) { 163 int idx = 0, cells[4] = {0}; 164 165 /* TODO: cleanup this code */ 166 167 if(cell % cols && cell - 1 >= 0 && ISSET(grid[cell - 1], A << WALLS)) 168 cells[idx++] = cell - 1; 169 170 if((cell % cols) != (cols - 1) && 171 cell + 1 < rows * cols && ISSET(grid[cell + 1], A << WALLS)) 172 cells[idx++] = cell + 1; 173 174 if(cell - cols >= 0 && ISSET(grid[cell - cols], A << WALLS)) 175 cells[idx++] = cell - cols; 176 177 if(cell + cols < rows * cols && ISSET(grid[cell + cols], A << WALLS)) 178 cells[idx++] = cell + cols; 179 180 return ! idx ? -1 : cells[random() / (RAND_MAX / idx + 1)]; 181 } 182 183 void 184 pulldown(short int *grid, int CurrentCell, int cell) { 185 186 /* Note: I have to pull down the walls in both CurrentCell and cell in 187 * order be make showmaze() works properly. */ 188 189 if(CurrentCell == cell - 1) { 190 grid[CurrentCell] ^= E << WALLS; 191 grid[cell] ^= W << WALLS; 192 } 193 194 else if(CurrentCell == cell + 1) { 195 grid[CurrentCell] ^= W << WALLS; 196 grid[cell] ^= E << WALLS; 197 } 198 199 else if(CurrentCell < cell) { 200 grid[CurrentCell] ^= S << WALLS; 201 grid[cell] ^= N << WALLS; 202 } 203 204 else if(CurrentCell > cell) { 205 grid[CurrentCell] ^= N << WALLS; 206 grid[cell] ^= S << WALLS; 207 } 208 209 } 210 211 void 212 maze_show(short int *grid) { 213 int i; 214 215 printf(" "); 216 for(i = 0; i < cols; i++) 217 printf("%c%c", ISSET(grid[i], N << WALLS) ? 218 '_' : ' ', (i == cols - 1 ? ' ' : '_')); 219 220 for(i = 0; i < rows * cols; i++) { 221 if(!(i % cols)) { 222 printf("\n%c", ISSET(grid[i], W << WALLS) ? '|' : ' '); 223 } 224 225 printf("%c%c", 226 ISSET(grid[i], S << WALLS) ? '_' : ' ', 227 ISSET(grid[i], E << WALLS) ? '|' : '_'); 228 } 229 230 printf("\n"); 231 232 }