Making a Simple Python Text Based Game
Tags: python, beginners, gamedev, oop
Published: 2023-07-29 07:48:16

Making a simple text based game is a great way for a beginner to work on their programming skills and get exposed to Object-Oriented Programming (OOP). Once you have a basic design it is easy to further expand upon this and make your game more complicated.  This post shows you a simple text based game and explains how you would create your own.

Goblins & Gladiators lets players can create goblin and gladiator type characters and make them fight (a little like Pokemon). You can find the Github repo for the game [here](https://github.com/fergus-mk/goblins_and_gladiators_game), please feel free to use this as inspiration for your own game.

As you can see the game has highly sophisticated industry-leading graphics developed by the Unreal Engine 5:

```
# A Goblin
               ||                                ||
               ||||                            ||||
               |||||                          |||||
               ||||||                        ||||||
               |||||||                      |||||||
               ||||||||||||||||||||||||||||||||||||
               ||||||||||||||||||||||||||||||||||||
               ||||||||||||||||||||||||||||||||||||
                ||||||||||||||||||||||||||||||||||
                 |||||||     ||||||||     |||||||
                 ||||||||||||||||||||||||||||||||
                 ||||||||||||||||||||||||||||||||
                 ||||||||||||||||||||||||||||||||
                 ||||||||||            ||||||||||
                 ||||||||||||||||||||||||||||||||
                   ||||||||||||||||||||||||||||
                      ||||||||||||||||||||||
                         ||||||||||||||||
                              ||||||
```

**The steps to make your game**
Designing and making your game follows 3 simple steps:

1. Brainstorm
2. Model
3. Coding


**Step 1 - Brainstorm**
Before you begin jumping in an writing code you want to have some idea of what your game will be like and how it will be played. Think along the lines of _What will the characters in the game be like? What will they do? Will they interact with each other and if so how?_. 

Personally I find using pen and paper can be great for sketching out initial ideas. You might come up with a diagram something like this:

![A simple sketch of the game](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/111ow75bnu1opfb2f53f.jpg)


**Step 2 - Model**
Creating a model is the next step in the process. This is generally done using Unified Markup Language (UML). This lets you take your sketch and describe your game and show how the different components will interact with each other. It is similar to the sketch but more detailed and more closely related to how the game will be coded. 

I have included an example from Goblins & Gladiators below. You will note that the design has changed a little from the sketch. This is totally fine, the process of designing and modelling your game should be iterative so don't worry if you have to go back and make changes. I am not going to explain my diagram in detail, I just wanted to give a rough idea of what one might look like. 

You will notice that goblin and gladiators both inherit from characters. This essentially means they are a type of character and share some of its features. If this is new to you I encourage you to learn a bit about OOP, I have included resources below.

![UML of the game](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2a58cw3kwuskguvw50c3.png)


**Step 3 - Code**
The final step is to actually code your game. Again this can be iterative and you may come up with new ideas and revisit the previous step to change the design. I have included an example of the character class in the game which serves as the basis for both goblins and gladiators.

```
class Character(ABC):
    __character_list = None  # Used in static method to return created characters

    def __init__(self, name, attack_strength, defence_strength, max_hp, item=""):
        """
        Constructor class that initiates Character objects
        """
        self.name = name
        self.attack_strength = attack_strength
        self.defence_strength = defence_strength
        self.max_hp = max_hp
        self.hp = max_hp  # Current hp can be equal to or less than max hp
        self.item = (
            item  # The character has no item as default but can equip up to 1 item
        )

    def attack(self, opponent):
        """
        Deals damage to opponent, note attack has random chance of missing and dealing 0 damage

        Parameters
        ----------
        opponent
            The character affected (Character)

        Returns
        --------
            If the attack is successfull, opponent receives damage from the attack. The amount of 
            damage dealt is equal to the attack_strength attacking character + randomized value 
            - defence_strength of defending chatacter. If the attack misses a string is printed to 
            notify attack has missed and no damage is dealt 
        """
        if self == opponent:
            print(f"{self.name} cannot attack itself, enter a different target")
            return None
        else:
            attack_hit_or_miss = randint(0, 4)  # Attack has random chance of missing
            if attack_hit_or_miss == 0:
                print("attack missed")
            else:
                random_damage_value = randint(
                    -1, 2
                )  # Random number altering the strength of attack

                opponent.receive_damage(
                    self.attack_strength
                    + random_damage_value
                    - opponent.defence_strength
                )
                if opponent.hp > 0:
                    print(
                        f"{self.name} attacked {opponent.name} who now has {opponent.hp} hp"
                    )
                elif opponent.hp <= 0:
                    print(
                        f"{self.name} defeated {opponent.name} who is now dead. {self.name} has won the contest"
                    )
                    visuals.win_visual()

    def equip_item(self, attatch_item):
        """
        Equips selected item to character

        Parameters
        ----------
        attatch_item
            The item the character is equipping (Item)
        
        Returns
        -------
            The character with its equipped item local parameter equal to the item name. This allows
            the character to use the equipped item
        """
        self.item = attatch_item.name
        print(f"{self.name} has now equipped {attatch_item.name}")

    def receive_damage(self, damage_amount):
        """
        Removes amount of damage recieved from own hp

        Parameters
        ----------
        damage_amount
            The amount of damage to recieve (int)

        Returns
        -------
            The character with damage_amount subtracted from its hp
        """
        self.hp = self.hp - damage_amount

    def character_details(self):
        """
        Gives key character details in a human readable sentence (more information than __str__)
        
        Returns
        --------
            The name, hp, attack_strength and defence_strength of the character 
        """
        return f"{self.name} has {self.hp} hp, {self.attack_strength} attack_strength and {self.defence_strength} defence_strength"

    @staticmethod
    def get_character_list():
        """
        Ensures only a singleton list of characters 

        Example
        -------
            the_characters = Character.get_character_list()
            the_characters.append(new_goblin_character)
            the_characters.append(new_gladiator_character)
            print(the_characters)
        """
        if Character.__character_list == None:
            Character.__character_list = []
        return Character.__character_list
```
Again don't get bogged down in the details. This has just been included as an example.

**Playing The Game**
Now of course this is the important part. Actually playing the game. There are detailed instructions in the [repo](https://github.com/fergus-mk/goblins_and_gladiators_game/tree/main), but I wanted to give you a quick preview. Basically creating characters looks a little like this:

```
flame_goblin = characters.Goblin('flame goblin', 'fire')
fighter = characters.Gladiator('fighter')
```
You can then get them to attack each other (or use healing spells and other special abilities):

```
flame_goblin.attack(fighter)
flame goblin attacked fighter who now has 8 hp"
```
The fight continues until one character dies and loses. The game also shows some of the graphics I showed earlier.


**Resources**

- This [course](https://www.linkedin.com/learning/programming-foundations-object-oriented-design-3) by Olivia Chiu Stone and Baron Stone is great for learning about object oriented design (note it is paid for)
- This [video](https://www.youtube.com/watch?v=2AK7j8pIh-0&t=2709s) is a great example of a simple Pokemon text based game
- This [page] (https://copyassignment.com/python-oop-projects-source-code-and-example/) has some more example project designs