Spring 2024 CS 32

Project 3
South Dakota Smith and the Temple of Doom

Time due: 11:00 PM Wednesday, May 22 Tuesday, May 28

Introduction

Archaeology professor South Dakota Smith used to go on explorations to steal ancient cultural artifacts, rather like his academic rival in Indiana. He now regrets his unethical behavior and has decided to market a computer game inspired by on one of his adventures, donating the profits to the descendants of the peoples who created the objects he stole. He has hired you to write the code.

You will be building a simple action-adventure game called Doom. In Doom, the player navigates through a multi-level temple in search of a golden idol. Upon locating the idol, the player is instantly transported out of the temple and wins the game.

Upon starting a new game, you the player are placed on the top level of the temple. From here, you must work your way through the rooms on that level, battling monsters, finding treasures, and using staircases to descend ever deeper into the temple's depths. When you reach the bottom level of the temple, you must reach the golden idol in order to win the game.

As you move through a level, you will encounter a number of different types of monsters that you will have to battle, including:

Each of these monsters has a different behavior and reacts differently to the player (who is shown on screen as an @). More details about the monsters are below.

In addition to monsters, you'll find many different objects during your adventure:

Here is an example of what the game display might look like:

######################################################################
######################             ##           ######################
######################          B  ##           ##              ######
######################             ##     G                  )  ######
######################             ##           ##       S      ######
#####             ####           ?D        #######    G         ######
##### >           ####      B      ###### ########              ######
#####        D    ####             ##         ########### ############
#####              S           )   ##         ########### ############
#####     G       ###################         ########### ############
#####             ################### D@      ########### ############
#####################################       S ########### ############
#####################################         ########          ######
######################################################   ?      ######
######################################################          ######
######################################################################
######################################################################
######################################################################
Level: 3, Hit points: 10, Armor: 3, Strength: 2, Dexterity: 2

Player slashes short sword at the Dragon and misses.
the Dragon swings long sword at Player and misses.

To control the player in the game, you issue commands from the keyboard. Use the getCharacter function we will supply you to read characters from the keyboard in a way that does not require the user to hit Enter after each command. (Also, getCharacter does not echo the character onto the screen.) Doom is not a "real-time" game; the game progresses only when the player issues a command. After each player command, the monsters each make a move and then the screen is updated to reflect the current state of the game. The player commands are:

If you type something not on this list, you do nothing for this turn, but the monsters take their turn.

This sample Windows executable, this sample Mac executable, or this sample Linux executable lets you get a feel for the game.

(You can run the Linux version by logging in to cs32.seas.ucla.edu and running the command /usr/local/cs/bin/doom. On a Mac, if you click on the downloaded zip file and then double-click on the executable file (e.g., doommac), your operating system may refuse to open it because it can't confirm who developed the program. In that case, right-click on the executable instead, select Open With and then Terminal.app. Select the gray Open button on the popup that again says it doesn't know the developer.)

High-level Game Details

This section describes high-level details of the Doom game. These are mandatory project requirements.

The Temple

Playing the Game

Actors

Since the player and the monsters share so many characteristics, let's classify them as actors. Each actor has these attributes:

Whenever an actor does any of the following, you must ensure a message is displayed below the statistics line the next time the level is displayed:

Player

The user can issue the following commands, each of which counts as the player's turn:

The w, r, and i commands should display the inventory in a manner such as this:

	Inventory:
	 a. short sword
	 b. long sword
	 c. A scroll called scroll of strength
	 d. long sword

Monsters

Monsters are limited in their actions; when it's a monster's turn, that monster will either:

If a monster is next to the player, it will attack the player. When a monster dies, it might drop an item onto the position where it dies if there is no item already at that position.

Bogeymen

Bogeymen appear only at temple level 2 or deeper. All bogeymen are created with the following values:

