Lesson 6 - Arena with warriors in Python
In the previous lesson, Warrior for the arena in Python, we created the Warrior
class. Our rolling die is already finished from the early lessons. In today's
Python tutorial, we're going to put it all together and create a fully
functioning arena. The tutorial is going to be simple and will help you get some
more practice on working with objects.
We'll need to write some code that will manage our warriors and print
messages to the user. Of course, we won't put all this in the main program but
we'll keep things organized. We'll create an Arena
object where our
fight will take place. The main program part will only provide the necessary
objects, and the Arena
object will take care of the rest.
The class will be rather simple, it'll include three needed instances as the attributes: the 2 warriors and the rolling die. These attributes will be initialized from the constructor parameters. The class code will be as following (add comments accordingly):
class Arena: def __init__(self, warrior_1, warrior_2, die): self.__warrior_1 = warrior_1 self.__warrior_2 = warrior_2 self.__die = die
Let's think about the methods. We're definitely going to need a public method
to simulate the fight. We'll make the program output fancy and allow the
Arena
class to access the console directly. We've decided that the
printing will be done by the Arena
class since it makes sense here.
If the printing was performed by warriors, the design would be flawed since the
warriors would not be universal. We need a method that prints information about
the round and the warriors' health to the console. The damage and defense
messages will be printed with a dramatic pause so as to make the fight more
intense. We'll create a helper method for this. Let's start with the method that
renders the information screen:
def __render(self): self.__clear_screen() print("-------------- Arena -------------- \n") print("Warriors health: \n") print("{0} {1}".format(self.__warrior_1, self.__warrior_1.health_bar())) print("{0} {1}".format(self.__warrior_2, self.__warrior_2.health_bar()))
At the beginning, we call another method, __clear_screen()
. We
call the methods within the object as follows:
self.method_name
The rest is clear. Both methods are private, we'll use them only within the class.
The code of the private __clear_screen()
method looks like
this:
def __clear_screen(self): import sys as _sys import subprocess as _subprocess if _sys.platform.startswith("win"): _subprocess.call(["cmd.exe", "/C", "cls"]) else: _subprocess.call(["clear"])
We import the sys
and subprocess
modules needed to
clear the console screen. According to the OS, we execute the appropriate
command to clear the screen. Let's create another private method that will print
messages with a dramatic pause:
def __print_message(self, message): import time as _time print(message) _time.sleep(0.75)
The code is obvious except for the sleep()
function from the
time
module, which suspends the program thread for a given number
of seconds. We'll work with threads at the end of the course.
Both methods just print to the console, I think we don't have to try them
now. Let's move on to the fighting part. The fight()
method will be
parameterless and won't return anything. There will be a loop inside calling the
warriors' attacks in turns and printing the information screen with the
messages. The method would look something like this:
def fight(self): print("Welcome to the Arena!") print("Today {0} will battle against {1}!".format(self.__warrior_1, self.__warrior_2)) print("Let the battle begin...", end=" ") input() # fight loop while (self.__warrior_1.is_alive() and self.__warrior_2.is_alive()): self.__warrior_1.attack(self.__warrior_2) self.__render() self.__print_message(self.__warrior_1.get_last_message()) # attack message self.__print_message(self.__warrior_2.get_last_message()) # defense message self.__warrior_2.attack(self.__warrior_1) self.__render() self.__print_message(self.__warrior_2.get_last_message()) # attack message self.__print_message(self.__warrior_1.get_last_message()) # defense message print("")
The code prints introductory lines and executes the fighting loop after the
user presses any key. It's a while
loop that repeats as long as
both warriors are alive. The first warrior attacks his opponent and his attack
internally calls the other warrior's defense. After the attack, we render the
information screen. The messages about the attack and defense are printed by our
__print_message()
method which makes a dramatic pause after the
printing. The same thing will happen with the other warrior.
Let's move back to the end of the program. We'll create the needed instances
and call the fight()
method on the arena:
# creating objects die = RollingDie(10) zalgoren = Warrior("Zalgoren", 100, 20, 10, die) shadow = Warrior("Shadow", 60, 18, 15, die) arena = Arena(zalgoren, shadow, die) # fight arena.fight() input()
You can change the values to whatever you'd like. Here's what the program looks like at runtime:
Console application
-------------- Arena --------------
Warriors health:
Zalgoren [## ]
Shadow [ ]
Shadow attacks with a hit worth 19 hp
Zalgoren blocked the hit
The result is quite impressive. The objects communicate with each other, the health bar decreases as expected, the experience is enhanced by a dramatic pause. However, our arena still has two issues:
- In the fight loop, the first warrior attacks the other one. Then, the second warrior attacks back, even if he has already been killed by the first warrior. Look at the output above, at the end, Shadow attacked even though he was dead. The while loop terminated just after that. There are no issues with the first warrior, but we have to check whether the second warrior is alive before letting him attack.
- The second problem is that the warriors always fight in the same order so "Zalgoren" has an unfair advantage. Let's use the rolling die to decide who will start the fight. Since there will always only be two warriors, we can set the warriors' turns based off of whether the rolled number is less or equal to half of the number of die sides. Meaning that if it rolls a number less than 5 on a ten-sided die, the second warrior goes first, otherwise, the first one does.
The updated version preventing the second warrior from attacking when he's already dead and letting the warriors start randomly can look like this:
def fight(self): import random as _random print("Welcome to the Arena!") print("Today {0} will battle against {1}!".format(self.__warrior_1, self.__warrior_2)) print("Let the battle begin...", end=" ") input() # swapping the warriors if _random.randint(0, 1): (self.__warrior_1, self.__warrior_2) = (self.__warrior_2, self.__warrior_1) # fight loop while (self.__warrior_1.is_alive() and self.__warrior_2.is_alive()): self.__warrior_1.attack(self.__warrior_2) self.__render() self.__print_message(self.__warrior_1.get_last_message()) # attack message self.__print_message(self.__warrior_2.get_last_message()) # defense message if self.__warrior_2.is_alive(): self.__warrior_2.attack(self.__warrior_1) self.__render() self.__print_message(self.__warrior_2.get_last_message()) # attack message self.__print_message(self.__warrior_1.get_last_message()) # defense message print("")
We swap the warriors using the parentheses expression above. If the parentheses weren't there, Python would have thrown an error. We wrapped a line at this place so that the code won't be too wide. The recommended line length is 80 characters.
Now, let's take her for a spin!
Console application
-------------- Arena --------------
Warriors health:
Zalgoren [########### ]
Shadow [ ]
Zalgoren attacks with a hit worth 27 hp
Shadow defended against the attack but still lost 9 hp, and died
Congratulations! If you've gotten this far and have actually read through, you have the basis of object-oriented programming in Python and should be able to create reasonable applications
In the next lesson, Inheritance and polymorphism in Python, we'll explain object-oriented programming in further detail. We mentioned that OOP is based on three core concepts - encapsulation, inheritance, and polymorphism. We're already familiar with the encapsulation, the other two await you in the next lesson.
Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.
Download
By downloading the following file, you agree to the license terms
Downloaded 46x (6.21 kB)
Application includes source codes in language Python