MAINT: Add type annotations / use functions

This commit is contained in:
Martin Thoma
2022-03-07 22:00:41 +01:00
parent 528df5215e
commit 8cf8bab742
5 changed files with 240 additions and 198 deletions

4
01_Acey_Ducey/python/acey_ducey.py Normal file → Executable file
View File

@@ -23,7 +23,7 @@ cards = {
}
def play_game():
def play_game() -> None:
"""Play the game"""
cash = 100
while cash > 0:
@@ -63,7 +63,7 @@ def play_game():
print("Sorry, friend, but you blew your wad")
def main():
def main() -> None:
"""Main"""
keep_playing = True

View File

@@ -11,41 +11,43 @@
#
######################################################
from typing import List
class Card:
def __init__(self, suit, rank):
def __init__(self, suit: str, rank: int):
self.suit = suit
self.rank = rank
def __str__(self):
r = self.rank
if r == 11:
def __str__(self) -> str:
r = str(self.rank)
if r == "11":
r = "J"
elif r == 12:
elif r == "12":
r = "Q"
elif r == 13:
elif r == "13":
r = "K"
elif r == 14:
elif r == "14":
r = "A"
return f"{r}{self.suit}"
class Deck:
def __init__(self):
self.cards = []
self.cards: List[Card] = []
self.build()
def build(self):
def build(self) -> None:
for suit in ["\u2665", "\u2666", "\u2663", "\u2660"]:
for rank in range(2, 15):
self.cards.append(Card(suit, rank))
def shuffle(self):
def shuffle(self) -> None:
import random
random.shuffle(self.cards)
def deal(self):
def deal(self) -> Card:
return self.cards.pop()
@@ -58,7 +60,7 @@ class Game:
self.money = 100
self.not_done = True
def play(self):
def play(self) -> None:
while self.not_done:
while self.money > 0:
card_a = self.card_a
@@ -90,9 +92,9 @@ class Game:
print("Chicken!")
print(f"Your deal should have been: {player_card}")
if card_a.rank < player_card.rank < card_b.rank:
print(f"You could have won!")
print("You could have won!")
else:
print(f"You would lose, so it was wise of you to chicken out!")
print("You would lose, so it was wise of you to chicken out!")
self.not_done = False
break

View File

