Week 5 Tutorial: Polymorphism & Abstraction
Problem 1: Transport System
You are writing a simple traffic simulation where different forms of transport move using a single common command, regardless of what type of transport they are.
- Create a class
Carwith a methodmove()that returns the string"Driving on the road.". - Create a class
Airplanewith a methodmove()that returns the string"Flying through the sky.". - Create a function
start_journey(vehicle)that takes a transport object as an argument, calls itsmove()method, and prints the result.
Input
car = Car()
plane = Airplane()
start_journey(car)
start_journey(plane)
Expected Output
Driving on the road.
Flying through the sky.
Problem 2: Virtual Orchestra
A music application plays different sounds depending on the instrument selected. You will build a base class and override its method in subclasses.
- Create a base class
Instrumentwith an__init__method that accepts and storesname. Add a methodplay()that returns the string"Playing the {name}.". - Create a subclass
Guitarthat inherits fromInstrument. Override theplay()method to return"Strumming the {name}.". - Create a subclass
Drumthat inherits fromInstrument. Override theplay()method to return"Beating the {name}.". - Create a function
concert(instruments)that accepts a list of instrument objects and prints the result of callingplay()on each one.
Input
instruments = [
Instrument("flute"),
Guitar("acoustic guitar"),
Drum("snare drum")
]
concert(instruments)
Expected Output
Playing the flute.
Strumming the acoustic guitar.
Beating the snare drum.
Problem 3: Fitness Calorie Calculator
A fitness tracking app calculates the total calories burned for different activities. Each workout type has a different calorie burn rate.
- Create a class
Runningwith a methodcalories_burned(minutes)that returns theminutesmultiplied by 10. - Create a class
Yogawith a methodcalories_burned(minutes)that returns theminutesmultiplied by 4. - Create a function
workout_summary(workout, minutes)that accepts a workout object and the duration in minutes. It should print"Burned {amount} calories in {minutes} minutes."by calling the object’scalories_burnedmethod.
Input
run = Running()
yoga = Yoga()
workout_summary(run, 30)
workout_summary(yoga, 45)
Expected Output
Burned 300 calories in 30 minutes.
Burned 180 calories in 45 minutes.
Problem 4: Payment Gateway
An online store supports multiple payment methods. To ensure every new payment method is implemented correctly, you must use an Abstract Base Class (ABC) to enforce a strict contract.
- Import
ABCandabstractmethodfrom theabcmodule. - Create an abstract base class called
PaymentProcessorthat inherits fromABC. - Define an abstract method
process_payment(self, amount). - Create a subclass
CreditCardProcessorthat inherits fromPaymentProcessor. Implementprocess_payment(amount)so it returns the string"Processing credit card payment of ${amount}". - Create a subclass
PayPalProcessorthat inherits fromPaymentProcessor. Implementprocess_payment(amount)so it returns the string"Processing PayPal payment of ${amount}". - Write a function
checkout(processor, amount)that accepts a payment processor object and an amount, calls itsprocess_payment()method, and prints the result.
Input
cc = CreditCardProcessor()
paypal = PayPalProcessor()
checkout(cc, 150)
checkout(paypal, 45.50)
try:
p = PaymentProcessor()
except TypeError:
print("Cannot instantiate abstract class")
Expected Output
Processing credit card payment of $150
Processing PayPal payment of $45.5
Cannot instantiate abstract class
Problem 5: Document Formatter
You are building a system that formats raw text into different output styles. The system should have a common publishing workflow — wrapping formatted text between *** delimiters — but each formatter decides how the text is actually formatted.
Design an abstract base class TextFormatter that enforces subclasses to implement a format_text(self, text) method, while providing a regular publish(self, text) method that wraps the formatted result between *** lines.
Then create two formatters:
LoudFormatter— converts text to uppercase.MarkdownFormatter— wraps text in bold markdown syntax (i.e., surrounds text with double asterisks:**text**).
Finally, write a function generate_output(formatter, text) that uses the formatter to publish and print the result.
Input
loud = LoudFormatter()
md = MarkdownFormatter()
generate_output(loud, "Warning")
generate_output(md, "Bold statement")
Expected Output
***
WARNING
***
***
**Bold statement**
***
Problem 6: Rambo's Jungle Survival
Rambo is trapped in the jungle. He must use whatever he finds to fight enemies and survive.
Items in the jungle fall into two categories: weapons (things that can attack a target) and survival gear (things that can be used by a person). Some items — like an explosive arrow — belong to both categories.
Design a system using abstract base classes Weapon (with method attack(self, target)) and SurvivalGear (with method use(self, user)) so that any subclass must implement these methods. Then create an Enemy class with name and health attributes, and three item classes:
M60MachineGun— a weapon that deals 40 damage. Itsattackmethod prints"Rambo sprays bullets at {target.name}! (-40 HP)".Medkit— survival gear that heals the user by 50 health points. Itsusemethod prints"Rambo bandages his wounds. (+50 HP)".ExplosiveArrow— an item that is both a weapon (deals 60 damage) and survival gear (heals user by 20 health points). Itsattackmethod prints"Rambo fires an explosive arrow at {target.name}!! BOOM! (-60 HP)". Itsusemethod prints"Rambo uses the arrow powder to start a campfire. (+20 HP)".
The attack method should reduce the target’s health (but not below 0). The use method should increase the user’s health.
Finally, create a Rambo class that starts with health = 100 and an empty inventory. Rambo can pick_up(self, item) items, engage(self, target) enemies (using only items that are weapons), and survive(self) (using only items that are survival gear).
Input
rambo = Rambo()
enemy = Enemy("Sheriff Teasle", 100)
rambo.pick_up(M60MachineGun())
rambo.pick_up(Medkit())
rambo.pick_up(ExplosiveArrow())
rambo.engage(enemy)
print(f"Sheriff Teasle's Health: {enemy.health}")
print("---")
rambo.survive()
print(f"Rambo's Health: {rambo.health}")
Expected Output
Rambo sprays bullets at Sheriff Teasle! (-40 HP)
Rambo fires an explosive arrow at Sheriff Teasle!! BOOM! (-60 HP)
Sheriff Teasle's Health: 0
---
Rambo bandages his wounds. (+50 HP)
Rambo uses the arrow powder to start a campfire. (+20 HP)
Rambo's Health: 170
Problem 7: Space Fleet Command

