Introduction
It’s time to get back to building out that dungeon crawler. In part 1 of this series, I defined the basics of the game along with the rules. Now, it’s time to pull out the data structures to the various parts. This will be used to create an architecture for the coding of the game.
When building out the structures, remember to always adhere to the requirements. This is to avoid scope creep. If there really is a gap, go back to the requirements and make an amendment. Everything does not have to stop but it’s important to keep the blueprint clear and clean.
We will be evaluating the entities and the necessary attributes. This will involve reviewing the basics with the idea of isolating the highest level components. The end result will be to identify the entities as well as the attributes.
You may think I’m headed toward database development with the talk of entity and attributes. For this effort, I’m blending in object oriented programming (OOP) concepts with database design concepts. They are definitely two different branches of development methodology but both involve structures. A difference is that we need to take abstraction and encapsulation into an account (2 pillars of OOP). I’ll explain more as we go on.
Class Normalization
In a similar way to data structure normalization, OOP has its own approach. I have not seen it formally adopted in text books. This is discussed more in corners of the Internet. It might be obscure but I believe should be considered as a valid software design technique.
The reason I do believe normalization should be done is for efficiency. One purpose of OOP is to create modular code that is reused multiple times. In various projects, you will see class design get out of control to the point the benefits of modularization becomes a burden. Taking a moment during design to align the structures with this in mind can avoid over engineering.
*Note: There is a paper on normalization of classes you will want to read up on. You can find it here1.
Back to our project, we need to review the data structures and see how they transition over to a class structure. This project seems to be a good and simple environment for this approach. There are not many entities to deal with so it keeps the scope of exploration tight.
Data Requirements
Let’s review the data requirements set forth for the game.
1. The player takes on the role of a hero. That’s our first entity and has the following requirements:
- A hero has a name.
- A hero also has hit points, strength, dexterity, and wisdom.
- A hero has status that relates how he is doing based on hit points.
- A hero has an inventory that adjusts with use.
- The inventory is composed of a sword, arrows, magic spells, and healing potions.
- A hero has status based on the current amount of hit points.
- A hero gains points for a score under its wins and losses.
2. The monster is interesting. A monster is the same structure as the hero. So, the requirements are the same as the list above.
3. A die has sides that relate the numeric value coming from a roll.
Data Structure to Class Structure
Focus on the hero in the game and the monsters to battle. These two entities have overlapping attributes and defined as the same. Consider the possibility that they are instances of the same parent structure.
This case is a single class with 2 instances. If it gets more complicated and specialized in future iterations, it could be made into a parent class with 2 child classes (one for hero and one for monster). As previously mentioned, we avoid complicated, over-engineered software.
Reviewing the attributes we have the following for both hero and monster:
| Attribute | Data Type | Description | Example |
|---|---|---|---|
| Name | String | Player name that gets auto generated. | Sir Gallops A Lot |
| Hit Points | Integer | Health level of player. | 20 |
| Strength | Integer | Measures strength of player’s physical attacks | 10 |
| Dexterity | Integer | Measures agility of player’s defense | 15 |
| Wisdom | Integer | Measures strength of player’s magic attacks | 7 |
| Inventory | List | Contains what the player is holding. | [0,0,5,2,1] |
| Score | Integer | Measure of how the player is progressing | 1337 |
| Status | String | Text relaying how the player is feeling | You feel great! |
That’s where pure conceptual data structure analysis stops (for initial version). It’s a simple exercise here but the common attributes have been captured. If there is a save game functionality added in the future, the data store structure is already known.
We now step further into the OOP realm. Considering public methods for these entities/classes), there are:
| Method | Input | Output | Description |
|---|---|---|---|
| Attack | modifier_attribute | integer – attack roll | When player attacks, this simulates a dice roll and applies the modifiers. The end result is returned |
| Defense | None | integer – defense roll | Simulates a dice roll for defense and applies the modifier for dexterity. End result is returned. |
| Run Away | None | Integer – cowardice roll | Simulates a dice roll for running away and applies the modifier for dexterity. End result is returned. |
| Heal | None | None | Simulates a dice roll for healing and applies modifier based on wisdom. |
| Wounded | Damage value | None | Reduces hit points based on damage value. Status is also updated. |
| Damage Roll | modifier_attribute | Integer – damage roll | Simulates a standard dice roll and adds on the modifier based on which attack is performed (sword, arrow, magic). |
| Name Generator | None | String – Name | Builds a character name using pseudo random combinations |
Consider the private methods now.
| Method | Input | Output | Description |
|---|---|---|---|
| Modifier | Attribute Score (strength, dexterity, or wisdom) | Integer – Score Modifier | This calculates the modifier based on the particular attribute score. The range is between -2 and +2 depending on the value of the attribute. |
| Level Up | None | None | Adjusts the player’s attributes and inventory after battle ends via success or cowardice. |
| Update Status | None | None | Generates text status to relay how the player is feeling during battle. This gets adjusted based on hit point level. |
As you can see, we have created a class structure with methods and properties. This is similar (yet different) to the database concept of logical design. Since the decision is made to avoid specialization into parent and child classes (i.e. both the hero and monster are composed of the same structure) we can simply call this the player class. Any time a new character gets put into the play field, a new instance is created rather than a hardcoded effort.
*Note: A future thought is that a player can choose to be a monster in a multiplayer battleground. This would further underline keeping the structure unified.
We need design considerations for the dice itself. This could get complicated so let’s try to keep it simple. A dice roll consists of:
- Number of Sides
- Number of Rolls
- Take the Best of N Rolls
- Take the Top N Rolls
There could be a variety of other methods too, but we will simplify it to just one method:
This seems like a good case for a utility library rather than a class. For now, we’ll keep it as a single method, called dice.
This is a good stopping point. The structure has been designed out and optimized. It might seem simple but that’s the point. Start off with a core design that sets a foundation for future growth. Putting too much in up-front (as in a waterfall approach) can lead to the project never getting finished.
Next Steps
This step of structuring the player class and dice function might seem trivial but it saves future work. Instead of coding for each specific case, we abstracted the requirements into a single class (player) for the player as well as all of the monsters.
Developing the structures into a class is an exercise worth undertaking. The ability to see data as moving into object oriented structures opens up possibilities. Since this is a project about a simple game, having a creative outlook is essential. Don’t be afraid to try a different approach than accepted patterns.
In the next article of this series, I will get into the User Experience aspect of the design. I will go through and prototype the interface.
Thank you for reading!
Happy New Year!
References
[1] Scott Ambler. Introduction to Class Normalization: Clean Class Design. Retrieved from: https://agiledata.org/essays/classnormalization.html
