diff --git a/.github/workflows/check-python.yml b/.github/workflows/check-python.yml index 5df15c8b..b58cd3bd 100644 --- a/.github/workflows/check-python.yml +++ b/.github/workflows/check-python.yml @@ -20,9 +20,6 @@ jobs: run: | python -m pip install --upgrade pip pip install -r 00_Utilities/python/ci-requirements.txt - - name: Test with pytest - run: | - pytest -m "not slow" - name: Test with mypy 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 diff --git a/00_Utilities/python/ci-requirements.in b/00_Utilities/python/ci-requirements.in index 218d0de9..eaa0bc14 100644 --- a/00_Utilities/python/ci-requirements.in +++ b/00_Utilities/python/ci-requirements.in @@ -1,4 +1,3 @@ -pytest flake8 flake8-bugbear flake8-comprehensions diff --git a/00_Utilities/python/ci-requirements.txt b/00_Utilities/python/ci-requirements.txt index 921b2325..0ba583b1 100644 --- a/00_Utilities/python/ci-requirements.txt +++ b/00_Utilities/python/ci-requirements.txt @@ -8,7 +8,6 @@ attrs==20.3.0 # via # flake8-bugbear # flake8-implicit-str-concat - # pytest flake8==4.0.1 # via # -r ci-requirements.in @@ -20,8 +19,6 @@ flake8-comprehensions==3.8.0 # via -r ci-requirements.in flake8-implicit-str-concat==0.2.0 # via -r ci-requirements.in -iniconfig==1.1.1 - # via pytest mccabe==0.6.1 # via flake8 more-itertools==8.12.0 @@ -30,23 +27,11 @@ mypy==0.931 # via -r ci-requirements.in mypy-extensions==0.4.3 # via mypy -packaging==21.3 - # via pytest -pluggy==1.0.0 - # via pytest -py==1.11.0 - # via pytest pycodestyle==2.8.0 # via flake8 pyflakes==2.4.0 # via flake8 -pyparsing==3.0.7 - # via packaging -pytest==7.0.1 - # via -r ci-requirements.in tomli==2.0.1 - # via - # mypy - # pytest + # via mypy typing-extensions==4.1.1 # via mypy diff --git a/01_Acey_Ducey/python/acey_ducey_oo.py b/01_Acey_Ducey/python/acey_ducey_oo.py deleted file mode 100644 index c195c842..00000000 --- a/01_Acey_Ducey/python/acey_ducey_oo.py +++ /dev/null @@ -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() diff --git a/01_Acey_Ducey/python/aceyducey.py b/01_Acey_Ducey/python/aceyducey.py deleted file mode 100644 index e546963e..00000000 --- a/01_Acey_Ducey/python/aceyducey.py +++ /dev/null @@ -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). -# -######################################################## diff --git a/01_Acey_Ducey/python/test_acey_ducey.py b/01_Acey_Ducey/python/test_acey_ducey.py deleted file mode 100644 index 44ea38dd..00000000 --- a/01_Acey_Ducey/python/test_acey_ducey.py +++ /dev/null @@ -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" - ) diff --git a/01_Acey_Ducey/python/test_acey_ducey_oo.py b/01_Acey_Ducey/python/test_acey_ducey_oo.py deleted file mode 100644 index 12b85212..00000000 --- a/01_Acey_Ducey/python/test_acey_ducey_oo.py +++ /dev/null @@ -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 diff --git a/02_Amazing/python/test_amazing.py b/02_Amazing/python/test_amazing.py deleted file mode 100644 index 1d01cbd5..00000000 --- a/02_Amazing/python/test_amazing.py +++ /dev/null @@ -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() diff --git a/03_Animal/python/test_animal.py b/03_Animal/python/test_animal.py deleted file mode 100644 index 74c73143..00000000 --- a/03_Animal/python/test_animal.py +++ /dev/null @@ -1,5 +0,0 @@ -from animal import initial_message - - -def test_initial_message() -> None: - initial_message() diff --git a/04_Awari/python/test_awari.py b/04_Awari/python/test_awari.py deleted file mode 100644 index 7ff61b95..00000000 --- a/04_Awari/python/test_awari.py +++ /dev/null @@ -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() diff --git a/05_Bagels/python/test_bagels.py b/05_Bagels/python/test_bagels.py deleted file mode 100644 index 06071545..00000000 --- a/05_Bagels/python/test_bagels.py +++ /dev/null @@ -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 diff --git a/06_Banner/python/test_banner.py b/06_Banner/python/test_banner.py deleted file mode 100644 index cd5d45ff..00000000 --- a/06_Banner/python/test_banner.py +++ /dev/null @@ -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() diff --git a/07_Basketball/python/test_basketball.py b/07_Basketball/python/test_basketball.py deleted file mode 100644 index 8644dc86..00000000 --- a/07_Basketball/python/test_basketball.py +++ /dev/null @@ -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() diff --git a/08_Batnum/python/test_batnum.py b/08_Batnum/python/test_batnum.py deleted file mode 100644 index 50929388..00000000 --- a/08_Batnum/python/test_batnum.py +++ /dev/null @@ -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 diff --git a/09_Battle/python/battle_oo.py b/09_Battle/python/battle_oo.py deleted file mode 100755 index 2227a22a..00000000 --- a/09_Battle/python/battle_oo.py +++ /dev/null @@ -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() diff --git a/09_Battle/python/test_battle.py b/09_Battle/python/test_battle.py deleted file mode 100644 index 30164350..00000000 --- a/09_Battle/python/test_battle.py +++ /dev/null @@ -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() diff --git a/10_Blackjack/python/test_blackjack.py b/10_Blackjack/python/test_blackjack.py deleted file mode 100644 index 56b7c262..00000000 --- a/10_Blackjack/python/test_blackjack.py +++ /dev/null @@ -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() diff --git a/11_Bombardment/python/test_bombardment.py b/11_Bombardment/python/test_bombardment.py deleted file mode 100644 index ddf8943a..00000000 --- a/11_Bombardment/python/test_bombardment.py +++ /dev/null @@ -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() diff --git a/12_Bombs_Away/python/test_bombs_away.py b/12_Bombs_Away/python/test_bombs_away.py deleted file mode 100644 index 764cb7ad..00000000 --- a/12_Bombs_Away/python/test_bombs_away.py +++ /dev/null @@ -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() diff --git a/13_Bounce/python/test_bounce.py b/13_Bounce/python/test_bounce.py deleted file mode 100644 index 2960919d..00000000 --- a/13_Bounce/python/test_bounce.py +++ /dev/null @@ -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") diff --git a/14_Bowling/python/test_bowling.py b/14_Bowling/python/test_bowling.py deleted file mode 100644 index 04bb7cf6..00000000 --- a/14_Bowling/python/test_bowling.py +++ /dev/null @@ -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") diff --git a/15_Boxing/python/test_boxing.py b/15_Boxing/python/test_boxing.py deleted file mode 100644 index 71b2a6a9..00000000 --- a/15_Boxing/python/test_boxing.py +++ /dev/null @@ -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") diff --git a/16_Bug/python/bug_overengineered.py b/16_Bug/python/bug_overengineered.py deleted file mode 100644 index bc12a3c3..00000000 --- a/16_Bug/python/bug_overengineered.py +++ /dev/null @@ -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() diff --git a/16_Bug/python/test_bug.py b/16_Bug/python/test_bug.py deleted file mode 100644 index 0b5b008a..00000000 --- a/16_Bug/python/test_bug.py +++ /dev/null @@ -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() diff --git a/17_Bullfight/python/test_bullfight.py b/17_Bullfight/python/test_bullfight.py deleted file mode 100644 index f61c6b65..00000000 --- a/17_Bullfight/python/test_bullfight.py +++ /dev/null @@ -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() diff --git a/18_Bullseye/python/test_bullseye.py b/18_Bullseye/python/test_bullseye.py deleted file mode 100644 index d3d0735f..00000000 --- a/18_Bullseye/python/test_bullseye.py +++ /dev/null @@ -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() diff --git a/39_Golf/python/test_golf.py b/39_Golf/python/test_golf.py deleted file mode 100644 index 95b461d1..00000000 --- a/39_Golf/python/test_golf.py +++ /dev/null @@ -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 == "" diff --git a/43_Hammurabi/python/test_hamurabi.py b/43_Hammurabi/python/test_hamurabi.py deleted file mode 100644 index 16377b1d..00000000 --- a/43_Hammurabi/python/test_hamurabi.py +++ /dev/null @@ -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" diff --git a/77_Salvo/python/salvo.ipynb b/77_Salvo/python/salvo.ipynb deleted file mode 100644 index 1cc71208..00000000 --- a/77_Salvo/python/salvo.ipynb +++ /dev/null @@ -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\", 2), (\"DESTROYER\", 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 2 [(2, 5), (1, 5)]\nDESTROYER 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 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": [] - } - ] -} diff --git a/84_Super_Star_Trek/python/test_superstartrek.py b/84_Super_Star_Trek/python/test_superstartrek.py deleted file mode 100644 index ab553fcb..00000000 --- a/84_Super_Star_Trek/python/test_superstartrek.py +++ /dev/null @@ -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() diff --git a/90_Tower/python/test_tower.py b/90_Tower/python/test_tower.py deleted file mode 100644 index 3fa4771b..00000000 --- a/90_Tower/python/test_tower.py +++ /dev/null @@ -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() diff --git a/95_Weekday/python/test_weekday.py b/95_Weekday/python/test_weekday.py deleted file mode 100644 index 67185b72..00000000 --- a/95_Weekday/python/test_weekday.py +++ /dev/null @@ -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