Remove tests and alternative Python implementations

Ad discussed here:
https://github.com/coding-horror/basic-computer-games/issues/548#issuecomment-1081008471
This commit is contained in:
Martin Thoma
2022-03-29 06:56:06 +02:00
parent 6f9cb1bac2
commit 17a75813b1
32 changed files with 1 additions and 2352 deletions

View File

@@ -20,9 +20,6 @@ jobs:
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
pip install -r 00_Utilities/python/ci-requirements.txt pip install -r 00_Utilities/python/ci-requirements.txt
- name: Test with pytest
run: |
pytest -m "not slow"
- name: Test with mypy - name: Test with mypy
run: | run: |
mypy . --exclude 79_Slalom --exclude 27_Civil_War --exclude 38_Fur_Trader --exclude 81_Splat --exclude 09_Battle --exclude 40_Gomoko --exclude 36_Flip_Flop --exclude 43_Hammurabi --exclude 04_Awari --exclude 78_Sine_Wave --exclude 77_Salvo --exclude 34_Digits --exclude 17_Bullfight --exclude 16_Bug mypy . --exclude 79_Slalom --exclude 27_Civil_War --exclude 38_Fur_Trader --exclude 81_Splat --exclude 09_Battle --exclude 40_Gomoko --exclude 36_Flip_Flop --exclude 43_Hammurabi --exclude 04_Awari --exclude 78_Sine_Wave --exclude 77_Salvo --exclude 34_Digits --exclude 17_Bullfight --exclude 16_Bug

View File

@@ -1,4 +1,3 @@
pytest
flake8 flake8
flake8-bugbear flake8-bugbear
flake8-comprehensions flake8-comprehensions

View File

@@ -8,7 +8,6 @@ attrs==20.3.0
# via # via
# flake8-bugbear # flake8-bugbear
# flake8-implicit-str-concat # flake8-implicit-str-concat
# pytest
flake8==4.0.1 flake8==4.0.1
# via # via
# -r ci-requirements.in # -r ci-requirements.in
@@ -20,8 +19,6 @@ flake8-comprehensions==3.8.0
# via -r ci-requirements.in # via -r ci-requirements.in
flake8-implicit-str-concat==0.2.0 flake8-implicit-str-concat==0.2.0
# via -r ci-requirements.in # via -r ci-requirements.in
iniconfig==1.1.1
# via pytest
mccabe==0.6.1 mccabe==0.6.1
# via flake8 # via flake8
more-itertools==8.12.0 more-itertools==8.12.0
@@ -30,23 +27,11 @@ mypy==0.931
# via -r ci-requirements.in # via -r ci-requirements.in
mypy-extensions==0.4.3 mypy-extensions==0.4.3
# via mypy # via mypy
packaging==21.3
# via pytest
pluggy==1.0.0
# via pytest
py==1.11.0
# via pytest
pycodestyle==2.8.0 pycodestyle==2.8.0
# via flake8 # via flake8
pyflakes==2.4.0 pyflakes==2.4.0
# via flake8 # via flake8
pyparsing==3.0.7
# via packaging
pytest==7.0.1
# via -r ci-requirements.in
tomli==2.0.1 tomli==2.0.1
# via # via mypy
# mypy
# pytest
typing-extensions==4.1.1 typing-extensions==4.1.1
# via mypy # via mypy

View File

