Discussion 5: Object-Oriented Programming

Download Handout download

The goal of today’s discussion is to practice some of the principles of object-oriented design and development. Object-oriented programming is a powerful tool for structuring code which enables readability, maintainability, and extensibility. Today you’ll get a start on assignment A5, a simple text-based battle game (think a super pared-down version of D&D combat, if you’re familiar with that). The task for this discussion is to get familiar with the structure of the assignment code and implement the main combat loop; the assignment will focus on extending and adding new functionality to the activity you’re completing today.

Learning Outcomes

Reminder: Discussion Guidelines

The work that you complete in discussion serves as a formative assessment tool, meaning it offers the opportunity to assess your understanding of the material and for our course staff to get a “pulse” on how things are going so we can make adjustments in future classes. Your grade in discussion is based entirely on attendance and participation; if you show up and you are actively engaged with the activity (working on the activity on your computer, discussing the activity with your group, asking and answering questions, etc.) for the entire 50-minute section period, you will earn full credit. If you complete the activity early, helping other students is a great way to further your own understanding. You do not need to submit any of the work that you complete during discussion.

Since discussion activities are not graded for correctness, we do not place any restrictions on resources that you may use to complete them, which include notes, books, unrestricted conversations with other students, internet searches, and the use of large language models or other generative AI. We advise you to be pragmatic about your use of these resources and think critically about whether they are enhancing your learning. Discussion activities are intended to serve as “strength training” for programming tasks we will expect on assignments and exams (and that you will encounter in future courses and careers), and their main benefit comes from critically thinking to “puzzle” them out.

Working together in small groups is encouraged during discussion!

Background

In Assignment 5, you’ll be implementing a text-based combat simulator for a simple tabletop roleplaying game. Today’s discussion is meant to get you started on this. We’ll be working with the diagram shown below; you’ll be adding more components to this later as you work on A5, but we’ll confine today’s activity to the given classes and interface.

Imagine you and some friends are playing D&D. If you’ve never done this, don’t worry; the details of the actual game are not important (and will be vastly oversimplified for our purposes anyway), and we’ll tell you everything you need to know. Essentially, you and your friends form an adventure party; you are the Players in this situation. Your objective is to defeat the Monsters you encounter in combat, subject to some system of rules. In the process of translating this to code, we might come up with something like the following diagram.

Class Diagram for Discussion 5

There is a GameEngine class whose objects are responsible for running a game. Its behaviors include processing user input, maintaining the state of the Players and Monsters in the current battle, and managing turn order and advancement. Both Players and Monsters (which implement the Actor interface) are able to take some actions on their turn. For now, let’s assume the following about the actions that Players and Monsters can take:

Take a look at the JavaDoc pages for our game’s classes. The JavaDoc provides more information (the complete method specifications) than our class diagram shown above, so it is written at a later stage of the development process.

Written Questions

Exercise 1: Execution Semantics
Suppose that we execute the following gameSnippet1() method:
1
2
3
4
5
6
7
8
/** Simulates the first turn of a game instance with one player and one monster. */
static void gameSnippet1() {
  GameEngine engine = new GameEngine();
  Actor actor1 = new Player("Frieren", "elf", engine);
  Actor actor2 = new Monster("Demon King", engine);
  actor1.takeTurn();
  actor2.takeTurn();
}
1
2
3
4
5
6
7
8
/** Simulates the first turn of a game instance with one player and one monster. */
static void gameSnippet1() {
  GameEngine engine = new GameEngine();
  Actor actor1 = new Player("Frieren", "elf", engine);
  Actor actor2 = new Monster("Demon King", engine);
  actor1.takeTurn();
  actor2.takeTurn();
}
(a)

Draw a memory diagram that depicts the state of the gameSnippet1() call frame (and all heap objects that can be reached from this call frame by following one or more references) just after line 5 finishes executing. You can fill in any valid value for each Actor’s power.

To simplify your diagram a bit, you can visualize the GameEngine as an empty rounded rectangle (omitting its fields). Draw all fields of any other objects.

(b)

What is the static type of the variable actor1? What is the dynamic type of the object that it references?

(c)

When line 7 executes, which method body do we enter? How does Java figure this out?

Exercise 2: Compilation Semantics
Now consider the following gameSnippet2() method:
1
2
3
4
5
6
/** Simulates the creation of a game instance with one human player. */
static void gameSnippet2() {
  GameEngine engine = new GameEngine();
  Actor actor = new Player("Himmel", "human", engine);
  System.out.println("Created a " + actor.species());
}
1
2
3
4
5
6
/** Simulates the creation of a game instance with one human player. */
static void gameSnippet2() {
  GameEngine engine = new GameEngine();
  Actor actor = new Player("Himmel", "human", engine);
  System.out.println("Created a " + actor.species());
}
What happens when we (try to) run this method? Explain your answer, referencing ideas from lecture.

Programming Tasks

Download and look through the starter code for this game. You have been given complete implementations of our basic Monster and Player classes (along with the Actor interface that they implement) and a partial implementation of the GameEngine class. For the rest of this discussion, you’ll add additional functionality to the GameEngine class.

Before we start coding, open the GameEngine class and read the documentation of its fields to understand its class invariant. As an implementer of the GameEngine class, it will be your responsibility to restore this class invariant at the end of every (non-private) method that you write.

processMonsterDeath() and processPlayerDeath()

Complete the definitions of the processMonsterDeath() (TODO 1) and processPlayerDeath() (TODO 2) methods to conform to its specifications. Where do you think that these methods would be called within the other classes? Take a look at the rest of the code to check if you were correct.

The Main Game Loop

Implement the main game loop method in GameEngine.java according to its specification (TODOs 3-6).

1
2
3
4
5
6
7
8
/**
 * Runs the main game loop. Terminates when the Players have won (all Monsters are dead), or when the Players have 
 * lost (all Players are dead). While there are still living Players and Monsters, generates a turn order for all 
 * active Actors at the start of each turn, and executes each Actor's turn in the generated order. At the end of 
 * the game, if the Players have won, the message "Congratulations! You defeated the monsters!" is printed. 
 * Otherwise, if the Players have lost, the message "The monsters defeated you. Better luck next time!" is printed.
 */
private void mainGameLoop() { ... }
1
2
3
4
5
6
7
8
/**
 * Runs the main game loop. Terminates when the Players have won (all Monsters are dead), or when the Players have 
 * lost (all Players are dead). While there are still living Players and Monsters, generates a turn order for all 
 * active Actors at the start of each turn, and executes each Actor's turn in the generated order. At the end of 
 * the game, if the Players have won, the message "Congratulations! You defeated the monsters!" is printed. 
 * Otherwise, if the Players have lost, the message "The monsters defeated you. Better luck next time!" is printed.
 */
private void mainGameLoop() { ... }

Think carefully about how to do this part so that, if we were to add new types of Players (which you’ll do in the assignment), we would not have to add additional cases/logic to the implementation of this function. This is possible, and showcases polymorphism and dynamic dispatch; this is really neat, since this modular design allows us to easily support new features.