You are designing the control software for an intergalactic space fleet. Ships in the fleet have different capabilities: some can move, some can attack, some can heal, and some can do a combination. Your system must handle both official ships that follow strict interfaces and unknown alien technology that doesn’t belong to your class hierarchy but may still have useful abilities.
All ships have name, health, and location attributes. Official ships start with location = "Hangar". The AlienDrone is not part of your class hierarchy, so its location starts as "Unknown".
Define three abstract capability interfaces: Moveable (with warp(self, destination)), Attacker (with fire(self, target)), and Healer (with repair(self, target)).
Then create the following ship classes (each accepts name and health):
Fighter— can move and attack.warpupdateslocationand prints"{name} warps to {destination}.".firedeals 30 damage to the target (reducetarget.health, but not below 0) and prints"{name} fires at {target.name}! (-30 HP)".MedicalCruiser— can move and heal.warpupdateslocation.repairrestores 25 HP to the target and prints"{name} repairs {target.name}. (+25 HP)".DefensePlatform— can only attack (stationary).firedeals 50 damage (not below 0).AlienDrone— canfire(deals 20 damage, not below 0) but is not part of your official class hierarchy.
Create an EnemyShip class with name and health.
Finally, build a Fleet class with add_ship(self, ship) and three command methods:
move_fleet(self, destination)— moves all ships that are capable of moving (updating theirlocation); for those that aren’t, print"{ship.name} cannot move!".support(self, target)— has all healers repair the target (increasing itshealth).all_out_attack(self, target)— fires every ship that has the ability to fire (reducingtarget.health), including alien ships.
Input
fleet = Fleet()
fighter = Fighter("X-Wing", 100)
medic = MedicalCruiser("Rescue-1", 80)
platform = DefensePlatform("Alpha Station", 200)
drone = AlienDrone("Bzzz-7", 60)
fleet.add_ship(fighter)
fleet.add_ship(medic)
fleet.add_ship(platform)
fleet.add_ship(drone)
fighter.health = 50 # damaged in earlier battle
print("--- Moving Fleet ---")
fleet.move_fleet("Sector 7")
print(f"X-Wing location: {fighter.location}")
print(f"Rescue-1 location: {medic.location}")
print(f"Alpha Station location: {platform.location}")
print(f"Bzzz-7 location: {drone.location}")
print("\n--- Supporting ---")
fleet.support(fighter)
print(f"X-Wing health: {fighter.health}")
print("\n--- All Out Attack ---")
enemy = EnemyShip("Borg Cube", 150)
fleet.all_out_attack(enemy)
print(f"Borg Cube health: {enemy.health}")
Expected Output
--- Moving Fleet ---
X-Wing warps to Sector 7.
Rescue-1 warps to Sector 7.
Alpha Station cannot move!
Bzzz-7 cannot move!
X-Wing location: Sector 7
Rescue-1 location: Sector 7
Alpha Station location: Hangar
Bzzz-7 location: Unknown
--- Supporting ---
Rescue-1 repairs X-Wing. (+25 HP)
X-Wing health: 75
--- All Out Attack ---
X-Wing fires at Borg Cube! (-30 HP)
Alpha Station fires at Borg Cube! (-50 HP)
Bzzz-7 fires at Borg Cube! (-20 HP)
Borg Cube health: 50
Problem 8: Dungeon Crawler