@@ -1,136 +0,0 @@
"""
AceyDuchy
From: BASIC Computer Games (1978)
Edited by David Ahl
"The original BASIC program author was Bill Palmby
of Prairie View, Illinois."
Python port by Aviyam Fischer, 2022
"""
from typing import List, Literal, TypeAlias, get_args
Suit: TypeAlias = Literal["\u2665", "\u2666", "\u2663", "\u2660"]
Rank: TypeAlias = Literal[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
class Card:
def __init__(self, suit: Suit, rank: Rank) -> None:
self.suit = suit
self.rank = rank
def __str__(self) -> str:
r = str(self.rank)
r = {"11": "J", "12": "Q", "13": "K", "14": "A"}.get(r, r)
return f"{r}{self.suit}"
class Deck:
def __init__(self) -> None:
self.cards: List[Card] = []
self.build()
def build(self) -> None:
for suit in get_args(Suit):
for rank in get_args(Rank):
self.cards.append(Card(suit, rank))
def shuffle(self) -> None:
import random
random.shuffle(self.cards)
def deal(self) -> Card:
return self.cards.pop()
class Game:
def __init__(self) -> None:
self.deck = Deck()
self.deck.shuffle()
self.card_a = self.deck.deal()
self.card_b = self.deck.deal()
self.money = 100
self.not_done = True
def play(self) -> None:
while self.not_done:
while self.money > 0:
card_a = self.card_a
card_b = self.card_b
if card_a.rank > card_b.rank:
card_a, card_b = card_b, card_a
if card_a.rank == card_b.rank:
self.card_b = self.deck.deal()
card_b = self.card_b
print(f"You have:\t ${self.money} ")
print(f"Your cards:\t {card_a} {card_b}")
bet = int(input("What is your bet? "))
player_card = self.deck.deal()
if 0 < bet <= self.money:
print(f"Your deal:\t {player_card}")
if card_a.rank < player_card.rank < card_b.rank:
print("You Win!")
self.money += bet
else:
print("You Lose!")
self.money -= bet
self.not_done = False
else:
print("Chicken!")
print(f"Your deal should have been: {player_card}")
if card_a.rank < player_card.rank < card_b.rank:
print("You could have won!")
else:
print("You would lose, so it was wise of you to chicken out!")
self.not_done = False
break
if len(self.deck.cards) <= 3:
print("You ran out of cards. Game over.")
self.not_done = False
break
self.card_a = self.deck.deal()
self.card_b = self.deck.deal()
if self.money == 0:
self.not_done = False
def game_loop() -> None:
game_over = False
while not game_over:
game = Game()
game.play()
print(f"You have ${game.money} left")
print("Would you like to play again? (y/n)")
if input() == "n":
game_over = True
def main():
print(
"""
Acey Ducey is a card game where you play against the computer.
The Dealer(computer) will deal two cards facing up.
You have an option to bet or not bet depending on whether or not you
feel the card will have a value between the first two.
If you do not want to bet input a 0
"""
)
game_loop()
print("\nThanks for playing!")
if __name__ == "__main__":
main()

View File

@@ -1,212 +0,0 @@
"""aceyducey.py contains game code"""
########################################################
#
# Acey Ducey
#
# From: BASIC Computer Games (1978)
# Edited by David Ahl
#
# "This is a simulation of the Acey Ducey card game.
# In the game, the dealer (the computer) deals two
# cards face up. You have an option to bet or not to
# bet depending on whether or not you feel the next
# card dealt will have a value between the first two.
#
# "Your initial money is set to $100. The game keeps
# going on until you lose all your money or interrupt
# the program.
#
# "The original BASIC program author was Bill Palmby
# of Prairie View, Illinois."
#
# Python port by Jeff Jetton, 2019
#
########################################################
import random
# "You may alter [the following statement] if you want
# to start with more or less than $100."
DEFAULT_BANKROLL = 100
def deal_card_num() -> int:
"""Get card number"""
return random.randint(0, 12)
def get_card_name(number: int) -> str:
"""Get card name"""
card_names = (
" 2",
" 3",
" 4",
" 5",
" 6",
" 7",
" 8",
" 9",
" 10",
"Jack",
"Queen",
"King",
"Ace",
)
return card_names[number]
def display_bankroll(bank_roll: int) -> None:
"""Print current bankroll"""
if bank_roll > 0:
print(f"You now have {bank_roll} dollars\n")
def main() -> None:
"""Display initial title and instructions."""
print("\n Acey Ducey Card Game")
print("Creative Computing Morristown, New Jersey")
print("\n\n")
print("Acey-Ducey is played in the following manner")
print("The dealer (computer) deals two cards face up")
print("You have an option to bet or not bet depending")
print("on whether or not you feel the card will have")
print("a value between the first two.")
print("If you do not want to bet, input a 0")
multiple_game_loop()
print("OK Hope you had fun\n")
def multiple_game_loop() -> None:
"""Loop for series of multiple games."""
keep_playing = True
while keep_playing:
# Initialize bankroll at start of each game
BANK_ROLL = DEFAULT_BANKROLL
display_bankroll(BANK_ROLL)
single_round(BANK_ROLL)
print("\n\nSorry, friend but you blew your wad")
player_response = input("Try again (yes or no) ")
if player_response.lower() == "yes":
print()
else:
keep_playing = False
def single_round(BANK_ROLL: int) -> None:
"""Loop for a single round. Repeat until out of money."""
while BANK_ROLL > 0:
# Deal out dealer cards
print("Here are your next two cards")
dealer1 = deal_card_num()
# If the cards match, we redeal 2nd card until they don't
dealer2 = dealer1
while dealer1 == dealer2:
dealer2 = deal_card_num()
# Organize the cards in order if they're not already
if dealer1 >= dealer2:
(dealer1, dealer2) = (dealer2, dealer1) # Ya gotta love Python!
# Show dealer cards to the player
# (use card name rather than internal number)
print(get_card_name(dealer1))
print(get_card_name(dealer2) + "\n")
# Get and handle player bet choice
BET_IS_VALID = False
while not BET_IS_VALID:
curr_bet_str = input("What is your bet? ")
try:
curr_bet = int(curr_bet_str)
except ValueError:
# Bad input? Just loop back up and ask again...
pass
else:
if curr_bet == 0:
BET_IS_VALID = True
print("Chicken!!\n")
elif curr_bet > BANK_ROLL:
print("Sorry, my friend but you bet too much")
print(f"You have only {BANK_ROLL} dollars to bet\n")
else:
# Deal player card
BET_IS_VALID = True
player = deal_card_num()
print(get_card_name(player))
# Did we win?
if dealer1 < player < dealer2:
print("You win!!!")
BANK_ROLL += curr_bet
else:
print("Sorry, you lose")
BANK_ROLL -= curr_bet
# Update player on new bankroll level
display_bankroll(BANK_ROLL)
if __name__ == "__main__":
main()
########################################################
#
# Porting notes:
#
# The original BASIC version had a variable named N
# that was initialized to 100 and then never used.
# Maybe it did something in feature that was edited
# out of the final version used in the book?
#
# The original program simply generated random numbers
# for each card. It did not simulate a true card deck,
# where the dealing of a card eliminates it from the
# deck and reduces the chances of the same value
# being drawn. This "infinite deck" logic (or "deal,
# with replacement after") has NOT been changed.
#
# In the interests of historical fidelity, the bug
# in the original BASIC listing that let you input a
# negative bet value has been faithfully reproduced.
# This lets the player lose money when they win and
# earn money when they lose! :-)
#
#
# Ideas for Modifications
#
# Give the user the ability to quit the game, perhaps
# by typing "quit" instead of making a bet. Provide a
# final assessment based on how much of the original
# bankroll they have left.
#
# Or have the game run for a set number of rounds or
# until a certain bankroll goal is attained.
#
# Implement an "ante"--a set amount the player has to
# bet each time rather than having the option to lay
# out entirely.
#
# See "porting notes" above about negative bet values.
# How would you fix this?
#
# When the player "chickens out", show them what the
# next card would've been and point out whether they
# made a good or bad decision.
#
# In what situations are the odds of winning high
# enough to justify making a bet? Create a cheat mode
# where the program identifies these situations and
# lets the player know.
#
# Change the card dealing to simulate deals from a
# single deck (or a user-selectable number of decks).
#
# Implement a two-player mode where players take turns
# betting (or both bet on the same dealer cards and
# get their own player card dealt).
#
########################################################

View File

@@ -1,29 +0,0 @@
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: MonkeyPatch, capsys: CaptureFixture
) -> None:
monkeypatch.setattr("sys.stdin", io.StringIO("100\n100"))
T = TypeVar("T")
def identity(x: T) -> T:
return x
mock_random_shuffle = identity # noqa: F841
play_game()
captured = capsys.readouterr()
assert captured.out == (
"You now have 100 dollars\n\n"
"Here are you next two cards\n King\n Ace\n\n"
"What is your bet? Queen\n"
"Sorry, you lose\n"
"Sorry, friend, but you blew your wad\n"
)

View File

@@ -1,36 +0,0 @@
from acey_ducey_oo import Card, Deck, Game
def test_card_init() -> None:
card = Card("\u2665", 2)
assert card.suit == "\u2665"
assert card.rank == 2
def test_card_str() -> None:
card = Card("\u2665", 2)
assert str(card) == "2\u2665"
def test_deck_init() -> None:
deck = Deck()
assert len(deck.cards) == 52
assert deck.cards[0].suit == "\u2665"
assert deck.cards[0].rank == 2
def test_deck_shuffle() -> None:
deck = Deck()
deck.shuffle()
def test_deck_deal() -> None:
deck = Deck()
card = deck.deal()
assert card.rank == 14
assert card.suit == "\u2660"
def test_game_init() -> None:
game = Game()
assert len(game.deck.cards) == 50 # two are already dealt

View File

@@ -1,58 +0,0 @@
import io
import pytest
from _pytest.capture import CaptureFixture
from _pytest.monkeypatch import MonkeyPatch
from amazing import build_maze, welcome_header, main
def test_welcome_header(capsys: CaptureFixture[str]) -> None:
capsys.readouterr()
welcome_header()
out, err = capsys.readouterr()
assert out == (
" AMAZING PROGRAM\n"
" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n\n"
)
assert err == ""
@pytest.mark.parametrize(
("width", "length"),
[
(1, 1),
(1, 0),
(1, -1),
(1, 2),
(2, 1),
],
)
def test_build_maze(width: int, length: int) -> None:
with pytest.raises(AssertionError):
build_maze(width, length)
@pytest.mark.parametrize(
("width", "length"),
[
(3, 3),
(10, 10),
],
)
def test_main(monkeypatch: MonkeyPatch, width: int, length: int) -> None:
monkeypatch.setattr(
"sys.stdin",
io.StringIO(f"{width},{length}"),
)
main()
def test_main_error(monkeypatch: MonkeyPatch) -> None:
width = 1
length = 2
monkeypatch.setattr(
"sys.stdin",
io.StringIO(f"{width},{length}\n3,3"),
)
main()

View File

@@ -1,5 +0,0 @@
from animal import initial_message
def test_initial_message() -> None:
initial_message()

View File

@@ -1,15 +0,0 @@
import io
from _pytest.monkeypatch import MonkeyPatch
import pytest
from awari import print_with_tab, main
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()

View File

@@ -1,35 +0,0 @@
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

View File

@@ -1,109 +0,0 @@
import io
from _pytest.monkeypatch import MonkeyPatch
from _pytest.capture import CaptureFixture
from banner import print_banner
def test_print_banner(monkeypatch: MonkeyPatch) -> None:
horizontal = "1"
vertical = "1"
centered = "1"
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()
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()

View File

@@ -1,16 +0,0 @@
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()

View File

@@ -1,21 +0,0 @@
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

View File

@@ -1,218 +0,0 @@
#!/usr/bin/env python3
from dataclasses import dataclass
from random import randrange
DESTROYER_LENGTH = 2
CRUISER_LENGTH = 3
AIRCRAFT_CARRIER_LENGTH = 4
@dataclass(frozen=True)
class Point:
x: int
y: int
@classmethod
def random(cls, start: int, stop: int) -> "Point":
return Point(randrange(start, stop), randrange(start, stop))
def __add__(self, vector: "Vector") -> "Point":
return Point(self.x + vector.x, self.y + vector.y)
@dataclass(frozen=True)
class Vector:
x: int
y: int
@staticmethod
def random() -> "Vector":
return Vector(randrange(-1, 2, 2), randrange(-1, 2, 2))
def __mul__(self, factor: int) -> "Vector":
return Vector(self.x * factor, self.y * factor)
class Sea:
WIDTH = 6
def __init__(self) -> None:
self._graph = tuple([0 for _ in range(self.WIDTH)] for _ in range(self.WIDTH))
def _validate_item_indices(self, point: Point) -> None:
if not isinstance(point, Point):
raise ValueError(f"Sea indices must be Points, not {type(point).__name__}")
if not ((1 <= point.x <= self.WIDTH) and (1 <= point.y <= self.WIDTH)):
raise IndexError("Sea index out of range")
# Allows us to get the value using a point as a key, for example, `sea[Point(3,2)]`
def __getitem__(self, point: Point) -> int:
self._validate_item_indices(point)
return self._graph[point.y - 1][point.x - 1]
# Allows us to get the value using a point as a key, for example, `sea[Point(3,2)] = 3`
def __setitem__(self, point: Point, value: int) -> None:
self._validate_item_indices(point)
self._graph[point.y - 1][point.x - 1] = value
# Allows us to check if a point exists in the sea for example, `if Point(3,2) in sea:`
def __contains__(self, point: Point) -> bool:
try:
self._validate_item_indices(point)
except IndexError:
return False
return True
# Redefines how python will render this object when asked as a str
def __str__(self) -> str:
# Display it encoded
return "\n".join(
[
" ".join(
[str(self._graph[y][x]) for y in range(self.WIDTH - 1, -1, -1)]
)
for x in range(self.WIDTH)
]
)
def has_ship(self, ship_code: int) -> bool:
return any(ship_code in row for row in self._graph)
def count_sunk(self, *ship_codes: int) -> int:
return sum(not self.has_ship(ship_code) for ship_code in ship_codes)
class Battle:
def __init__(self) -> None:
self.sea = Sea()
self.place_ship(DESTROYER_LENGTH, 1)
self.place_ship(DESTROYER_LENGTH, 2)
self.place_ship(CRUISER_LENGTH, 3)
self.place_ship(CRUISER_LENGTH, 4)
self.place_ship(AIRCRAFT_CARRIER_LENGTH, 5)
self.place_ship(AIRCRAFT_CARRIER_LENGTH, 6)
self.splashes = 0
self.hits = 0
def _next_target(self) -> Point:
while True:
try:
guess = input("? ")
coordinates = guess.split(",")
if len(coordinates) != 2:
raise ValueError()
point = Point(int(coordinates[0]), int(coordinates[1]))
if point not in self.sea:
raise ValueError()
return point
except ValueError:
print(
f"INVALID. SPECIFY TWO NUMBERS FROM 1 TO {Sea.WIDTH}, SEPARATED BY A COMMA."
)
@property
def splash_hit_ratio(self) -> str:
return f"{self.splashes}/{self.hits}"
@property
def _is_finished(self) -> bool:
return self.sea.count_sunk(*(i for i in range(1, 7))) == 6
def place_ship(self, size: int, ship_code: int) -> None:
while True:
start = Point.random(1, self.sea.WIDTH + 1)
vector = Vector.random()
# Get potential ship points
points = [start + vector * i for i in range(size)]
if not (
all([point in self.sea for point in points])
and not any([self.sea[point] for point in points])
):
# ship out of bounds or crosses other ship, trying again
continue
# We found a valid spot, so actually place it now
for point in points:
self.sea[point] = ship_code
break
def loop(self) -> None:
while True:
target = self._next_target()
target_value = self.sea[target]
if target_value < 0:
print(
f"YOU ALREADY PUT A HOLE IN SHIP NUMBER {abs(target_value)} AT THAT POINT."
)
if target_value <= 0:
print("SPLASH! TRY AGAIN.")
self.splashes += 1
continue
print(f"A DIRECT HIT ON SHIP NUMBER {target_value}")
self.hits += 1
self.sea[target] = -target_value
if not self.sea.has_ship(target_value):
print("AND YOU SUNK IT. HURRAH FOR THE GOOD GUYS.")
self._display_sunk_report()
if self._is_finished:
self._display_game_end()
break
print(f"YOUR CURRENT SPLASH/HIT RATIO IS {self.splash_hit_ratio}")
def _display_sunk_report(self) -> None:
print(
"SO FAR, THE BAD GUYS HAVE LOST",
f"{self.sea.count_sunk(1, 2)} DESTROYER(S),",
f"{self.sea.count_sunk(3, 4)} CRUISER(S),",
f"AND {self.sea.count_sunk(5, 6)} AIRCRAFT CARRIER(S).",
)
def _display_game_end(self) -> None:
print(
"YOU HAVE TOTALLY WIPED OUT THE BAD GUYS' FLEET "
f"WITH A FINAL SPLASH/HIT RATIO OF {self.splash_hit_ratio}"
)
if not self.splashes:
print("CONGRATULATIONS -- A DIRECT HIT EVERY TIME.")
print("\n****************************")
def main() -> None:
game = Battle()
print(
f"""
BATTLE
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
THE FOLLOWING CODE OF THE BAD GUYS' FLEET DISPOSITION
HAS BEEN CAPTURED BUT NOT DECODED:
{game.sea}
DE-CODE IT AND USE IT IF YOU CAN
BUT KEEP THE DE-CODING METHOD A SECRET.
START GAME"""
)
game.loop()
if __name__ == "__main__":
main()

View File

@@ -1,27 +0,0 @@
import io
from typing import Callable
import pytest
from _pytest.monkeypatch import MonkeyPatch
from battle import main as main_one
from battle_oo import main as main_oo
@pytest.mark.parametrize(
"main",
[main_one, main_oo],
)
def test_main(monkeypatch: MonkeyPatch, main: Callable[[], None]) -> None:
monkeypatch.setattr(
"sys.stdin",
io.StringIO(
"1,1\n1,2\n1,3\n1,4\n1,5\n1,6\n"
"2,1\n2,2\n2,3\n2,4\n2,5\n2,6\n"
"3,1\n3,2\n3,3\n3,4\n3,5\n3,6\n"
"4,1\n4,2\n4,3\n4,4\n4,5\n4,6\n"
"5,1\n5,2\n5,3\n5,4\n5,5\n5,6\n"
"6,1\n6,2\n6,3\n6,4\n6,5\n6,6\n"
),
)
main()

