Created
August 29, 2018 23:35
-
-
Save pepasflo/cc4f31219a21058aea63783ae834af28 to your computer and use it in GitHub Desktop.
Bowling scoring system
This file contains 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
#!/usr/bin/env python | |
import sys | |
import json | |
# Safe access for lists. | |
def lget(l, i, default=None): | |
if len(l) <= i: | |
return default | |
else: | |
return l[i] | |
# Safe access for lists of integers. | |
def iget(l, i): | |
return lget(l, i, 0) | |
# Transform a list of frames into a list of pinfalls. | |
# e.g. [['X'],[1,'/'],[4,3]] -> [10, 1, 9, 4, 3] | |
def pinfalls(frames): | |
falls = [] | |
for frame in frames: | |
for ball in frame: | |
if ball == 'X': | |
falls.append(10) | |
elif ball == '/': | |
falls.append(10 - falls[-1]) | |
else: | |
falls.append(int(ball)) | |
return falls | |
# Return the score of the first frame in the given list of frames. | |
# e.g. [['X'],[2,3]] -> 15 | |
def single_frame_score(frames): | |
frame = frames[0] | |
falls = pinfalls(frames) | |
if lget(frame, 0) == 'X' or lget(frame, 1) == '/' or len(frame) == 3: | |
return iget(falls, 0) + iget(falls, 1) + iget(falls, 2) | |
elif len(frame) == 2: | |
return iget(falls, 0) + iget(falls, 1) | |
else: | |
return iget(falls, 0) | |
# Compute the (individual) scores of the given frames. | |
# e.g. [['X'],[2,3]] -> [15,5] | |
def frame_scores(frames): | |
if len(frames) == 0: | |
return [] | |
elif len(frames) == 1: | |
return [single_frame_score(frames)] | |
else: | |
return [single_frame_score(frames)] + frame_scores(frames[1:]) | |
# Compute the running totals for the given set of frame scores. | |
# e.g. [1,2,3] -> [1,3,6] | |
def running_totals(scores): | |
if len(frames) == 0: | |
return [] | |
sum = 0 | |
totals = [] | |
for score in scores: | |
sum += score | |
totals.append(sum) | |
return totals | |
if __name__ == "__main__": | |
if len(sys.argv) > 1: | |
# if a command-line argument was given, assume that is a JSON array of frames. | |
input_string = sys.argv[1] | |
else: | |
# else, read the JSON array of frames from stdin. | |
input_string = sys.stdin.read() | |
frames = json.loads(input_string) | |
scores = running_totals(frame_scores(frames)) | |
# print out the scores as JSON to stdout. | |
print json.dumps(scores) |
This file contains 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
$ ./test.py | |
👉 Test case: full-game-all-strikes.json | |
👉 Running ./bowling.py '[["X"], ["X"], ["X"], ["X"], ["X"], ["X"], ["X"], ["X"], ["X"], ["X", "X", "X"]]' | |
✅ Passed: [30, 60, 90, 120, 150, 180, 210, 240, 270, 300] | |
👉 Test case: full-game-no-score.json | |
👉 Running ./bowling.py '[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]' | |
✅ Passed: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] | |
👉 Test case: ncna2013-example1.json | |
👉 Running ./bowling.py '[[4, "/"], [6, 2], [4, 2], [8, 1], ["X"], [6, 2], [7, 2], [9, 0], [0, 5], [6, 3]]' | |
✅ Passed: [16, 24, 30, 39, 57, 65, 74, 83, 88, 97] | |
👉 Test case: ncna2013-example2.json | |
👉 Running ./bowling.py '[[4, "/"], [6, 2], [4, 2], [8, 1], ["X"], [6, 2], [7, 2], [9, 0], [0, 5], [6, 3]]' | |
✅ Passed: [16, 24, 30, 39, 57, 65, 74, 83, 88, 97] | |
👉 Test case: ncna2013-example3.json | |
👉 Running ./bowling.py '[[4, "/"], [6, 2], [4, 2], [8, 1], ["X"], ["X"], [7, 2], [9, 0], [0, 5], [6, 3]]' | |
✅ Passed: [16, 24, 30, 39, 66, 85, 94, 103, 108, 117] | |
👉 Test case: ncna2013-example4.json | |
👉 Running ./bowling.py '[[4, "/"], [6, 2], [4, 2], [8, 1], ["X"], ["X"], [7, 2], [9, 0], [0, 5], [6, "/", 7]]' | |
✅ Passed: [16, 24, 30, 39, 66, 85, 94, 103, 108, 125] | |
👉 Test case: wikipedia-example1.json | |
👉 Running ./bowling.py '[["X"], [3, 6]]' | |
✅ Passed: [19, 28] | |
👉 Test case: wikipedia-example2.json | |
👉 Running ./bowling.py '[["X"], ["X"], [9, 0]]' | |
✅ Passed: [29, 48, 57] | |
👉 Test case: wikipedia-example3.json | |
👉 Running ./bowling.py '[["X"], ["X"], ["X"], [8, "/"], [8, 0]]' | |
✅ Passed: [30, 58, 78, 96, 104] | |
👉 Test case: wikipedia-example4.json | |
👉 Running ./bowling.py '[["X"], ["X"], ["X"], ["X"], ["X"], [7, 2]]' | |
✅ Passed: [30, 60, 90, 117, 136, 145] |
This file contains 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
#!/usr/bin/env python | |
# encoding: utf-8 | |
import sys | |
import os | |
import subprocess | |
import json | |
# replace this with the name of your program. | |
exe = "./bowling.py" | |
for test_fname in sorted(os.listdir("tests")): | |
# parse out the test case JSON: | |
j = json.load(open("tests/%s" % test_fname)) | |
frames = j["frames"] | |
expected_scores = j["scores"] | |
# feed this test case to the scoring program: | |
exe_input = json.dumps(frames) | |
sys.stdout.write("\n") | |
sys.stdout.write("👉 Test case: %s\n" % test_fname) | |
sys.stdout.write(" 👉 Running %s '%s'\n" % (exe, exe_input)) | |
sys.stdout.flush() | |
output = subprocess.check_output([exe, exe_input]).rstrip() | |
try: | |
computed_scores = json.loads(output) | |
except Exception as e: | |
sys.stderr.write(" ❌ Failed to decode output JSON: '%s'\n" % output) | |
raise e | |
# compare the computed scores to the actual scores: | |
if expected_scores == computed_scores: | |
sys.stdout.write(" ✅ Passed: %s\n" % computed_scores) | |
else: | |
sys.stderr.write(" ❌ Failed: %s\n" % test_fname) | |
sys.stderr.write(" expected output: %s\n" % expected_scores) | |
sys.stderr.write(" actual output: %s\n" % computed_scores) | |
sys.exit(1) |
This file contains 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
{ | |
"frames": [["X"],["X"],["X"],["X"],["X"],["X"],["X"],["X"],["X"],["X","X","X"]], | |
"scores": [30,60,90,120,150,180,210,240,270,300] | |
} |
This file contains 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
{ | |
"frames": [[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]], | |
"scores": [0,0,0,0,0,0,0,0,0,0] | |
} |
This file contains 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
{ | |
"frames": [[4,"/"],[6,2],[4,2],[8,1],["X"],[6,2],[7,2],[9,0],[0,5],[6,3]], | |
"scores": [16,24,30,39,57,65,74,83,88,97], | |
"comment": "https://ncna-region.unl.edu/regional_2013_final.pdf" | |
} |
This file contains 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
{ | |
"frames": [[4,"/"],[6,2],[4,2],[8,1],["X"],[6,2],[7,2],[9,0],[0,5],[6,3]], | |
"scores": [16,24,30,39,57,65,74,83,88,97], | |
"comment": "https://ncna-region.unl.edu/regional_2013_final.pdf" | |
} |
This file contains 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
{ | |
"frames": [[4,"/"],[6,2],[4,2],[8,1],["X"],["X"],[7,2],[9,0],[0,5],[6,3]], | |
"scores": [16,24,30,39,66,85,94,103,108,117], | |
"comment": "https://ncna-region.unl.edu/regional_2013_final.pdf" | |
} |
This file contains 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
{ | |
"frames": [[4,"/"],[6,2],[4,2],[8,1],["X"],["X"],[7,2],[9,0],[0,5],[6,"/",7]], | |
"scores": [16,24,30,39,66,85,94,103,108,125], | |
"comment": "https://ncna-region.unl.edu/regional_2013_final.pdf" | |
} |
This file contains 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
{ | |
"frames": [["X"],[3,6]], | |
"scores": [19,28], | |
"comment": "https://en.wikipedia.org/wiki/Ten-pin_bowling#Traditional_scoring" | |
} |
This file contains 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
{ | |
"frames": [["X"],["X"],[9,0]], | |
"scores": [29,48,57], | |
"comment": "https://en.wikipedia.org/wiki/Ten-pin_bowling#Traditional_scoring" | |
} |
This file contains 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
{ | |
"frames": [["X"],["X"],["X"],[8,"/"],[8,0]], | |
"scores": [30,58,78,96,104], | |
"comment": "https://en.wikipedia.org/wiki/Ten-pin_bowling#Traditional_scoring" | |
} |
This file contains 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
{ | |
"frames": [["X"],["X"],["X"],["X"],["X"],[7,2]], | |
"scores": [30,60,90,117,136,145], | |
"comment": "https://en.wikipedia.org/wiki/Ten-pin_bowling#Traditional_scoring" | |
} |
Note: you can re-use test.py
and the test cases for your own implementation. Just edit the line which says exe = './bowling.py'
. Design your implementation to accept a single command-line argument which is a JSON string describing the frames, and output a JSON string which describes the (running total) frame scores.
Note that test.py
actually expects the test JSON files to be in a directory called tests
.
To conveniently TDD your implementation, run this in a terminal: while true; do sleep 1; clear; ./test.py; done
. Each time you save your source file, check the terminal to see if the tests passed.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
oops, I accidentally made
tests_ncna2013-example1.json
andtests_ncna2013-example2.json
the same.tests_ncna2013-example1.json
should instead be: