Due dates:

Getting Started

Start by downloading CS101_Assign06_Fa24.zip, saving it in the directory H:\CS101. In Windows File Explorer, navigate to H:\CS101, right click on CS101_Assign06_Fa24.zip, and select Extract All to create a CS101_Assign06_Fa24 directory with the assignment files.

Start a Cygwin Bash Shell and run the following commands:

cd h:
cd CS101
cd CS101_Assign06_Fa24

You will add your code to these files:

H:\CS101\CS101_Assign06_Fa24\Scene.h

H:\CS101\CS101_Assign06_Fa24\Scene.cpp

H:\CS101\CS101_Assign06_Fa24\Player.h

H:\CS101\CS101_Assign06_Fa24\Player.cpp

Note: we will be using separate files for the game play, scene structure, and player structures.

The file Chomp.cpp contains the provided game loop, and the file Const.h includes symbolic constants to use throughout the program. You should not need to modify either of these files.

A sample Windows executable is included in the .zip file and can be run in Cygwin by

./ChompSolWin

or for Mac in a terminal window

./ChompSolMac

(the sample executable compiled for Mac may be a bit erratic, try adjusting the terminal window size while the program is running).

Your Task

Since many of you have either played or at least seen the retro arcade game Pacman, the purpose of this assignment is to write a similar game using terminal graphics. The object of the game is to move your player around a board collecting pellets and power-ups while avoiding the 4 ghosts.

When you are ready to compile the program, in the Cygwin window type the command

make

To run the program, in the Cygwin window type the command

./Chomp.exe

Use CTRL-c to exit the program.

Program structure

Note: The board size is set by the symbolic constants WIDTH (=28) and HEIGHT (=22) in Const.h which can be used when accessing the elements of the 2D array.

The overall layout of the program can be graphically illustrated by the following flowchart showing the relationships between the functions and which files they should be placed in:

image

This document shows the Scene struct organization Scene.

Initializing the Board

Code has been provided to load in the game board and display it on the screen.

The following fields are defined in the Scene structure (in Scene.h)

Loading in the board

The layout of the board is included in the text file board.txt where + indicates walls, . indicates pellets, and O indicates power ups.

The initialize_scene() function takes a Scene structure as a reference parameter. The function calls load_board() passing the board, number of pellets, and number of powerup fields by reference. load_board() reads the file and initializes a 2D board array parameter with the objects at each board location. Each element of the array will contain a symbolic constant for the object at that location as either:

The function will also return the number of pellets (num_pellets) and number of power-ups (num_powerups) the board contains through reference parameters.

initialize_scene() will be modified in the milestones to perform other initializations for the player and ghosts.

Drawing the board

The render_scene() function then takes a Scene as a reference parameter and calls the draw_board() function passing the board field through the s pointer.

The draw_board() function takes a 2D array of int’s representing the board and renders the board on the screen. Symbolic constants have been defined for the characters to render as:

You should see something like this when you run the program:

image

Use CTRL-c to exit the program.

Milestone 1

Milestone 1 will add the player and allow them to move around the board eating pellets and power-ups.

You will need to first create a structure named Player and then add the following fields to the Player structure in Player.h

Player functions

We will need to add accessor functions to initialize, draw and update the fields of the player.

Scene functions

Next we need to incorporate the player into the scene.

At this point you should be able to move the player around the board “gobbling” up the pellets and power-ups.

You should see something like this when you run the program:

image

Deliverables for Milestone 1

The code for Milestone 1 should be submitted to Marmoset (using the command make submit_ms1) by the end of the day on Friday, Nov 22nd.

Submitting Milestone 1

To submit your code, make sure all the files are saved, and in the Cygwin window type

make submit_ms1

Approach/Hints

DEVELOP INCREMENTALLY! Use CTRL-c to exit the program.

Step 1: Add the Player struct in Player.h

Step 2: Add the initialize_player and draw_player accessor functions in Player.cpp (with prototypes in Player.h)

Step 3: Add a Player field to the Scene struct in Scene.h

Step 4: Add a call to initialize_player to initialize_scene in Scene.cpp

Step 5: Add a call to draw_player to render_scene in Scene.cpp.

You should now see the player character on the board.

Step 6: Add the player_ai (without check_player_move) and update_player functions in Player.cpp (with prototypes in Player.h)

Step 7: Add a call to player_ai followed by a call to update_player to update_scene in Scene.cpp

You should now be able to move the player anywhere around the screen.

Step 8: Add the check_player_move function to Player.cpp (with the prototype in Player.h) to simply check if the current move would place the player in a wall and if so resetting the velocities to 0.

Step 9: Add a call to check_player_move to player_ai in Player.cpp after the keypress sets the desired velocities.

You should now only be able to move the player around the corridors on the board.

Step 10: Add code to update_scene in Scene.cpp after the call to update_player to check if the player’s location on the board is a PELLET or POWER_UP, and if so set that board location to EMPTY decrementing either num_pellets or num_powerups.

At this point you should be able to move the player around the corridors on the board gobbling up the pellets and power-ups (thus Chomp, Chomp, Chomp!)

Step 11: Add code to check_player_move to determine if the player is at one of the tunnel entrances and trying to move into the tunnel, and if so modifying the player’s x field to the other side of the board.

Finally you should be able to move around the corridors on the board gobbling up pellets and power-ups along with going through the tunnel.

Milestone 2