View File

@@ -1,17 +0,0 @@
import io
from _pytest.monkeypatch import MonkeyPatch
from _pytest.capture import CaptureFixture
from blackjack import main
def test_blackjack(monkeypatch: MonkeyPatch, capsys: CaptureFixture[str]) -> None:
nb_players = 1
instructions = "y"
bet = 100
monkeypatch.setattr(
"sys.stdin",
io.StringIO(f"{nb_players}\n{instructions}\n\n{bet}\ns\nn\n"),
)
main()

View File

@@ -1,17 +0,0 @@
import io
from _pytest.monkeypatch import MonkeyPatch
from bombardment import play
def test_bombardment(monkeypatch: MonkeyPatch) -> None:
monkeypatch.setattr(
"sys.stdin",
io.StringIO(
"\n1 2 3 4\n6\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10"
"\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20"
"\n21\n22\n23\n24\n25"
),
)
play()

View File

@@ -1,16 +0,0 @@
import io
from _pytest.monkeypatch import MonkeyPatch
from bombs_away import play_game
def test_bombs_away(monkeypatch: MonkeyPatch) -> None:
side = 1
target = 1
missions = 1
monkeypatch.setattr(
"sys.stdin",
io.StringIO(f"{side}\n{target}\n{missions}\n3\n50"),
)
play_game()

View File

@@ -1,72 +0,0 @@
import io
from _pytest.monkeypatch import MonkeyPatch
from _pytest.capture import CaptureFixture
from bounce import main
def test_bounce(monkeypatch: MonkeyPatch, capsys: CaptureFixture[str]) -> None:
time_increment = 0.1
velocity = 30
coefficient = 1
monkeypatch.setattr(
"sys.stdin",
io.StringIO(f"{time_increment:0.1f}\n{velocity}\n{coefficient}\n"),
)
main()
actual = capsys.readouterr().out
expected = """ BOUNCE
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
THIS SIMULATION LETS YOU SPECIFY THE INITIAL VELOCITY
OF A BALL THROWN STRAIGHT UP, AND THE COEFFICIENT OF
ELASTICITY OF THE BALL. PLEASE USE A DECIMAL FRACTION
COEFFICIENCY (LESS THAN 1).
YOU ALSO SPECIFY THE TIME INCREMENT TO BE USED IN
'STROBING' THE BALL'S FLIGHT (TRY .1 INITIALLY).
TIME INCREMENT (SEC)?
VELOCITY (FPS)?
COEFFICIENT?
FEET
14 000 000 000
0 0 0
13 0 0 0 0 0 0
12 0 0 0 0 0 0
11 0 0 0
0 0 0
10
0 0 0
9 0 0 0
8
0 0 0
7 0 0 0
6
0 0 0
5 0 0 0
4
3 0 0 0
2 0 0 0
1
00 0 0
...................................................................
0 1 2 3 4 5 6
SECONDS
""" # noqa: W291
assert actual.split("\n") == expected.split("\n")

View File

@@ -1,273 +0,0 @@
import io
from typing import List
from _pytest.capture import CaptureFixture
from _pytest.monkeypatch import MonkeyPatch
from bowling import main
def test_bowling_strikes(monkeypatch: MonkeyPatch, capsys: CaptureFixture[str]) -> None:
def perfect_roll(pins: List[int]) -> None:
for i in range(20):
x = i
if x < len(pins):
pins[x] = 1
monkeypatch.setattr("bowling.simulate_roll", perfect_roll)
instructions1 = "Y"
players1 = 1
name1 = "Martin"
another_game1 = "Y"
instructions2 = "N"
players2 = 2
name21 = "Anna"
name22 = "Bob"
another_game2 = "N"
monkeypatch.setattr(
"sys.stdin",
io.StringIO(
f"{instructions1}\n{players1}\n{name1}\n{another_game1}\n"
f"{instructions2}\n{players2}\n{name21}\n{name22}\n{another_game2}"
),
)
main()
actual = capsys.readouterr().out
expected = """ Bowl
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
WELCOME TO THE ALLEY.
BRING YOUR FRIENDS.
OKAY LET'S FIRST GET ACQUAINTED.
THE INSTRUCTIONS (Y/N)? THE GAME OF BOWLING TAKES MIND AND SKILL. DURING THE GAME
THE COMPUTER WILL KEEP SCORE. YOU MAY COMPETE WITH
OTHER PLAYERS[UP TO FOUR]. YOU WILL BE PLAYING TEN FRAMES.
ON THE PIN DIAGRAM 'O' MEANS THE PIN IS DOWN...'+' MEANS THE
PIN IS STANDING. AFTER THE GAME THE COMPUTER WILL SHOW YOUR
SCORES.
FIRST OF ALL...HOW MANY ARE PLAYING?
VERY GOOD...
Enter name for player 1:
O O O O
O O O
O O
O
10 for Martin
STRIKE!!!
O O O O
O O O
O O
O
10 for Martin
STRIKE!!!
O O O O
O O O
O O
O
10 for Martin
STRIKE!!!
O O O O
O O O
O O
O
10 for Martin
STRIKE!!!
O O O O
O O O
O O
O
10 for Martin
STRIKE!!!
O O O O
O O O
O O
O
10 for Martin
STRIKE!!!
O O O O
O O O
O O
O
10 for Martin
STRIKE!!!
O O O O
O O O
O O
O
10 for Martin
STRIKE!!!
O O O O
O O O
O O
O
10 for Martin
STRIKE!!!
O O O O
O O O
O O
O
10 for Martin
STRIKE!!!
Extra rolls for Martin
Martin: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10], total:300
DO YOU WANT ANOTHER GAME?
THE INSTRUCTIONS (Y/N)? FIRST OF ALL...HOW MANY ARE PLAYING?
VERY GOOD...
Enter name for player 1: Enter name for player 2:
O O O O
O O O
O O
O
10 for Anna
STRIKE!!!
O O O O
O O O
O O
O
10 for Bob
STRIKE!!!
O O O O
O O O
O O
O
10 for Anna
STRIKE!!!
O O O O
O O O
O O
O
10 for Bob
STRIKE!!!
O O O O
O O O
O O
O
10 for Anna
STRIKE!!!
O O O O
O O O
O O
O
10 for Bob
STRIKE!!!
O O O O
O O O
O O
O
10 for Anna
STRIKE!!!
O O O O
O O O
O O
O
10 for Bob
STRIKE!!!
O O O O
O O O
O O
O
10 for Anna
STRIKE!!!
O O O O
O O O
O O
O
10 for Bob
STRIKE!!!
O O O O
O O O
O O
O
10 for Anna
STRIKE!!!
O O O O
O O O
O O
O
10 for Bob
STRIKE!!!
O O O O
O O O
O O
O
10 for Anna
STRIKE!!!
O O O O
O O O
O O
O
10 for Bob
STRIKE!!!
O O O O
O O O
O O
O
10 for Anna
STRIKE!!!
O O O O
O O O
O O
O
10 for Bob
STRIKE!!!
O O O O
O O O
O O
O
10 for Anna
STRIKE!!!
O O O O
O O O
O O
O
10 for Bob
STRIKE!!!
O O O O
O O O
O O
O
10 for Anna
STRIKE!!!
Extra rolls for Anna
O O O O
O O O
O O
O
10 for Bob
STRIKE!!!
Extra rolls for Bob
Anna: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10], total:300
Bob: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10], total:300
DO YOU WANT ANOTHER GAME? """ # noqa: W291
assert actual.split("\n") == expected.split("\n")

