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""" """Play the game"""
cash = 100 cash = 100
while cash > 0: while cash > 0:
@@ -63,7 +63,7 @@ def play_game():
print("Sorry, friend, but you blew your wad") print("Sorry, friend, but you blew your wad")
def main(): def main() -> None:
"""Main""" """Main"""
keep_playing = True keep_playing = True

View File

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

View File

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

View File

@@ -1,118 +1,147 @@
import random import random
from typing import List, NamedTuple, Tuple
# Python translation by Frank Palazzolo - 2/2021 # Python translation by Frank Palazzolo - 2/2021
print(" " * 28 + "AMAZING PROGRAM")
print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
print()
print()
print()
while True: class Maze(NamedTuple):
width, length = input("What are your width and length?").split(",") used: List[List[int]]
width = int(width) walls: List[List[int]]
length = int(length) enter_col: int
if width != 1 and length != 1: width: int
break length: int
print("Meaningless dimensions. Try again.")
# 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 = [] def main() -> None:
walls = [] welcome_header()
for i in range(length): width, length = get_maze_dimensions()
used.append([0] * width) maze = build_maze(width, length)
walls.append([0] * width) print_maze(maze)
# 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 def welcome_header() -> None:
enter_col = random.randint(0, width - 1) print(" " * 28 + "AMAZING PROGRAM")
row, col = 0, enter_col print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
count = 1
used[row][col] = count
count = count + 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]
if col == 0 or used[row][col - 1] != 0:
possible_dirs.remove(GO_LEFT)
if row == 0 or used[row - 1][col] != 0:
possible_dirs.remove(GO_UP)
if col == width - 1 or used[row][col + 1] != 0:
possible_dirs.remove(GO_RIGHT)
if row == length - 1 or used[row + 1][col] != 0:
possible_dirs.remove(GO_DOWN)
# If we can move in a direction, move and make opening
if len(possible_dirs) != 0:
direction = random.choice(possible_dirs)
if direction == GO_LEFT:
col = col - 1
walls[row][col] = EXIT_RIGHT
elif direction == GO_UP:
row = row - 1
walls[row][col] = EXIT_DOWN
elif direction == GO_RIGHT:
walls[row][col] = walls[row][col] + EXIT_RIGHT
col = col + 1
elif direction == GO_DOWN:
walls[row][col] = walls[row][col] + EXIT_DOWN
row = row + 1
used[row][col] = count
count = count + 1
# otherwise, move to the next used cell, and try again
else:
while True:
if col != width - 1:
col = col + 1
elif row != length - 1:
row, col = row + 1, 0
else:
row, col = 0, 0
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
# Print the maze
for col in range(width):
if col == enter_col:
print(". ", end="")
else:
print(".--", end="")
print(".")
for row in range(length):
print("I", end="")
for col in range(width):
if walls[row][col] < 2:
print(" I", end="")
else:
print(" ", end="")
print() print()
for col in range(width): print()
if walls[row][col] == 0 or walls[row][col] == 2: print()
print(":--", end="")
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
# 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:
# remove possible directions that are blocked or
# hit cells that we have already processed
possible_dirs = [GO_LEFT, GO_UP, GO_RIGHT, GO_DOWN]
if col == 0 or used[row][col - 1] != 0:
possible_dirs.remove(GO_LEFT)
if row == 0 or used[row - 1][col] != 0:
possible_dirs.remove(GO_UP)
if col == width - 1 or used[row][col + 1] != 0:
possible_dirs.remove(GO_RIGHT)
if row == length - 1 or used[row + 1][col] != 0:
possible_dirs.remove(GO_DOWN)
# If we can move in a direction, move and make opening
if len(possible_dirs) != 0:
direction = random.choice(possible_dirs)
if direction == GO_LEFT:
col = col - 1
walls[row][col] = EXIT_RIGHT
elif direction == GO_UP:
row = row - 1
walls[row][col] = EXIT_DOWN
elif direction == GO_RIGHT:
walls[row][col] = walls[row][col] + EXIT_RIGHT
col = col + 1
elif direction == GO_DOWN:
walls[row][col] = walls[row][col] + EXIT_DOWN
row = row + 1
used[row][col] = count
count = count + 1
# otherwise, move to the next used cell, and try again
else: else:
print(": ", end="") while True:
if col != width - 1:
col = col + 1
elif row != length - 1:
row, col = row + 1, 0
else:
row, col = 0, 0
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
return Maze(used, walls, enter_col, width, length)
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(".") print(".")
for row in range(maze.length):
print("I", end="")
for col in range(maze.width):
if maze.walls[row][col] < 2:
print(" I", end="")
else:
print(" ", end="")
print()
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: class Node:
""" """
Node of the binary tree of questions. 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 # the nodes that are leafs have as text the animal's name, otherwise
# a yes/no question # a yes/no question
self.text = text self.text = text
self.yes_node = yes_node self.yes_node = yes_node
self.no_node = no_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 # update the leaf with a question
old_animal = self.text old_animal = self.text
# we replace the animal with a new question # we replace the animal with a new question
@@ -69,13 +75,13 @@ class Node:
self.no_node = Node(new_animal, None, None) self.no_node = Node(new_animal, None, None)
# the leafs have as children None # the leafs have as children None
def is_leaf(self): def is_leaf(self) -> bool:
return self.yes_node == None and self.no_node == None 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 # Traversing the tree by recursion until we reach the leafs
if root_node == None: if root_node is None:
return return
if root_node.is_leaf(): if root_node.is_leaf():
@@ -89,91 +95,93 @@ def list_known_animals(root_node):
list_known_animals(root_node.no_node) list_known_animals(root_node.no_node)
def parse_input(message, check_list, root_node): def parse_input(message: str, check_list: bool, root_node: Optional[Node]) -> str:
# only accepts yes or no inputs and recognizes list operation """only accepts yes or no inputs and recognizes list operation"""
correct_input = False token = ""
while not correct_input: while token not in ["y", "n"]:
try: inp = input(message)
inp = input(message)
if check_list and inp.lower() == "list": if check_list and inp.lower() == "list":
print("Animals I already know are:") print("Animals I already know are:")
list_known_animals(root_node) list_known_animals(root_node)
print("\n") print("\n")
if len(inp) > 0:
token = inp[0].lower() token = inp[0].lower()
if token == "y" or token == "n": else:
correct_input = True token = ""
except IndexError:
pass
return token return token
def avoid_void_input(message): def avoid_void_input(message: str) -> str:
answer = "" answer = ""
while answer == "": while answer == "":
answer = input(message) answer = input(message)
return answer return answer
def initial_message(): def initial_message() -> None:
print(" " * 32 + "Animal") print(" " * 32 + "Animal")
print(" " * 15 + "Creative Computing Morristown, New Jersey\n") print(" " * 15 + "Creative Computing Morristown, New Jersey\n")
print("Play ´Guess the Animal´") print("Play ´Guess the Animal´")
print("Think of an animal and the computer will try to guess it.\n") print("Think of an animal and the computer will try to guess it.\n")
# Initial tree def main() -> None:
yes_child = Node("Fish", None, None) # Initial tree
no_child = Node("Bird", None, None) yes_child = Node("Fish", None, None)
root = Node("Does it swim?", yes_child, no_child) 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:
keep_asking = True
# Start traversing the tree by the root
actual_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)
if answer == "y":
actual_node = actual_node.yes_node
else:
actual_node = actual_node.no_node
else:
# we have reached a possible answer
answer = parse_input(f"Is it a {actual_node.text}? ", False, None)
if answer == "n":
# add the new animal to the tree
new_animal = avoid_void_input(
"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
)
)
answer_new_question = parse_input(
f"for a {new_animal} the answer would be: ", False, None
)
actual_node.update_node(
new_question + "?", answer_new_question, new_animal
)
else:
print("Why not try another animal?")
keep_asking = False
# Main loop of game
initial_message()
keep_playing = parse_input("Are you thinking of an animal? ", True, root) == "y" 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: 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
answer = parse_input(f"Is it a {actual_node.text}? ", False, None)
if answer == "n":
# add the new animal to the tree
new_animal = avoid_void_input(
"The animal you were thinking of was a ? "
)
new_question = avoid_void_input(
"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
)
actual_node.update_node(
new_question + "?", answer_new_question, new_animal
)
else:
print("Why not try another animal?")
keep_asking = False
keep_playing = parse_input("Are you thinking of an animal? ", True, root) == "y"
######################################################## ########################################################
@@ -193,3 +201,6 @@ while keep_playing:
# function (Lines 120 to 130, 135, 158, 160, 168, 173) # function (Lines 120 to 130, 135, 158, 160, 168, 173)
######################################################## ########################################################
if __name__ == "__main__":
main()