mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-21 14:50:54 -08:00
Python: Add tests and type annotations
This commit is contained in:
@@ -1,12 +1,14 @@
|
||||
import io
|
||||
from unittest import mock
|
||||
from typing import TypeVar
|
||||
from _pytest.capture import CaptureFixture
|
||||
from _pytest.monkeypatch import MonkeyPatch
|
||||
|
||||
from acey_ducey import play_game
|
||||
|
||||
|
||||
@mock.patch("random.shuffle")
|
||||
def test_play_game_lose(mock_random_shuffle, monkeypatch, capsys) -> None:
|
||||
def test_play_game_lose(mock_random_shuffle, monkeypatch: MonkeyPatch, capsys: CaptureFixture) -> None:
|
||||
monkeypatch.setattr("sys.stdin", io.StringIO("100\n100"))
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
@@ -1,46 +1,44 @@
|
||||
########################################################
|
||||
#
|
||||
# Animal
|
||||
#
|
||||
# From: Basic computer Games(1978)
|
||||
#
|
||||
# Unlike other computer games in which the computer
|
||||
# picks a number or letter and you must guess what it is,
|
||||
# in this game you think of an animal and the computer asks
|
||||
# you questions and tries to guess the name of your animal.
|
||||
# If the computer guesses incorrectly, it will ask you for a
|
||||
# question that differentiates the animal it guessed
|
||||
# from the one you were thinking of. In this way the
|
||||
# computer "learns" new animals. Questions to differentiate
|
||||
# new animals should be input without a question mark.
|
||||
# This version of the game does not have a SAVE feature.
|
||||
# If your sistem allows, you may modify the program to
|
||||
# save array A$, then reload the array when you want
|
||||
# to play the game again. This way you can save what the
|
||||
# computer learns over a series of games.
|
||||
# At any time if you reply 'LIST' to the question "ARE YOU
|
||||
# THINKING OF AN ANIMAL", the computer will tell you all the
|
||||
# animals it knows so far.
|
||||
# The program starts originally by knowing only FISH and BIRD.
|
||||
# As you build up a file of animals you should use broad,
|
||||
# general questions first and then narrow down to more specific
|
||||
# ones with later animals. For example, If an elephant was to be
|
||||
# your first animal, the computer would ask for a question to distinguish
|
||||
# an elephant from a bird. Naturally there are hundreds of possibilities,
|
||||
# however, if you plan to build a large file of animals a good question
|
||||
# would be "IS IT A MAMAL".
|
||||
# This program can be easily modified to deal with categories of
|
||||
# things other than animals by simply modifying the initial data
|
||||
# in Line 530 and the dialogue references to animal in Lines 10,
|
||||
# 40, 50, 130, 230, 240 and 600. In an educational environment, this
|
||||
# would be a valuable program to teach the distinguishing chacteristics
|
||||
# of many classes of objects -- rock formations, geography, marine life,
|
||||
# cell structures, etc.
|
||||
# Originally developed by Arthur Luehrmann at Dartmouth College,
|
||||
# Animal was subsequently shortened and modified by Nathan Teichholtz at
|
||||
# DEC and Steve North at Creative Computing
|
||||
#
|
||||
########################################################
|
||||
"""
|
||||
Animal
|
||||
|
||||
From: Basic computer Games(1978)
|
||||
|
||||
Unlike other computer games in which the computer
|
||||
picks a number or letter and you must guess what it is,
|
||||
in this game you think of an animal and the computer asks
|
||||
you questions and tries to guess the name of your animal.
|
||||
If the computer guesses incorrectly, it will ask you for a
|
||||
question that differentiates the animal it guessed
|
||||
from the one you were thinking of. In this way the
|
||||
computer "learns" new animals. Questions to differentiate
|
||||
new animals should be input without a question mark.
|
||||
This version of the game does not have a SAVE feature.
|
||||
If your sistem allows, you may modify the program to
|
||||
save array A$, then reload the array when you want
|
||||
to play the game again. This way you can save what the
|
||||
computer learns over a series of games.
|
||||
At any time if you reply 'LIST' to the question "ARE YOU
|
||||
THINKING OF AN ANIMAL", the computer will tell you all the
|
||||
animals it knows so far.
|
||||
The program starts originally by knowing only FISH and BIRD.
|
||||
As you build up a file of animals you should use broad,
|
||||
general questions first and then narrow down to more specific
|
||||
ones with later animals. For example, If an elephant was to be
|
||||
your first animal, the computer would ask for a question to distinguish
|
||||
an elephant from a bird. Naturally there are hundreds of possibilities,
|
||||
however, if you plan to build a large file of animals a good question
|
||||
would be "IS IT A MAMAL".
|
||||
This program can be easily modified to deal with categories of
|
||||
things other than animals by simply modifying the initial data
|
||||
in Line 530 and the dialogue references to animal in Lines 10,
|
||||
40, 50, 130, 230, 240 and 600. In an educational environment, this
|
||||
would be a valuable program to teach the distinguishing chacteristics
|
||||
of many classes of objects -- rock formations, geography, marine life,
|
||||
cell structures, etc.
|
||||
Originally developed by Arthur Luehrmann at Dartmouth College,
|
||||
Animal was subsequently shortened and modified by Nathan Teichholtz at
|
||||
DEC and Steve North at Creative Computing
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
@@ -80,7 +78,7 @@ class Node:
|
||||
|
||||
|
||||
def list_known_animals(root_node: Optional[Node]) -> None:
|
||||
# Traversing the tree by recursion until we reach the leafs
|
||||
"""Traversing the tree by recursion until we reach the leafs."""
|
||||
if root_node is None:
|
||||
return
|
||||
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
from awari import print_with_tab
|
||||
import io
|
||||
from _pytest.monkeypatch import MonkeyPatch
|
||||
import pytest
|
||||
|
||||
from awari import print_with_tab, main
|
||||
|
||||
|
||||
def test_print_with_tab():
|
||||
def test_print_with_tab() -> None:
|
||||
print_with_tab(3, "Hello")
|
||||
|
||||
|
||||
def test_main(monkeypatch: MonkeyPatch) -> None:
|
||||
monkeypatch.setattr("sys.stdin", io.StringIO("1\n2\n3\n4\n5\n6"))
|
||||
with pytest.raises(EOFError):
|
||||
main()
|
||||
|
||||
@@ -1,35 +1,33 @@
|
||||
######################################################################
|
||||
#
|
||||
# Bagels
|
||||
#
|
||||
# From: BASIC Computer Games (1978)
|
||||
# Edited by David H. Ahl
|
||||
#
|
||||
# "In this game, the computer picks a 3-digit secret number using
|
||||
# the digits 0 to 9 and you attempt to guess what it is. You are
|
||||
# allowed up to twenty guesses. No digit is repeated. After
|
||||
# each guess the computer will give you clues about your guess
|
||||
# as follows:
|
||||
#
|
||||
# PICO One digit is correct, but in the wrong place
|
||||
# FERMI One digit is in the correct place
|
||||
# BAGELS No digit is correct
|
||||
#
|
||||
# "You will learn to draw inferences from the clues and, with
|
||||
# practice, you'll learn to improve your score. There are several
|
||||
# good strategies for playing Bagels. After you have found a good
|
||||
# strategy, see if you can improve it. Or try a different strategy
|
||||
# altogether and see if it is any better. While the program allows
|
||||
# up to twenty guesses, if you use a good strategy it should not
|
||||
# take more than eight guesses to get any number.
|
||||
#
|
||||
# "The original authors of this program are D. Resek and P. Rowe of
|
||||
# the Lawrence Hall of Science, Berkeley, California."
|
||||
#
|
||||
#
|
||||
# Python port by Jeff Jetton, 2019
|
||||
#
|
||||
######################################################################
|
||||
"""
|
||||
Bagels
|
||||
|
||||
From: BASIC Computer Games (1978)
|
||||
Edited by David H. Ahl
|
||||
|
||||
"In this game, the computer picks a 3-digit secret number using
|
||||
the digits 0 to 9 and you attempt to guess what it is. You are
|
||||
allowed up to twenty guesses. No digit is repeated. After
|
||||
each guess the computer will give you clues about your guess
|
||||
as follows:
|
||||
|
||||
PICO One digit is correct, but in the wrong place
|
||||
FERMI One digit is in the correct place
|
||||
BAGELS No digit is correct
|
||||
|
||||
"You will learn to draw inferences from the clues and, with
|
||||
practice, you'll learn to improve your score. There are several
|
||||
good strategies for playing Bagels. After you have found a good
|
||||
strategy, see if you can improve it. Or try a different strategy
|
||||
altogether and see if it is any better. While the program allows
|
||||
up to twenty guesses, if you use a good strategy it should not
|
||||
take more than eight guesses to get any number.
|
||||
|
||||
"The original authors of this program are D. Resek and P. Rowe of
|
||||
the Lawrence Hall of Science, Berkeley, California."
|
||||
|
||||
|
||||
Python port by Jeff Jetton, 2019
|
||||
"""
|
||||
|
||||
|
||||
import random
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
from bagels import build_result_string
|
||||
import io
|
||||
from _pytest.capture import CaptureFixture
|
||||
from _pytest.monkeypatch import MonkeyPatch
|
||||
|
||||
from bagels import build_result_string, main, pick_number
|
||||
|
||||
|
||||
def test_build_result_string() -> None:
|
||||
build_result_string(["a", "b", "c"], "abc")
|
||||
|
||||
|
||||
def test_pick_number() -> None:
|
||||
picked = pick_number()
|
||||
assert len(picked) == 3
|
||||
for el in picked:
|
||||
assert el in "0123456789"
|
||||
|
||||
|
||||
def test_main(monkeypatch: MonkeyPatch, capsys: CaptureFixture) -> None:
|
||||
# Succeed
|
||||
round_1 = "Y\n4444\nabc\n444\n456\n145\n321\n123"
|
||||
|
||||
# Fail after 20 guesses
|
||||
round_2 = (
|
||||
"666\n132\n321\n312\n132\n213\n678\n678\n678\n678\n678\n"
|
||||
"678\n678\n678\n678\n678\n678\n678\n678\n678\n678\nNo"
|
||||
)
|
||||
monkeypatch.setattr("sys.stdin", io.StringIO(f"{round_1}\nYES\n{round_2}"))
|
||||
monkeypatch.setattr("bagels.pick_number", lambda: ["1", "2", "3"])
|
||||
main()
|
||||
captured = capsys.readouterr()
|
||||
assert "Would you like the rules" in captured.out
|
||||
assert "I have a number in mind" in captured.out
|
||||
assert "My number was" in captured.out
|
||||
assert "Hope you had fun." in captured.out
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# BANNER
|
||||
#
|
||||
# Converted from BASIC to Python by Trevor Hobson
|
||||
"""
|
||||
BANNER
|
||||
|
||||
Converted from BASIC to Python by Trevor Hobson
|
||||
"""
|
||||
|
||||
letters = {
|
||||
" ": [0, 0, 0, 0, 0, 0, 0],
|
||||
@@ -56,8 +58,8 @@ def print_banner() -> None:
|
||||
|
||||
while True:
|
||||
try:
|
||||
x = int(input("Horizontal "))
|
||||
if x < 1:
|
||||
horizontal = int(input("Horizontal "))
|
||||
if horizontal < 1:
|
||||
raise ValueError("Horizontal must be greater than zero")
|
||||
break
|
||||
|
||||
@@ -65,8 +67,8 @@ def print_banner() -> None:
|
||||
print("Please enter a number greater than zero")
|
||||
while True:
|
||||
try:
|
||||
y = int(input("Vertical "))
|
||||
if y < 1:
|
||||
vertical = int(input("Vertical "))
|
||||
if vertical < 1:
|
||||
raise ValueError("Vertical must be greater than zero")
|
||||
break
|
||||
|
||||
@@ -75,18 +77,20 @@ def print_banner() -> None:
|
||||
g1 = 0
|
||||
if input("Centered ").lower().startswith("y"):
|
||||
g1 = 1
|
||||
mStr = input("Character (type 'ALL' if you want character being printed) ").upper()
|
||||
aStr = input("Statement ")
|
||||
character = input(
|
||||
"Character (type 'ALL' if you want character being printed) "
|
||||
).upper()
|
||||
statement = input("Statement ")
|
||||
|
||||
input("Set page ") # This means to prepare printer, just press Enter
|
||||
|
||||
for lStr in aStr:
|
||||
s = letters[lStr].copy()
|
||||
xStr = mStr
|
||||
if mStr == "ALL":
|
||||
xStr = lStr
|
||||
for statement_char in statement:
|
||||
s = letters[statement_char].copy()
|
||||
xStr = character
|
||||
if character == "ALL":
|
||||
xStr = statement_char
|
||||
if xStr == " ":
|
||||
print("\n" * (7 * x))
|
||||
print("\n" * (7 * horizontal))
|
||||
else:
|
||||
for u in range(0, 7):
|
||||
for k in range(8, -1, -1):
|
||||
@@ -98,16 +102,16 @@ def print_banner() -> None:
|
||||
if s[u] == 1:
|
||||
f[u] = 8 - k
|
||||
break
|
||||
for _t1 in range(1, x + 1):
|
||||
line_str = " " * int((63 - 4.5 * y) * g1 / len(xStr) + 1)
|
||||
for _t1 in range(1, horizontal + 1):
|
||||
line_str = " " * int((63 - 4.5 * vertical) * g1 / len(xStr) + 1)
|
||||
for b in range(0, f[u] + 1):
|
||||
if j[b] == 0:
|
||||
for _ in range(1, y + 1):
|
||||
for _ in range(1, vertical + 1):
|
||||
line_str = line_str + " " * len(xStr)
|
||||
else:
|
||||
line_str = line_str + xStr * y
|
||||
line_str = line_str + xStr * vertical
|
||||
print(line_str)
|
||||
print("\n" * (2 * x - 1))
|
||||
print("\n" * (2 * horizontal - 1))
|
||||
# print("\n" * 75) # Feed some more paper from the printer
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import io
|
||||
|
||||
from _pytest.monkeypatch import MonkeyPatch
|
||||
from _pytest.capture import CaptureFixture
|
||||
from banner import print_banner
|
||||
|
||||
|
||||
def test_print_banner(monkeypatch) -> None:
|
||||
def test_print_banner(monkeypatch: MonkeyPatch) -> None:
|
||||
horizontal = "1"
|
||||
vertical = "1"
|
||||
centered = "1"
|
||||
@@ -17,3 +18,92 @@ def test_print_banner(monkeypatch) -> None:
|
||||
),
|
||||
)
|
||||
print_banner()
|
||||
|
||||
|
||||
def test_print_banner_horizontal_0(
|
||||
monkeypatch: MonkeyPatch, capsys: CaptureFixture
|
||||
) -> None:
|
||||
horizontal = "1"
|
||||
vertical = "1"
|
||||
centered = "1"
|
||||
char = "*"
|
||||
statement = "O" # only capital letters
|
||||
set_page = "2"
|
||||
monkeypatch.setattr(
|
||||
"sys.stdin",
|
||||
io.StringIO(
|
||||
f"0\n{horizontal}\n{vertical}\n{centered}\n{char}\n{statement}\n{set_page}"
|
||||
),
|
||||
)
|
||||
print_banner()
|
||||
captured = capsys.readouterr()
|
||||
assert "Please enter a number greater than zero" in captured.out
|
||||
|
||||
|
||||
def test_print_banner_vertical_0(
|
||||
monkeypatch: MonkeyPatch, capsys: CaptureFixture
|
||||
) -> None:
|
||||
horizontal = "1"
|
||||
vertical = "1"
|
||||
centered = "1"
|
||||
char = "*"
|
||||
statement = "O" # only capital letters
|
||||
set_page = "2"
|
||||
monkeypatch.setattr(
|
||||
"sys.stdin",
|
||||
io.StringIO(
|
||||
f"{horizontal}\n0\n{vertical}\n{centered}\n{char}\n{statement}\n{set_page}"
|
||||
),
|
||||
)
|
||||
print_banner()
|
||||
captured = capsys.readouterr()
|
||||
assert "Please enter a number greater than zero" in captured.out
|
||||
|
||||
|
||||
def test_print_banner_centered(
|
||||
monkeypatch: MonkeyPatch, capsys: CaptureFixture
|
||||
) -> None:
|
||||
horizontal = "1"
|
||||
vertical = "1"
|
||||
centered = "Y"
|
||||
char = "*"
|
||||
statement = "O" # only capital letters
|
||||
set_page = "2"
|
||||
monkeypatch.setattr(
|
||||
"sys.stdin",
|
||||
io.StringIO(
|
||||
f"{horizontal}\n{vertical}\n{centered}\n{char}\n{statement}\n{set_page}"
|
||||
),
|
||||
)
|
||||
print_banner()
|
||||
captured = capsys.readouterr()
|
||||
expected = (
|
||||
"Horizontal Vertical Centered Character "
|
||||
"(type 'ALL' if you want character being printed) Statement Set page "
|
||||
" *****\n"
|
||||
" * *\n"
|
||||
" * *\n"
|
||||
" * *\n"
|
||||
" * *\n"
|
||||
" * *\n"
|
||||
" *****\n\n\n"
|
||||
)
|
||||
assert captured.out.split("\n") == expected.split("\n")
|
||||
|
||||
|
||||
def test_print_banner_all_statement(
|
||||
monkeypatch: MonkeyPatch, capsys: CaptureFixture
|
||||
) -> None:
|
||||
horizontal = "1"
|
||||
vertical = "1"
|
||||
centered = "1"
|
||||
char = "UNIT TESTING"
|
||||
statement = "ALL" # only capital letters
|
||||
set_page = "2"
|
||||
monkeypatch.setattr(
|
||||
"sys.stdin",
|
||||
io.StringIO(
|
||||
f"{horizontal}\n{vertical}\n{centered}\n{char}\n{statement}\n{set_page}"
|
||||
),
|
||||
)
|
||||
print_banner()
|
||||
|
||||
@@ -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()
|
||||
@@ -1,18 +1,44 @@
|
||||
from enum import Enum
|
||||
from typing import Tuple, Union
|
||||
from enum import IntEnum
|
||||
from typing import Tuple, Any
|
||||
|
||||
|
||||
class WinOptions(Enum):
|
||||
class WinOptions(IntEnum):
|
||||
Undefined = 0
|
||||
TakeLast = 1
|
||||
AvoidLast = 2
|
||||
|
||||
@classmethod
|
||||
def _missing_(cls, value: Any) -> "WinOptions":
|
||||
try:
|
||||
int_value = int(value)
|
||||
except Exception:
|
||||
return WinOptions.Undefined
|
||||
if int_value == 1:
|
||||
return WinOptions.TakeLast
|
||||
elif int_value == 2:
|
||||
return WinOptions.AvoidLast
|
||||
else:
|
||||
return WinOptions.Undefined
|
||||
|
||||
class StartOptions(Enum):
|
||||
|
||||
class StartOptions(IntEnum):
|
||||
Undefined = 0
|
||||
ComputerFirst = 1
|
||||
PlayerFirst = 2
|
||||
|
||||
@classmethod
|
||||
def _missing_(cls, value: Any) -> "StartOptions":
|
||||
try:
|
||||
int_value = int(value)
|
||||
except Exception:
|
||||
return StartOptions.Undefined
|
||||
if int_value == 1:
|
||||
return StartOptions.ComputerFirst
|
||||
elif int_value == 2:
|
||||
return StartOptions.PlayerFirst
|
||||
else:
|
||||
return StartOptions.Undefined
|
||||
|
||||
|
||||
def print_intro() -> None:
|
||||
"""Prints out the introduction and rules for the game."""
|
||||
@@ -34,7 +60,7 @@ def print_intro() -> None:
|
||||
return
|
||||
|
||||
|
||||
def get_params() -> Tuple[int, int, int, int, int]:
|
||||
def get_params() -> Tuple[int, int, int, StartOptions, WinOptions]:
|
||||
"""This requests the necessary parameters to play the game.
|
||||
|
||||
Returns a set with the five game parameters:
|
||||
@@ -46,46 +72,69 @@ def get_params() -> Tuple[int, int, int, int, int]:
|
||||
winOption - 1 if the goal is to take the last object
|
||||
or 2 if the goal is to not take the last object
|
||||
"""
|
||||
pile_size = get_pile_size()
|
||||
if pile_size < 0:
|
||||
return (-1, 0, 0, StartOptions.Undefined, WinOptions.Undefined)
|
||||
win_option = get_win_option()
|
||||
min_select, max_select = get_min_max()
|
||||
start_option = get_start_option()
|
||||
return (pile_size, min_select, max_select, start_option, win_option)
|
||||
|
||||
|
||||
def get_pile_size() -> int:
|
||||
# A negative number will stop the game.
|
||||
pile_size = 0
|
||||
win_option: Union[WinOptions, int] = WinOptions.Undefined
|
||||
while pile_size == 0:
|
||||
try:
|
||||
pile_size = int(input("ENTER PILE SIZE "))
|
||||
except ValueError:
|
||||
pile_size = 0
|
||||
return pile_size
|
||||
|
||||
|
||||
def get_win_option() -> WinOptions:
|
||||
win_option: WinOptions = WinOptions.Undefined
|
||||
while win_option == WinOptions.Undefined:
|
||||
win_option = WinOptions(input("ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST: ")) # type: ignore
|
||||
return win_option
|
||||
|
||||
|
||||
def get_min_max() -> Tuple[int, int]:
|
||||
min_select = 0
|
||||
max_select = 0
|
||||
start_option: Union[StartOptions, int] = StartOptions.Undefined
|
||||
|
||||
while pile_size < 1:
|
||||
pile_size = int(input("ENTER PILE SIZE "))
|
||||
while win_option == WinOptions.Undefined:
|
||||
win_option = int(input("ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST: "))
|
||||
assert isinstance(win_option, int)
|
||||
while min_select < 1 or max_select < 1 or min_select > max_select:
|
||||
(min_select, max_select) = (
|
||||
int(x) for x in input("ENTER MIN AND MAX ").split(" ")
|
||||
)
|
||||
return min_select, max_select
|
||||
|
||||
|
||||
def get_start_option() -> StartOptions:
|
||||
start_option: StartOptions = StartOptions.Undefined
|
||||
while start_option == StartOptions.Undefined:
|
||||
start_option = int(input("ENTER START OPTION - 1 COMPUTER FIRST, 2 YOU FIRST "))
|
||||
assert isinstance(start_option, int)
|
||||
return (pile_size, min_select, max_select, start_option, win_option)
|
||||
start_option = StartOptions(input("ENTER START OPTION - 1 COMPUTER FIRST, 2 YOU FIRST ")) # type: ignore
|
||||
return start_option
|
||||
|
||||
|
||||
def player_move(
|
||||
pile_size, min_select, max_select, start_option, win_option
|
||||
pile_size: int, min_select: int, max_select: int, win_option: WinOptions
|
||||
) -> Tuple[bool, int]:
|
||||
"""This handles the player's turn - asking the player how many objects
|
||||
to take and doing some basic validation around that input. Then it
|
||||
checks for any win conditions.
|
||||
|
||||
Returns a boolean indicating whether the game is over and the new pileSize."""
|
||||
playerDone = False
|
||||
while not playerDone:
|
||||
playerMove = int(input("YOUR MOVE "))
|
||||
if playerMove == 0:
|
||||
player_done = False
|
||||
while not player_done:
|
||||
player_move = int(input("YOUR MOVE "))
|
||||
if player_move == 0:
|
||||
print("I TOLD YOU NOT TO USE ZERO! COMPUTER WINS BY FORFEIT.")
|
||||
return (True, pile_size)
|
||||
if playerMove > max_select or playerMove < min_select:
|
||||
if player_move > max_select or player_move < min_select:
|
||||
print("ILLEGAL MOVE, REENTER IT")
|
||||
continue
|
||||
pile_size = pile_size - playerMove
|
||||
playerDone = True
|
||||
pile_size = pile_size - player_move
|
||||
player_done = True
|
||||
if pile_size <= 0:
|
||||
if win_option == WinOptions.AvoidLast:
|
||||
print("TOUGH LUCK, YOU LOSE.")
|
||||
@@ -95,7 +144,9 @@ def player_move(
|
||||
return (False, pile_size)
|
||||
|
||||
|
||||
def computer_pick(pile_size, min_select, max_select, start_option, win_option) -> int:
|
||||
def computer_pick(
|
||||
pile_size: int, min_select: int, max_select: int, win_option: WinOptions
|
||||
) -> int:
|
||||
"""This handles the logic to determine how many objects the computer
|
||||
will select on its turn.
|
||||
"""
|
||||
@@ -110,7 +161,7 @@ def computer_pick(pile_size, min_select, max_select, start_option, win_option) -
|
||||
|
||||
|
||||
def computer_move(
|
||||
pile_size, min_select, max_select, start_option, win_option
|
||||
pile_size: int, min_select: int, max_select: int, win_option: WinOptions
|
||||
) -> Tuple[bool, int]:
|
||||
"""This handles the computer's turn - first checking for the various
|
||||
win/lose conditions and then calculating how many objects
|
||||
@@ -132,13 +183,19 @@ def computer_move(
|
||||
return (True, pile_size)
|
||||
|
||||
# Otherwise, we determine how many the computer selects
|
||||
currSel = computer_pick(pile_size, min_select, max_select, start_option, win_option)
|
||||
pile_size = pile_size - currSel
|
||||
print(f"COMPUTER TAKES {currSel} AND LEAVES {pile_size}")
|
||||
curr_sel = computer_pick(pile_size, min_select, max_select, win_option)
|
||||
pile_size = pile_size - curr_sel
|
||||
print(f"COMPUTER TAKES {curr_sel} AND LEAVES {pile_size}")
|
||||
return (False, pile_size)
|
||||
|
||||
|
||||
def play_game(pile_size, min_select, max_select, start_option, win_option) -> None:
|
||||
def play_game(
|
||||
pile_size: int,
|
||||
min_select: int,
|
||||
max_select: int,
|
||||
start_option: StartOptions,
|
||||
win_option: WinOptions,
|
||||
) -> None:
|
||||
"""This is the main game loop - repeating each turn until one
|
||||
of the win/lose conditions is met.
|
||||
"""
|
||||
@@ -150,30 +207,29 @@ def play_game(pile_size, min_select, max_select, start_option, win_option) -> No
|
||||
while not game_over:
|
||||
if players_turn:
|
||||
(game_over, pile_size) = player_move(
|
||||
pile_size, min_select, max_select, start_option, win_option
|
||||
pile_size, min_select, max_select, win_option
|
||||
)
|
||||
players_turn = False
|
||||
if game_over:
|
||||
return
|
||||
if not players_turn:
|
||||
(game_over, pile_size) = computer_move(
|
||||
pile_size, min_select, max_select, start_option, win_option
|
||||
pile_size, min_select, max_select, win_option
|
||||
)
|
||||
players_turn = True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
pileSize = 0
|
||||
minSelect = 0
|
||||
maxSelect = 0
|
||||
# 1 = to take last, 2 = to avoid last
|
||||
winOption = 0
|
||||
# 1 = computer first, 2 = user first
|
||||
startOption = 0
|
||||
|
||||
def main() -> None:
|
||||
while True:
|
||||
print_intro()
|
||||
(pileSize, minSelect, maxSelect, startOption, winOption) = get_params()
|
||||
(pile_size, min_select, max_select, start_option, win_option) = get_params()
|
||||
|
||||
if pile_size < 0:
|
||||
return
|
||||
|
||||
# Just keep playing the game until the user kills it with ctrl-C
|
||||
play_game(pileSize, minSelect, maxSelect, startOption, winOption)
|
||||
play_game(pile_size, min_select, max_select, start_option, win_option)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
21
08_Batnum/python/test_batnum.py
Normal file
21
08_Batnum/python/test_batnum.py
Normal file
@@ -0,0 +1,21 @@
|
||||
import io
|
||||
from _pytest.capture import CaptureFixture
|
||||
from _pytest.monkeypatch import MonkeyPatch
|
||||
|
||||
from batnum import main
|
||||
|
||||
|
||||
def test_main_win(monkeypatch: MonkeyPatch, capsys: CaptureFixture[str]) -> None:
|
||||
pile_size = 1
|
||||
monkeypatch.setattr("sys.stdin", io.StringIO(f"{pile_size}\n1\n1 2\n2\n1\n-1\n"))
|
||||
main()
|
||||
captured = capsys.readouterr()
|
||||
assert "CONGRATULATIONS, YOU WIN" in captured.out
|
||||
|
||||
|
||||
def test_main_lose(monkeypatch: MonkeyPatch, capsys: CaptureFixture[str]) -> None:
|
||||
pile_size = 3
|
||||
monkeypatch.setattr("sys.stdin", io.StringIO(f"{pile_size}\n2\n1 2\n2\n1\n1\n-1\n"))
|
||||
main()
|
||||
captured = capsys.readouterr()
|
||||
assert "TOUGH LUCK, YOU LOSE" in captured.out
|
||||
Reference in New Issue
Block a user