View File

@@ -1,61 +0,0 @@
import io
from _pytest.capture import CaptureFixture
from _pytest.monkeypatch import MonkeyPatch
from boxing import play
def test_boxing_bad_opponent(
monkeypatch: MonkeyPatch, capsys: CaptureFixture[str]
) -> None:
monkeypatch.setattr("boxing.Player.get_punch_choice", lambda self: 1)
monkeypatch.setattr("boxing.get_opponent_stats", lambda: (2, 1))
monkeypatch.setattr("boxing.is_opponents_turn", lambda: False)
opponent = "Anna"
my_man = "Bob"
strength = "1"
weakness = "2"
monkeypatch.setattr(
"sys.stdin",
io.StringIO(f"{opponent}\n{my_man}\n{strength}\n{weakness}\n1\n1\n1"),
)
play()
actual = capsys.readouterr().out
expected = """BOXING
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
BOXING OLYMPIC STYLE (3 ROUNDS -- 2 OUT OF 3 WINS)
WHAT IS YOUR OPPONENT'S NAME? WHAT IS YOUR MAN'S NAME? DIFFERENT PUNCHES ARE 1 FULL SWING 2 HOOK 3 UPPERCUT 4 JAB
WHAT IS YOUR MAN'S BEST? WHAT IS HIS VULNERABILITY? Anna'S ADVANTAGE is 1 AND VULNERABILITY IS SECRET.
ROUND 1 BEGINS...
Bob SWINGS AND HE CONNECTS!
Bob SWINGS AND HE CONNECTS!
Bob SWINGS AND HE CONNECTS!
Bob SWINGS AND HE CONNECTS!
Bob SWINGS AND HE CONNECTS!
Bob SWINGS AND HE CONNECTS!
Bob SWINGS AND HE CONNECTS!
Bob WINS ROUND 1
ROUND 2 BEGINS...
Bob SWINGS AND HE CONNECTS!
Bob SWINGS AND HE CONNECTS!
Bob SWINGS AND HE CONNECTS!
Bob SWINGS AND HE CONNECTS!
Bob SWINGS AND HE CONNECTS!
Bob SWINGS AND HE CONNECTS!
Bob SWINGS AND HE CONNECTS!
Bob WINS ROUND 2
ROUND 3 BEGINS...
Bob AMAZINGLY WINS
AND NOW GOODBYE FROM THE OLYMPIC ARENA.
"""
assert actual.split("\n") == expected.split("\n")

View File