Bogeymen can smell when the player is near, but are not too bright. If the bogeyman could take one through five steps, ignoring walls or other monsters, and reach the player, then it smells the player. (Of course, like other actors, bogeymen can't actually walk through walls or monsters.) If the bogeyman smells the player on its turn, it will move one step closer to the row or column the player is on, if possible, but never one step farther from the player's row or column. If the bogeyman is too far away to smell the player, it will not move. Consider the following example:

	..B.DB..B.
	B#####.##.
	..#.@..DB.
	.B........

The bottommost bogeyman would move one step up or right, closer to the player's row or column; it's too stupid to consider whether moving right is better than moving up in this case. The leftmost bogeyman would move one step down. The top left one would move one step right, closer to the player's column. The top middle one will not move; the wall and the dragon are blocking it from moving closer to the player's row or column, and it's too stupid to move away from the player's column in order to go around the wall. The top right bogeyman is too far away to smell the player, so will not move. The bottom right bogeyman will not move; since it's on the same row as the player, it will not move off it, and it's blocked by the dragon from moving closer to the player's column.

When a bogeyman dies, there is a 1 in 10 chance it will drop a magic axe if there is no item at the position where it dies.

Snakewomen

Snakewomen may appear on any temple level. All snakewomen are created with the following values:

Snakewomen are as dumb as bogeymen, and don't smell as well. They move according to the same criteria as bogeyman, except that they can smell the player only from a distance of three or less, not five or less. When a snakewoman dies, there is a 1 in 3 chance she will drop her magic fangs of sleep if there is no item at the position where she dies.

Dragons

Dragons appear only at temple level 3 or deeper. All dragons are created with the following values:

Dragons do not move, since they want to protect their treasure, but like any monster, will attack the player if the player is next to it. Dragons are the only monster that, like the player, occasionally regains hit points. Before each turn a dragon takes, there is a 1 in 10 chance the dragon will regain 1 hit point, up to its initial maximum number of hit points. When a dragon dies, there is a 100% chance it will drop a scroll of some kind if there is no item at the position where it dies.

Goblins

Goblins may appear on any temple level. All goblins are created with the following values:

Goblins are very intelligent creatures, and can smell the player if the player is reachable in 15 steps or less. (Notice this is different from bogeymen and snakewomen, who can smell through walls and other monsters; for a goblin to be able to smell the player, there must be a path it could move along to reach the player in 15 steps or less.) If the goblin smells the player on its turn, it will make an optimal move (there may be more than one) to get one step closer to the player along a traversable path. If the goblin is too far away to smell the player, it will not move. Consider the following example:

	#################
	#.....G.#G......G
	#D#.###.#####.###
	#.#..@..#........
	#.GD..#...####..G
	############G..##
	#################

The top left goblin will move one step right, since that puts it closer to the player along a path than if it moved one step left. The bottom left goblin will not move, because the player is not reachable (the goblin is blocked by walls and dragons). The bottom middle goblin will move one step right, and the top right goblin will move one step left, because both can just barely smell the player (they're exactly 15 steps away). The bottom right goblin will move either up or left, since either one is an optimal move. The top middle goblin will not move, because it could not reach the player in 15 steps (the shortest traversable path is 16 steps).

The algorithm you write for determining an optimal goblin move must use recursion in a significant way. ("Significant" means, for example, that you can't just slap a trivial recursive call onto a fundamentally non-recursive algorithm, such as void f(int n) { if (n != 0) f(0); else solveNonRecursively(); }.) Your game should not slow to a crawl every time the algorithm is run, although it may run a bit slower.

When a goblin dies, there is a 1 in 3 chance it will drop either a magic axe or magic fangs of sleep if there is no item at the position where it dies.

This spec defines the distance a goblin can smell as 15. In fact, your program must use the argument passed to the Game constructor as the distance. (In the main routine we provided, it is 15.) The reason for this is that if you carelessly code your recursive goblin move algorithm, it might take an uncomfortably long time to search up to 15 steps away. Until you improve your algorithm, to be able to comfortably test the game as you work on other parts of the code, you can reduce the value of the argument that main passes to the Game constructor, so that the search distance is smaller and thus faster. When we build your program for our testing, we will use 15 as the argument as per this spec, but if your program is uncomfortably slow, we will reduce that argument (for a small loss of points) as the only way we will try to speed things up; we will not try to find other places in your code where you hard-coded a value like 15.

Game Objects

Since weapons, scrolls, and the golden idol share some characteristics (e.g., they can be picked up, they can occupy the same space as an actor, etc.), let's classify them as game objects. Each game object has a name (e.g. "long sword").

Weapons

All weapons have:

The weapons are:

Scrolls

Scrolls are magical parchments that can be read in order to cast spells. When a scroll is read, it performs its effect and then magically self-destructs (i.e., is removed from the player's inventory). These are the types of scrolls and their effect:

Implementation suggestions

We don't expect that everyone will finish all aspects of the game. Make sure you turn in something that is at least minimally playable under g32 without crashing (e.g., the player can move and be blocked by walls). The more of the feel of the game you implement, the better. Start with something really simple (e.g., no monsters or scrolls, one big room, etc.), get that working, and add functionality a little at a time.

If you're having trouble wielding a weapon from your inventory or reading a scroll, you probably want to learn about dynamic_cast.

While you can build your program under Xcode, if you launch the resulting executable from Xcode, you'll find that non-arrow key characters you type are echoed to the screen, and the screen won't clear; this is because Xcode does not provide a true terminal window. Running from the command line in a Terminal window should work fine. Open Terminal, change directory to the one where Xcode places your executable file, and run the program. If your project is named Doom, for example, and the top-level project folder is /Users/fred/CS32/Doom, then say

	cd /Users/fred/CS32/Doom/DerivedData/Doom/Build/Products/Debug
	./Doom

If you leave the Terminal window open after your game is over, then you can run the program again by just typing ./Doom. If you rebuild the program after making a code change, that command will run the new version of the program.

If you want to build your program from the command line under Linux or macOS:

  1. Put all the .h and .cpp files in some directory — perhaps ~/Proj3
  2. Open a shell window from the Terminal utility.
  3. Execute these commands:
    	cd ~/Proj3
    	g++ -o doom *.cpp
    
    This should produce an executable file named doom. On cs32.seas.ucla.edu, using g32 instead of g++ will check for runtime issues like bad pointer accesses or memory leaks.
  4. Execute the command ./doom to run the program.

Turn it in

By Tuesday, May 21, there will be a link on the class webpage that will enable you to turn in your source files and report. You will turn in a zip file containing:

Here's how we will evaluate what you turn in: