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