Python Terminal Project Review- The Arena

This is a summary of my experience with the project

I was tasked with creating a Terminal game. I knew it was coming and had been preparing for weeks for it as I plowed through the Computer Science course, fully confident that I would tackle this project with ease.

I think I stared at my IDE for about 2 hours before I figured out how to get started. I began building a class I simply called “Human”. This was gong to represent both myself, and the NPCs in my little text-based world. Oh the grand ideas I had. Hours blew by and I typed away.

Before I realized what was going on, I had overloaded myself. Every time I had a though, I’d insert blocks of code to make that a reality and the project blew up beyond a simple little terminal game. I could have saved myself hours if I had had a little humility about my actual capabilities. Instead hours turned into days, and then a week later I have about 400 lines of code and none of it works.

I had managed to exhaustively iron out the exception-causing bugs, but nothing behaved as I intended. NPCs would get stuck in one room, weapons (the second class I created to satisfy the ‘2 Classes’ Requirement of the project) weren’t being properly equipped. Combat was hilariously broken, and I, as the player, couldn’t even leave the room I’d crafted to enter one of the other two.

So today I saved the project as a WIP to visit later with better tools and reduced it significantly to you and your opponent. No great fanfare of worldbuilding, no trying to move around from room to room, not even the complex turn-based battle system I’d dreamed up.

You pick a name, you pick a weapon, you enter the main loop, and you and an NPC whack at eachother (or defend) until one of you falls unconscious.

I did get to keep one fun little toy I’d dreamed up. I envisioned a mace as a special weapon, one that could immediately end the fight by knocking your opponent unconscious. I reduced the chance of this to nearly nothing, and was treated on my first playtest to being immediately defeated in the first hit by a mace wielding NPC.

Unfortunately I didn’t think to document my journey with screenshots, so all I have is post-completion. The final product. Behold the culmination of a week and a half of free-time consuming effort:

And here's the code that makes it work
import random as r

#I'm lazy and don't want to type r.randinit every time I want to roll a number
def roll(num1, num2):
    roll = r.randint(num1, num2)
    return roll