@@ -30,13 +30,13 @@ import random
# to start with more or less than $100."
DEFAULT_BANKROLL = 100
# functions
def deal_card_num():
def deal_card_num() -> int:
"""Get card number"""
return random.randint(0, 12)
def get_card_name(number):
def get_card_name(number: int) -> str:
"""Get card name"""
card_names = (
" 2",
@@ -56,7 +56,7 @@ def get_card_name(number):
return card_names[number]
def display_bankroll(bank_roll):
def display_bankroll(bank_roll: int) -> None:
"""Print current bankroll"""
if BANK_ROLL > 0:
print("You now have %s dollars\n" % bank_roll)
@@ -103,9 +103,9 @@ while KEEP_PLAYING:
# Get and handle player bet choice
BET_IS_VALID = False
while not BET_IS_VALID:
curr_bet = input("What is your bet? ")
curr_bet_str = input("What is your bet? ")
try:
curr_bet = int(curr_bet)
curr_bet = int(curr_bet_str)
except ValueError:
# Bad input? Just loop back up and ask again...
pass

View File

@@ -1,54 +1,66 @@
import random
from typing import List, NamedTuple, Tuple
# Python translation by Frank Palazzolo - 2/2021
print(" " * 28 + "AMAZING PROGRAM")
print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
print()
print()
print()
while True:
width, length = input("What are your width and length?").split(",")
width = int(width)
length = int(length)
if width != 1 and length != 1:
break
print("Meaningless dimensions. Try again.")
class Maze(NamedTuple):
used: List[List[int]]
walls: List[List[int]]
enter_col: int
width: int
length: int
# Build two 2D arrays
#
# used:
# Initially set to zero, unprocessed cells
# Filled in with consecutive non-zero numbers as cells are processed
#
# walls:
# Initially set to zero, (all paths blocked)
# Remains 0 if there is no exit down or right
# Set to 1 if there is an exit down
# Set to 2 if there is an exit right
# Set to 3 if there are exits down and right
used = []
walls = []
for i in range(length):
def main() -> None:
welcome_header()
width, length = get_maze_dimensions()
maze = build_maze(width, length)
print_maze(maze)
def welcome_header() -> None:
print(" " * 28 + "AMAZING PROGRAM")
print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
print()
print()
print()
def build_maze(width: int, length: int) -> Maze:
# Build two 2D arrays
#
# used:
# Initially set to zero, unprocessed cells
# Filled in with consecutive non-zero numbers as cells are processed
#
# walls:
# Initially set to zero, (all paths blocked)
# Remains 0 if there is no exit down or right
# Set to 1 if there is an exit down
# Set to 2 if there is an exit right
# Set to 3 if there are exits down and right
used = []
walls = []
for _ in range(length):
used.append([0] * width)
walls.append([0] * width)
# Use direction variables with nice names
GO_LEFT, GO_UP, GO_RIGHT, GO_DOWN = [0, 1, 2, 3]
# Give Exit directions nice names
EXIT_DOWN = 1
EXIT_RIGHT = 2
# Use direction variables with nice names
GO_LEFT, GO_UP, GO_RIGHT, GO_DOWN = [0, 1, 2, 3]
# Give Exit directions nice names
EXIT_DOWN = 1
EXIT_RIGHT = 2
# Pick a random entrance, mark as used
enter_col = random.randint(0, width - 1)
row, col = 0, enter_col
count = 1
used[row][col] = count
count = count + 1
# Pick a random entrance, mark as used
enter_col = random.randint(0, width - 1)
row, col = 0, enter_col
count = 1
used[row][col] = count
count = count + 1
while count != width * length + 1:
while count != width * length + 1:
# remove possible directions that are blocked or
# hit cells that we have already processed
possible_dirs = [GO_LEFT, GO_UP, GO_RIGHT, GO_DOWN]
@@ -90,29 +102,46 @@ while count != width * length + 1:
if used[row][col] != 0:
break
# Add a random exit
col = random.randint(0, width - 1)
row = length - 1
walls[row][col] = walls[row][col] + 1
# Add a random exit
col = random.randint(0, width - 1)
row = length - 1
walls[row][col] = walls[row][col] + 1
return Maze(used, walls, enter_col, width, length)
# Print the maze
for col in range(width):
if col == enter_col:
def get_maze_dimensions() -> Tuple[int, int]:
while True:
width_str, length_str = input("What are your width and length?").split(",")
width = int(width_str)
length = int(length_str)
if width > 1 and length > 1:
break
print("Meaningless dimensions. Try again.")
return width, length
def print_maze(maze: Maze) -> None:
for col in range(maze.width):
if col == maze.enter_col:
print(". ", end="")
else:
print(".--", end="")
print(".")
for row in range(length):
print(".")
for row in range(maze.length):
print("I", end="")
for col in range(width):
if walls[row][col] < 2:
for col in range(maze.width):
if maze.walls[row][col] < 2:
print(" I", end="")
else:
print(" ", end="")
print()
for col in range(width):
if walls[row][col] == 0 or walls[row][col] == 2:
for col in range(maze.width):
if maze.walls[row][col] == 0 or maze.walls[row][col] == 2:
print(":--", end="")
else:
print(": ", end="")
print(".")
if __name__ == "__main__":
main()

View File

@@ -42,20 +42,26 @@
#
########################################################
from typing import Optional
class Node:
"""
Node of the binary tree of questions.
"""
def __init__(self, text, yes_node, no_node):
def __init__(
self, text: str, yes_node: Optional["Node"], no_node: Optional["Node"]
):
# the nodes that are leafs have as text the animal's name, otherwise
# a yes/no question
self.text = text
self.yes_node = yes_node
self.no_node = no_node
def update_node(self, new_question, answer_new_ques, new_animal):
def update_node(
self, new_question: str, answer_new_ques: str, new_animal: str
) -> None:
# update the leaf with a question
old_animal = self.text
# we replace the animal with a new question
@@ -69,13 +75,13 @@ class Node:
self.no_node = Node(new_animal, None, None)
# the leafs have as children None
def is_leaf(self):
return self.yes_node == None and self.no_node == None
def is_leaf(self) -> bool:
return self.yes_node is None and self.no_node is None
def list_known_animals(root_node):
def list_known_animals(root_node: Optional[Node]) -> None:
# Traversing the tree by recursion until we reach the leafs
if root_node == None:
if root_node is None:
return
if root_node.is_leaf():
@@ -89,11 +95,10 @@ def list_known_animals(root_node):
list_known_animals(root_node.no_node)
def parse_input(message, check_list, root_node):
# only accepts yes or no inputs and recognizes list operation
correct_input = False
while not correct_input:
try:
def parse_input(message: str, check_list: bool, root_node: Optional[Node]) -> str:
"""only accepts yes or no inputs and recognizes list operation"""
token = ""
while token not in ["y", "n"]:
inp = input(message)
if check_list and inp.lower() == "list":
@@ -101,51 +106,55 @@ def parse_input(message, check_list, root_node):
list_known_animals(root_node)
print("\n")
if len(inp) > 0:
token = inp[0].lower()
if token == "y" or token == "n":
correct_input = True
except IndexError:
pass
else:
token = ""
return token
def avoid_void_input(message):
def avoid_void_input(message: str) -> str:
answer = ""
while answer == "":
answer = input(message)
return answer
def initial_message():
def initial_message() -> None:
print(" " * 32 + "Animal")
print(" " * 15 + "Creative Computing Morristown, New Jersey\n")
print("Play ´Guess the Animal´")
print("Think of an animal and the computer will try to guess it.\n")
# Initial tree
yes_child = Node("Fish", None, None)
no_child = Node("Bird", None, None)
root = Node("Does it swim?", yes_child, no_child)
def main() -> None:
# Initial tree
yes_child = Node("Fish", None, None)
no_child = Node("Bird", None, None)
root = Node("Does it swim?", yes_child, no_child)
# Main loop of game
initial_message()
keep_playing = parse_input("Are you thinking of an animal? ", True, root) == "y"
while keep_playing:
# Main loop of game
initial_message()
keep_playing = parse_input("Are you thinking of an animal? ", True, root) == "y"
while keep_playing:
keep_asking = True
# Start traversing the tree by the root
actual_node = root
actual_node: Node = root
while keep_asking:
if not actual_node.is_leaf():
# we have to keep asking i.e. traversing nodes
answer = parse_input(actual_node.text, False, None)
# As this is an inner node, both children are not None
if answer == "y":
assert actual_node.yes_node is not None
actual_node = actual_node.yes_node
else:
assert actual_node.no_node is not None
actual_node = actual_node.no_node
else:
# we have reached a possible answer
@@ -156,9 +165,8 @@ while keep_playing:
"The animal you were thinking of was a ? "
)
new_question = avoid_void_input(
"Please type in a question that would distinguish a {} from a {}: ".format(
new_animal, actual_node.text
)
"Please type in a question that would distinguish a "
f"{new_animal} from a {actual_node.text}: "
)
answer_new_question = parse_input(
f"for a {new_animal} the answer would be: ", False, None
@@ -193,3 +201,6 @@ while keep_playing:
# function (Lines 120 to 130, 135, 158, 160, 168, 173)
########################################################
if __name__ == "__main__":
main()