Milestone 2 incorporates the ghosts to complete the game play. The ghosts will simply be represented by an array of Player’s that uses a different function to determine their desired velocities (basic AI).

You will need to first add a field to the Scene structure (in Scene.h)

and the Player structure (in Player.h)

Player functions

Note the draw_player(), check_player_move(), and update_player() functions can be used for both the user and ghosts without modification.

Scene functions

At this point you will have a playable game, but the ghosts will move extremely fast (although not very intelligently). To make the ghosts move at a more reasonable speed, determine a way to only update them every GHOST_DELAY (another constant in Const.h) update cycles. Hint: consider adding a counter field to the Scene structure and checking it in update_scene().

You should see something like this when you run the program:

image

Deliverables for Milestone 2

The code for Milestone 2 should be submitted to Marmoset (using the command make submit_ms2) by the end of the day on Friday, Dec 6th.

Submitting Milestone 2

To submit your code, make sure all your files are saved, and in the Cygwin window type

make submit_ms2

Approach/Hints

This milestone is actually somewhat simpler as you will be reusing many of the functions from Milestone 1.

DEVELOP INCREMENTALLY! Use CTRL-c to exit the program.

Step 1: Add an array of Player structs field in Scene.h for the ghosts

Step 2: Add a call to initialize_player to initialize_scene for each ghost in Scene.cpp (Hint: use a loop).

Step 3: Add a call to draw_player to render_scene for each ghost in Scene.cpp (Hint: use a loop).

You should now see the ghosts on the board.

Step 4: Add the ghost_ai function in Player.cpp (with a prototype in Player.h)

Step 5: Add a call to ghost_ai followed by a call to update_player for each ghost to update_scene in Scene.cpp (Hint: use a loop).

You should now see the ghosts moving around the screen (but not capturing the player).

Step 6: Add a score field to the Player struct in Player.h, initialize it to 0 in initialize_player in Player.cpp, display it to the right of the board in render_scene in Scene.cpp, and update its value in update_scene whenever the player eats a pellet or power-up.

You should now see a changing score as the player moves around the board.

Step 7: Add code to update_scene after each ghost is updated to check if it is at the same location as the player, and if so return true indicating game over. Also check if num_pellets and num_powerups are both 0, and if so return true indicating game over.

You should now have a fully functional game!

Step 8: Add a counter field to the Scene struct, initialize it in initialize_scene, and increment it in update_scene. Then in update\scene only call ghost_ai and update_player for the ghosts whenever the counter exceeds GHOST_DELAY to slow things down.

Grading

Your grade will be determined as follows:

Milestone 1 - 75 points

Milestone 2 - 75 points

We expect you to use good coding style. Points may be deducted for poor variable names, inconsistent or missing indentation, and/or lack of comments.

Extra Credit - 50 points

Improve player character - 5 points

Currently the program uses the < character for the player, regardless of the direction the player is moving. Alternatively, it looks better if the player’s “mouth” is open in the direction of motion to look like they are “eating” the pellets and power-ups. One way to accomplish this is to change the player’s character field in player_ai based on the direction of motion such that

Improve the ghost AI - 10 points

One drastic improvement that can be made to the ghost AI is rather than select a random direction to attempt to move (which often results in moving into a wall), select only from the valid directions the ghost can move (with a strong preference to continue moving in its current direction, a lesser preference to turn if possible, and with a relatively small preference to turn around). This will make the ghost movement appear much smoother (and smarter). Additionally the ghost’s prefered direction can be further biased towards the player if they are within a certain range of the player (basically providing a “chase” mode when they are close). There should be a small probability that the ghost will stop chasing otherwise the game will become very difficult to play (the ghosts will essentially ambush the player pinning them in a corner).

Additional levels - 10 points

Alternate levels can be designed using the same symbols as board.txt, i.e. + for walls, . for pellets, O for power-ups, and blanks for empty locations. Make sure your boards are 28 characters wide by 22 characters high (to match the board array used in the load_board() function). Be careful if you choose to use tunnels that they are at row TUNNEL_Y otherwise you will need to modify the check_player_move() function. Once a level is completed, load and begin another level.

Capturable ghosts - 25 points

Probably the most complicated improvement (in the spirit of the original video game) is to allow the ghosts to be “captured” for a brief period of time after the player “eats” a power-up. If the player and ghost are at the same location during this time, the player earns extra points and the ghost is returned to its home location while no longer being capturable. The player should receive some type of warning that the capturable time is about to expire. In the original game, the ghosts would turn blue while they were capturable and flash when they were about to become non-capturable. Consider adding flags to the Player structure that tracks the state of each ghost and a counter to the Scene structure that is set to a value and counts down whenever a power-up is eaten (and can be used to determine when to make the ghosts “flash”).

Submitting

To submit your code, make sure all the files are saved, and in the Cygwin window type one of the following commands (depending on whether you are submitting Milestone 1 or Milestone 2).

For Milestone 1:

make submit_ms1

File Milestone 2:

make submit_ms2

Enter your Marmoset username and password (which you should have received by email.) Note that your password will not be echoed to the screen. Make sure that after you enter your username and password, you see a message indicating that the submission was successful.

Make sure that you check the file(s) you submitted to ensure that they are correct. See the instructions for Verifying your submission.

Important: It is your responsibility to verify that you submitted the correct files. You may receive a grade of 0 for incorrectly submitted work.