You are building the core engine for a dungeon crawler game. A hero explores a dungeon made up of rooms, picking up items and fighting monsters along the way.
Interfaces
Define two abstract base classes:
Interactable— enforces aninteract(self, character)method. Items likePotionandSwordinherit from this.Fightable— enforces atake_hit(self, damage)method. BothHeroandMonsterinherit from this. The method should reduce health (not below 0) and returnTrueif still alive,Falseif dead.
Items
- A
Potionhas apowervalue. When it interacts with a character, it increases the character’shealthbypowerand returns"Healed {power} HP". - A
Swordhas abonusvalue. When it interacts with a character, it increases the character’sattackbybonusand returns"Attack +{bonus}". - A
Mimicalso has aninteractmethod — but it is not part of theInteractablehierarchy. It deals 15 damage to the character and returns"It's a Mimic! -15 HP". It works purely through duck typing.
Characters
- A
Monsterhasname,health, andattack. - A
Herohasname, starts withhealth = 100,attack = 10, and an emptyinventory. The hero canpick_upitems (storing them and callinginteract) andfightmonsters. In combat, the hero and monster alternate hits (hero first) until one dies. Each hit prints who hits whom and for how much damage. When one dies, print that they have been defeated.
Dungeon
- A
Roomhas aname, can hold items (place_item) and optionally a monster (place_monster). It has anis_clearedmethod that returnsTrueif there are no items and no living monster. - A
Dungeontakes a list of rooms. Itsexploremethod moves the hero through each room: print the room name, pick up all items (printing each result), then fight the monster if present. If the hero dies, print"Game Over"and stop. After all rooms, print"Dungeon cleared!".
Input
room1 = Room("Armory")
room1.place_item(Potion(30))
room1.place_item(Sword(5))
room2 = Room("Trap Room")
room2.place_item(Mimic())
room3 = Room("Boss Chamber")
room3.place_item(Potion(50))
room3.place_monster(Monster("Dragon", 60, 25))
hero = Hero("Aldric")
dungeon = Dungeon([room1, room2, room3])
dungeon.explore(hero)
print(f"{hero.name}'s final health: {hero.health}")
print(f"{hero.name}'s attack: {hero.attack}")
Expected Output
Entering Armory...
Healed 30 HP
Attack +5
Entering Trap Room...
It's a Mimic! -15 HP
Entering Boss Chamber...
Healed 50 HP
Aldric hits Dragon for 15 damage
Dragon hits Aldric for 25 damage
Aldric hits Dragon for 15 damage
Dragon hits Aldric for 25 damage
Aldric hits Dragon for 15 damage
Dragon hits Aldric for 25 damage
Aldric hits Dragon for 15 damage
Dragon has been defeated!
Dungeon cleared!
Aldric's final health: 90
Aldric's attack: 15
Input 2 — Hero dies in the first room and never reaches the second:
room_a = Room("Dark Hallway")
room_a.place_monster(Monster("Golem", 30, 120))
hero2 = Hero("Bren")
dungeon2 = Dungeon([room_a, Room("Treasure Room")])
dungeon2.explore(hero2)
print(f"{hero2.name}'s final health: {hero2.health}")
Expected Output 2
Entering Dark Hallway...
Bren hits Golem for 10 damage
Golem hits Bren for 120 damage
Bren has been defeated!
Game Over
Bren's final health: 0
Input 3 — Testing is_cleared and abstract class instantiation:
room_x = Room("Empty Room")
print(f"Empty room cleared? {room_x.is_cleared()}")
room_x.place_item(Potion(10))
print(f"Room with item cleared? {room_x.is_cleared()}")
room_y = Room("Monster Den")
room_y.place_monster(Monster("Rat", 5, 2))
print(f"Room with alive monster cleared? {room_y.is_cleared()}")
room_y.monster.take_hit(100)
print(f"Room with dead monster cleared? {room_y.is_cleared()}")
try:
Interactable()
except TypeError:
print("Cannot instantiate Interactable")
try:
Fightable()
except TypeError:
print("Cannot instantiate Fightable")
Expected Output 3
Empty room cleared? True
Room with item cleared? False
Room with alive monster cleared? False
Room with dead monster cleared? True
Cannot instantiate Interactable
Cannot instantiate Fightable