mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-23 07:29:02 -08:00
Python: Add tests and type annotations
This commit is contained in:
@@ -1,48 +1,53 @@
|
||||
import random
|
||||
from typing import Optional
|
||||
"""
|
||||
The basketball class is a computer game that allows you to play as
|
||||
Dartmouth College's captain and playmaker
|
||||
The game uses set probabilites to simulate outcomes of each posession
|
||||
You are able to choose your shot types as well as defensive formations
|
||||
"""
|
||||
|
||||
# The basketball class is a computer game that allows you to play as
|
||||
# Dartmouth College's captain and playmaker
|
||||
# The game uses set probabilites to simulate outcomes of each posession
|
||||
# You are able to choose your shot types as well as defensive formations
|
||||
import random
|
||||
from typing import Optional, List, Literal
|
||||
|
||||
|
||||
def explain_keyboard_inputs():
|
||||
print("\t\t\t Basketball")
|
||||
print("\t Creative Computing Morristown, New Jersey\n\n\n")
|
||||
print("This is Dartmouth College basketball. ")
|
||||
print("Υou will be Dartmouth captain and playmaker.")
|
||||
print("Call shots as follows:")
|
||||
print(
|
||||
"1. Long (30ft.) Jump Shot; "
|
||||
"2. Short (15 ft.) Jump Shot; "
|
||||
"3. Lay up; 4. Set Shot"
|
||||
)
|
||||
print("Both teams will use the same defense. Call Defense as follows:")
|
||||
print("6. Press; 6.5 Man-to-Man; 7. Zone; 7.5 None.")
|
||||
print("To change defense, just type 0 as your next shot.")
|
||||
print("Your starting defense will be? ", end="")
|
||||
|
||||
|
||||
class Basketball:
|
||||
def __init__(self) -> None:
|
||||
self.time = 0
|
||||
self.score = [0, 0] # first value is opponents score, second is home
|
||||
self.defense_choices = [6, 6.5, 7, 7.5]
|
||||
self.defense_choices: List[float] = [6, 6.5, 7, 7.5]
|
||||
self.shot: Optional[int] = None
|
||||
self.shot_choices = [0, 1, 2, 3, 4]
|
||||
self.shot_choices: List[Literal[0, 1, 2, 3, 4]] = [0, 1, 2, 3, 4]
|
||||
self.z1: Optional[float] = None
|
||||
|
||||
# Explains the keyboard inputs
|
||||
print("\t\t\t Basketball")
|
||||
print("\t Creative Computing Morristown, New Jersey\n\n\n")
|
||||
print("This is Dartmouth College basketball. ")
|
||||
print("Υou will be Dartmouth captain and playmaker.")
|
||||
print("Call shots as follows:")
|
||||
print(
|
||||
"1. Long (30ft.) Jump Shot; "
|
||||
"2. Short (15 ft.) Jump Shot; "
|
||||
"3. Lay up; 4. Set Shot"
|
||||
)
|
||||
print("Both teams will use the same defense. Call Defense as follows:")
|
||||
print("6. Press; 6.5 Man-to-Man; 7. Zone; 7.5 None.")
|
||||
print("To change defense, just type 0 as your next shot.")
|
||||
print("Your starting defense will be? ", end="")
|
||||
explain_keyboard_inputs()
|
||||
|
||||
self.defense = get_defense(self.defense_choices)
|
||||
self.defense = get_defense_choice(self.defense_choices)
|
||||
|
||||
# takes input for opponent's name
|
||||
print("\nChoose your opponent? ", end="")
|
||||
|
||||
self.opponent = input()
|
||||
self.opponent = get_opponents_name()
|
||||
self.start_of_period()
|
||||
|
||||
# adds points to the score
|
||||
# team can take 0 or 1, for opponent or Dartmouth, respectively
|
||||
def add_points(self, team, points) -> None:
|
||||
def add_points(self, team: Literal[0, 1], points: Literal[0, 1, 2]) -> None:
|
||||
"""
|
||||
Add points to the score.
|
||||
|
||||
Team can take 0 or 1, for opponent or Dartmouth, respectively
|
||||
"""
|
||||
self.score[team] += points
|
||||
self.print_score()
|
||||
|
||||
@@ -50,8 +55,8 @@ class Basketball:
|
||||
print("Ball passed back to you. ", end="")
|
||||
self.dartmouth_ball()
|
||||
|
||||
# change defense, called when the user enters 0 for their shot
|
||||
def change_defense(self) -> None:
|
||||
"""change defense, called when the user enters 0 for their shot"""
|
||||
defense = None
|
||||
|
||||
while defense not in self.defense_choices:
|
||||
@@ -64,8 +69,8 @@ class Basketball:
|
||||
self.defense = defense
|
||||
self.dartmouth_ball()
|
||||
|
||||
# simulates two foul shots for a player and adds the points
|
||||
def foul_shots(self, team) -> None:
|
||||
def foul_shots(self, team: Literal[0, 1]) -> None:
|
||||
"""Simulate two foul shots for a player and adds the points."""
|
||||
print("Shooter fouled. Two shots.")
|
||||
if random.random() > 0.49:
|
||||
if random.random() > 0.75:
|
||||
@@ -79,18 +84,18 @@ class Basketball:
|
||||
|
||||
self.print_score()
|
||||
|
||||
# called when t = 50, starts a new period
|
||||
def halftime(self) -> None:
|
||||
"""called when t = 50, starts a new period"""
|
||||
print("\n ***** End of first half *****\n")
|
||||
self.print_score()
|
||||
self.start_of_period()
|
||||
|
||||
# prints the current score
|
||||
def print_score(self) -> None:
|
||||
print("Score: " + str(self.score[1]) + " to " + str(self.score[0]) + "\n")
|
||||
"""Print the current score"""
|
||||
print(f"Score: {self.score[1]} to {self.score[0]}\n")
|
||||
|
||||
# simulates a center jump for posession at the beginning of a period
|
||||
def start_of_period(self) -> None:
|
||||
"""Simulate a center jump for posession at the beginning of a period"""
|
||||
print("Center jump")
|
||||
if random.random() > 0.6:
|
||||
print("Dartmouth controls the tap.\n")
|
||||
@@ -99,12 +104,12 @@ class Basketball:
|
||||
print(self.opponent + " controls the tap.\n")
|
||||
self.opponent_ball()
|
||||
|
||||
# called when t = 92
|
||||
def two_minute_warning(self) -> None:
|
||||
"""called when t = 92"""
|
||||
print(" *** Two minutes left in the game ***")
|
||||
|
||||
# called when the user enters 1 or 2 for their shot
|
||||
def dartmouth_jump_shot(self) -> None:
|
||||
"""called when the user enters 1 or 2 for their shot"""
|
||||
self.time += 1
|
||||
if self.time == 50:
|
||||
self.halftime()
|
||||
@@ -156,9 +161,12 @@ class Basketball:
|
||||
self.add_points(1, 2)
|
||||
self.opponent_ball()
|
||||
|
||||
# called when the user enters 0, 3, or 4
|
||||
# lay up, set shot, or defense change
|
||||
def dartmouth_non_jump_shot(self) -> None:
|
||||
"""
|
||||
Lay up, set shot, or defense change
|
||||
|
||||
called when the user enters 0, 3, or 4
|
||||
"""
|
||||
self.time += 1
|
||||
if self.time == 50:
|
||||
self.halftime()
|
||||
@@ -202,22 +210,9 @@ class Basketball:
|
||||
self.add_points(1, 2)
|
||||
self.opponent_ball()
|
||||
|
||||
# plays out a Dartmouth posession, starting with your choice of shot
|
||||
def dartmouth_ball(self) -> None:
|
||||
print("Your shot? ", end="")
|
||||
shot = None
|
||||
try:
|
||||
shot = int(input())
|
||||
except ValueError:
|
||||
shot = None
|
||||
|
||||
while shot not in self.shot_choices:
|
||||
print("Incorrect answer. Retype it. Your shot? ", end="")
|
||||
try:
|
||||
shot = int(input())
|
||||
except Exception:
|
||||
continue
|
||||
assert isinstance(shot, int)
|
||||
"""plays out a Dartmouth posession, starting with your choice of shot"""
|
||||
shot = get_dartmouth_ball_choice(self.shot_choices)
|
||||
self.shot = shot
|
||||
|
||||
if self.time < 100 or random.random() < 0.5:
|
||||
@@ -251,8 +246,8 @@ class Basketball:
|
||||
self.time = 93
|
||||
self.start_of_period()
|
||||
|
||||
# simulates the opponents jumpshot
|
||||
def opponent_jumpshot(self) -> None:
|
||||
"""Simulate the opponents jumpshot"""
|
||||
print("Jump Shot.")
|
||||
if 8 / self.defense * random.random() > 0.35:
|
||||
if 8 / self.defense * random.random() > 0.75:
|
||||
@@ -292,8 +287,8 @@ class Basketball:
|
||||
self.add_points(0, 2)
|
||||
self.dartmouth_ball()
|
||||
|
||||
# simulates opponents lay up or set shot
|
||||
def opponent_non_jumpshot(self) -> None:
|
||||
"""Simulate opponents lay up or set shot."""
|
||||
if self.z1 > 3: # type: ignore
|
||||
print("Set shot.")
|
||||
else:
|
||||
@@ -329,9 +324,12 @@ class Basketball:
|
||||
self.add_points(0, 2)
|
||||
self.dartmouth_ball()
|
||||
|
||||
# simulates an opponents possesion
|
||||
# #randomly picks jump shot or lay up / set shot.
|
||||
def opponent_ball(self) -> None:
|
||||
"""
|
||||
Simulate an opponents possesion
|
||||
|
||||
Randomly picks jump shot or lay up / set shot.
|
||||
"""
|
||||
self.time += 1
|
||||
if self.time == 50:
|
||||
self.halftime()
|
||||
@@ -342,8 +340,8 @@ class Basketball:
|
||||
self.opponent_jumpshot()
|
||||
|
||||
|
||||
def get_defense(defense_choices) -> float:
|
||||
# takes input for a defense
|
||||
def get_defense_choice(defense_choices: List[float]) -> float:
|
||||
"""Takes input for a defense"""
|
||||
try:
|
||||
defense = float(input())
|
||||
except ValueError:
|
||||
@@ -360,5 +358,29 @@ def get_defense(defense_choices) -> float:
|
||||
return defense
|
||||
|
||||
|
||||
def get_dartmouth_ball_choice(shot_choices: List[Literal[0, 1, 2, 3, 4]]) -> int:
|
||||
print("Your shot? ", end="")
|
||||
shot = None
|
||||
try:
|
||||
shot = int(input())
|
||||
except ValueError:
|
||||
shot = None
|
||||
|
||||
while shot not in shot_choices:
|
||||
print("Incorrect answer. Retype it. Your shot? ", end="")
|
||||
try:
|
||||
shot = int(input())
|
||||
except Exception:
|
||||
continue
|
||||
assert isinstance(shot, int)
|
||||
return shot
|
||||
|
||||
|
||||
def get_opponents_name() -> str:
|
||||
"""Take input for opponent's name"""
|
||||
print("\nChoose your opponent? ", end="")
|
||||
return input()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
Basketball()
|
||||
|
||||
16
07_Basketball/python/test_basketball.py
Normal file
16
07_Basketball/python/test_basketball.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import io
|
||||
|
||||
import pytest
|
||||
from _pytest.monkeypatch import MonkeyPatch
|
||||
from _pytest.capture import CaptureFixture
|
||||
|
||||
from basketball import Basketball
|
||||
|
||||
|
||||
def test_basketball(monkeypatch: MonkeyPatch, capsys: CaptureFixture[str]) -> None:
|
||||
monkeypatch.setattr(
|
||||
"sys.stdin",
|
||||
io.StringIO("\n1\n6\n1\n2\n1\n2\n1\n2\n1\n2\n3\n4\n5\n4"),
|
||||
)
|
||||
with pytest.raises(EOFError):
|
||||
Basketball()
|
||||
Reference in New Issue
Block a user