Andy’s Neural Works

A wise folly can lead to great things!


A Text-Based Dungeon Crawler, Part 2 – Structure of a Hero

Published by

on

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:

  1. A hero has a name.
  2. A hero also has hit points, strength, dexterity, and wisdom.
  3. A hero has status that relates how he is doing based on hit points.
  4. A hero has an inventory that adjusts with use.
  5. The inventory is composed of a sword, arrows, magic spells, and healing potions.
  6. A hero has status based on the current amount of hit points.
  7. 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:

AttributeData TypeDescriptionExample
NameStringPlayer name that gets auto generated.Sir Gallops A Lot
Hit PointsIntegerHealth level of player.20
StrengthIntegerMeasures strength of player’s physical attacks10
DexterityIntegerMeasures agility of player’s defense15
WisdomIntegerMeasures strength of player’s magic attacks7
InventoryListContains what the player is holding.[0,0,5,2,1]
ScoreIntegerMeasure of how the player is progressing1337
StatusStringText relaying how the player is feelingYou 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:

MethodInputOutputDescription
Attackmodifier_attributeinteger – attack rollWhen player attacks, this simulates a dice roll and applies the modifiers. The end result is returned
DefenseNoneinteger – defense rollSimulates a dice roll for defense and applies the modifier for dexterity. End result is returned.
Run AwayNoneInteger – cowardice rollSimulates a dice roll for running away and applies the modifier for dexterity. End result is returned.
HealNoneNoneSimulates a dice roll for healing and applies modifier based on wisdom.
WoundedDamage valueNoneReduces hit points based on damage value. Status is also updated.
Damage Rollmodifier_attributeInteger – damage rollSimulates a standard dice roll and adds on the modifier based on which attack is performed (sword, arrow, magic).
Name GeneratorNoneString – NameBuilds a character name using pseudo random combinations

Consider the private methods now.

MethodInputOutputDescription
ModifierAttribute Score (strength, dexterity, or wisdom)Integer – Score ModifierThis calculates the modifier based on the particular attribute score. The range is between -2 and +2 depending on the value of the attribute.
Level UpNoneNoneAdjusts the player’s attributes and inventory after battle ends via success or cowardice.
Update StatusNoneNoneGenerates 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