class Human:
  def __init__(self, name, weapon, player=False): = name
    #Randomize stats to make each fight truly unique. You might be a buff god made of iron, or a weak worm made of paper. Same goes for the NPC
    self.strength = roll(1, 10)
    self.defense = roll(1, 10) = 100
    self.weapon = weapon
    self.player = player
    self.conscious = True
    self.is_defending = False
  def __repr__(self):
      return "A Human"
  #This took an absurd amount of my time, both in the original script and in this one
  def attack(self, target=None):
        if self.conscious and target.conscious:
            attack_roll = roll(1, 20) + self.weapon.attack
            defense_roll = roll(1, 20)
            if defense_roll + target.defense >= attack_roll + self.strength:
                print("{attacker}'s attack missed!".format(
            elif target.is_defending:
                block_chance = roll(1, 100) + target.defense
                if block_chance >= 90:
                    print("{defender} blocked the attack!".format(
                    damage_roll = roll(1, 10) + self.weapon.damage - target.defense
                    if damage_roll < 0:
                        damage_roll = 0
                        "{defender} partially deflected {attacker}'s attack! {defender} only takes {damage_roll} damage!".format(
                  ,, damage_roll=damage_roll
           -= damage_roll
                target.is_defending = False
                damage_roll = roll(1, 10) + self.weapon.damage
                #Go ahead and create a roll for the knockout check
                knockout_roll = roll(1, 100)
                #Check if you or the NPC has a mace, if so, here's the chance to crush their skull
                if self.weapon.make == "Mace" and knockout_roll >= 95:
                    target.conscious = False
                        "{attacker}'s Mace lands on {defender}'s head with a loud crunch! {defender} has been knocked out!".format(
           -= damage_roll
                        "{attacker}'s weapon lands a blow! {defender} takes {damage} damage!".format(
                  ,, damage=damage_roll
        elif not target.conscious:
            print("{target} is already unconscious!".format(

  def defend(self):
    if self.conscious and self.is_defending == False:
      self.is_defending = True
      print("{defender} prepares for an attack...".format(defender =
    elif self.conscious and self.is_defending and self.player:
      print("{defender} can't be any more prepared for an attack!")

class Weapon:
    def __init__(self, make):
        self.make = make
        #kinda proud of this. I didn't do it alone, it was built on a suggestion by ChatGPT, but the values and use are mine
        self.stats = {
            "Longsword": {"damage": 5, "attack": 10},
            "Mace": {"damage": 3, "attack": 3},
            "Dagger": {"damage": 2, "attack": 15},
            "Greatsword": {"damage": 15, "attack": 5}

        if self.make in self.stats:
            self.damage = self.stats[self.make]["damage"]
            self.attack = self.stats[self.make]["attack"]
            self.damage = 0
            self.attack = 0

    def __repr__(self):
        return self.make

longsword = Weapon("Longsword")
mace = Weapon("Mace")
dagger = Weapon("Dagger")
greatsword = Weapon("Greatsword")
weapon_rack = [longsword, mace, dagger, greatsword]

#Nailed this one on the first try. I think I'm most proud of this function, out of this entire script, small, but it was a victory for me
def assign_name():
    first_names = ["Nicostratus", "Sostratus", "Patricius", "Leonius", "Beringarius", "Nerva", "Saul", "Demetrius", "Viggo", "Philandrus", "Philocrates", "Ladislaus", "Amatus", "Fidelis"]
    last_names = ["Procillus", "Cossus", "Clineas", "Blasio", "Laevinus", "Regillensis", "Pictor", "Libo", "Spinther", "Rutilus", "Mocilla", "Sacerdos", "Sisenna", "Crus", "Potitus"]
    name = first_names[roll(0, len(first_names) - 1)] + " " + last_names[roll(0, len(last_names) - 1)]
    return name

npc = Human(assign_name(), weapon = weapon_rack[roll(0, len(weapon_rack) - 1)])

#No matter how much I stripped it down, I couldn't help but put in a little bit of fanfare

You enter the arena to the roar of the crowd. A man in the emperor's box with a booming voice shouts, "Welcome, one and all, back to The Arena!"

The crowd roars again. After the din dies down, the announcer continues. "Today, we see battle between two fierce competitors! First, we have {npc} using a {weapon1}!"

Once more the arena erupts in cheers, some even chanting {npc}'s name.

"And second, we have...
""".format(npc =, weapon1 = npc.weapon))

player_name = input("Enter your name: ")


{player} using a...

""".format(player = player_name))

weapon = ""
while weapon == "":
    weapon_choice = input("Select a weapon from the following: DAGGER, GREATSWORD, LONGSWORD, MACE ")
    clean_weapon = weapon_choice.lower()
    if clean_weapon == "dagger":
        weapon = dagger
    elif clean_weapon == "greatsword" or clean_weapon == "great sword":
        weapon = greatsword
    elif clean_weapon == "longsword" or clean_weapon == "long sword":
        weapon = longsword
    elif clean_weapon == "mace":
        weapon = mace
        print("{choice} is not a valid choice".format(choice = weapon_choice))


{player_weapon}!" the crowd erupts into cheers again. Some chanting your name this time.

"Without further ado ladies and gentlemen, let the battle BEGIN!" he shouts, the crowd erupts in roars and your opponent rushes you!

""".format(player_weapon = weapon))
player = Human(player_name, weapon, True)

#Flags to make sure neither the user nor the NPC gets unlimited turns
player_turn = True
npc_turn = False

#Main game loop. If you or the NPC gets knocked out, game over
while player.conscious and npc.conscious:
    while player_turn == True and npc_turn == False:
        player_action = input("What do you want to do? ATTACK, DEFEND, or CHECK HEALTH? ").lower()
        if player_action == "attack":
            player_turn = False
            npc_turn = True
        elif player_action == "defend":
            player_turn = False
            npc_turn = True
        elif player_action == "check health":
            print("You have {health} health left.".format(health =
            print("You can't do that!")
        #Admittedly this was annoying difficult. Hours upon hours figuring out why the loop wouldn't break appropriately or return the
        #right output. All because I declared this and the NPC unconscious condition at the top of the game loop
        if <= 0:

            print("You have won! Congratulations! The crowd roars and chants your name over and over.")
            npc.conscious = False

    #NPC Logic. Not smart enough to be called AI. Ensures you get a unique fight every game load.
    while npc_turn == True and player_turn == False:
        action_choice = roll(1,100)
        if action_choice >= 80:
            npc_turn = False
            player_turn = True
            npc_turn = False
            player_turn = True
        if <= 0:
            print("You have been defeated. Game Over...")
            player.conscious = False