@@ -1,350 +0,0 @@
"""
BUG (overengineered)
Overengineered version of bug game
Demonstrates function-based Model View Controller pattern
Ported by Peter Sharp
"""
from collections import namedtuple
from random import randint
from typing import Any, Callable, Dict, List, Literal, Optional, Tuple, TypedDict, Union
PAGE_WIDTH = 64
OneParamFunc = Callable[[Any], Any]
TwoParamFunc = Callable[[Any, Any], Any]
StateFunctions = Tuple[OneParamFunc, OneParamFunc, TwoParamFunc]
Action = Literal["instructions", "game", "pictures", "won", "start", "exit"]
Bodypart = namedtuple("Bodypart", ["name", "count", "depends"])
# body part types used by the game to work out whether a player's body part can be added
part_types = (
Bodypart(name="BODY", count=1, depends=None),
Bodypart(name="NECK", count=1, depends=0),
Bodypart(name="HEAD", count=1, depends=1),
Bodypart(name="FEELERS", count=2, depends=2),
Bodypart(name="TAIL", count=1, depends=0),
Bodypart(name="LEGS", count=6, depends=0),
)
class DataDict(TypedDict):
state: Action
partNo: Optional[Any]
players: Dict[str, List[int]]
partTypes: Tuple[Bodypart, ...]
finished: List[Any]
logs: List[Any]
def game_loop(states: Dict[Action, StateFunctions], data: DataDict) -> None:
"""
Starts the game loop using given states and data
Uses a modified version of the MVC (Model View Controller) pattern that uses functions instead of objects
each state in the game has one of each of the following:
View, displays data
Control, converts raw command from user into something the model understands
Model, updates game data based on action received from controller
"""
while True:
if data["state"] == "exit":
break
view, control, model = states[data["state"]]
cmd = view(data)
action = control(cmd)
data = model(data, action)
def print_start(_: Any) -> str:
"""
Prints start message
"""
print_centered("BUG")
print_centered("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
print()
print()
print()
print("THE GAME BUG")
print("I HOPE YOU ENJOY THIS GAME.")
print()
return input("DO YOU WANT INSTRUCTIONS? ")
def control_start(cmd: str) -> str:
"""
Controls the start state
"""
if cmd.lower() in ("y", "yes"):
action = "instructions"
else:
action = "game"
return action
def print_instructions(data: DataDict) -> str:
"""
Prints game instructions
"""
print("THE OBJECT OF BUG IS TO FINISH YOUR BUG BEFORE I FINISH")
print("MINE. EACH NUMBER STANDS FOR A PART OF THE BUG BODY.")
print("I WILL ROLL THE DIE FOR YOU, TELL YOU WHAT I ROLLED FOR YOU")
print("WHAT THE NUMBER STANDS FOR, AND IF YOU CAN GET THE PART.")
print("IF YOU CAN GET THE PART I WILL GIVE IT TO YOU.")
print("THE SAME WILL HAPPEN ON MY TURN.")
print("IF THERE IS A CHANGE IN EITHER BUG I WILL GIVE YOU THE")
print("OPTION OF SEEING THE PICTURES OF THE BUGS.")
print("THE NUMBERS STAND FOR PARTS AS FOLLOWS:")
print_table(
[
("NUMBER", "PART", "NUMBER OF PART NEEDED"),
*[
(i + 1, part.name, part.count)
for i, part in enumerate(data["partTypes"])
],
]
)
print()
print()
return ""
def goto_game(_: Any) -> Literal["game"]:
return "game"
def update_state(data: DataDict, action: Action) -> DataDict:
"""
sets game state to given player value
"""
return {**data, "state": action} # type: ignore
def update_game(data: DataDict, action: Action) -> DataDict:
"""
Updates game data for player turns until one player successfully gets a body part
"""
# stores logs of what happened during a particular round
Log1 = Tuple[str, int, Any]
Log2 = Tuple[str, int, Any, Any]
logs: List[Union[Log1, Log2]] = []
if action == "pictures":
data["state"] = "pictures"
else:
part_added = False
while not part_added:
for player, parts in data["players"].items():
# rolls the dice for a part
new_part_idx = randint(1, 6) - 1
# gets information about the picked part
part_type = data["partTypes"][new_part_idx]
# gets the number of existing parts of that type the player has
part_count = parts[new_part_idx]
logs.append(("rolled", new_part_idx, player))
# a new part can only be added if the player has the parts
# the new part depends on and doesn't have enough of the part already
overMaxParts = part_type.count < part_count + 1
missing_part_dep = (
part_type.depends is not None and parts[part_type.depends] == 0
)
if not overMaxParts and not missing_part_dep:
# adds a new part
part_count += 1
logs.append(("added", new_part_idx, player))
part_added = True
elif missing_part_dep:
logs.append(("missingDep", new_part_idx, player, part_type.depends))
if overMaxParts:
logs.append(("overMax", new_part_idx, player, part_count))
data["players"][player][new_part_idx] = part_count
data["logs"] = logs
# checks if any players have finished their bug
finished = get_finished(data)
if len(finished) > 0:
# and sets the state to 'won' if that's the case
data["finished"] = finished
data["state"] = "won"
return data
def get_finished(data: DataDict) -> List[str]:
"""
Gets players who have finished their bugs
"""
total_parts = sum(part_type.count for part_type in data["partTypes"])
finished = []
for player, parts in data["players"].items():
if sum(parts) == total_parts:
finished.append(player)
return finished
def print_game(data: DataDict) -> str:
"""
Displays the results of the game turn
"""
for log in data["logs"]:
code, part_idx, player, *logdata = log
part_type = data["partTypes"][part_idx]
if code == "rolled":
print()
print(f"{player} ROLLED A {part_idx + 1}")
print(f"{part_idx + 1}={part_type.name}")
elif code == "added":
if player == "YOU":
if part_type.name in ["FEELERS", "LEGS", "TAIL"]:
print(f"I NOW GIVE YOU A {part_type.name.replace('s', '')}.")
else:
print(f"YOU NOW HAVE A {part_type.name}.")
elif player == "I":
if part_type.name in ["BODY", "NECK", "TAIL"]:
print(f"I NOW HAVE A {part_type.name}.")
elif part_type.name == "FEELERS":
print("I GET A FEELER.")
if part_type.count > 2:
print(
f"{player} NOW HAVE {data['players'][player][part_idx]} {part_type.name}"
)
elif code == "missingDep":
(dep_idx,) = logdata
dep = data["partTypes"][dep_idx]
print(
f"YOU DO NOT HAVE A {dep.name}"
if player == "YOU"
else f"I NEEDED A {dep.name}"
)
elif code == "overMax":
(part_count,) = logdata
if part_count > 1:
num = "TWO" if part_count == 2 else part_count
maxMsg = f"HAVE {num} {part_type.name}S ALREADY"
else:
maxMsg = f"ALREADY HAVE A {part_type.name}"
print(f"{player} {maxMsg}")
return input("DO YOU WANT THE PICTURES? ") if len(data["logs"]) else "n"
def print_pictures(data: DataDict) -> None:
"""
Displays what the bugs look like for each player
"""
typeIxs = {part_type.name: idx for idx, part_type in enumerate(data["partTypes"])}
PIC_WIDTH = 22
for player, parts in data["players"].items():
print(f"*****{'YOUR' if player == 'YOU' else 'MY'} BUG*****")
print()
print()
if parts[typeIxs["BODY"]] > 0:
if parts[typeIxs["FEELERS"]] > 0:
F = " ".join(["F"] * parts[typeIxs["FEELERS"]])
for _ in range(4):
print(" " * 9 + F)
if parts[typeIxs["HEAD"]] > 0:
print_centered("HHHHHHH", PIC_WIDTH)
print_centered("H H", PIC_WIDTH)
print_centered("H O O H", PIC_WIDTH)
print_centered("H H", PIC_WIDTH)
print_centered("H V H", PIC_WIDTH)
print_centered("HHHHHHH", PIC_WIDTH)
if parts[typeIxs["NECK"]] > 0:
for _ in range(2):
print_centered("N N", PIC_WIDTH)
print_centered("BBBBBBBBBBBB", PIC_WIDTH)
for _ in range(2):
print_centered("B B", PIC_WIDTH)
if parts[typeIxs["TAIL"]] > 0:
print("TTTTTB B")
print_centered("BBBBBBBBBBBB", PIC_WIDTH)
if parts[typeIxs["LEGS"]] > 0:
L = "L" * parts[typeIxs["LEGS"]]
for _ in range(2):
print(" " * 5 + L)
print()
def control_game(cmd: str) -> Literal["pictures", "game"]:
"""
returns state based on command
"""
if cmd.lower() in ("y", "yes"):
action = "pictures"
else:
action = "game"
return action # type: ignore
def print_winner(data: DataDict) -> None:
"""
Displays the winning message
"""
for player in data["finished"]:
print(f"{'YOUR' if player == 'YOU' else 'MY'} BUG IS FINISHED.")
print("I HOPE YOU ENJOYED THE GAME, PLAY IT AGAIN SOON!!")
def exit_game(_: Any) -> Literal["exit"]:
"""Exist the game regardless of input"""
return "exit"
def print_centered(msg: str, width: int = PAGE_WIDTH) -> None:
"""Print given message centered to given width."""
spaces = " " * ((width - len(msg)) // 2)
print(spaces + msg)
def print_table(rows: List[Any]) -> None:
for row in rows:
print(*row, sep="\t")
def main() -> None:
# The main states in the game
states: Dict[Action, StateFunctions] = {
# Initial state of the game
"start": (print_start, control_start, update_state),
# displays game instructions
"instructions": (print_instructions, goto_game, update_state),
# the main game state
"game": (print_game, control_game, update_game),
# displays pictures before returning to game
"pictures": (print_pictures, goto_game, update_state),
# Displays the winning players and message
"won": (print_winner, exit_game, update_state),
}
# all the data used by the game
data = DataDict(
state="start",
partNo=None,
players={"YOU": [0] * len(part_types), "I": [0] * len(part_types)},
partTypes=part_types,
finished=[],
logs=[],
)
game_loop(states, data)
if __name__ == "__main__":
main()

View File

@@ -1,25 +0,0 @@
import io
from typing import Callable
import pytest
from _pytest.monkeypatch import MonkeyPatch
from bug import main
from bug_overengineered import main as overengineered_main
@pytest.mark.parametrize(
"main",
[main, overengineered_main],
)
def test_main(monkeypatch: MonkeyPatch, main: Callable[[], None]) -> None:
monkeypatch.setattr("time.sleep", lambda n: n)
instructions = "Y"
pictures = "Y"
monkeypatch.setattr(
"sys.stdin",
io.StringIO(
f"{instructions}\n{pictures}\nN\nN\nN\nN\nN\nN\nN\nN\nN\nN\nN\nN\nN\nN\nN\nN\nN\nN\nN\nN\nN\nN\n"
),
)
main()

View File

@@ -1,16 +0,0 @@
import io
from _pytest.monkeypatch import MonkeyPatch
from bullfight import main
def test_main(monkeypatch: MonkeyPatch) -> None:
instructions = "Y"
kill_bull = "YES"
kill_method = "0"
run_from_ring = "YES"
monkeypatch.setattr(
"sys.stdin",
io.StringIO(f"{instructions}\n{kill_bull}\n{kill_method}\n{run_from_ring}\n"),
)
main()

View File

@@ -1,13 +0,0 @@
import io
from _pytest.monkeypatch import MonkeyPatch
from bullseye import main
def test_main(monkeypatch: MonkeyPatch) -> None:
nb_players = 1
monkeypatch.setattr(
"sys.stdin",
io.StringIO(f"{nb_players}\nMartin\n3\n2\n1" + ("\n2" * 21)),
)
main()

View File

@@ -1,88 +0,0 @@
import io
import math
import pytest
from golf import (
CircleGameObj,
GameObjType,
Golf,
Point,
RectGameObj,
get_distance,
is_in_rectangle,
odds,
to_degrees_360,
to_radians,
)
def test_odds() -> None:
n = 1000
p = sum(odds(50) for i in range(n)) / n
assert abs(p - 0.5) < 0.1
@pytest.mark.parametrize(
("p1", "p2", "expected"),
[
((0, 0), (0, 0), 0),
((0, 0), (1, 0), 1),
((0, 0), (0, 1), 1),
((0, 1), (0, 0), 1),
((1, 0), (0, 0), 1),
((0, 0), (2, 0), 2),
((0, 0), (0, 2), 2),
((0, 2), (0, 0), 2),
((2, 0), (0, 0), 2),
((0, 0), (1, 1), 2**0.5),
((2, 3), (4, 5), (2**2 + 2**2) ** 0.5),
],
)
def test_get_distance(p1, p2, expected):
assert get_distance(Point(*p1), Point(*p2)) == expected
@pytest.mark.parametrize(
("pt", "rect", "expected"),
[
(
CircleGameObj(1, 1, 1, GameObjType.BALL),
RectGameObj(0, 0, 2, 2, GameObjType.GREEN),
True,
),
(
CircleGameObj(1, 1, 1, GameObjType.BALL),
RectGameObj(0, 0, 1, 1, GameObjType.GREEN),
False,
),
],
)
def test_is_in_rectangle(pt, rect, expected):
assert is_in_rectangle(pt, rect) == expected
@pytest.mark.parametrize(
("angle", "radians"),
[
(0, 0),
(180, math.pi),
(360, 2 * math.pi),
],
)
def test_to_radians(angle, radians):
assert to_radians(angle) == radians
assert to_degrees_360(radians) == angle
def test_golf(monkeypatch, capsys):
handycap = 10
difficulty = 4
club = 10
swing = 10
monkeypatch.setattr(
"sys.stdin",
io.StringIO(f"10\na\n{handycap}\n{difficulty}\n{club}\n{swing}\nQUIT"),
)
Golf()
out, err = capsys.readouterr()
assert err == ""

View File

@@ -1,30 +0,0 @@
import io
import hamurabi
def test_main(monkeypatch, capsys):
monkeypatch.setattr("sys.stdin", io.StringIO("100\n100\n100"))
hamurabi.main()
captured = capsys.readouterr()
actual_lines = captured.out.splitlines()
expected_lines = [
"HAMURABI", # 0
"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY", # 1
"", # 2
"", # 3
"", # 4
"", # 5
"TRY YOUR HAND AT GOVERNING ANCIENT SUMERIA", # 6
"FOR A TEN-YEAR TERM OF OFFICE.", # 7
"", # 8
"", # 9
"", # 10
"", # 11
"HAMURABI: I BEG TO REPORT TO YOU\n", # 12
"IN YEAR 1 , 0 PEOPLE STARVED, 5 CAME TO THE CITY,\n", # 13
"POPULATION IS NOW 100\n", # 14
"THE CITY NOW OWNS 1000.0 ACRES.", # 15
]
for i, (actual, expected) in enumerate(zip(actual_lines, expected_lines)):
assert actual.strip() == expected.strip(), f"Line {i} is wrong"

View File

@@ -1,328 +0,0 @@
{
"metadata": {
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3-final"
},
"orig_nbformat": 2,
"kernelspec": {
"name": "python3",
"display_name": "Python 3",
"language": "python"
}
},
"nbformat": 4,
"nbformat_minor": 2,
"cells": [
{
"cell_type": "code",
"execution_count": 727,
"metadata": {},
"outputs": [],
"source": [
"import random"
]
},
{
"cell_type": "code",
"execution_count": 728,
"metadata": {},
"outputs": [],
"source": [
"BOARD_WIDTH = 10\n",
"BOARD_HEIGHT = 10\n",
"\n",
"SHIPS = [(\"BATTLESHIP\", 5), (\"CRUISER\", 3), (\"DESTROYER<A>\", 2), (\"DESTROYER<B>\", 2)]\n",
"\n",
"VALID_MOVES = [[-1, 0], [-1, 1], [0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1]]"
]
},
{
"cell_type": "code",
"execution_count": 729,
"metadata": {},
"outputs": [],
"source": [
"# random number functions\n",
"#\n",
"# seed the random number generator\n",
"random.seed()\n",
"\n",
"# random_x_y\n",
"#\n",
"# generate a valid x,y coordinate on the board\n",
"# returns: x,y\n",
"# x: integer between 1 and BOARD_HEIGHT\n",
"# y: integer between 1 and BOARD WIDTH\n",
"def random_x_y():\n",
" x = random.randrange(1, BOARD_WIDTH + 1)\n",
" y = random.randrange(1, BOARD_HEIGHT + 1)\n",
" return (x, y)"
]
},
{
"cell_type": "code",
"execution_count": 730,
"metadata": {},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"5 3\n"
]
}
],
"source": [
"x, y = random_x_y()\n",
"print(x, y)"
]
},
{
"cell_type": "code",
"execution_count": 731,
"metadata": {},
"outputs": [],
"source": [
"# TODO: add an optional starting coordinate for testing\n",
"# purposes\n",
"def generate_ship_coordinates(ship):\n",
"\n",
" # randomly generate starting x,y coordinates\n",
" start_x, start_y = random_x_y()\n",
"\n",
" # using starting coordinates and the ship type,\n",
" # generate a vector of possible directions the ship\n",
" # could be placed. directions are numbered 0-7 along\n",
" # points of the compass (N, NE, E, SE, S, SW, W, NW)\n",
" # clockwise. a vector of valid directions where the\n",
" # ship does not go off the board is determined\n",
" ship_len = SHIPS[ship][1] - 1\n",
" dirs = [False for x in range(8)]\n",
" dirs[0] = (start_x - ship_len) >= 1\n",
" dirs[2] = (start_y + ship_len) <= BOARD_WIDTH\n",
" dirs[1] = dirs[0] and dirs[2]\n",
" dirs[4] = (start_x + ship_len) <= BOARD_HEIGHT\n",
" dirs[3] = dirs[2] and dirs[4]\n",
" dirs[6] = (start_y - ship_len) >= 1\n",
" dirs[5] = dirs[4] and dirs[6]\n",
" dirs[7] = dirs[6] and dirs[0]\n",
" directions = [p for p in range(len(dirs)) if dirs[p]]\n",
"\n",
" # using the vector of valid directions, pick a\n",
" # random direction to place the ship\n",
" dir_idx = random.randrange(len(directions))\n",
" direction = directions[dir_idx]\n",
"\n",
" # using the starting x,y, direction and ship\n",
" # type, return the coordinates of each point\n",
" # of the ship. VALID_MOVES is a staic array\n",
" # of coordinate offsets to walk from starting\n",
" # coordinate to the end coordinate in the\n",
" # chosen direction\n",
" ship_len = SHIPS[ship][1] - 1\n",
" d_x = VALID_MOVES[direction][0]\n",
" d_y = VALID_MOVES[direction][1]\n",
"\n",
" coords = [(start_x, start_y)]\n",
" x_coord = start_x\n",
" y_coord = start_y\n",
" for i in range(ship_len):\n",
" x_coord = x_coord + d_x\n",
" y_coord = y_coord + d_y\n",
" coords.append((x_coord, y_coord))\n",
" return coords"
]
},
{
"cell_type": "code",
"execution_count": 732,
"metadata": {},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"BATTLESHIP 5 [(10, 2), (10, 3), (10, 4), (10, 5), (10, 6)]\nCRUISER 3 [(9, 4), (8, 4), (7, 4)]\nDESTROYER<A> 2 [(2, 5), (1, 5)]\nDESTROYER<B> 2 [(7, 8), (7, 9)]\n"
]
}
],
"source": [
"for ship in range(len(SHIPS)):\n",
" coords = generate_ship_coordinates(ship)\n",
" print(f\"{SHIPS[ship][0]:15}\", f\"{SHIPS[ship][1]:2}\", coords)"
]
},
{
"cell_type": "code",
"execution_count": 733,
"metadata": {},
"outputs": [],
"source": [
"def create_blank_board():\n",
" return [[None for y in range(BOARD_WIDTH)] for x in range(BOARD_HEIGHT)]"
]
},
{
"cell_type": "code",
"execution_count": 734,
"metadata": {},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
" 1 2 3 4 5 6 7 8 9 10\n 1 \n 2 \n 3 \n 4 \n 5 \n 6 \n 7 \n 8 \n 9 \n10 \n"
]
}
],
"source": [
"def print_board(board):\n",
"\n",
" # print board header (column numbers)\n",
" print(\" \", end=\"\")\n",
" for z in range(BOARD_WIDTH):\n",
" print(f\"{z+1:3}\", end=\"\")\n",
" print(\"\")\n",
"\n",
" for x in range(len(board)):\n",
" print(f\"{x+1:2}\", end=\"\")\n",
" for y in range(len(board[x])):\n",
" if board[x][y] is None:\n",
" print(f\"{' ':3}\", end=\"\")\n",
" else:\n",
" print(f\"{board[x][y]:3}\", end=\"\")\n",
" print(\"\")\n",
"\n",
"\n",
"computer_board = create_blank_board()\n",
"print_board(computer_board)"
]
},
{
"cell_type": "code",
"execution_count": 735,
"metadata": {},
"outputs": [],
"source": [
"def place_ship(board, coords, ship):\n",
" for coord in coords:\n",
" board[coord[0] - 1][coord[1] - 1] = ship"
]
},
{
"cell_type": "code",
"execution_count": 736,
"metadata": {},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"DESTROYER<B> 2 [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]\n 1 2 3 4 5 6 7 8 9 10\n 1 0 \n 2 0 \n 3 0 \n 4 0 \n 5 0 \n 6 \n 7 \n 8 \n 9 \n10 \n"
]
}
],
"source": [
"# test place_ship\n",
"board = create_blank_board()\n",
"coords = generate_ship_coordinates(0)\n",
"print(f\"{SHIPS[ship][0]:15}\", f\"{SHIPS[ship][1]:2}\", coords)\n",
"place_ship(board, coords, 0)\n",
"print_board(board)"
]
},
{
"cell_type": "code",
"execution_count": 737,
"metadata": {},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
" 1 2 3 4 5 6 7 8 9 10\n",
" 1 \n",
" 2 1 \n",
" 3 1 2 \n",
" 4 1 2 \n",
" 5 0 \n",
" 6 0 \n",
" 7 0 \n",
" 8 0 \n",
" 9 0 3 \n",
"10 3 \n"
]
}
],
"source": [
"# NOTE: A little quirk that exists here and in the orginal\n",
"# game: Ships are allowed to cross each other!\n",
"# For example: 2 destroyers, length 2, one at\n",
"# [(1,1),(2,2)] and other at [(2,1),(1,2)]\n",
"\n",
"\n",
"def generate_board():\n",
" board = create_blank_board()\n",
"\n",
" for ship in range(len(SHIPS)):\n",
" placed = False\n",
" coords = []\n",
" while not placed:\n",
" coords = generate_ship_coordinates(ship)\n",
" clear = True\n",
" for coord in coords:\n",
" if board[coord[0] - 1][coord[1] - 1] is not None:\n",
" clear = False\n",
" break\n",
" if clear:\n",
" placed = True\n",
" place_ship(board, coords, ship)\n",
" return board\n",
"\n",
"\n",
"print_board(generate_board())"
]
},
{
"cell_type": "code",
"execution_count": 738,
"metadata": {},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"[(4, 1), (3, 6), (6, 10), (10, 6), (4, 5)]\n"
]
}
],
"source": [
"def generate_shots(number):\n",
" shots = []\n",
" for i in range(number):\n",
" shots.append(random_x_y())\n",
" return shots\n",
"\n",
"\n",
"shots = generate_shots(5)\n",
"print(shots)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
]
}

