Last active
March 30, 2022 07:09
-
-
Save mdenson-dayspring/adde4b607f575038597abec4cac4c70b to your computer and use it in GitHub Desktop.
An a little over the top Bowling Score app.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# An a little over the top Bowling Score app. | |
# Copyright 2022 by Matthew Denson <[email protected]> | |
# | |
# Example Usage: | |
# To score a set of bowls provided in JSON format in stdin. | |
# $ echo "[1,4,4,5,6,4,5,5,10,0,1,7,3,6,4,10,2,8,9]" | python3 bowling.py stdin | |
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | |
# | 1| 2| 3| 4| 5| 6| 7| 8| 9| 10| | |
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | |
# |1 4|4 5|6 /|5 /| X|0 1|7 /|6 /| X|2 / 9| | |
# | | | | | | | | | | | | |
# | 5 | 14 | 29 | 49 | 60 | 61 | 77 | 97 | 117 | 136 | | |
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | |
# | |
# To score a game interactively | |
# $ python3 bowling.py play | |
# Let's play! | |
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | |
# | 1| 2| 3| 4| 5| 6| 7| 8| 9| 10| | |
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | |
# | | | | | | | | | | | | |
# | | | | | | | | | | | | |
# | | | | | | | | | | | | |
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | |
# Your next roll: (0-10,x,X) | |
# 8 | |
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | |
# | 1| 2| 3| 4| 5| 6| 7| 8| 9| 10| | |
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | |
# |8 | | | | | | | | | | | |
# | | | | | | | | | | | | |
# | | | | | | | | | | | | |
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | |
# Your next roll: (0-2,/) | |
# / | |
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | |
# | 1| 2| 3| 4| 5| 6| 7| 8| 9| 10| | |
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | |
# |8 /| | | | | | | | | | | |
# | | | | | | | | | | | | |
# | | | | | | | | | | | | |
# +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | |
# Your next roll: (0-10,x,X) | |
# ..... | |
import argparse | |
import json | |
import sys | |
def score_game(pins): | |
return score_frame([], pins) | |
def score_frame(running_score, pins): | |
frame_num = len(running_score) + 1 | |
#terminal cases | |
if len(pins) == 0: | |
return running_score | |
elif frame_num > 10: | |
running_score[9]['pins'].extend(pins) | |
return running_score | |
if frame_num == 1: | |
prev_score = 0 | |
else: | |
prev_score = running_score[len(running_score) - 1]['score'] | |
if len(pins) > 0 and pins[0] == 10: | |
# strike | |
if prev_score == None or len(pins) < 3: | |
score = None | |
else: | |
score = prev_score + pins[0] + pins[1] + pins[2] | |
running_score.append({"frame": frame_num, "pins": pins[:1], "score": score}) | |
return score_frame(running_score, pins[1:]) | |
elif len(pins) > 1 and pins[0] + pins[1] == 10: | |
# spare | |
if prev_score == None or len(pins) < 3: | |
score = None | |
else: | |
score = prev_score + pins[0] + pins[1] + pins[2] | |
running_score.append({"frame": frame_num, "pins": pins[:2], "score": score}) | |
return score_frame(running_score, pins[2:]) | |
else: | |
# open | |
if prev_score == None or len(pins) < 2: | |
score = None | |
else: | |
score = prev_score + pins[0] + pins[1] | |
running_score.append({"frame": frame_num, "pins": pins[:2], "score": score}) | |
return score_frame(running_score, pins[2:]) | |
def print_game(frames): | |
top_bottom = "" | |
num = "" | |
bowls = "" | |
space = "" | |
scores = "" | |
for f in frames: | |
top_bottom = top_bottom + "+-----" | |
num = num + "| {frame:>2}".format(frame = f["frame"]) | |
pins = f["pins"] | |
if f["frame"] == 10: # 10th frame (special) | |
pin_str = "" | |
sum = 0 | |
for p in pins: | |
sum = sum + p | |
if p == 10: | |
pin_str = pin_str + "X" | |
sum = 0 | |
elif sum == 10: | |
pin_str = pin_str + "/" | |
sum = 0 | |
else: | |
pin_str = pin_str + str(p) | |
pin_str = pin_str + " " | |
pin_str = pin_str.strip().ljust(5) | |
bowls = bowls + "|" + pin_str | |
elif len(pins) == 1: | |
if pins[0] == 10: | |
bowls = bowls + "| X" | |
else: | |
bowls = bowls + "|" + str(pins[0]) + " " | |
else: | |
if pins[0] + pins[1] == 10: | |
bowls = bowls + "|" + str(pins[0]) + " /" | |
else: | |
bowls = bowls + "|" + str(pins[0]) + " " + str(pins[1]) | |
space = space + "| " | |
if f["score"]: | |
scores = scores + "| {score:^3} ".format(score = f["score"]) | |
else: | |
scores = scores + "| " | |
for f in range(len(frames)+1, 11): | |
top_bottom = top_bottom + "+-----" | |
num = num + "| {frame:>2}".format(frame = f) | |
bowls = bowls + "| " | |
space = space + "| " | |
scores = scores + "| " | |
print(top_bottom + "+") | |
print(num + "|") | |
print(top_bottom + "+") | |
print(bowls + "|") | |
print(space + "|") | |
print(scores + "|") | |
print(top_bottom + "+") | |
def from_stdin(args): | |
print_game(score_game(json.load(sys.stdin))) | |
def interactive_game(args): | |
print("Let's play!") | |
frame_pins = 0 | |
curr_frame = 1 | |
pins = [] | |
done = False | |
while not done: | |
score = score_game(pins) | |
print_game(score) | |
if (len(score) == 10 and score[9]["score"]): | |
done = True | |
else: | |
if len(score) > 0 and score[len(score) - 1]['score'] or frame_pins == 10: | |
frame_pins = 0 | |
first_roll = True | |
elif len(score) > 0: | |
first_roll = False | |
else: | |
first_roll = True | |
keep_reading = True | |
if (first_roll): | |
allowed = "0-10,x,X"; | |
else: | |
max = 10 - frame_pins; | |
allowed = "0-" + str(max) + ",/"; | |
while keep_reading: | |
print("Your next roll: (" + allowed + ")") | |
count = 0 | |
roll = input() | |
if roll in ['0','1','2','3','4','5','6','7','8','9','10','/','x','X']: | |
if roll == '/' and frame_pins > 0: | |
count = 10 - frame_pins | |
keep_reading = False | |
elif roll == 'x' or roll == 'X': | |
if frame_pins == 0: | |
count = 10 | |
keep_reading = False | |
else: | |
count = int(roll) | |
if count + frame_pins <=10: | |
keep_reading = False | |
pins.append(count) | |
frame_pins = frame_pins + count | |
parser = argparse.ArgumentParser() | |
parser.add_argument('--version', action='version', version='1.0.0') | |
subparsers = parser.add_subparsers() | |
stdin_parser = subparsers.add_parser('stdin') | |
stdin_parser.set_defaults(func=from_stdin) | |
play_parser = subparsers.add_parser('play') | |
play_parser.set_defaults(func=interactive_game) | |
if __name__ == '__main__': | |
args = parser.parse_args() | |
args.func(args) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment