Maze Runner Program
++ Maze Runner Files ++ Makefile for Maze Runner
This commit is contained in:
parent
42fc64e613
commit
bf83664da4
6 changed files with 324 additions and 0 deletions
39
MazeRunner/README.txt
Normal file
39
MazeRunner/README.txt
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
In this project, you will fill in the code necessary to solve a maze. I have
|
||||
already written the code which is capable of generating and displaying the
|
||||
maze, as well as a few function that will let you interact with the maze. All
|
||||
you have to do is write the code that actually marks the path through the maze.
|
||||
|
||||
I have outlined an algorithm in English by which you might accomplish this in
|
||||
the file "runner.c" which is the *only* file that you should edit and submit
|
||||
for this assignment.
|
||||
|
||||
Since this program will consist of multiple files that all need to be connected
|
||||
together to work, I have provided a Makefile to assist with compiling it. If
|
||||
you are on Mac, you should already have the "make" program installed. If you
|
||||
are on WSL, execute the command "sudo apt-get install build-essential" to gain
|
||||
access to "make".
|
||||
|
||||
To build the project, open up a shell, move to the directory containing the
|
||||
project files, and simply issue the command 'make'. This will build the
|
||||
project and show any compiler warnings and/or errors. If the build is
|
||||
successful, you will be able to execute the program via the command
|
||||
"./maze_runner". Do this once now to see the usage text which tells you how
|
||||
the program ought to be invoked.
|
||||
|
||||
Now, all that's left for you to do is to open up the various code files and
|
||||
explore them and then to write your part of the code in runner.c.
|
||||
|
||||
The point of this assignment is for you to get practice breaking a larger
|
||||
problem down into subproblems via functions, so make sure that you try to do
|
||||
that. For reference, in my solution to this problem, I created 7 other
|
||||
functions that are called by runner_solve(). While it is possible to complete
|
||||
this project with fewer functions, you should probably have at least 4.
|
||||
|
||||
Again, don't edit any files except runner.c as I will be testing your
|
||||
submission with the "stock" version of all the other project files. Should you
|
||||
accidentally modify another file, you can always just unzip the archive they
|
||||
came in again. But, ***BE CAREFUL*** that you don't accidentally override the
|
||||
file you've done all your work in if you do that.
|
||||
|
||||
When you are satisfied with your work, simply upload *only* your runner.c file
|
||||
to the BBLearn assignment submission. Good luck!
|
||||
54
MazeRunner/maze_runner.c
Normal file
54
MazeRunner/maze_runner.c
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "mazelib.h"
|
||||
#include "runner.h"
|
||||
|
||||
void usage(void);
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc < 3) {
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int width = (int) strtol(argv[1], NULL, 10);
|
||||
int height = (int) strtol(argv[2], NULL, 10);
|
||||
|
||||
// The dimensions are valid when the width is between 9 and 79 (inclusive) and
|
||||
// the height is between 9 and 25 (inclusive). Both the width and the height
|
||||
// must also be odd.
|
||||
bool dimensions_are_valid = ((9 <= width && width <= 79) &&
|
||||
(9 <= height && height <= 25) &&
|
||||
(width % 2 == 1) && (height % 2 == 1));
|
||||
if(!dimensions_are_valid) {
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Oh, if you want to reuse the same maze for testing purposes, you can pass
|
||||
// a secret 3rd argument to the program. Any integer will do, just pass the
|
||||
// same one each time and it will be the same maze
|
||||
unsigned int seed;
|
||||
if (argc == 4) {
|
||||
seed = (int) strtol(argv[3], NULL, 10);
|
||||
} else {
|
||||
seed = time(NULL);
|
||||
}
|
||||
|
||||
maze_init(width, height, seed);
|
||||
|
||||
maze_display();
|
||||
printf("\n\n\n");
|
||||
// You must implement this function in runner.c and declare it in runner.h
|
||||
runner_solve();
|
||||
maze_display();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This function displays the usage message if the parameters are missing or incorrect.
|
||||
void usage(void) {
|
||||
printf("Usage: mazerunner width height \n");
|
||||
printf("\t width must be an odd integer between 9 and 79 inclusive\n");
|
||||
printf("\t height must be an odd integer between 9 and 25 inclusive\n\n");
|
||||
}
|
||||
162
MazeRunner/mazelib.c
Normal file
162
MazeRunner/mazelib.c
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
// PAY NO MIND TO ANYTHING FROM HERE...
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include "mazelib.h"
|
||||
|
||||
#define NORTH 0
|
||||
#define EAST 1
|
||||
#define SOUTH 2
|
||||
#define WEST 3
|
||||
|
||||
static void carve(int, int);
|
||||
static void shuffle(int[4]);
|
||||
|
||||
static int maze_width;
|
||||
static int maze_height;
|
||||
static char *maze;
|
||||
static bool is_maze_being_initialized = false;
|
||||
// ... TO HERE
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// BEGIN: THINGS YOU SHOULD LOOK AT (mostly the comments) //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// Returns the character in the maze at the given row and column. First checks
|
||||
// to ensure that the attempted access is valid and, if not, terminates the
|
||||
// program.
|
||||
char maze_get_char(int row, int col) {
|
||||
bool is_in_bounds = ((0 <= row && row < maze_height) &&
|
||||
(0 <= col && col < maze_width));
|
||||
if (!is_in_bounds) {
|
||||
printf("Illegal maze access at row %i and col %i\n", row, col);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return maze[(row * maze_width) + col];
|
||||
}
|
||||
|
||||
|
||||
// Sets the character at the given row and column to the replacement
|
||||
// character. Replacement of the start and end signifiers and walls (#'s)
|
||||
// will be prevented unless the maze is being initialized, in which case, it
|
||||
// is allowed.
|
||||
//
|
||||
// Since it calls maze_get_char first, this function will also cause program
|
||||
// termination if passed a row or column that is out of bounds
|
||||
void maze_set_char(int row, int col, char replacement) {
|
||||
char c = maze_get_char(row, col);
|
||||
if (!is_maze_being_initialized && (c == '#' || c == 'S' || c == 'E')) {
|
||||
return;
|
||||
}
|
||||
|
||||
maze[(row * maze_width) + col] = replacement;
|
||||
}
|
||||
|
||||
|
||||
void maze_display(void) {
|
||||
for(int row = 0; row < maze_height; row++) {
|
||||
for(int col = 0; col < maze_width; col++) {
|
||||
printf("%c", maze_get_char(row, col));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// BEGIN: STUFF YOU DON'T NEED TO LOOK AT OR UNDERSTAND FOR THE PROJECT //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// maze_init allocates the memory for the maze array based on width and height,
|
||||
// fills every square of the maze with walls, and then calls carve() to
|
||||
// remove walls in a way that creates valid maze paths
|
||||
void maze_init(int width, int height, int seed) {
|
||||
maze_width = width;
|
||||
maze_height = height;
|
||||
|
||||
maze = malloc(sizeof(*maze) * maze_width * maze_height);
|
||||
|
||||
// Fill maze completely with walls
|
||||
for(int i = 0; i < maze_width * maze_height; i++) {
|
||||
maze[i] = '#';
|
||||
}
|
||||
|
||||
is_maze_being_initialized = true;
|
||||
// Seed the random number generator, since carve() uses of random numbers
|
||||
srand(seed);
|
||||
// We always start carving the maze from row 1 column 1 to ensure
|
||||
// that we always have a valid start there and a valid end in the bottom
|
||||
// right hand corner
|
||||
carve(1, 1);
|
||||
|
||||
maze_set_char(1, 1, 'S');
|
||||
maze_set_char(maze_height-2, maze_width-2, 'E');
|
||||
is_maze_being_initialized = false;
|
||||
}
|
||||
|
||||
/*
|
||||
Starts at a given index and recursively carves toward every direction in a
|
||||
randomized order. Recursion ends at a particular location when the blocks in
|
||||
every possible direction have already been carved.
|
||||
*/
|
||||
static void carve(int row, int col) {
|
||||
maze_set_char(row, col, ' ');
|
||||
|
||||
int directions[4] = {NORTH, EAST, SOUTH, WEST};
|
||||
shuffle(directions);
|
||||
|
||||
for (int i=0; i < 4; i++) {
|
||||
// Find the appropriate offsets for the row and column to move in the
|
||||
// current direction
|
||||
|
||||
int row_offset = 0;
|
||||
int col_offset = 0;
|
||||
int current_direction = directions[i];
|
||||
// Only one offset will be changed and the other will remain zero
|
||||
if (current_direction == NORTH) {
|
||||
row_offset = -1;
|
||||
}
|
||||
else if (current_direction == SOUTH) {
|
||||
row_offset = +1;
|
||||
}
|
||||
else if (current_direction == WEST) {
|
||||
col_offset = -1;
|
||||
}
|
||||
else {
|
||||
col_offset = +1;
|
||||
}
|
||||
|
||||
// Find the row and col for the spot *two* squares forward in the current
|
||||
// direction
|
||||
int next_row = row + (2 * row_offset);
|
||||
int next_col = col + (2 * col_offset);
|
||||
|
||||
bool is_in_bounds = ((0 <= next_row && next_row < maze_height) &&
|
||||
(0 <= next_col && next_col < maze_width));
|
||||
|
||||
// If that spot is in bounds and hasn't already been carved out...
|
||||
if (is_in_bounds && maze_get_char(next_row, next_col) == '#') {
|
||||
// carve out the wall between the current spot from and the next spot...
|
||||
maze_set_char(row + row_offset, col + col_offset, ' ');
|
||||
// and then continue carving from the new spot
|
||||
carve(next_row, next_col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void shuffle(int directions[4]) {
|
||||
for(int i = 0; i < 4; i++) {
|
||||
// Random number between 0 and 3
|
||||
int random_index = rand() % 4;
|
||||
// Swap the thing at the random index and the current index (i)
|
||||
int temp = directions[random_index];
|
||||
directions[random_index] = directions[i];
|
||||
directions[i] = temp;
|
||||
}
|
||||
}
|
||||
14
MazeRunner/mazelib.h
Normal file
14
MazeRunner/mazelib.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
//-------------Functions you SHOULD use in your runner.c file------------------
|
||||
// Take a look in mazelib.c to see how they work and what they are for
|
||||
char maze_get_char(int, int);
|
||||
void maze_set_char(int, int, char);
|
||||
|
||||
//----Functions you may use for debugging, but not in your final submission----
|
||||
void maze_display(void);
|
||||
|
||||
//-----------------------Functions you should NOT use--------------------------
|
||||
void maze_init(int, int, int);
|
||||
52
MazeRunner/runner.c
Normal file
52
MazeRunner/runner.c
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
// Including a header file lets your file refer to the functions *declared* in
|
||||
// that header file. The functions are usually *defined* elsewhere though, in
|
||||
// this case, mazelib.c
|
||||
#include "mazelib.h"
|
||||
|
||||
#define NORTH 0
|
||||
#define EAST 1
|
||||
#define SOUTH 2
|
||||
#define WEST 3
|
||||
|
||||
|
||||
void runner_solve(void) {
|
||||
// Use a few variables to keep track of the current row and column position
|
||||
// of the "runner". The runner should always start at the 'S' symbol in the
|
||||
// maze. Luckily, the 'S' symbol is always in the same place
|
||||
|
||||
// You should also keep track of which way the runner is facing. You can use
|
||||
// the preprocessor defines at the top of this file to make your life easier
|
||||
// instead of just having to remember "0 is North, 1 is East, etc."
|
||||
// Basically, any time you use NORTH in your code, a zero will be substituted
|
||||
// in for it at compile time.
|
||||
|
||||
// Ok, algorithm time:
|
||||
//
|
||||
// 1. As long as you are not standing on the 'E', do the following:
|
||||
// 2. Turn to your left
|
||||
// 3. Can you go that way? I.E., is there not a wall in front of you?
|
||||
// 4. If your path is blocked, turn to your right and go back to (3)
|
||||
// 5. If your path is open, throw down the appropriate breadcrumb (see note)
|
||||
// 6. Move forward and go back to (1)
|
||||
// 7. You are standing on the 'E', STOP, you're done!
|
||||
|
||||
// NOTE: By "appropriate breadcrumb" I mean this:
|
||||
// If you are in an empty square, the breadcrumb should be '.'
|
||||
// If you are in a square with '.', the breadcrumb should be 'o'
|
||||
// If you are in a square with 'o', the breadcrumb should be 'O'
|
||||
// If you are in a square with 'O', the breadcrumb should be '@'
|
||||
//
|
||||
// Here is an example of a solved maze:
|
||||
//
|
||||
// ###########################
|
||||
// #S....#.oo..#.oooOoooooo..#
|
||||
// #####.###.#.#####o#####.#.#
|
||||
// # #.#...#.....#.#.....#.#
|
||||
// # # #.#.#######.###.#####.#
|
||||
// # # #...# #.#...#.....#
|
||||
// # ####### # ###.#.###.#####
|
||||
// # # ...# ....E#
|
||||
// ###########################
|
||||
}
|
||||
3
MazeRunner/runner.h
Normal file
3
MazeRunner/runner.h
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
void runner_solve(void);
|
||||
Loading…
Add table
Add a link
Reference in a new issue