View File

@@ -1,11 +0,0 @@
import io
import pytest
from superstartrek import main
def test_main(monkeypatch, capsys):
monkeypatch.setattr("sys.stdin", io.StringIO("NAV\n1\n1\nSRS\nXXX\nXXX\n"))
with pytest.raises(SystemExit):
main()
# captured = capsys.readouterr()

View File

@@ -1,49 +0,0 @@
import unittest
import tower
class TowerTestCase(unittest.TestCase):
def test_something(self):
t = tower.Tower()
self.assertTrue(t.empty())
d = tower.Disk(3)
t.add(d)
self.assertFalse(t.empty())
d5 = tower.Disk(5)
self.assertRaises(Exception, t.add, d5)
self.assertFalse(t.empty())
def test_oksize(self):
t = tower.Tower()
self.assertTrue(t.empty())
d5 = tower.Disk(5)
t.add(d5)
self.assertFalse(t.empty())
d3 = tower.Disk(3)
t.add(d3)
self.assertFalse(t.empty())
self.assertEqual(t.top(), d3)
self.assertEqual(t.pop(), d3)
self.assertEqual(t.pop(), d5)
def test_game(self):
g = tower.Game()
self.assertEqual(g.moves(), 0)
self.assertFalse(g.winner())
def test_format(self):
t = tower.Tower()
d3 = tower.Disk(3)
d5 = tower.Disk(5)
t.add(d5)
t.add(d3)
if __name__ == "__main__":
unittest.main()

