diff --git a/16_Bug/python/bug.py b/16_Bug/python/bug.py index 6c27b35a..d87c53c7 100644 --- a/16_Bug/python/bug.py +++ b/16_Bug/python/bug.py @@ -1,37 +1,25 @@ +""" +BUG + +Ported by Peter Sharp +""" + from random import randint from collections import namedtuple PAGE_WIDTH = 64 -def main(): - states = { - 'start': (print_start, control_start, update_state), - 'instructions': (print_instructions, goto_game, update_state), - 'game': (print_game, control_game, update_game), - 'pictures': (print_pictures, goto_game, update_state), - 'won': (print_winner, exit_game, update_state) - } - - partTypes = ( - 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) - ) - - data = { - 'state': 'start', - 'partNo': None, - 'players': { - 'YOU': [0] * len(partTypes), - 'I': [0] * len(partTypes) - }, - 'partTypes': partTypes, - 'finished': [], - 'logs': [] - } +def main(states, data): + """ + 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 'exit' == data['state']: break @@ -43,6 +31,9 @@ def main(): Bodypart = namedtuple('Bodypart', ['name', 'count', 'depends']) def print_start(_): + """ + Prints start message + """ print_centered("BUG") print_centered("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") print() @@ -54,6 +45,9 @@ def print_start(_): return input("DO YOU WANT INSTRUCTIONS? ") def control_start(cmd): + """ + Controls the start state + """ if cmd.lower() in ('y', 'yes'): action = 'instructions' else: @@ -61,6 +55,9 @@ def control_start(cmd): return action def print_instructions(data): + """ + 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") @@ -80,12 +77,22 @@ def print_instructions(data): return '' def goto_game(_): + """ + Returns game + """ return 'game' def update_state(data, action): + """ + sets game state to given player value + """ return {**data, 'state': action} def update_game(data, action): + """ + Updates game data for player turns until one player successfully gets a body part + """ + # stores logs of what happened during a particular round logs = [] if 'pictures' == action: @@ -94,16 +101,24 @@ def update_game(data, action): partAdded = False while partAdded == False: for player, parts in data['players'].items(): + # rolls the dice for a part newPartIdx = randint(1,6) - 1 + + # gets information about the picked part partType = data['partTypes'][newPartIdx] + + # gets the number of existing parts of that type the player has partCount = parts[newPartIdx] logs.append(('rolled', newPartIdx, 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 = partType.count < partCount + 1 missingPartDep = partType.depends != None and parts[partType.depends] == 0 if not overMaxParts and not missingPartDep: + # adds a new part partCount += 1 logs.append(('added', newPartIdx, player)) partAdded = True @@ -115,13 +130,18 @@ def update_game(data, action): data['players'][player][newPartIdx] = partCount 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): + """ + Gets players who have finished their bugs + """ totalParts = sum([partType.count for partType in data['partTypes']]) finished = [] for player, parts in data['players'].items(): @@ -129,6 +149,9 @@ def get_finished(data): return finished def print_game(data): + """ + Displays the results of the game turn + """ for log in data['logs']: code, partIdx, player, *logdata = log partType = data['partTypes'][partIdx] @@ -170,6 +193,9 @@ def print_game(data): return input("DO YOU WANT THE PICTURES? ") if len(data['logs']) else 'n' def print_pictures(data): + """ + Displays what the bugs look like for each player + """ typeIxs = { partType.name: idx for idx, partType in enumerate(data['partTypes']) } PIC_WIDTH = 22 for player, parts in data['players'].items(): @@ -205,6 +231,9 @@ def print_pictures(data): print() def control_game(cmd): + """ + returns state based on command + """ if cmd.lower() in ('y', 'yes'): action = 'pictures' else: @@ -212,14 +241,23 @@ def control_game(cmd): return action def print_winner(data): + """ + Displays the winning message + """ for player in data['finished']: print(f"{'YOUR' if 'YOU' == player else 'MY'} BUG IS FINISHED.") print("I HOPE YOU ENJOYED THE GAME, PLAY IT AGAIN SOON!!") def exit_game(_): + """ + Exists the game regardless of input + """ return 'exit' def print_centered(msg, width=PAGE_WIDTH): + """ + Prints given message centered to given width + """ spaces = " " * ((width - len(msg)) // 2) print(spaces + msg) @@ -228,4 +266,45 @@ def print_table(rows): print(*row, sep="\t") if __name__ == '__main__': - main() \ No newline at end of file + + # The main states in the game + states = { + # 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) + } + + # body part types used by the game to work out whether a player's body part can be added + partTypes = ( + 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) + ) + + # all the data used by the game + data = { + 'state': 'start', + 'partNo': None, + 'players': { + 'YOU': [0] * len(partTypes), + 'I': [0] * len(partTypes) + }, + 'partTypes': partTypes, + 'finished': [], + 'logs': [] + } + main(states, data) \ No newline at end of file