→This assignment is due by Friday, February 17, 2023, 11:59 PM.←
→ As with all assignments, this must be an individual effort and cannot be pair programmed. Any debugging assistance must follow the course collaboration policy and be cited in the comment header block for the assignment.←
→ Do not forget to complete the following labs with this set: L2A,
L2B
←
→ Do not forget to complete zyBooks Assignment 2 for this set.←
· Instructions · Rubric ·Submission ·
For this assignment you will process the game history for a simulated game of Hunt the Wumpus. Your program will read in the contents of a custom game file and then write out a game log that displays the sequence of events from the game.
Hunt the Wumpus
In the game, the Hero is trying to hunt the Wumpus in a dark cave and shoot it with one of his crooked arrows. Every turn the Hero and Wumpus move. If the Hero and Wumpus end in the same location, then the Hero shoots the Wumpus and the game ends with the Hero winning. Beware! There is also a pit hiding in the cave. If either player falls into the pit, then the game ends as well. If after all moves the Wumpus still lives, then the Hero loses the game.
(Note: This is mostly how the game is played. For our purposes the gameplay has been slightly modified and simplified.)
Wumpus Game History (.wgh) File Format
A sample wgh file is shown below as viewed in a text editor:
5 5
W 4 4
H 1 3
P 2 2
10
W N
H E
W W
H N
W W
H W
W S
H S
W E
H E
The file is comprised of three sections:
- The cave size
- The object placements
- The move sequence
The first line contains the width and height of the cave. Both of these values will be integers. We can think of the cave as a grid with the southwest corner denoted by the coordinate (1, 1) and the northeast corner denoted by the coordinate (width, height). Note: These values will be within the range 1 ≤ w, h, ≤ 100.
The next three lines contain the starting locations of the Hero (H), Wumpus (W), and Pit (P). These lines can be in any order. After the character denoting
which object is being placed, the integer (x, y) starting coordinate follows. Note: It is possible the character is something other than H
,
W
, or P
. It will be guaranteed that the (x, y) coordinate is within the range (1, 1) ≤ (w, h).
The remaining lines comprise the move sequence. The first line of this sections is an integer corresponding to the number of moves made. There will then follow an equal number of lines. The first character on the line denotes who is moving, the (H)ero or the (W)umpus. The next character then denotes which cardinal direction they are moving: (N)orth, (E)ast, (S)outh, or (W)est. Note: It is possible either character on the line may not be one of the expected values.
Several sample wgh files are provided with the starter pack for you to test with. You are encouraged to create your own Wumpus Game History files as well!
Instructions
For this assignment, we will write a program to read in a wgh file, process it, and then output a new log file. A starter code pack has been provided. Download and extract the source code. The source code package contains several items:
input/
- folder containing sample wgh images to read.output/
- empty folder to generate results into.solutions/
- folder containing the expected results for the public test cases. Compare your results against the expected output to verify proper execution.main.cpp
- expected program execution. You will not submit nor edit this file. Your solution will need to conform to the expected flow. More details about program execution are given below.Makefile
- used to build program. You will not submit nor edit this file. Your solution will need to conform to the expected flow.
The contents of main.cpp
are shown below:
/* CSCI 200: Assignment 2: A2 - Watch the Wumpus
*
* Author: Dr. Jeffrey Paone
*
* Starting template to process a Wumpus Game History (.wgh) file and
* output the corresponding game log.
*
* DO NOT EDIT THIS FILE
*/
#include "wumpus_functions.h"
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main() {
const string WUMPUS_FILE_NAME = enter_wumpus_file();
const string GAME_LOG_FILE_NAME = enter_game_log_file();
ifstream wumpusFileIn;
ofstream gameLogOut;
if( !open_files(wumpusFileIn, gameLogOut, WUMPUS_FILE_NAME, GAME_LOG_FILE_NAME) ) {
cerr << "Error opening files" << endl;
return -1;
}
int caveWidth, caveHeight;
read_world_header(wumpusFileIn, caveWidth, caveHeight);
int wumpusX = -1, wumpusY = -1;
int heroX = -1, heroY = -1;
int pitX = -1, pitY = -1;
if( !read_object_header(wumpusFileIn,
wumpusX, wumpusY,
heroX, heroY,
pitX, pitY) ) {
cerr << "Not all objects placed in world" << endl;
return -2;
}
int numMoves;
read_number_moves(wumpusFileIn, numMoves);
const bool PLAY_AS_HERO = choose_player();
play_game(wumpusFileIn, gameLogOut,
caveWidth, caveHeight,
numMoves,
wumpusX, wumpusY,
heroX, heroY,
pitX, pitY,
PLAY_AS_HERO);
return 0;
}
Our program now reads as a set of steps to perform. The steps are described below and then the function interface to implement these steps follow afterwards.
Program Flow
The execution follows as below.
Begin by prompting the user for which file they wish to read from.
Enter wumpus file to read from:
Then prompt the user for which file they wish to write to.
Enter game log file to write to:
Once we have two file names, open the corresponding input and output files.
If either file cannot be opened, kindly alert the user and gracefully end the program. This should be testable if you attempt to read from private.wgh
.
Now we're ready to start reading in the file and read the data!
We'll need to read the first two values (two integers) to know the size about our cave.
Next the object header (hero, wumpus, pit placement) needs to be read in. There will always be three lines in this section. If after reading the three lines one or more of the three objects have not been placed, then we'll need to alert the user the game is not properly setup and gracefully end the program.
Once we have the three objects placed, we'll ready how many turns we need to simulate.
In order to print the game log from the correct point of view, ask the user if they wish to play as the Hero or the Wumpus.
Who do you wish to play as?
H - Hero
W - Wumpus
Character:
Be sure to validate that the user enters only H
or W
.
If anything else is entered, then print Invalid choice, please select again.
and reprompt the user.
Regardless of the user choice, its time to simulate the game. For each move, first determine who is moving -
the Hero or the Wumpus. If the character is anything other than H
or W
, then ignore the rest of the line.
Once we know which direction the Hero or Wumpus is moving, update their position accordingly:
- North -> Y + 1
- South -> Y - 1
- East -> X + 1
- West -> X - 1
If the Hero or Wumpus is at the edge of the cave, then do not let them move that direction. Print a message in the format of
C cannot move D.
Where C
is Hero
or Wumpus
and D
is the direction being moved.
Then after every move, print the current location in the format of
C is at (X, Y).
Where C
is Hero
or Wumpus
and X, Y
is current location.
If after any move the Hero or Wumpus falls into the pit (is at the same location as the pit), then display who fell in the pit and who wins the game.
If after any move the Hero and Wumpus are at the same location, then display the Hero kills the Wumpus and the Hero wins the game.
For all messages, replace Hero
or Wumpus
with You
based on the role
the user has chosen to play as. Therefore, possible messages to print are of the form:
The Hero cannot move D.
The Wumpus cannot move D.
You cannot move D.
The Hero is at (X, Y).
The Wumpus is at (X, Y).
You are at (X, Y).
You have killed the Wumpus. You win!
You have been killed by the Hero. You lose.
The Hero has fallen in the pit. You win!
The Wumpus has fallen in the pit. You win!
You have fallen in the pit. You lose.
You have evaded the Hero. You win!
The Wumpus has evaded you. You lose.
All messages will be logged to the output file and every message must match the corresponding template exactly as appropriate. See
the solutions/
folder in the starter pack for expected output.
Code Structure & Functions
We will want to implement this program in a procedural style using functions. These functions will be declared in a separate
wumpus_functions.h
file with their implementations defined in a corresponding wumpus_functions.cpp
file.
Create, and use, the following functions:
-
- Name:
enter_wumpus_file()
- Input: None
- Output:
string
- Description: Prompts the user for the .wgh file to open.
- Name:
-
- Name:
enter_game_log_file()
- Input: None
- Output:
string
- Description: Prompts the user for the .log file to open.
- Name:
-
- Name:
open_files()
- Input: an
ifstream
object to open, anofstream
object to open, astring
corresponding to the file to read from, astring
corresponding to the file to write to - Output: true if both files successfully opened, false otherwise
- Description: Opens the corresponding files for reading and writing based on the file names provided. Verifies that both files successfully opened, returning true if so. Otherwise, if either file failed to open, returns false. (Hint: this can be tested by trying to open the private test file which doesn't exist in the starter pack.)
- Name:
-
- Name:
read_world_header()
- Input: an
ifstream
object to read from, integers corresponding to the width & height passed by reference - Output: None
- Description: Reads the cave size information of the file (the first two values). Upon completion, the parameters corresponding to the width & height will match the values read from the file.
- Name:
-
- Name:
read_object_header()
- Input: an
ifstream
object to read from, six integers corresponding to the (X, Y) location of the wumpus, hero, and pit respectively passed by reference - Output: true if all three objects are placed within the cave, false otherwise
- Description: Reads the object header information of the file (the next six values). Upon completion, the parameters corresponding to the (X, Y) location of each object will correspond to the values read from the file.
- Name:
-
- Name:
read_number_moves()
- Input: an
ifstream
object to read from, an integer corresponding to the number of moves that follow passed by reference - Output: None
- Description: Reads the number of moves information of the file (the next values). Upon completion, the parameter corresponding to the number of moves will match the value read from the file.
- Name:
-
- Name:
choose_player()
- Input: None
- Output: true if the user wishes to be the Hero, false otherwise
- Description: Prompts the user until they enter
H
to be the Hero orW
to be the Wumpus.
- Name:
-
- Name:
play_game()
- Input: an
ifstream
object to read from, anofstream
object to write to, nine integers corresponding to (1 & 2) the cave size (3) the number of moves (4 & 5) the wumpus location (6 & 7) the hero location (8 & 9) the pit location, and a boolean if the user is the hero - Output: None
- Description: Simulates the game play as laid out in the input file and writes the game log to the output file.
- Name:
Requirements
For this assignment, the output written to the game log must match the format specified exactly.
Be sure to mark parameters that do not change in the scope of the function constant as appropriate.
Hints
Take note that the read/eval/print steps all take place within the game loop (giving us our REPL). These steps also largely correlate to the functions we are writing. Implement and test your functions one at a time.
Due to the procedural style, your main.cpp
is nothing more than creating a few variables and
then calling the functions in the appropriate order. All the specifics and details are abstracted away to wumpus_functions.cpp
.
Best Practices To Follow
- One clear and consistent coding style used (such as K&R, 1TBS, or Allman).
- Course naming scheme is followed for variable, function, class, and other identifiers. See the course style guide for more specifics.
- Code is self-documenting. Variables sensibly named, function names descriptive of their purpose.
- Don't use global variables unless absolutely necessary. Instead, encapsulate them and design your interfaces effectively. If there is no way around using a global variable, be prepared to defend and justify its usage.
- Program flow uses structural blocks (conditionals/loops) effectively, appropriately, and efficiently.
- Code compiles and links without any errors or warnings.
- Program runs without any run time errors. Exceptions are properly caught, user input is validated appropriately, and program exits successfully without error.
- Use
const
wherever possible:- If you declare a variable and that variable is never modified, that variable should be
const
. - If your function takes a parameter and does not modify that parameter, that parameter should be
const
. - If a member function does not modify the callee, that member function should be
const
. - If you are pointing at a value that does not change, the pointer should point at a constant value (e.g.
const T*
). - If the pointer itself is never modified, the pointer should be a constant pointer (e.g.
T* const
). - If the pointer itself is never modified AND the value pointed at does not change, the pointer should be a constant pointer AND the pointer should point at a constant value (e.g.
const T* const
).
- If you declare a variable and that variable is never modified, that variable should be
- Keep your headers clean. Put the absolute minimum required in your headers for your interface to be used. Anything that can go in a source file should. Don't
#include
any system headers in your .h files that aren't absolutely required in that file specifically. Avoidusing namespace
in headers. - Implement the Big-3 as appropriate.
- Don't leak memory. Every allocation using
new
needs to have a correspondingdelete
. - Use appropriate inheritance access. Only expose necessary members to derived classes.
- Use
virtual
andoverride
as appropraite. Mark members asfinal
wherever possible and/or appropriate on derived classes. - Follow and apply the following design principles:
- Write Once, Use Many / Write Once, Read Many (WORM) / Don't Repeat Yourself (DRY): Use loops, functions, classes, and
const
as appropriate. - Encapsulate what varies: Use functions and classes as appropriate. Identify the aspects that vary and separate them from what stays the same.
- Favor composition over inheritance.
- Program to an interface, not an implementation & SOLID Principles: When using object-oriented inheritance & polymorphism, do the following:
- No variable should hold a reference to a concrete class.
- No class should derive from a concrete class.
- No method should override an implemented method of any of its base classes.
- Write Once, Use Many / Write Once, Read Many (WORM) / Don't Repeat Yourself (DRY): Use loops, functions, classes, and
Grading Rubric
Your submission will be graded according to the following rubric.
Points | Requirement Description |
10 | All labs completed and submitted L2A, L2B |
24 | Each function input/output correct as specified and performs correct task meeting the functional requirements. |
4 | Function files structured appropriately. |
1 | Public input test files generate correct results. |
2 | Private input test file generates correct results. |
5 | Best practices are followed:
|
46 | Total Points |
→This assignment is due by Friday, February 17, 2023, 11:59 PM.←
→ As with all assignments, this must be an individual effort and cannot be pair programmed. Any debugging assistance must follow the course collaboration policy and be cited in the comment header block for the assignment.←
→ Do not forget to complete the following labs with this set: L2A,
L2B
←
→ Do not forget to complete zyBooks Assignment 2 for this set.←
Submission
Always, always, ALWAYS update the header comments at the top of your main.cpp file. And if you ever get stuck, remember that there is LOTS of help available.
It is critical that you follow these steps when submitting homework.
If you do not follow these instructions, your assignment will receive a major deduction. Why all the fuss? Because we have several hundred of these assignments to grade, and we use computer tools to automate as much of the process as possible. If you deviate from these instructions, our grading tools will not work.
Submission Instructions
Here are step-by-step instructions for submitting your homework properly:
-
Make sure you have the appropriate comment header block at the top of every source code file for this set. The header
block should include the following information at a minimum.
Be sure to fill in the appropriate information, including:/* CSCI 200: Assignment 2: A2 - Watch the Wumpus
* * Author: XXXX (INSERT_NAME) * Resources used (Office Hours, Tutoring, Other Students, etc & in what capacity): * // list here any outside assistance you used/received while following the * // CS@Mines Collaboration Policy and the Mines Academic Code of Honor * * XXXXXXXX (MORE_COMPLETE_DESCRIPTION_HERE) */- Assignment number
- Assignment title
- Your name
- If you received any type of assistance (office hours - whose, tutoring - when), then list where/what/who gave you the assistance and describe the assistance received
- A description of the assignment task and what the code in this file accomplishes.
Additionally, update theMakefile
for A2 to generate a target executable namedA2
.
- File and folder names are extremely important in this process.
Please double-check carefully, to ensure things are named correctly.
- The top-level folder of your project must be named
Set2
- Inside
Set2
, create 3 sub-folders that are required for this Set. The name of each sub-folder is defined in that Set (e.g.L2A
,L2B
, andA2
). - Copy your files into the subdirectories of
Set2
(steps 2-3), zip thisSet2
folder (steps 4-5), and then submit the zipped file (steps 6-11) to Canvas. - For example, when you zip/submit
Set2
, there will be 3 sub-folders calledL2A
,L2B
, andA2
inside theSet2
folder, and each of these sub-folders will have the associated files.
- The top-level folder of your project must be named
- Using Windows Explorer (not to be confused with Internet Explorer), find the files
named
wumpus_functions.h, wumpus_functions.cpp
.
STOP: Are you really sure you are viewing the correct assignment's folder? - Now, for A2, right click on
wumpus_functions.h, wumpus_functions.cpp
to copy the files. Then, return to theSet2/A2
folder and right click to paste the files. In other words, put a copy of your homework'swumpus_functions.h, wumpus_functions.cpp
source code into theSet2/A2
folder.
Follow the same steps for each lab to put a copy of each lab's deliverable into theSet2/L2
folders. Do this process forSet2/L2A
(main.cpp, Makefile
),Set2/L2B
(color_conversion.h, color_conversion.cpp, main.cpp, Makefile
).
STOP: Are you sure yourSet2
folder now has all your code to submit?
The structure of the submission is as follows:- Set2/
- A2/
- wumpus_functions.h
- wumpus_functions.cpp
- L2A/
- main.cpp
- Makefile
- L2B/
- color_conversion.h
- color_conversion.cpp
- main.cpp
- Makefile
- A2/
- Set2/
- Now, right-click on the
"Set2"
folder.- In the pop-up menu that opens, move the mouse
"Send to..."
and expand the sub-menu. - In the sub-menu that opens, select
"Compressed (zipped) folder"
.
STOP: Are you really sure you are zipping aSet2
folder with sub-folders that each contain amain.cpp
file in it?
- In the pop-up menu that opens, move the mouse
- After the previous step, you should now see a
"Set2.zip"
file.
- Now visit the Canvas page for this course
and click the
"Assignments"
button in the sidebar.
- Find Set2, click on it, find the
"Submit Assignment"
area, and then click the"Choose File"
button.
- Find the
"Set2.zip"
file created earlier and click the"Open"
button.
STOP: Are you really sure you are selecting the right homework assignment? Are you double-sure?
- WAIT! There's one more super-important step. Click on the blue
"Submit Assignment"
button to submit your homework.
- No, really, make sure you click the
"Submit Assignment"
button to actually submit your homework. Clicking the"Choose File"
button in the previous step kind of makes it feel like you're done, but you must click the Submit button as well! And you must allow the file time to upload before you turn off your computer!
- Canvas should say "Submitted!". Click "Submission Details" and you can download the zip file you just submitted. In other words, verify you submitted what you think you submitted!
In summary, you must zip the "Set2"
folder
and only the "Set2"
folder, this zip folder must have several sub-folders, you must name all these folders correctly, you must submit the correct zip file for this
homework, and you must click the "Submit Assignment"
button. Not doing these steps is like bringing your
homework to class but forgetting to hand it in. No concessions will be made for
incorrectly submitted work. If you incorrectly submit your homework, we will not be able to
give you full credit. And that makes us unhappy.
→This assignment is due by Friday, February 17, 2023, 11:59 PM.←
→ As with all assignments, this must be an individual effort and cannot be pair programmed. Any debugging assistance must follow the course collaboration policy and be cited in the comment header block for the assignment.←
→ Do not forget to complete the following labs with this set: L2A,
L2B
←
→ Do not forget to complete zyBooks Assignment 2 for this set.←