COMP 142: Project 4 --- Hogwarts Dueling Club
Introduction
In the world of Harry Potter, the students of Hogwarts School of Witchcraft and Wizardry
have the opportunity to participating in a dueling club to sharpen their spellcasting skills
should they ever find themselves in a hostile situation. You will write a program to simulate
a duel among a group of wizards and witches taking part in the club.
The duels all take place on a ten by ten grid, as shown below.
Dueling basics
- A dueling tournament may involve any number of students.
- Each dueler involved in the tournament always occupies exactly one grid
square at a time, though any number of students may share a grid square at once.
- Duelers are removed from the grid in one of two ways: either by losing a duel,
or walking off of the edge of the grid, which only happens in a certain manner (described below).
In either case, once a student loses the duel or walks off the grid, they no longer exist
on the grid and are no longer involved in the dueling process.
- Individual duels only happen between a pair of students. One student challenges another
student to a duel. When this happens, usually one of the students will win the duel,
and the other will lose, though there is the possibility of a tie. The loser (if there
is one) leaves the board immediately. Ties allow both duelers to remain on the board
for the time being.
Movement on the grid
Encountering other duelers
- When a dueler moves, they may move into a square occupied by another dueler.
When this happens, the dueler who moved into the square (Dueler A) immediately must
challenge the current occupant of the square (Dueler B) to a duel, assuming they
are from a different house. Players from the same house never duel each other.
- Every dueler always has a certain amount of magical energy. The amount of energy
each dueler begins with may vary, but it can only decrease during a tournament.
- When Dueler A challenges Dueler B to a duel, the winner is determined from the
relative magical energy levels of the duelers: the winner is the one with more energy
(if the energies are the same, it's a tie).
- After a duel, energy is removed from both players as follows. For a tie, neither
player loses any energy. If A beats B, then A loses however much energy B started
with, and B loses all of their energy.
- Example: A has 10 energy and B has 8. If they duel, A wins and B loses. A
ends up with 2 energy and B ends up with zero. A remains on the board in the same
square as the duel, while B is removed from the board.
- Example: A has 10 energy and B has 10. If they duel, they will tie. They both
end with 10 energy, and neither player is removed from the board.
- Ordering of duels: Because it is possible for two or more students to occupy the
same grid square, it is possible for multiple duels to be triggered by a third student
entering the square. The dueler moving into the square challenges duelers in order
of their numbers.
- Example: Student 3 moves into a square that is currently occupied by Students 1
and 6. Student 3 will first challenge Student 1 to a duel (assuming they are in
different houses). After that duel is finished, assuming Student 3 was not
removed from the board for losing, Student 3 will then challenge
Student 6 to a duel.
Moving off the grid and earning points
- The object of the game is for each student to move in their assigned direction
on the grid with the goal of making it all the way across without losing a duel
(which results in their energy dropping to zero and they are removed from the board).
If a dueler makes it all the way across the grid, on the turn in which they would
walk off the edge of the grid, they are removed from the grid entirely and earn
a point for their house.
- The tournament ends after all duelers are off the grid (they either have lost
a duel or made it all the way across). At this time, the points per house are tallied up.
- Note that a game may last at most ten rounds, because that is the maximum amount
of time it takes to move entirely across the board.
Example of a complete tournament
Suppose Harry (Gryffindor) and Draco (Slytherin) are the only two participants
in a tournament. Harry (student 0) starts with 10 energy at position (0, 5), while Draco (student 1)
starts with 8 energy at position (9, 5).
Here is the grid at the beginning of the tournament (before round 1):
In this tournament, Harry always moves first and Draco second, since that is the ordering
of their numbers.
In Round 1, Harry moves to (1, 5) and then Draco moves to (8, 5).
In Round 2, Harry moves to (2, 5) and then Draco moves to (7, 5).
In Round 3, Harry moves to (3, 5) and then Draco moves to (6, 5).
In Round 4, Harry moves to (4, 5) and then Draco moves to (5, 5).
In Round 5, Harry moves first, so he moves to (5, 5). He sees Draco in that square
and immediately challenges him to a duel. Harry wins, since his energy of 10 is greater
than Draco's of 8. Draco immediately leaves the grid (he doesn't even get a chance to
move), and his energy is now at 0. Harry's energy is now 2.
After Round 5 is done, the grid looks like this:
The remaining rounds involve Harry continuing to move rightward across the board.
During round 9, Harry moves to position (9, 5) --- the edge of the board.
During round 10, he steps off the grid completely and earns a point for Gryffindor.
Implementing the project
You will write code to simulate a dueling club tournament. All the information
about the students and the starting grid setup will be read from a file. Your code
will simulate the duelers moving around, dueling each other, and you will tally up
the points at the end of the game.
We will introduce the idea of separate compilation in this project. This means
your project will be spread over three separate files, that must be named
as follows.
- dueler.h: This file contains the declaration of the dueler class, but not the method bodies.
- dueler.cpp: This file contains the method bodies of the dueler class.
- main.cpp: This file contains the main program logic.
I have separated the project into four main sections.
CLion set up
- Create a new CLion project. I called mine cs142proj4. This creates main.cpp
for you. Cut and paste the main.cpp skeleton code from here into your main.cpp. [skeleton main.cpp]
- Right-click on main.cpp in the left panel, choose New, then C++ class.
- In the box that appears, in the Name field, type dueler and press OK.
You will see two new files be created, dueler.cpp and dueler.h. Copy the skeleton
code for these files into them. [skeleton dueler.h] [skeleton dueler.cpp]
- Compile and run the program. Your program should compile and run just fine, though
it shouldn't do much of anything (it should ask you for a filename then quit).
Implementing the dueler class
As you implement the code below, write tests in main() to make sure your code is working.
Do not modify main() other than to write tests. In particular, the main
game loop logic should not be altered.
- A dueler object has six fields (see the code). They should be self explanatory.
- Add six getter methods for these six fields. Name them appropriately --- the one
for onboard should be called is_on_board(). The one
for name is already done for you. Put the declarations of the methods in dueler.h,
and the definition (bodies) of the methods in dueler.cpp. This is standard C++ practice
for real-world classes. Test your code in main().
- Uncomment and write the secondary constructor. This constructor takes five arguments
corresponding to all the fields except is_on_board (which should be set to true). Test in main().
- Write the print() method in the dueler class. This method should print/cout a string
that looks exactly like this:
Harry (Gryffindor) has 10 energy and is at position (0, 5).
If the dueler is not on the grid, the message should be:
Harry (Gryffindor) has 2 energy and is not on the board.
The print() function is the only method in your dueler class that may use cout.
- Write code in main() to test your print() function.
- Uncomment and implement the move() method. This function will be called
when a dueler is supposed to move one square in their prescribed direction. Your
code should update posx, posy, and on_board as needed. Test in main().
- Uncomment and implement the duel() method. This function will be called
when a dueler encounters an opponent in their same square. Your code should update
(as appropriate)
the dueler's & opponent's energies and both dueler's onboard fields. This method
must return an integer corresponding to if the person who initiated the duel
won (1), lost (-1), or tied (0). Test in main().
Reading the input files
- Download game1.txt, game2.txt, and game3.txt and either put them into your
cmake-build-debug folder inside of your project folder, or create those files
from scratch in CLion.
- To create them from scratch, right-click on cmake-build-debug, choose New, then File.
Type in game1.txt and paste in the text file from above. Repeat for all three files.
You should end up with a structure like this:
- Look at the contents of game1.txt to get a feel for how the file is structured.
Every line contains 5 pieces: the name of the dueler, their house (as a char), their
starting magical energy, and their starting position on the board (x, y).
- Write the read_file() function in main(). This function should read the file
specified by the string argument, and return a vector of duelers corresponding
to the contents of the file. Test in main() --- see the comments there for directions.
- Write the print_game_state() function. This functions just loops over all the
duelers in the argument and calls print() on them. So for the starting game described
above, this function should print:
Harry (Gryffindor) has 10 energy and is at position (0, 5).
Draco (Slytherin) has 8 energy and is at position (9, 5).
- Test in main().
Tournament logic
- Implement the game_is_over() function. This function detects if the game is over
(all players are off the grid) and returns true if so, and false if not.
This code is uses a simple loop over the duelers. Do not use any cout statements
in this function. Test in main().
- Implement the take_turn() function. This function handles the "turn" for a dueler,
which involves two steps. First, the dueler should move. (Call your move() method).
Then, the player should challenge all players --- in order --- occupying their current
square to a duel.
This function must print information about where the dueler is moving to, and
the state of all duels as they happen. See the sample output below for details.
Test in main().
- Implement the calculate_statistics() function. This function prints a simple table
of points for each house at the end of the game. Test in main().
Coding style
All the normal guidelines for coding from previous projects apply.
Do not use any global variables.
Testing your program
You should test your program thoroughly to make sure it works correctly.
In particular, you should make up your own game text files to handle situations that
the sample files do not test. For instance, does your code correctly handle two players
from the same house moving into the same square? (They shouldn't duel.)
Sample input and output
The program's output is in normal text; what the user types is in bold. Each test
of the program is shown separately; the SAMPLE RUN parts are not part of the output.
Your output must match mine exactly.
========
SAMPLE 1
========
Enter a game filename: game1.txt
At start of game:
Harry (Gryffindor) has 10 energy and is at position (0, 5).
Draco (Slytherin) has 8 energy and is at position (9, 5).
Beginning round 1.
Harry moves to (1, 5).
Draco moves to (8, 5).
Beginning round 2.
Harry moves to (2, 5).
Draco moves to (7, 5).
Beginning round 3.
Harry moves to (3, 5).
Draco moves to (6, 5).
Beginning round 4.
Harry moves to (4, 5).
Draco moves to (5, 5).
Beginning round 5.
Harry moves to (5, 5).
Harry challenges Draco to a duel.
Harry wins.
Beginning round 6.
Harry moves to (6, 5).
Beginning round 7.
Harry moves to (7, 5).
Beginning round 8.
Harry moves to (8, 5).
Beginning round 9.
Harry moves to (9, 5).
Beginning round 10.
Harry moves off the board.
At end of game:
Harry (Gryffindor) has 2 energy and is not on the board.
Draco (Slytherin) has 0 energy and is not on the board.
Final score:
Gryffindor: 1
Ravenclaw: 0
Hufflepuff: 0
Slytherin: 0
========
SAMPLE 2
========
Enter a game filename: game2.txt
At start of game:
Harry (Gryffindor) has 10 energy and is at position (0, 5).
Draco (Slytherin) has 8 energy and is at position (9, 5).
Luna (Ravenclaw) has 7 energy and is at position (7, 0).
Hannah (Hufflepuff) has 11 energy and is at position (7, 9).
Beginning round 1.
Harry moves to (1, 5).
Draco moves to (8, 5).
Luna moves to (7, 1).
Hannah moves to (7, 8).
Beginning round 2.
Harry moves to (2, 5).
Draco moves to (7, 5).
Luna moves to (7, 2).
Hannah moves to (7, 7).
Beginning round 3.
Harry moves to (3, 5).
Draco moves to (6, 5).
Luna moves to (7, 3).
Hannah moves to (7, 6).
Beginning round 4.
Harry moves to (4, 5).
Draco moves to (5, 5).
Luna moves to (7, 4).
Hannah moves to (7, 5).
Beginning round 5.
Harry moves to (5, 5).
Harry challenges Draco to a duel.
Harry wins.
Luna moves to (7, 5).
Luna challenges Hannah to a duel.
Hannah wins.
Hannah moves to (7, 4).
Beginning round 6.
Harry moves to (6, 5).
Hannah moves to (7, 3).
Beginning round 7.
Harry moves to (7, 5).
Hannah moves to (7, 2).
Beginning round 8.
Harry moves to (8, 5).
Hannah moves to (7, 1).
Beginning round 9.
Harry moves to (9, 5).
Hannah moves to (7, 0).
Beginning round 10.
Harry moves off the board.
Hannah moves off the board.
At end of game:
Harry (Gryffindor) has 2 energy and is not on the board.
Draco (Slytherin) has 0 energy and is not on the board.
Luna (Ravenclaw) has 0 energy and is not on the board.
Hannah (Hufflepuff) has 4 energy and is not on the board.
Final score:
Gryffindor: 1
Ravenclaw: 0
Hufflepuff: 1
Slytherin: 0
========
SAMPLE 2
========
Enter a game filename: game3.txt
At start of game:
Harry (Gryffindor) has 8 energy and is at position (0, 2).
Draco (Slytherin) has 8 energy and is at position (9, 2).
Luna (Ravenclaw) has 7 energy and is at position (8, 0).
Hannah (Hufflepuff) has 11 energy and is at position (8, 9).
Hermione (Gryffindor) has 9 energy and is at position (0, 4).
Pansy (Slytherin) has 6 energy and is at position (9, 4).
Marcus (Ravenclaw) has 4 energy and is at position (4, 0).
Ernie (Hufflepuff) has 5 energy and is at position (4, 9).
Ron (Gryffindor) has 9 energy and is at position (0, 6).
Vincent (Slytherin) has 1 energy and is at position (9, 6).
Cho (Ravenclaw) has 10 energy and is at position (2, 0).
Justin (Hufflepuff) has 3 energy and is at position (2, 9).
Beginning round 1.
Harry moves to (1, 2).
Draco moves to (8, 2).
Luna moves to (8, 1).
Hannah moves to (8, 8).
Hermione moves to (1, 4).
Pansy moves to (8, 4).
Marcus moves to (4, 1).
Ernie moves to (4, 8).
Ron moves to (1, 6).
Vincent moves to (8, 6).
Cho moves to (2, 1).
Justin moves to (2, 8).
Beginning round 2.
Harry moves to (2, 2).
Draco moves to (7, 2).
Luna moves to (8, 2).
Hannah moves to (8, 7).
Hermione moves to (2, 4).
Pansy moves to (7, 4).
Marcus moves to (4, 2).
Ernie moves to (4, 7).
Ron moves to (2, 6).
Vincent moves to (7, 6).
Cho moves to (2, 2).
Cho challenges Harry to a duel.
Cho wins.
Justin moves to (2, 7).
Beginning round 3.
Draco moves to (6, 2).
Luna moves to (8, 3).
Hannah moves to (8, 6).
Hermione moves to (3, 4).
Pansy moves to (6, 4).
Marcus moves to (4, 3).
Ernie moves to (4, 6).
Ron moves to (3, 6).
Vincent moves to (6, 6).
Cho moves to (2, 3).
Justin moves to (2, 6).
Beginning round 4.
Draco moves to (5, 2).
Luna moves to (8, 4).
Hannah moves to (8, 5).
Hermione moves to (4, 4).
Pansy moves to (5, 4).
Marcus moves to (4, 4).
Marcus challenges Hermione to a duel.
Hermione wins.
Ernie moves to (4, 5).
Ron moves to (4, 6).
Vincent moves to (5, 6).
Cho moves to (2, 4).
Justin moves to (2, 5).
Beginning round 5.
Draco moves to (4, 2).
Luna moves to (8, 5).
Luna challenges Hannah to a duel.
Hannah wins.
Hannah moves to (8, 4).
Hermione moves to (5, 4).
Hermione challenges Pansy to a duel.
Pansy wins.
Pansy moves to (4, 4).
Ernie moves to (4, 4).
Ernie challenges Pansy to a duel.
Ernie wins.
Ron moves to (5, 6).
Ron challenges Vincent to a duel.
Ron wins.
Cho moves to (2, 5).
Cho challenges Justin to a duel.
Justin wins.
Justin moves to (2, 4).
Beginning round 6.
Draco moves to (3, 2).
Hannah moves to (8, 3).
Ernie moves to (4, 3).
Ron moves to (6, 6).
Justin moves to (2, 3).
Beginning round 7.
Draco moves to (2, 2).
Hannah moves to (8, 2).
Ernie moves to (4, 2).
Ron moves to (7, 6).
Justin moves to (2, 2).
Justin challenges Draco to a duel.
Draco wins.
Beginning round 8.
Draco moves to (1, 2).
Hannah moves to (8, 1).
Ernie moves to (4, 1).
Ron moves to (8, 6).
Beginning round 9.
Draco moves to (0, 2).
Hannah moves to (8, 0).
Ernie moves to (4, 0).
Ron moves to (9, 6).
Beginning round 10.
Draco moves off the board.
Hannah moves off the board.
Ernie moves off the board.
Ron moves off the board.
At end of game:
Harry (Gryffindor) has 0 energy and is not on the board.
Draco (Slytherin) has 7 energy and is not on the board.
Luna (Ravenclaw) has 0 energy and is not on the board.
Hannah (Hufflepuff) has 4 energy and is not on the board.
Hermione (Gryffindor) has 0 energy and is not on the board.
Pansy (Slytherin) has 0 energy and is not on the board.
Marcus (Ravenclaw) has 0 energy and is not on the board.
Ernie (Hufflepuff) has 4 energy and is not on the board.
Ron (Gryffindor) has 8 energy and is not on the board.
Vincent (Slytherin) has 0 energy and is not on the board.
Cho (Ravenclaw) has 0 energy and is not on the board.
Justin (Hufflepuff) has 0 energy and is not on the board.
Final score:
Gryffindor: 1
Ravenclaw: 0
Hufflepuff: 2
Slytherin: 1
What to turn in
Through Moodle, upload three files: main.cpp, dueler.h, and dueler.cpp.
Do not upload the game text files.
Challenge Problem
Enhance the statistics at the end of the game to print things like total number
of duels, player who won the most duels, the house(s) who won the whole tournament,
or other interesting statistics.