Maze generator game in C
Writing an interactive Maze generator is a task that helps to illustrate various computer science concepts in a simple way. This is a C implementation based of one school project I did a while ago.
Introduction
During my school subject of Operating Systems we had to write a simple program
demonstrating the usage for the ncurses
library. I choose to
write a Maze generator and it turned out to be a really great challenge and
fun project.
Maze generation is a really interesting area of application of diverse
algorithms and user interaction techniques for it to become a game. I will
try to share with you how to write a maze game using the C language and the
ncurses
library.
The game should generate a random maze and allow the player to navigate through it using the arrow keys. The goal is for the user to reach the finish point. Let’s dive into the code!
Libraries and Constants
The very first thing we need to do is to include the ncessary libraries and define the constatns required for the game:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // Libraries #include <stdio.h> // We need standard I/O! #include <ncurses.h> // 🪄 magic for our text UI! 🪄 #include <time.h> // We need to have a random(ish) seed for our map generation! #include <stdlib.h> // We use `malloc()` and such. // Constants #define WALL_CHAR '#' // The character to show as a wall. #define EMPTY_CHAR ' ' // The character to show as an empty space. #define PLAYER_CHAR '@' // The character to show a player. #define GOAL_CHAR 'G' // The character to show at the finish point! #define DIRECTION_UP 0 // The int ID for up direction. #define DIRECTION_LEFT 1 // The int ID for left direction. #define DIRECTION_RIGHT 2 // The int ID for right direction. #define DIRECTION_DOWN 3 // The int ID for down direction. #define EXIT_OPTION_LOWER 'q' // Lowercase letter for exiting the game. #define EXIT_OPTION_UPPER 'Q' // Uppercase letter for quitting the game. |
In the libraries section, we include ncurses.h
in order to use the ncurses
library and manipulate more efficiently our terminal interface. We also have
time.h
over there because we’ll have to initilize the random seed for our
map using functions from that library.
The constants defined are for redifining the characters we use to display the map, the direction ID’s and the letter that quits the game.
Enumerations and Structures
We define an enumeration CellContent
for the cell’s content and a structure
Cell
to represent the maze cells:
1 2 3 4 5 6 7 8 9 10 11 12 | enum CellContent { Wall, Empty, Player, Goal }; typedef struct Cell{ int row, // Y coordinate. column; // X coordinate. enum CellContent content; } Cell; |
Each cell has to be in one of the following states: Wall
, Empty
, Player
or
Goal
. We are going to be working with cartesian coordinates for the map so
we store the row
and column
for each Cell
.
Function Prototypes
By declaring our prototypes at the top of the file we can use the functions in
our main()
code without having to implement the function there (it doesn’t
even need to be in the same file!).
With just the signatures we’re able to grasp what the print_map
, depth_first_search
and get_available_directions
do:
1 2 3 | void print_map(WINDOW *maze_window, int rows, int cols, Cell **map); void depth_first_search(int current_row, int current_col, int rows, int cols, Cell **map); void get_available_directions(int current_row, int current_col, int rows, int cols, Cell **map, int available_directions[4]); |
However, if you’d like, you can jump into the implementation section where you can find the details of what (and why) each function does what it does.
Main Function
The main
function does a bunch of stuff:
- Initializes the
ncurses
’screen
. - Allocates memory for the maze.
- Generates a random maze using Depth-First Search.
- Handles the player’s input to move through the maze.
Many other things happen under the hood, but they’re mostly ramifications of those ones. Take a look:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | int main() { WINDOW *maze_window; // ncurses Window. int rows, // The amount of rows in the screen. cols, // The amount of columns in the screen. current_row, // The current row for iterations. current_col, // The current column for iterations. maze_window_rows, // The amount of rows in the maze window. maze_window_cols; // The amount of columns in the maze window. int player_row = 1, // The current row of the player. player_col = 1, // The current column of the player. goal_row, // The row for the goal. goal_col, // The column for the goal. key_pressed; // The ID of the key pressed by the user. Cell **map; // The map is a matrix of Cells. // Initialize the random seed. srand(time(NULL)); // Initialize the screen. initscr(); // Get the maximum amount of rows and columns for the current screen. getmaxyx(stdscr, rows, cols); // Calculate the amount of rows and columns available (margin). maze_window_rows = rows - 2; maze_window_cols = cols - 2; // Allocate memory for the array of pointers (arrays). map = malloc(maze_window_rows * sizeof(*map)); // Allocate memory for each row. for (current_row = 0; current_row < maze_window_rows; current_row++) { map[current_row] = malloc(maze_window_cols * sizeof *map[current_row]); } // Clear the screen. clear(); // Deactivate the "echo" function of the prompt. noecho(); // Eliminate the breaks. cbreak(); // Create the window. maze_window = newwin(maze_window_rows, maze_window_cols, 2, 1); // Associate the keypad to the window. keypad(maze_window, TRUE); // Print simple information. mvprintw(0, 0, "Dynamic Maze using ncurses.h - ESCOM Operating Systems"); mvprintw(1, 0, "Use the arrow keys to move through the window, reach the goal '%c' to win or press '%c' to exit.", GOAL_CHAR, EXIT_OPTION_LOWER); // Refresh the screen to show the changes. refresh(); // We randomly generate the maze by using a custom DFS function which will // go through the map and _carve_ its path through it. depth_first_search(1, 1, maze_window_rows, maze_window_cols, map); // Set the player in the map. map[player_row][player_col].content = Player; // Set the goal location in the map, the condition is to deal with odd sizes. if (map[maze_window_rows - 1][maze_window_cols - 1].content == Empty) { goal_row = maze_window_rows - 1; goal_col = maze_window_cols - 1; } else { goal_row = maze_window_rows - 2; goal_col = maze_window_cols - 2; } map[goal_row][goal_col].content = Goal; // Print the initial map. print_map(maze_window, maze_window_rows, maze_window_cols, map); // While the player doesn't reach the goal... while (player_row != goal_row || player_col != goal_col) { // Get the key pressed by the user. key_pressed = wgetch(maze_window); // Erase the user from it's current position. map[player_row][player_col].content = Empty; // Act based on the key entered. switch (key_pressed) { case KEY_UP: if (player_row > 1 && map[player_row - 1][player_col].content != Wall) { player_row -= 1; } break; case KEY_DOWN: if (player_row < (maze_window_rows - 1) && map[player_row + 1][player_col].content != Wall) { player_row += 1; } break; case KEY_LEFT: if (player_col > 1 && map[player_row][player_col - 1].content != Wall) { player_col -= 1; } break; case KEY_RIGHT: if (player_col < (maze_window_cols - 1) && map[player_row][player_col + 1].content != Wall) { player_col += 1; } break; case EXIT_OPTION_UPPER: case EXIT_OPTION_LOWER: // Set the coordinates to win to break the loop. player_col = goal_col; player_row = goal_row; break; } // Set the position of the player in the map. map[player_row][player_col].content = Player; // Print the map. print_map(maze_window, maze_window_rows, maze_window_cols, map); } // If the user won or lost, end the window. endwin(); // Free the memory allocated to each row within the map. for (current_row = 0; current_row < maze_window_rows; current_row++) { free(map[current_row]); } // Now free the map itself. free(map); // Print a simple message. printf("Congratulations! You reached the goal!\n"); // End the execution. return 0; } |
Helper Functions
Remember the functions’ prototypes we declared earlier? It’s time to look at the implementation. Each function is written with simplicity in mind, however you may have to look into the main function section to trace where is everything getting called.
Printing the map
The print_map
function does what it’s name says, maybe the only peculiarities
regarding this implementation is that we need to map our current map
into the
size of the maze_window
, other than that is read-and-display.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | void print_map(WINDOW *maze_window, int rows, int cols, Cell **map) { int row, col; // We iterate through each row. for (row = 0; row < rows; row++) { // We iterate throgh each column in each row. for (col = 0; col < cols; col++) { // Switch the content of the Cell. switch (map[row][col].content) { case Wall: mvwaddch(maze_window, row, col, WALL_CHAR); break; case Empty: mvwaddch(maze_window, row, col, EMPTY_CHAR); break; case Player: mvwaddch(maze_window, row, col, PLAYER_CHAR); break; case Goal: mvwaddch(maze_window, row, col, GOAL_CHAR); break; default: mvwaddch(maze_window, row, col, '?'); } } } // We have to refresh the window to show our changes in the screen. wrefresh(maze_window); } |
Depth-First Search (DFS)
We use a modified version of the well-known Depth-First Search algorithm (known
also as DFS) to get a random maze generated when the game
starts. If you see our main program we initialize the map by
filling it in with the Wall
enumeration value. Our DFS will try to carve into
the map the valid pathways for the player to navigate. Let’s see the definition:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | void depth_first_search(int current_row, int current_col, int rows, int cols, Cell **map) { int available_directions[4] = {0, 0, 0, 0}, // Array for storing the available directions for each cell. direction; // The selected direction. do { // Set the current location as Empty. map[current_row][current_col].content = Empty; // Get the UPDATED and available directions for the current Cell. get_available_directions(current_row, current_col, rows, cols, map, available_directions); // Check for the base case for our recursion: no available directions. if (!available_directions[DIRECTION_UP] && !available_directions[DIRECTION_DOWN] && !available_directions[DIRECTION_LEFT] && !available_directions[DIRECTION_RIGHT]); { // Bye bye! return; } // Get a random, avilable, direction. do { // Random value between 0 and 4 (0-3). direction = rand() % 4; } while(availble_directions[direction] == 0); // While the random direction is a not available direction. // We move to the next Cell based on the direction. switch(direction) { case DIRECTION_UP: // Carve the wall with a space to walk through. map[current_row - 1][current_col].content = Empty; // Move recursively to the next Cell. depth_first_search(current_row - 2, current_col, rows, cols, map); break; case DIRECTION_LEFT: // Carve the wall with a space to walk through. map[current_row][current_col - 1].content = Empty; // Move recursively to the next Cell. depth_first_search(current_row, current_col - 2, rows, cols, map); break; case DIRECTION_RIGHT: // Carve the wall with a space to walk through. map[current_row][current_col + 1].content = Empty; // Move recursively to the next Cell. depth_first_search(current_row, current_col + 2, rows, cols, map); break; case DIRECTION_DOWN: // Carve the wall with a space to walk through. map[current_row + 1][current_col].content = Empty; // Move recursively to the next Cell. depth_first_search(current_row + 2, current_col, rows, cols, map); break; } } while (available_directions[DIRECTION_UP] || available_directions[DIRECTION_DOWN] || available_directions[DIRECTION_LEFT] || available_directions[DIRECTION_RIGHT]) } |
So what we are doing here is:
- Set the current position as empty.
- Get the
available_directions
for our currentCell
. - Check for the base case of recursion: no directions available.
- Select pseudo-randomly1 a
direction
. - Carve an
Empty
space between our currentCell
and our destination. - Repeat from 1 until there are no available directions.
Get available directions
Each Cell
can have four available directions (ideally): DIRECTION_UP
,
DIRECTION_DOWN
, DIRECTION_RIGHT
and DIRECTION_LEFT
. A direction is available
if, 2 blocks from our Cell
, in the direction we’re testing, the Cell
is
a Wall
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | // Get the available directions for a cell. void get_available_directions(int current_row, int current_col, int rows, int cols, Cell **map, int available_directions[4]) { // DIRECTION_UP if ((current_row - 2) >= 1 && map[current_row - 2][current_col].content == Wall) { available_directions[DIRECTION_UP] = 1; } else { available_directions[DIRECTION_UP] = 0; } // DIRECTION_DOWN if ((current_row + 2) <= (rows - 2) && map[current_row + 2][current_col].content == Wall) { available_directions[DIRECTION_DOWN] = 1; } else { available_directions[DIRECTION_DOWN] = 0; } // DIRECTION_RIGHT if ((current_col + 2) <= (cols - 2) && map[current_row][current_col + 2].content == Wall) { available_directions[DIRECTION_RIGHT] = 1; } else { available_directions[DIRECTION_RIGHT] = 0; } // DIRECTION_LEFT if ((current_col - 2) >= 1 && map[current_row][current_col - 2].content == Wall) { available_directions[DIRECTION_LEFT] = 1; } else { available_directions[DIRECTION_LEFT] = 0; } } |
Compilation
In order to compile this program we need to have the ncurses
library installed,
for this you can follow the online instructions for your specific operating
system.
If you’re on macOS and using Homebrew, you just need to run the following
command:brew install libncurses
Once instaled the compilation flag required for linking our project is: -lncurses
.
The final command looks like this:
1 | $ gcc -o MazeGame.out -lncurses MazeGame.c
|
Execution
It’s time for the show time! Here are some screen captures of the mazes generated by the game:
And now here’s a GIF of me winning a game:
Final Code
The final code for this simple project is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 | // Libraries #include <stdio.h> // We need standard I/O! #include <ncurses.h> // 🪄 magic for our text UI! 🪄 #include <time.h> // We need to have a random(ish) seed for our map generation! #include <stdlib.h> // We use `malloc()` and such. // Constants #define WALL_CHAR '#' // The character to show as a wall. #define EMPTY_CHAR ' ' // The character to show as an empty space. #define PLAYER_CHAR '@' // The character to show a player. #define GOAL_CHAR 'G' // The character to show at the finish point! #define DIRECTION_UP 0 // The int ID for up direction. #define DIRECTION_LEFT 1 // The int ID for left direction. #define DIRECTION_RIGHT 2 // The int ID for right direction. #define DIRECTION_DOWN 3 // The int ID for down direction. #define EXIT_OPTION_LOWER 'q' // Lowercase letter for exiting the game. #define EXIT_OPTION_UPPER 'Q' // Uppercase letter for quitting the game. // Enumerations and Structures. enum CellContent { Wall, Empty, Player, Goal }; typedef struct Cell{ int row, // Y coordinate. column; // X coordinate. enum CellContent content; } Cell; // Function prototypes. void print_map(WINDOW *maze_window, int rows, int cols, Cell **map); void depth_first_search(int current_row, int current_col, int rows, int cols, Cell **map); void get_available_directions(int current_row, int current_col, int rows, int cols, Cell **map, int available_directions[4]); int main() { WINDOW *maze_window; // ncurses Window. int rows, // The amount of rows in the screen. cols, // The amount of columns in the screen. current_row, // The current row for iterations. current_col, // The current column for iterations. maze_window_rows, // The amount of rows in the maze window. maze_window_cols; // The amount of columns in the maze window. int player_row = 1, // The current row of the player. player_col = 1, // The current column of the player. goal_row, // The row for the goal. goal_col, // The column for the goal. key_pressed; // The ID of the key pressed by the user. Cell **map; // The map is a matrix of Cells. // Initialize the random seed. srand(time(NULL)); // Initialize the screen. initscr(); // Get the maximum amount of rows and columns for the current screen. getmaxyx(stdscr, rows, cols); // Calculate the amount of rows and columns available (margin). maze_window_rows = rows - 2; maze_window_cols = cols - 2; // Allocate memory for the array of pointers (arrays). map = malloc(maze_window_rows * sizeof(*map)); // Allocate memory for each row. for (current_row = 0; current_row < maze_window_rows; current_row++) { map[current_row] = malloc(maze_window_cols * sizeof *map[current_row]); } // Clear the screen. clear(); // Deactivate the "echo" function of the prompt. noecho(); // Eliminate the breaks. cbreak(); // Create the window. maze_window = newwin(maze_window_rows, maze_window_cols, 2, 1); // Associate the keypad to the window. keypad(maze_window, TRUE); // Print simple information. mvprintw(0, 0, "Dynamic Maze using ncurses.h - ESCOM Operating Systems"); mvprintw(1, 0, "Use the arrow keys to move through the window, reach the goal '%c' to win or press '%c' to exit.", GOAL_CHAR, EXIT_OPTION_LOWER); // Refresh the screen to show the changes. refresh(); // We randomly generate the maze by using a custom DFS function which will // go through the map and _carve_ its path through it. depth_first_search(1, 1, maze_window_rows, maze_window_cols, map); // Set the player in the map. map[player_row][player_col].content = Player; // Set the goal location in the map, the condition is to deal with odd sizes. if (map[maze_window_rows - 1][maze_window_cols - 1].content == Empty) { goal_row = maze_window_rows - 1; goal_col = maze_window_cols - 1; } else { goal_row = maze_window_rows - 2; goal_col = maze_window_cols - 2; } map[goal_row][goal_col].content = Goal; // Print the initial map. print_map(maze_window, maze_window_rows, maze_window_cols, map); // While the player doesn't reach the goal... while (player_row != goal_row || player_col != goal_col) { // Get the key pressed by the user. key_pressed = wgetch(maze_window); // Erase the user from it's current position. map[player_row][player_col].content = Empty; // Act based on the key entered. switch (key_pressed) { case KEY_UP: if (player_row > 1 && map[player_row - 1][player_col].content != Wall) { player_row -= 1; } break; case KEY_DOWN: if (player_row < (maze_window_rows - 1) && map[player_row + 1][player_col].content != Wall) { player_row += 1; } break; case KEY_LEFT: if (player_col > 1 && map[player_row][player_col - 1].content != Wall) { player_col -= 1; } break; case KEY_RIGHT: if (player_col < (maze_window_cols - 1) && map[player_row][player_col + 1].content != Wall) { player_col += 1; } break; case EXIT_OPTION_UPPER: case EXIT_OPTION_LOWER: // Set the coordinates to win to break the loop. player_col = goal_col; player_row = goal_row; break; } // Set the position of the player in the map. map[player_row][player_col].content = Player; // Print the map. print_map(maze_window, maze_window_rows, maze_window_cols, map); } // If the user won or lost, end the window. endwin(); // Free the memory allocated to each row within the map. for (current_row = 0; current_row < maze_window_rows; current_row++) { free(map[current_row]); } // Now free the map itself. free(map); // Print a simple message. printf("Congratulations! You reached the goal!\n"); // End the execution. return 0; } // Helper functions. // Print the map. void print_map(WINDOW *maze_window, int rows, int cols, Cell **map) { int row, col; // We iterate through each row. for (row = 0; row < rows; row++) { // We iterate throgh each column in each row. for (col = 0; col < cols; col++) { // Switch the content of the Cell. switch (map[row][col].content) { case Wall: mvwaddch(maze_window, row, col, WALL_CHAR); break; case Empty: mvwaddch(maze_window, row, col, EMPTY_CHAR); break; case Player: mvwaddch(maze_window, row, col, PLAYER_CHAR); break; case Goal: mvwaddch(maze_window, row, col, GOAL_CHAR); break; default: mvwaddch(maze_window, row, col, '?'); } } } // We have to refresh the window to show our changes in the screen. wrefresh(maze_window); } // Depth-First Search to randomly generate the Maze map. void depth_first_search(int current_row, int current_col, int rows, int cols, Cell **map) { int available_directions[4] = {0, 0, 0, 0}, // Array for storing the available directions for each cell. direction; // The selected direction. do { // Set the current location as Empty. map[current_row][current_col].content = Empty; // Get the UPDATED and available directions for the current Cell. get_available_directions(current_row, current_col, rows, cols, map, available_directions); // Check for the base case for our recursion: no available directions. if (!available_directions[DIRECTION_UP] && !available_directions[DIRECTION_DOWN] && !available_directions[DIRECTION_LEFT] && !available_directions[DIRECTION_RIGHT]); { // Bye bye! return; } // Get a random, avilable, direction. do { // Random value between 0 and 4 (0-3). direction = rand() % 4; } while(availble_directions[direction] == 0); // While the random direction is a not available direction. // We move to the next Cell based on the direction. switch(direction) { case DIRECTION_UP: // Carve the wall with a space to walk through. map[current_row - 1][current_col].content = Empty; // Move recursively to the next Cell. depth_first_search(current_row - 2, current_col, rows, cols, map); break; case DIRECTION_LEFT: // Carve the wall with a space to walk through. map[current_row][current_col - 1].content = Empty; // Move recursively to the next Cell. depth_first_search(current_row, current_col - 2, rows, cols, map); break; case DIRECTION_RIGHT: // Carve the wall with a space to walk through. map[current_row][current_col + 1].content = Empty; // Move recursively to the next Cell. depth_first_search(current_row, current_col + 2, rows, cols, map); break; case DIRECTION_DOWN: // Carve the wall with a space to walk through. map[current_row + 1][current_col].content = Empty; // Move recursively to the next Cell. depth_first_search(current_row + 2, current_col, rows, cols, map); break; } } while (available_directions[DIRECTION_UP] || available_directions[DIRECTION_DOWN] || available_directions[DIRECTION_LEFT] || available_directions[DIRECTION_RIGHT]) } // Get the available directions for a cell. void get_available_directions(int current_row, int current_col, int rows, int cols, Cell **map, int available_directions[4]) { // DIRECTION_UP if ((current_row - 2) >= 1 && map[current_row - 2][current_col].content == Wall) { available_directions[DIRECTION_UP] = 1; } else { available_directions[DIRECTION_UP] = 0; } // DIRECTION_DOWN if ((current_row + 2) <= (rows - 2) && map[current_row + 2][current_col].content == Wall) { available_directions[DIRECTION_DOWN] = 1; } else { available_directions[DIRECTION_DOWN] = 0; } // DIRECTION_RIGHT if ((current_col + 2) <= (cols - 2) && map[current_row][current_col + 2].content == Wall) { available_directions[DIRECTION_RIGHT] = 1; } else { available_directions[DIRECTION_RIGHT] = 0; } // DIRECTION_LEFT if ((current_col - 2) >= 1 && map[current_row][current_col - 2].content == Wall) { available_directions[DIRECTION_LEFT] = 1; } else { available_directions[DIRECTION_LEFT] = 0; } } |
If you’re interested in reading my class project from which all of this is based on, you can visit the GitHub Repository for it:
Thank you for reading!
-
As you can imagine, generating real random numbers is a bit of a hassle in computer science. We must assume the output of
rand()
to be a pseudo-random value. If you have more questions about it you can read it’s documentation at: https://en.cppreference.com/w/c/numeric/random/rand ↩