← 返回 openai 的题目列表Monster Battle System
类型:qbank
Design and implement a battle simulation system where two teams of monsters fight each other in turn-based combat. The problem is structured in three progressive parts: basic battle logic, elemental type effectiveness, and an optimized smart-targeting strategy. It is an Object-Oriented Design problem that emphasizes encapsulation, single responsibility, and extensibility.
Problem Description
Create a battle simulation where two teams of monsters fight. The fight happens in turns. Each team has a list of monsters. Every monster has a specific amount of health and attack power. The teams take turns attacking until one team has no monsters left. You need to print a log that describes what happens during the fight.
This is an Object-Oriented Design problem.
Part 1: Simple Battle Logic
Problem Requirements
Build a system with these rules:
Monster: Every monster has:
A Name (string)
Health Points (HP) - a positive integer
Attack Power - a positive integer
Team: Every team has:
A Team Name
A list of Monsters
Rules of the Fight:
Team A goes first. Teams switch turns after each attack.
The first living monster on the attacking team hits the first living monster on the defending team.
Damage Math: The defender loses HP equal to the attacker's power (Defender HP = Defender HP - Attacker Power).
The attacker does NOT take damage back.
If a monster's HP hits 0 or less, it is eliminated.
The battle ends when all monsters on one team are eliminated.
Output Log: You must print:
Every attack (who hit whom and the damage amount).
When a monster is eliminated.
Who won the battle.
Class Blueprint
class Monster:
def __init__(self, name: str, health: int, attack: int):
"""
Create a monster with a name, HP, and attack power.
"""
pass
def is_alive(self) -> bool:
"""Return True if health is greater than 0."""
pass
def take_damage(self, damage: int) -> None:
"""Lower health by the damage amount."""
pass
class Team:
def __init__(self, name: str, monsters: list[Monster]):
"""Create a team with a name and a list of monsters."""
pass
def get_first_alive(self) -> Monster | None:
"""Find the first monster that is still alive. Return None if all are dead."""
pass
def is_defeated(self) -> bool:
"""Return True if all monsters are eliminated."""
pass
def battle(team_a: Team, team_b: Team) -> list[str]:
"""
Run the battle between two teams.
Return a list of strings describing the events.
"""
pass
Usage Example
# Create monsters for Team A
dragon = Monster("Dragon", health=100, attack=25)
griffin = Monster("Griffin", health=80, attack=20)
team_a = Team("Heroes", [dragon, griffin])
# Create monsters for Team B
goblin = Monster("Goblin", health=30, attack=10)
orc = Monster("Orc", health=50, attack=15)
troll = Monster("Troll", health=70, attack=12)
team_b = Team("Monsters", [goblin, orc, troll])
# Run the battle
event_log = battle(team_a, team_b)
# The output should look like this:
# [
# "Battle begins: Heroes vs Monsters",
# "Dragon attacks Goblin for 25 damage. Goblin has 5 HP remaining.",
# "Goblin attacks Dragon for 10 damage. Dragon has 90 HP remaining.",
# "Dragon attacks Goblin for 25 damage. Goblin is eliminated!",
# ...
# "Battle ends: Heroes wins!"
# ]
Solution Approach
class Monster:
def __init__(self, name: str, health: int, attack: int):
self.name = name
self.health = health
self.attack = attack
def is_alive(self) -> bool:
return self.health > 0
def take_damage(self, damage: int) -> None:
self.health -= damage
class Team:
def __init__(self, name: str, monsters: list[Monster]):
self.name = name
self.monsters = monsters
def get_first_alive(self) -> Monster | None:
for monster in self.monsters:
if monster.is_alive():
return monster
return None
def is_defeated(self) -> bool:
# Check if every monster is not alive
return all(not monster.is_alive() for monster in self.monsters)
def battle(team_a: Team, team_b: Team) -> list[str]:
events = []
events.append(f"Battle begins: {team_a.name} vs {team_b.name}")
# Set initial attacker and defender teams
current_attacker = team_a
current_defender = team_b
# Loop until one team loses
while not team_a.is_defeated() and not team_b.is_defeated():
attacker = current_attacker.get_first_alive()
defender_monster = current_defender.get_first_alive()
# Safety check
if attacker is None or defender_monster is None:
break
# Apply damage
damage = attacker.attack
defender_monster.take_damage(damage)
# Record what happened
if defender_monster.is_alive():
events.append(
f"{attacker.name} attacks {defender_monster.name} for {damage} damage. "
f"{defender_monster.name} has {defender_monster.health} HP remaining."
)
else:
events.append(
f"{attacker.name} attacks {defender_monster.name} for {damage} damage. "
f"{defender_monster.name} is eliminated!"
)
# Switch turns
current_attacker, current_defender = current_defender, current_attacker
# Check who won
if team_b.is_defeated():
events.append(f"Battle ends: {team_a.name} wins!")
else:
events.append(f"Battle ends: {team_b.name} wins!")
return events
Part 2: Adding Elemental Types
Problem Requirements
Update the system to include monster types. Certain types are strong or weak against others.
Monster Types: Give every monster a type (like Fire, Water, Grass, Electric).
Type Logic:
Double Damage (2x): Strong matchups.
Half Damage (0.5x): Weak matchups.
Normal Damage (1x): Neutral matchups.
Type Chart:
Fire beats Grass (2x)
Fire is weak to Water (0.5x)
Water beats Fire (2x)
Water is weak to Grass (0.5x)
Grass beats Water (2x)
Grass is weak to Fire (0.5x)
Electric beats Water (2x)
Everything else is normal (1x)
Order: The attack order stays the same (first alive vs. first alive).
Class Updates
from enum import Enum
class MonsterType(Enum):
FIRE = "Fire"
WATER = "Water"
GRASS = "Grass"
ELECTRIC = "Electric"
class Monster:
def __init__(self, name: str, health: int, attack: int, monster_type: MonsterType):
self.name = name
self.health = health
self.attack = attack
self.monster_type = monster_type
def calculate_damage(self, defender: 'Monster') -> int:
"""Calculate how much damage to deal based on types."""
pass
Usage Example
# Create monsters with types
fire_dragon = Monster("FireDragon", health=100, attack=20, monster_type=MonsterType.FIRE)
water_serpent = Monster("WaterSerpent", health=80, attack=15, monster_type=MonsterType.WATER)
# Math:
# FireDragon vs WaterSerpent: 20 * 0.5 = 10 damage (Weak)
# WaterSerpent vs FireDragon: 15 * 2.0 = 30 damage (Strong)
Solution Details
from enum import Enum
class MonsterType(Enum):
FIRE = "Fire"
WATER = "Water"
GRASS = "Grass"
ELECTRIC = "Electric"
# Dictionary mapping (Attacker Type, Defender Type) to a multiplier
TYPE_CHART = {
(MonsterType.FIRE, MonsterType.GRASS): 2.0,
(MonsterType.FIRE, MonsterType.WATER): 0.5,
(MonsterType.WATER, MonsterType.FIRE): 2.0,
(MonsterType.WATER, MonsterType.GRASS): 0.5,
(MonsterType.GRASS, MonsterType.WATER): 2.0,
(MonsterType.GRASS, MonsterType.FIRE): 0.5,
(MonsterType.ELECTRIC, MonsterType.WATER): 2.0,
}
class Monster:
def __init__(self, name: str, health: int, attack: int, monster_type: MonsterType):
self.name = name
self.health = health
self.attack = attack
self.monster_type = monster_type
def is_alive(self) -> bool:
return self.health > 0
def take_damage(self, damage: int) -> None:
self.health -= damage
def calculate_damage(self, defender: 'Monster') -> int:
"""Return damage adjusted by the type chart."""
multiplier = TYPE_CHART.get(
(self.monster_type, defender.monster_type),
1.0 # Default to 1.0 if the pair isn't in the chart
)
return int(self.attack * multiplier)
class Team:
def __init__(self, name: str, monsters: list[Monster]):
self.name = name
self.monsters = monsters
def get_first_alive(self) -> Monster | None:
for monster in self.monsters:
if monster.is_alive():
return monster
return None
def is_defeated(self) -> bool:
return all(not monster.is_alive() for monster in self.monsters)
def battle(team_a: Team, team_b: Team) -> list[str]:
events = []
events.append(f"Battle begins: {team_a.name} vs {team_b.name}")
current_attacker = team_a
current_defender = team_b
while not team_a.is_defeated() and not team_b.is_defeated():
attacker = current_attacker.get_first_alive()
defender_monster = current_defender.get_first_alive()
if attacker is None or defender_monster is None:
break
# Calculate damage using the new method
damage = attacker.calculate_damage(defender_monster)
defender_monster.take_damage(damage)
# Create a text note for effectiveness
base_damage = attacker.attack
if damage > base_damage:
effectiveness = " (Super effective!)"
elif damage < base_damage:
effectiveness = " (Not very effective...)"
else:
effectiveness = ""
# Log the event
if defender_monster.is_alive():
events.append(
f"{attacker.name} attacks {defender_monster.name} for {damage} damage{effectiveness}. "
f"{defender_monster.name} has {defender_monster.health} HP remaining."
)
else:
events.append(
f"{attacker.name} attacks {defender_monster.name} for {damage} damage{effectiveness}. "
f"{defender_monster.name} is eliminated!"
)
current_attacker, current_defender = current_defender, current_attacker
if team_b.is_defeated():
events.append(f"Battle ends: {team_a.name} wins!")
else:
events.append(f"Battle ends: {team_b.name} wins!")
return events
Part 3: Optimized Targeting Strategy
Problem Requirements
Change the rules so the attacking team acts smarter. Instead of always using the first monster, they should pick the monster that deals the most damage.
Choosing the Attacker:
Look at the defending team's current monster (first alive).
Check all living monsters on the attacking team.
Pick the one that will do the highest damage to the defender.
If there is a tie, pick the one that comes first in the list.
Choosing the Defender: This does not change. It is still the first alive monster on the defending team.
Type Effectiveness: Use the same chart from Part 2.
Usage Example
# Team A members:
# - FireDragon (Attack: 20, Type: FIRE)
# - ElectricEel (Attack: 15, Type: ELECTRIC)
# Defender:
# - WaterSerpent (Type: WATER)
# Calculations:
# - FireDragon hits WaterSerpent: 20 * 0.5 = 10 damage.
# - ElectricEel hits WaterSerpent: 15 * 2.0 = 30 damage.
# Result: ElectricEel is chosen because 30 > 10.
Solution Details
class Team:
def __init__(self, name: str, monsters: list[Monster]):
self.name = name
self.monsters = monsters
def get_first_alive(self) -> Monster | None:
for monster in self.monsters:
if monster.is_alive():
return monster
return None
def get_best_attacker(self, defender: Monster) -> Monster | None:
"""
Find the alive monster that does the most damage to the defender.
If damage is equal, pick the first one found.
"""
best_attacker = None
best_damage = -1
for monster in self.monsters:
if monster.is_alive():
damage = monster.calculate_damage(defender)
if damage > best_damage:
best_damage = damage
best_attacker = monster
return best_attacker
def is_defeated(self) -> bool:
return all(not monster.is_alive() for monster in self.monsters)
def battle(team_a: Team, team_b: Team) -> list[str]:
events = []
events.append(f"Battle begins: {team_a.name} vs {team_b.name}")
current_attacker_team = team_a
current_defender_team = team_b
while not team_a.is_defeated() and not team_b.is_defeated():
# Identify the defender
defender_monster = current_defender_team.get_first_alive()
if defender_monster is None:
break
# Identify the best attacker for this specific defender
attacker = current_attacker_team.get_best_attacker(defender_monster)
if attacker is None:
break
# Apply damage
damage = attacker.calculate_damage(defender_monster)
defender_monster.take_damage(damage)
# Check effectiveness for the log
base_damage = attacker.attack
if damage > base_damage:
effectiveness = " (Super effective!)"
elif damage < base_damage:
effectiveness = " (Not very effective...)"
else:
effectiveness = ""
# Log the event
if defender_monster.is_alive():
events.append(
f"{attacker.name} attacks {defender_monster.name} for {damage} damage{effectiveness}. "
f"{defender_monster.name} has {defender_monster.health} HP remaining."
)
else:
events.append(
f"{attacker.name} attacks {defender_monster.name} for {damage} damage{effectiveness}. "
f"{defender_monster.name} is eliminated!"
)
# Switch turns
current_attacker_team, current_defender_team = current_defender_team, current_attacker_team
if team_b.is_defeated():
events.append(f"Battle ends: {team_a.name} wins!")
else:
events.append(f"Battle ends: {team_b.name} wins!")
return events
Complete Example with Smart Targeting
# Create Team A with mixed types
fire_dragon = Monster("FireDragon", health=100, attack=20, monster_type=MonsterType.FIRE)
electric_eel = Monster("ElectricEel", health=60, attack=15, monster_type=MonsterType.ELECTRIC)
grass_golem = Monster("GrassGolem", health=120, attack=18, monster_type=MonsterType.GRASS)
team_a = Team("Elements", [fire_dragon, electric_eel, grass_golem])
# Create Team B with water types
water_serpent = Monster("WaterSerpent", health=80, attack=15, monster_type=MonsterType.WATER)
water_sprite = Monster("WaterSprite", health=50, attack=12, monster_type=MonsterType.WATER)
team_b = Team("Aquatics", [water_serpent, water_sprite])
event_log = battle(team_a, team_b)
# Because of smart targeting, the ElectricEel attacks the WaterSerpent.
# It does 30 damage. FireDragon would have only done 10 damage.
Technical Design Choices
Object-Oriented Principles Used
Encapsulation: We protect the monster's data. You cannot change health directly; you must use the take_damage method.
Single Responsibility: Each class has one job. Monster holds data, Team manages the group, and battle runs the loop.
Open/Closed: We can add new types to the TYPE_CHART without rewriting the monster class logic.
Separation of Concerns: The rules of the battle are separate from the definition of a monster.
How to Test
def test_basic_battle():
m1 = Monster("A", 50, 30, MonsterType.FIRE)
m2 = Monster("B", 100, 10, MonsterType.WATER)
team_a = Team("TeamA", [m1])
team_b = Team("TeamB", [m2])
log = battle(team_a, team_b)
# Check the math:
# Fire hits Water: 30 * 0.5 = 15 damage
# Water hits Fire: 10 * 2.0 = 20 damage
# Verify the log output here
def test_smart_targeting():
fire = Monster("Fire", 50, 20, MonsterType.FIRE)
elec = Monster("Electric", 50, 15, MonsterType.ELECTRIC)
water = Monster("Water", 100, 10, MonsterType.WATER)
team_a = Team("A", [fire, elec])
team_b = Team("B", [water])
log = battle(team_a, team_b)
# The Electric monster should attack first because
# 15*2 (30) is greater than 20*0.5 (10).
assert "Electric attacks Water" in log[1]
Format
Typically structured as 3 progressive parts, and it shows up at both the phone screen and onsite coding slots. Pacing mirrors the other multi-part OpenAI prompts: get a clean, correct version of each part before moving on.