Boxing (Python): Split configuration from logic

This commit is contained in:
Martin Thoma
2022-03-23 12:13:58 +01:00
parent 0064fd328a
commit 8ee56f94a1
2 changed files with 274 additions and 141 deletions

View File

@@ -1,7 +1,70 @@
#!/usr/bin/env python3
import random
from dataclasses import dataclass
from typing import Tuple, Dict, NamedTuple, Literal
class HitStats(NamedTuple):
choices: int
threshold: int
hit_damage: int
block_damage: int
pre_msg: str
hit_msg: str
blocked_msg: str
knockout_possible: bool = False
def is_hit(self) -> bool:
return random.randint(1, self.choices) <= self.threshold
@dataclass
class Player:
name: str
best: int # TODO: This is never used!
is_computer: bool
weakness: int
# for each of the 4 punch types, we have a probability of hitting
hit_stats: Dict[Literal[1, 2, 3, 4], HitStats]
damage: int = 0
score: int = 0
knockedout: bool = False
def get_punch_choice(self) -> Literal[1, 2, 3, 4]:
if self.is_computer:
return random.randint(1, 4) # type: ignore
else:
punch = -1
while punch not in [1, 2, 3, 4]:
print(f"{self.name}'S PUNCH", end="? ")
punch = int(input())
return punch # type: ignore
KNOCKOUT_THRESHOLD = 35
# Texts
QUESTION_PROMPT = "? "
KNOCKED_COLD = "{loser} IS KNOCKED COLD AND {winner} IS THE WINNER AND CHAMP"
def get_vulnerability() -> int:
print("WHAT IS HIS VULNERABILITY", end=QUESTION_PROMPT)
vulnerability = int(input())
return vulnerability
def get_opponent_stats() -> Tuple[int, int]:
opponent_best = 0
opponent_weakness = 0
while opponent_best == opponent_weakness:
opponent_best = random.randint(1, 4)
opponent_weakness = random.randint(1, 4)
return opponent_best, opponent_weakness
def play() -> None:
@@ -10,15 +73,6 @@ def play() -> None:
print("\n\n")
print("BOXING OLYMPIC STYLE (3 ROUNDS -- 2 OUT OF 3 WINS)")
opponent_score = 0
player_score = 0
opponent_damage = 0
player_damage = 0
opponent_knockedout = False
player_knockedout = False
print("WHAT IS YOUR OPPONENT'S NAME", end=QUESTION_PROMPT)
opponent_name = input()
print("WHAT IS YOUR MAN'S NAME", end=QUESTION_PROMPT)
@@ -27,147 +81,164 @@ def play() -> None:
print("DIFFERENT PUNCHES ARE 1 FULL SWING 2 HOOK 3 UPPERCUT 4 JAB")
print("WHAT IS YOUR MAN'S BEST", end=QUESTION_PROMPT)
player_best = int(input()) # noqa: TODO - this likely is a bug!
print("WHAT IS HIS VULNERABILITY", end=QUESTION_PROMPT)
player_weakness = int(input())
opponent_best = 0
opponent_weakness = 0
while opponent_best == opponent_weakness:
opponent_best = random.randint(1, 4)
opponent_weakness = random.randint(1, 4)
print(
"{}'S ADVANTAGE is {} AND VULNERABILITY IS SECRET.".format(
opponent_name, opponent_weakness
)
player_weakness = get_vulnerability()
player = Player(
name=player_name,
best=player_best,
weakness=player_weakness,
is_computer=False,
hit_stats={
1: HitStats( # FULL SWING
choices=30,
threshold=10,
hit_damage=15,
block_damage=0,
pre_msg="{active.name} SWINGS AND",
hit_msg="HE CONNECTS!",
blocked_msg="HE MISSES",
),
2: HitStats( # HOOK
choices=2,
threshold=1,
hit_damage=7,
block_damage=0,
pre_msg="{active.name} GIVES THE HOOK...",
hit_msg="",
blocked_msg="",
),
3: HitStats( # UPPERCUT
choices=100,
threshold=50,
hit_damage=4,
block_damage=0,
pre_msg="{player_name} TRIES AN UPPERCUT",
hit_msg="AND HE CONNECTS!",
blocked_msg="AND IT'S BLOCKED (LUCKY BLOCK!)",
),
4: HitStats( # JAB
choices=8,
threshold=3,
hit_damage=3,
block_damage=0,
pre_msg="{active.name} JABS AT {passive.name}'S HEAD",
hit_msg="AND HE CONNECTS!",
blocked_msg="AND IT'S BLOCKED (LUCKY BLOCK!)",
),
},
)
for round in (1, 2, 3):
print(f"ROUND {round} BEGINS...\n")
if opponent_score >= 2 or player_score >= 2:
break
opponent_best, opponent_weakness = get_opponent_stats()
opponent = Player(
name=opponent_name,
best=opponent_best,
weakness=opponent_weakness,
is_computer=True,
hit_stats={
1: HitStats( # FULL SWING
choices=60,
threshold=29,
hit_damage=15,
block_damage=0,
knockout_possible=True,
pre_msg="{active.name} TAKES A FULL SWING",
hit_msg="AND POW!!!! HE HITS HIM RIGHT IN THE FACE!",
blocked_msg="BUT IT'S BLOCKED!",
),
2: HitStats( # HOOK
choices=1,
threshold=1,
hit_damage=12,
block_damage=0,
knockout_possible=True,
pre_msg="{active.name} GETS {passive.name} IN THE JAW (OUCH!)....AND AGAIN",
hit_msg="CONNECTS...",
blocked_msg="BUT IT'S BLOCKED!!!!!!!!!!!!!",
),
3: HitStats( # UPPERCUT
choices=200,
threshold=125,
hit_damage=8,
block_damage=5,
pre_msg="{passive.name} IS ATTACKED BY AN UPPERCUT (OH,OH)",
hit_msg="AND {active.name} CONNECTS...",
blocked_msg="{passive.name} BLOCKS AND HITS {active.name} WITH A HOOK",
),
4: HitStats( # JAB
choices=7,
threshold=3,
hit_damage=3,
block_damage=0,
pre_msg="{active.name} JABS AND",
hit_msg="BLOOD SPILLS !!!",
blocked_msg="AND IT'S BLOCKED (LUCKY BLOCK!)",
),
},
)
for _action in range(7):
if random.randint(1, 10) > 5:
# opponent swings
punch = random.randint(1, 4)
print(
f"{opponent.name}'S ADVANTAGE is {opponent.weakness} AND VULNERABILITY IS SECRET."
)
if punch == player_weakness:
player_damage += 2
for round_number in (1, 2, 3):
play_round(round_number, player, opponent)
if punch == 1:
print(f"{opponent_name} TAKES A FULL SWING AND", end=" ")
if player_weakness == 1 or random.randint(1, 60) < 30:
print("POW!!!! HE HITS HIM RIGHT IN THE FACE!")
if player_damage > 35:
player_knockedout = True
break
player_damage += 15
else:
print("BUT IT'S BLOCKED!")
elif punch == 2:
print(
"{} GETS {} IN THE JAW (OUCH!)".format(
opponent_name, player_name
),
end=" ",
)
player_damage += 7
print("....AND AGAIN")
if player_damage > 35:
player_knockedout = True
break
player_damage += 5
elif punch == 3:
print(f"{player_name} IS ATTACKED BY AN UPPERCUT (OH,OH)")
if player_weakness == 3 or random.randint(1, 200) > 75:
print(
"{} BLOCKS AND HITS {} WITH A HOOK".format(
player_name, opponent_name
)
)
opponent_damage += 5
else:
print(f"AND {opponent_name} CONNECTS...")
player_damage += 8
else:
print(f"{opponent_name} JABS AND", end=" ")
if player_weakness == 4 or random.randint(1, 7) > 4:
print("BLOOD SPILLS !!!")
player_damage += 3
else:
print("AND IT'S BLOCKED (LUCKY BLOCK!)")
else:
print(f"{player_name}'S PUNCH", end="? ")
punch = int(input())
if punch == opponent_weakness:
opponent_damage += 2
if punch == 1:
print(f"{player_name} SWINGS AND", end=" ")
if opponent_weakness == 1 or random.randint(1, 30) < 10:
print("HE CONNECTS!")
if opponent_damage > 35:
opponent_knockedout = True
break
opponent_damage += 15
else:
print("HE MISSES")
elif punch == 2:
print(f"{player_name} GIVES THE HOOK...", end=" ")
if opponent_weakness == 2 or random.randint(1, 2) == 1:
print("CONNECTS...")
opponent_damage += 7
else:
print("BUT IT'S BLOCKED!!!!!!!!!!!!!")
elif punch == 3:
print(f"{player_name} TRIES AN UPPERCUT", end=" ")
if opponent_weakness == 3 or random.randint(1, 100) < 51:
print("AND HE CONNECTS!")
opponent_damage += 4
else:
print("AND IT'S BLOCKED (LUCKY BLOCK!)")
else:
print(
f"{player_name} JABS AT {opponent_name}'S HEAD",
end=" ",
)
if opponent_weakness == 4 or random.randint(1, 8) < 4:
print("AND HE CONNECTS!")
opponent_damage += 3
else:
print("AND IT'S BLOCKED (LUCKY BLOCK!)")
if player_knockedout or opponent_knockedout:
break
elif player_damage > opponent_damage:
print(f"{opponent_name} WINS ROUND {round}")
opponent_score += 1
else:
print(f"{player_name} WINS ROUND {round}")
player_score += 1
if player_knockedout:
print(
"{} IS KNOCKED COLD AND {} IS THE WINNER AND CHAMP".format(
player_name, opponent_name
)
)
elif opponent_knockedout:
print(
"{} IS KNOCKED COLD AND {} IS THE WINNER AND CHAMP".format(
opponent_name, player_name
)
)
elif opponent_score > player_score:
print(f"{opponent_name} WINS (NICE GOING), {player_name}")
if player.knockedout:
print(KNOCKED_COLD.format(loser=player.name, winner=opponent.name))
elif opponent.knockedout:
print(KNOCKED_COLD.format(loser=opponent.name, winner=player.name))
elif opponent.score > player.score:
print(f"{opponent.name} WINS (NICE GOING), {player.name}")
else:
print(f"{player_name} AMAZINGLY WINS")
print(f"{player.name} AMAZINGLY WINS")
print("\n\nAND NOW GOODBYE FROM THE OLYMPIC ARENA.")
def is_opponents_turn() -> bool:
return random.randint(1, 10) > 5
def play_round(round_number: int, player: Player, opponent: Player) -> None:
print(f"ROUND {round_number} BEGINS...\n")
if opponent.score >= 2 or player.score >= 2:
return
for _action in range(7):
if is_opponents_turn():
punch = opponent.get_punch_choice()
active = opponent
passive = player
else:
punch = player.get_punch_choice()
active = player
passive = opponent
# Load the hit characteristics of the current player's punch
hit_stats = active.hit_stats[punch]
if punch == passive.weakness:
passive.damage += 2
print(hit_stats.pre_msg.format(active=active, passive=passive), end=" ")
if passive.weakness == punch or hit_stats.is_hit():
print(hit_stats.hit_msg.format(active=active, passive=passive))
if hit_stats.knockout_possible and passive.damage > KNOCKOUT_THRESHOLD:
passive.knockedout = True
break
passive.damage += hit_stats.hit_damage
else:
print(hit_stats.blocked_msg.format(active=active, passive=passive))
active.damage += hit_stats.block_damage
if player.knockedout or opponent.knockedout:
return
elif player.damage > opponent.damage:
print(f"{opponent.name} WINS ROUND {round_number}")
opponent.score += 1
else:
print(f"{player.name} WINS ROUND {round_number}")
player.score += 1
if __name__ == "__main__":
play()