Skip to content

Instantly share code, notes, and snippets.

@mdenson-dayspring
Last active March 30, 2022 07:09
Show Gist options
  • Save mdenson-dayspring/adde4b607f575038597abec4cac4c70b to your computer and use it in GitHub Desktop.
Save mdenson-dayspring/adde4b607f575038597abec4cac4c70b to your computer and use it in GitHub Desktop.
An a little over the top Bowling Score app.
#
# An a little over the top Bowling Score app.
# Copyright 2022 by Matthew Denson <mdenson@dayspringpartners.com>
#
# 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