View File

@@ -1,49 +0,0 @@
import datetime
import pytest
from weekday import calculate_day_of_week
@pytest.mark.parametrize(
("year", "month", "day"),
[
(yr, m, d)
for yr in range(1600, 2021)
for m in range(1, 12)
for d in range(1, 28)
],
)
@pytest.mark.slow # Those are 125,037 tests!
def test_weekday_calc(year, month, day):
dt = datetime.date(year, month, day)
python_weekday = dt.weekday() # Monday = 0, Sunday = 6
basic_weekday = calculate_day_of_week(year, month, day) # Sunday = 1, Saturday = 7
if ((python_weekday + 2) % 7) != (basic_weekday % 7):
print(f"testing yr {year} month {month} day {day}")
print(f"python says {python_weekday}")
print(f"BASIC says {basic_weekday}")
assert False
@pytest.mark.parametrize(
("year", "month", "day"),
[
(yr, m, d)
for yr in range(2016, 2021)
for m in range(1, 12)
for d in range(1, 28)
],
)
def test_weekday_calc_4_years(year, month, day):
dt = datetime.date(year, month, day)
python_weekday = dt.weekday() # Monday = 0, Sunday = 6
basic_weekday = calculate_day_of_week(year, month, day) # Sunday = 1, Saturday = 7
if ((python_weekday + 2) % 7) != (basic_weekday % 7):
print(f"testing yr {year} month {month} day {day}")
print(f"python says {python_weekday}")
print(f"BASIC says {basic_weekday}")
assert False