mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-12 15:50:20 -08:00
562 lines
18 KiB
Python
562 lines
18 KiB
Python
"""
|
|
HOCKEY
|
|
|
|
A simulation of an ice hockey game.
|
|
|
|
The original author is Robert Puopolo;
|
|
modifications by Steve North of Creative Computing.
|
|
|
|
Ported to Python by Martin Thoma in 2022
|
|
"""
|
|
|
|
from dataclasses import dataclass, field
|
|
from random import randint
|
|
from typing import List, Tuple
|
|
|
|
NB_PLAYERS = 6
|
|
|
|
|
|
@dataclass
|
|
class Team:
|
|
# TODO: It would be better to use a Player-class (name, goals, assits)
|
|
# and have the attributes directly at each player. This would avoid
|
|
# dealing with indices that much
|
|
#
|
|
# I'm also rather certain that I messed up somewhere with the indices
|
|
# - instead of using those, one could use actual player positions:
|
|
# LEFT WING, CENTER, RIGHT WING
|
|
# LEFT DEFENSE, RIGHT DEFENSE, GOALKEEPER
|
|
name: str
|
|
players: List[str] # 6 players
|
|
shots_on_net: int = 0
|
|
goals: List[int] = field(default_factory=lambda: [0 for _ in range(NB_PLAYERS)])
|
|
assists: List[int] = field(default_factory=lambda: [0 for _ in range(NB_PLAYERS)])
|
|
score: int = 0
|
|
|
|
def show_lineup(self) -> None:
|
|
print(" " * 10 + f"{self.name} STARTING LINEUP")
|
|
for player in self.players:
|
|
print(player)
|
|
|
|
|
|
def ask_binary(prompt: str, error_msg: str) -> bool:
|
|
while True:
|
|
answer = input(prompt).lower()
|
|
if answer in ["y", "yes"]:
|
|
return True
|
|
if answer in ["n", "no"]:
|
|
return False
|
|
print(error_msg)
|
|
|
|
|
|
def get_team_names() -> Tuple[str, str]:
|
|
while True:
|
|
answer = input("ENTER THE TWO TEAMS: ")
|
|
if answer.count(",") == 1:
|
|
return answer.split(",") # type: ignore
|
|
print("separated by a single comma")
|
|
|
|
|
|
def get_pass() -> int:
|
|
while True:
|
|
answer = input("PASS? ")
|
|
try:
|
|
passes = int(answer)
|
|
if passes >= 0 and passes <= 3:
|
|
return passes
|
|
except ValueError:
|
|
print("ENTER A NUMBER BETWEEN 0 AND 3")
|
|
|
|
|
|
def get_minutes_per_game() -> int:
|
|
while True:
|
|
answer = input("ENTER THE NUMBER OF MINUTES IN A GAME ")
|
|
try:
|
|
minutes = int(answer)
|
|
if minutes >= 1:
|
|
return minutes
|
|
except ValueError:
|
|
print("ENTER A NUMBER")
|
|
|
|
|
|
def get_player_names(prompt: str) -> List[str]:
|
|
players = []
|
|
print(prompt)
|
|
for i in range(1, 7):
|
|
player = input(f"PLAYER {i}: ")
|
|
players.append(player)
|
|
return players
|
|
|
|
|
|
def make_shot(
|
|
controlling_team: int, team_a: Team, team_b: Team, player_index: List[int], j: int
|
|
) -> Tuple[int, int, int, int]:
|
|
while True:
|
|
try:
|
|
s = int(input("SHOT? "))
|
|
except ValueError:
|
|
continue
|
|
if s >= 1 and s <= 4:
|
|
break
|
|
if controlling_team == 1:
|
|
print(team_a.players[player_index[j - 1]])
|
|
else:
|
|
print(team_b.players[player_index[j - 1]])
|
|
g = player_index[j - 1]
|
|
g1 = 0
|
|
g2 = 0
|
|
if s == 1:
|
|
print(" LET'S A BOOMER GO FROM THE RED LINE!!\n") # line 400
|
|
z = 10
|
|
elif s == 2:
|
|
print(" FLIPS A WRISTSHOT DOWN THE ICE\n") # line 420
|
|
# Probable missing line 430 in original
|
|
elif s == 3:
|
|
print(" BACKHANDS ONE IN ON THE GOALTENDER\n")
|
|
z = 25
|
|
elif s == 4:
|
|
print(" SNAPS A LONG FLIP SHOT\n")
|
|
# line 460
|
|
z = 17
|
|
return z, g, g1, g2
|
|
|
|
|
|
def print_header() -> None:
|
|
print(" " * 33 + "HOCKEY")
|
|
print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n")
|
|
|
|
|
|
def instructions() -> None:
|
|
if wants_it := ask_binary(
|
|
"WOULD YOU LIKE THE INSTRUCTIONS? ", "ANSWER YES OR NO!!"
|
|
):
|
|
print()
|
|
print("THIS IS A SIMULATED HOCKEY GAME.")
|
|
print("QUESTION RESPONSE")
|
|
print("PASS TYPE IN THE NUMBER OF PASSES YOU WOULD")
|
|
print(" LIKE TO MAKE, FROM 0 TO 3.")
|
|
print("SHOT TYPE THE NUMBER CORRESPONDING TO THE SHOT")
|
|
print(" YOU WANT TO MAKE. ENTER:")
|
|
print(" 1 FOR A SLAPSHOT")
|
|
print(" 2 FOR A WRISTSHOT")
|
|
print(" 3 FOR A BACKHAND")
|
|
print(" 4 FOR A SNAP SHOT")
|
|
print("AREA TYPE IN THE NUMBER CORRESPONDING TO")
|
|
print(" THE AREA YOU ARE AIMING AT. ENTER:")
|
|
print(" 1 FOR UPPER LEFT HAND CORNER")
|
|
print(" 2 FOR UPPER RIGHT HAND CORNER")
|
|
print(" 3 FOR LOWER LEFT HAND CORNER")
|
|
print(" 4 FOR LOWER RIGHT HAND CORNER")
|
|
print("AT THE START OF THE GAME, YOU WILL BE ASKED FOR THE NAMES")
|
|
print("OF YOUR PLAYERS. THEY ARE ENTERED IN THE ORDER: ")
|
|
print("LEFT WING, CENTER, RIGHT WING, LEFT DEFENSE,")
|
|
print("RIGHT DEFENSE, GOALKEEPER. ANY OTHER INPUT REQUIRED WILL")
|
|
print("HAVE EXPLANATORY INSTRUCTIONS.")
|
|
|
|
|
|
def team1_action(
|
|
pass_value: int, player_index: List[int], team_a: Team, team_b: Team, j: int
|
|
) -> Tuple[int, int, int, int]:
|
|
if pass_value == 1:
|
|
print(
|
|
team_a.players[player_index[j - 2]]
|
|
+ " LEADS "
|
|
+ team_a.players[player_index[j - 1]]
|
|
+ " WITH A PERFECT PASS.\n"
|
|
)
|
|
print(team_a.players[player_index[j - 1]] + " CUTTING IN!!!\n")
|
|
scoring_player = player_index[j - 1]
|
|
goal_assistant1 = player_index[j - 2]
|
|
goal_assistant2 = 0
|
|
z1 = 3
|
|
elif pass_value == 2:
|
|
print(
|
|
team_a.players[player_index[j - 2]]
|
|
+ " GIVES TO A STREAKING "
|
|
+ team_a.players[player_index[j - 1]]
|
|
)
|
|
print(
|
|
team_a.players[player_index[j - 3]]
|
|
+ " COMES DOWN ON "
|
|
+ team_b.players[4]
|
|
+ " AND "
|
|
+ team_b.players[3]
|
|
)
|
|
scoring_player = player_index[j - 3]
|
|
goal_assistant1 = player_index[j - 1]
|
|
goal_assistant2 = player_index[j - 2]
|
|
z1 = 2
|
|
elif pass_value == 3:
|
|
print("OH MY GOD!! A ' 4 ON 2 ' SITUATION\n")
|
|
print(
|
|
team_a.players[player_index[j - 3]]
|
|
+ " LEADS "
|
|
+ team_a.players[player_index[j - 2]]
|
|
+ "\n"
|
|
)
|
|
print(team_a.players[player_index[j - 2]] + " IS WHEELING THROUGH CENTER.\n")
|
|
print(
|
|
team_a.players[player_index[j - 2]]
|
|
+ " GIVES AND GOEST WITH "
|
|
+ team_a.players[player_index[j - 1]]
|
|
)
|
|
print("PRETTY PASSING!\n")
|
|
print(
|
|
team_a.players[player_index[j - 1]]
|
|
+ " DROPS IT TO "
|
|
+ team_a.players[player_index[j - 4]]
|
|
)
|
|
scoring_player = player_index[j - 4]
|
|
goal_assistant1 = player_index[j - 1]
|
|
goal_assistant2 = player_index[j - 2]
|
|
z1 = 1
|
|
return scoring_player, goal_assistant1, goal_assistant2, z1
|
|
|
|
|
|
def team2_action(
|
|
pass_value: int, player_index: List[int], team_a: Team, team_b: Team, j: int
|
|
) -> Tuple[int, int, int, int]:
|
|
if pass_value == 1:
|
|
print(
|
|
team_b.players[player_index[j - 1]]
|
|
+ " HITS "
|
|
+ team_b.players[player_index[j - 2]]
|
|
+ " FLYING DOWN THE LEFT SIDE\n"
|
|
)
|
|
scoring_player = player_index[j - 2]
|
|
goal_assistant1 = player_index[j - 1]
|
|
goal_assistant2 = 0
|
|
z1 = 3
|
|
elif pass_value == 2:
|
|
print("IT'S A ' 3 ON 2 '!\n")
|
|
print(f"ONLY {team_a.players[3]} AND {team_a.players[4]}" + " ARE BACK.\n")
|
|
print(
|
|
team_b.players[player_index[j - 2]]
|
|
+ " GIVES OFF TO "
|
|
+ team_b.players[player_index[j - 1]]
|
|
)
|
|
print(
|
|
team_b.players[player_index[j - 1]]
|
|
+ " DROPS TO "
|
|
+ team_b.players[player_index[j - 3]]
|
|
)
|
|
scoring_player = player_index[j - 3]
|
|
goal_assistant1 = player_index[j - 1]
|
|
goal_assistant2 = player_index[j - 2]
|
|
z1 = 2
|
|
elif pass_value == 3:
|
|
print(" A '3 ON 2 ' WITH A ' TRAILER '!\n")
|
|
print(
|
|
team_b.players[player_index[j - 4]]
|
|
+ " GIVES TO "
|
|
+ team_b.players[player_index[j - 2]]
|
|
+ " WHO SHUFFLES IT OFF TO\n"
|
|
)
|
|
print(
|
|
team_b.players[player_index[j - 1]] + " WHO FIRES A WING TO WING PASS TO \n"
|
|
)
|
|
print(team_b.players[player_index[j - 3]] + " AS HE CUTS IN ALONE!!\n")
|
|
scoring_player = player_index[j - 3]
|
|
goal_assistant1 = player_index[j - 1]
|
|
goal_assistant2 = player_index[j - 2]
|
|
z1 = 1
|
|
return scoring_player, goal_assistant1, goal_assistant2, z1
|
|
|
|
|
|
def final_message(team_a: Team, team_b: Team, player_index: List[int]) -> None:
|
|
# Bells chime
|
|
print("THAT'S THE SIREN\n")
|
|
print("\n")
|
|
print(" " * 15 + "FINAL SCORE:\n")
|
|
if team_b.score <= team_a.score:
|
|
print(f"{team_a.name}: {team_a.score}\t{team_b.name}: {team_b.score}\n")
|
|
else:
|
|
print(f"{team_b.name}: {team_b.score}\t{team_a.name}\t:{team_a.score}\n")
|
|
print("\n")
|
|
print(" " * 10 + "SCORING SUMMARY\n")
|
|
print("\n")
|
|
print(" " * 25 + team_a.name + "\n")
|
|
print("\tNAME\tGOALS\tASSISTS\n")
|
|
print("\t----\t-----\t-------\n")
|
|
for i in range(1, 6):
|
|
print(f"\t{team_a.players[i]}\t{team_a.goals[i]}\t{team_a.assists[i]}\n")
|
|
print("\n")
|
|
print(" " * 25 + team_b.name + "\n")
|
|
print("\tNAME\tGOALS\tASSISTS\n")
|
|
print("\t----\t-----\t-------\n")
|
|
for t in range(1, 6):
|
|
print(f"\t{team_b.players[t]}\t{team_b.goals[t]}\t{team_b.assists[t]}\n")
|
|
print("\n")
|
|
print("SHOTS ON NET\n")
|
|
print(f"{team_a.name}: {team_a.shots_on_net}\n")
|
|
print(f"{team_b.name}: {team_b.shots_on_net}\n")
|
|
|
|
|
|
def main() -> None:
|
|
# Intro
|
|
print_header()
|
|
player_index: List[int] = [0 for _ in range(21)]
|
|
print("\n" * 3)
|
|
instructions()
|
|
|
|
# Gather input
|
|
team_name_a, team_name_b = get_team_names()
|
|
print()
|
|
minutes_per_game = get_minutes_per_game()
|
|
print()
|
|
players_a = get_player_names(f"WOULD THE {team_name_a} COACH ENTER HIS TEAM")
|
|
print()
|
|
players_b = get_player_names(f"WOULD THE {team_name_b} COACH DO THE SAME")
|
|
team_a = Team(team_name_a, players_a)
|
|
team_b = Team(team_name_b, players_b)
|
|
print()
|
|
referee = input("INPUT THE REFEREE FOR THIS GAME: ")
|
|
print()
|
|
team_a.show_lineup()
|
|
print()
|
|
team_b.show_lineup()
|
|
print("WE'RE READY FOR TONIGHTS OPENING FACE-OFF.")
|
|
print(
|
|
f"{referee} WILL DROP THE PUCK BETWEEN "
|
|
f"{team_a.players[0]} AND {team_b.players[0]}"
|
|
)
|
|
remaining_time = minutes_per_game
|
|
|
|
# Play the game
|
|
while remaining_time > 0:
|
|
cont, remaining_time = simulate_game_round(
|
|
team_a, team_b, player_index, remaining_time
|
|
)
|
|
remaining_time -= 1
|
|
if cont == "break":
|
|
break
|
|
|
|
# Outro
|
|
final_message(team_a, team_b, player_index)
|
|
|
|
|
|
def handle_hit(
|
|
controlling_team: int,
|
|
team_a: Team,
|
|
team_b: Team,
|
|
player_index: List[int],
|
|
goal_player: int,
|
|
goal_assistant1: int,
|
|
goal_assistant2: int,
|
|
hit_area: int,
|
|
z: int,
|
|
) -> int:
|
|
while True:
|
|
player_index[20] = randint(1, 100)
|
|
if player_index[20] % z != 0:
|
|
break
|
|
a2 = randint(1, 100)
|
|
if a2 % 4 == 0:
|
|
if controlling_team == 1:
|
|
print(f"SAVE {team_b.players[5]} -- REBOUND\n")
|
|
else:
|
|
print(f"SAVE {team_a.players[5]} -- FOLLOW up\n")
|
|
continue
|
|
else:
|
|
hit_area += 1
|
|
if player_index[20] % z != 0:
|
|
if controlling_team == 1:
|
|
print(f"GOAL {team_a.name}\n")
|
|
team_a.score += 1
|
|
else:
|
|
print(f"SCORE {team_b.name}\n")
|
|
team_b.score += 1
|
|
# Bells in origninal
|
|
print("\n")
|
|
print("SCORE: ")
|
|
if team_b.score <= team_a.score:
|
|
print(f"{team_a.name}: {team_a.score}\t{team_b.name}: {team_b.score}\n")
|
|
else:
|
|
print(f"{team_b.name}: {team_b.score}\t{team_a.name}: {team_a.score}\n")
|
|
team = team_a if controlling_team == 1 else team_b
|
|
print(f"GOAL SCORED BY: {team.players[goal_player]}" + "\n")
|
|
if goal_assistant1 != 0:
|
|
if goal_assistant2 != 0:
|
|
print(
|
|
f" ASSISTED BY: {team.players[goal_assistant1]}"
|
|
f" AND {team.players[goal_assistant2]}"
|
|
)
|
|
else:
|
|
print(f" ASSISTED BY: {team.players[goal_assistant1]}")
|
|
team.assists[goal_assistant1] += 1
|
|
team.assists[goal_assistant2] += 1
|
|
else:
|
|
print(" UNASSISTED.\n")
|
|
team.goals[goal_player] += 1
|
|
|
|
return hit_area
|
|
|
|
|
|
def handle_miss(
|
|
controlling_team: int,
|
|
team_a: Team,
|
|
team_b: Team,
|
|
remaining_time: int,
|
|
goal_player: int,
|
|
) -> Tuple[str, int]:
|
|
saving_player = randint(1, 7)
|
|
if controlling_team == 1:
|
|
if saving_player == 1:
|
|
print(f"KICK SAVE AND A BEAUTY BY {team_b.players[5]}" + "\n")
|
|
print(f"CLEARED OUT BY {team_b.players[3]}" + "\n")
|
|
remaining_time -= 1
|
|
return ("continue", remaining_time)
|
|
if saving_player == 2:
|
|
print(f"WHAT A SPECTACULAR GLOVE SAVE BY {team_b.players[5]}" + "\n")
|
|
print(f"AND {team_b.players[5]}" + " GOLFS IT INTO THE CROWD\n")
|
|
return ("break", remaining_time)
|
|
if saving_player == 3:
|
|
print(f"SKATE SAVE ON A LOW STEAMER BY {team_b.players[5]}" + "\n")
|
|
remaining_time -= 1
|
|
return ("continue", remaining_time)
|
|
if saving_player == 4:
|
|
print(f"PAD SAVE BY {team_b.players[5]} OFF THE STICK\n")
|
|
print(
|
|
f"OF {team_a.players[goal_player]} AND "
|
|
f"{team_b.players[5]} COVERS UP\n"
|
|
)
|
|
return ("break", remaining_time)
|
|
if saving_player == 5:
|
|
print(f"WHISTLES ONE OVER THE HEAD OF {team_b.players[5]}\n")
|
|
remaining_time -= 1
|
|
return ("continue", remaining_time)
|
|
if saving_player == 6:
|
|
print(f"{team_b.players[5]} MAKES A FACE SAVE!! AND HE IS HURT\n")
|
|
print(f"THE DEFENSEMAN {team_b.players[5]} COVERS UP FOR HIM\n")
|
|
return ("break", remaining_time)
|
|
else:
|
|
if saving_player == 1:
|
|
print(f"STICK SAVE BY {team_a.players[5]}\n")
|
|
print(f"AND CLEARED OUT BY {team_a.players[3]}\n")
|
|
remaining_time -= 1
|
|
return ("continue", remaining_time)
|
|
if saving_player == 2:
|
|
print(
|
|
"OH MY GOD!! "
|
|
f"{team_b.players[goal_player]} RATTLES ONE OFF THE POST\n"
|
|
)
|
|
print(
|
|
f"TO THE RIGHT OF {team_a.players[5]} AND "
|
|
f"{team_a.players[5]} COVERS "
|
|
)
|
|
print("ON THE LOOSE PUCK!\n")
|
|
return ("break", remaining_time)
|
|
if saving_player == 3:
|
|
print(f"SKATE SAVE BY {team_a.players[5]}" + "\n")
|
|
print(team_a.players[5] + " WHACKS THE LOOSE PUCK INTO THE STANDS\n")
|
|
return ("break", remaining_time)
|
|
if saving_player == 4:
|
|
print(f"STICK SAVE BY {team_a.players[5]}" + " AND HE CLEARS IT OUT HIMSELF\n")
|
|
remaining_time -= 1
|
|
return ("continue", remaining_time)
|
|
if saving_player == 5:
|
|
print(f"KICKED OUT BY {team_a.players[5]}" + "\n")
|
|
print("AND IT REBOUNDS ALL THE WAY TO CENTER ICE\n")
|
|
remaining_time -= 1
|
|
return ("continue", remaining_time)
|
|
if saving_player == 6:
|
|
print(f"GLOVE SAVE {team_a.players[5]}" + " AND HE HANGS ON\n")
|
|
return ("break", remaining_time)
|
|
return ("continue", remaining_time)
|
|
|
|
|
|
def simulate_game_round(
|
|
team_a: Team, team_b: Team, player_index: List[int], remaining_time: int
|
|
) -> Tuple[str, int]:
|
|
controlling_team = randint(1, 2)
|
|
if controlling_team == 1:
|
|
print(f"{team_a.name} HAS CONTROL OF THE PUCK.")
|
|
else:
|
|
print(f"{team_b.name} HAS CONTROL.")
|
|
pass_value = get_pass()
|
|
for i in range(1, 4):
|
|
player_index[i] = 0
|
|
|
|
# Line 310:
|
|
while True:
|
|
j = 0
|
|
for j in range(1, (pass_value + 2) + 1):
|
|
player_index[j] = randint(1, 5)
|
|
if (
|
|
player_index[j - 1] == player_index[j - 2]
|
|
or pass_value >= 1
|
|
and (
|
|
player_index[j - 1] == player_index[j - 3]
|
|
or player_index[j - 2] == player_index[j - 3]
|
|
)
|
|
):
|
|
break
|
|
if pass_value == 0: # line 350
|
|
z, goal_player, goal_assistant1, goal_assistant2 = make_shot(
|
|
controlling_team, team_a, team_b, player_index, j
|
|
)
|
|
else:
|
|
if controlling_team == 1:
|
|
goal_player, goal_assistant1, goal_assistant2, z1 = team1_action(
|
|
pass_value, player_index, team_a, team_b, j
|
|
)
|
|
else:
|
|
goal_player, goal_assistant1, goal_assistant2, z1 = team2_action(
|
|
pass_value, player_index, team_a, team_b, j
|
|
)
|
|
while True:
|
|
shot_type = int(input("SHOT? "))
|
|
if shot_type >= 1 and shot_type <= 4:
|
|
break
|
|
if controlling_team == 1:
|
|
print(team_a.players[goal_player], end="")
|
|
else:
|
|
print(team_b.players[goal_player], end="")
|
|
if shot_type == 1:
|
|
print(" LET'S A BIG SLAP SHOT GO!!\n")
|
|
z = 4
|
|
z += z1
|
|
elif shot_type == 2:
|
|
print(" RIPS A WRIST SHOT OFF\n")
|
|
z = 2
|
|
z += z1
|
|
elif shot_type == 3:
|
|
print(" GETS A BACKHAND OFF\n")
|
|
z = 3
|
|
z += z1
|
|
elif shot_type == 4:
|
|
print(" SNAPS OFF A SNAP SHOT\n")
|
|
z = 2
|
|
z += z1
|
|
while True:
|
|
goal_area = int(input("AREA? "))
|
|
if goal_area >= 1 and goal_area <= 4:
|
|
break
|
|
if controlling_team == 1:
|
|
team_a.shots_on_net += 1
|
|
else:
|
|
team_b.shots_on_net += 1
|
|
hit_area = randint(1, 5)
|
|
if goal_area == hit_area:
|
|
hit_area = handle_hit(
|
|
controlling_team,
|
|
team_a,
|
|
team_b,
|
|
player_index,
|
|
goal_player,
|
|
goal_assistant1,
|
|
goal_assistant2,
|
|
hit_area,
|
|
z,
|
|
)
|
|
if goal_area != hit_area:
|
|
return handle_miss(
|
|
controlling_team, team_a, team_b, remaining_time, goal_player
|
|
)
|
|
print("AND WE'RE READY FOR THE FACE-OFF\n")
|
|
return ("continue", remaining_time)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|