From 58ef1d28174df767e929e52592af372a91864cbb Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Mon, 6 Feb 2017 00:35:39 -0800 Subject: [PATCH 1/6] bowling: be more explicit about when errors should happen The JSON file currently says "When to error is also left up to your implementation", but we should instead be explicit. Let's say I `roll 5`, then `roll 6`, then `roll 0` 18 times. Okay, that's 20 rolls, that's a full game, so I can take the score now, right? Now I try to take the score and find that I get an error. Argh! In the absolute best case, the error result from score would be so kind to tell me that my `roll 6` way back there was invalid, but in the worst case it might not, and I would be left to my own devices to figure out what happened, since there was no feedback on any of the individual rolls. This commit proposes that invalid rolls be made explicit right when the roll is made, not at some unspecified time in the future (when attempting to take the score of the game). This is done via a `roll_should_fail` key that is mutually exclusive with the `expected` key. Previous discussions I know of (since I was involved): * https://github.com/exercism/xrust/pull/213#issuecomment-250988227 * https://github.com/exercism/xhaskell/issues/440 --- exercises/bowling/canonical-data.json | 56 +++++++++++++++------------ 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/exercises/bowling/canonical-data.json b/exercises/bowling/canonical-data.json index 32dfd23379..6f14f33041 100644 --- a/exercises/bowling/canonical-data.json +++ b/exercises/bowling/canonical-data.json @@ -5,13 +5,21 @@ "Score should return the game's final score, when possible", "For brevity the tests display all the rolls in an array;", "each element of the rolls array should be passed to the roll method", - "The final tests define situations where the score can not be returned", - "The expection for these tests is -1, indicating an error", - "In these cases you should expect an error as is idiomatic for your language", - "When to error is also left up to your implementation. There are two options", - " - Error as soon as an invalid roll is made", - " - Error when scoring a game with an invalid roll", - "You can also error in both cases." + "", + "If the `roll_should_fail` key is present", + "Then after rolling the values in the `roll` key,", + "attempting to roll the value in the `roll_should_fail` key should produce an error.", + "", + "If the `expected` key is present and non-negative", + "all rolls should succeed, and the final score should equal that value.", + "", + "If the `expected` key is present and negative,", + "all rolls should succeed, but attempting to score the game should result in an error.", + "", + "Each case will have exactly one of `roll_should_fail` or `expected`.", + "", + "In all error cases you should expect an error as is idiomatic for your language", + "whether that be via exceptions, optional values, or otherwise." ], "score": { "description": [ @@ -75,36 +83,36 @@ "expected": 300 }, { "description": "rolls can not score negative points", - "rolls": [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - "expected": -1 + "rolls": [], + "roll_should_fail": -1 }, { "description": "a roll can not score more than 10 points", - "rolls": [11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - "expected": -1 + "rolls": [], + "roll_should_fail": 11 }, { "description": "two rolls in a frame can not score more than 10 points", - "rolls": [5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - "expected": -1 + "rolls": [5], + "roll_should_fail": 6 }, { "description": "bonus roll after a strike in the last frame can not score more than 10 points", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 11], - "expected": -1 + "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10], + "roll_should_fail": 11 }, { "description": "two bonus rolls after a strike in the last frame can not score more than 10 points", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 5, 6], - "expected": -1 + "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 5], + "roll_should_fail": 6 }, { "description": "two bonus rolls after a strike in the last frame can score more than 10 points if one is a strike", "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 6], "expected": 26 }, { "description": "the second bonus rolls after a strike in the last frame can not be a strike if the first one is not a strike", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 6, 10], - "expected": -1 + "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 6], + "roll_should_fail": 10 }, { "description": "second bonus roll after a strike in the last frame can not score than 10 points", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 11], - "expected": -1 + "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10], + "roll_should_fail": 11 }, { "description": "an unstarted game can not be scored", "rolls": [], @@ -114,9 +122,9 @@ "rolls": [0, 0], "expected": -1 }, { - "description": "a game with more than ten frames can not be scored", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - "expected": -1 + "description": "cannot roll if game already has ten frames", + "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "roll_should_fail": 0 }, { "description": "bonus rolls for a strike in the last frame must be rolled before score can be calculated", "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10], From 35df72bf1a238ab76a19d6120f450db15c01a6f6 Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Mon, 20 Feb 2017 19:04:18 -0800 Subject: [PATCH 2/6] bowling: Use error object defined in #401 --- exercises/bowling/canonical-data.json | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/exercises/bowling/canonical-data.json b/exercises/bowling/canonical-data.json index 6f14f33041..8c0d05f730 100644 --- a/exercises/bowling/canonical-data.json +++ b/exercises/bowling/canonical-data.json @@ -10,11 +10,9 @@ "Then after rolling the values in the `roll` key,", "attempting to roll the value in the `roll_should_fail` key should produce an error.", "", - "If the `expected` key is present and non-negative", - "all rolls should succeed, and the final score should equal that value.", - "", - "If the `expected` key is present and negative,", - "all rolls should succeed, but attempting to score the game should result in an error.", + "If the `expected` key is present, it will either be an integer or an error object.", + "If it is an integer, all rolls should succeed, and the final score should equal that value.", + "If it is an error object, all rolls should succeed, but attempting to score the game should result in an error.", "", "Each case will have exactly one of `roll_should_fail` or `expected`.", "", @@ -116,11 +114,11 @@ }, { "description": "an unstarted game can not be scored", "rolls": [], - "expected": -1 + "expected": {"error": "Score cannot be taken until the end of the game"} }, { "description": "an incomplete game can not be scored", "rolls": [0, 0], - "expected": -1 + "expected": {"error": "Score cannot be taken until the end of the game"} }, { "description": "cannot roll if game already has ten frames", "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], @@ -128,15 +126,15 @@ }, { "description": "bonus rolls for a strike in the last frame must be rolled before score can be calculated", "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10], - "expected": -1 + "expected": {"error": "Score cannot be taken until the end of the game"} }, { "description": "both bonus rolls for a strike in the last frame must be rolled before score can be calculated", "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10], - "expected": -1 + "expected": {"error": "Score cannot be taken until the end of the game"} }, { "description": "bonus roll for a spare in the last frame must be rolled before score can be calculated", "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3], - "expected": -1 + "expected": {"error": "Score cannot be taken until the end of the game"} }] } } From 5fc46084410c90b998c2a6f74e551cbc37cd0f3f Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Tue, 21 Feb 2017 21:34:46 -0800 Subject: [PATCH 3/6] bowling: unindent cases `git diff -w` on this commit should be mostly clean. In preparation for new schema. --- exercises/bowling/canonical-data.json | 231 +++++++++++++------------- 1 file changed, 113 insertions(+), 118 deletions(-) diff --git a/exercises/bowling/canonical-data.json b/exercises/bowling/canonical-data.json index 8c0d05f730..138f9ebe48 100644 --- a/exercises/bowling/canonical-data.json +++ b/exercises/bowling/canonical-data.json @@ -19,122 +19,117 @@ "In all error cases you should expect an error as is idiomatic for your language", "whether that be via exceptions, optional values, or otherwise." ], - "score": { - "description": [ - "returns the final score of a bowling game" - ], - "cases": [{ - "description": "should be able to score a game with all zeros", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - "expected": 0 - }, { - "description": "should be able to score a game with no strikes or spares", - "rolls": [3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6], - "expected": 90 - }, { - "description": "a spare followed by zeros is worth ten points", - "rolls": [6, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - "expected": 10 - }, { - "description": "points scored in the roll after a spare are counted twice", - "rolls": [6, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - "expected": 16 - }, { - "description": "consecutive spares each get a one roll bonus", - "rolls": [5, 5, 3, 7, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - "expected": 31 - }, { - "description": "a spare in the last frame gets a one roll bonus that is counted once", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, 7], - "expected": 17 - }, { - "description": "a strike earns ten points in a frame with a single roll", - "rolls": [10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - "expected": 10 - }, { - "description": "points scored in the two rolls after a strike are counted twice as a bonus", - "rolls": [10, 5, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - "expected": 26 - }, { - "description": "consecutive strikes each get the two roll bonus", - "rolls": [10, 10, 10, 5, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - "expected": 81 - }, { - "description": "a strike in the last frame gets a two roll bonus that is counted once", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 7, 1], - "expected": 18 - }, { - "description": "rolling a spare with the two roll bonus does not get a bonus roll", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 7, 3], - "expected": 20 - }, { - "description": "strikes with the two roll bonus do not get bonus rolls", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10], - "expected": 30 - }, { - "description": "a strike with the one roll bonus after a spare in the last frame does not get a bonus", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, 10], - "expected": 20 - }, { - "description": "all strikes is a perfect game", - "rolls": [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10], - "expected": 300 - }, { - "description": "rolls can not score negative points", - "rolls": [], - "roll_should_fail": -1 - }, { - "description": "a roll can not score more than 10 points", - "rolls": [], - "roll_should_fail": 11 - }, { - "description": "two rolls in a frame can not score more than 10 points", - "rolls": [5], - "roll_should_fail": 6 - }, { - "description": "bonus roll after a strike in the last frame can not score more than 10 points", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10], - "roll_should_fail": 11 - }, { - "description": "two bonus rolls after a strike in the last frame can not score more than 10 points", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 5], - "roll_should_fail": 6 - }, { - "description": "two bonus rolls after a strike in the last frame can score more than 10 points if one is a strike", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 6], - "expected": 26 - }, { - "description": "the second bonus rolls after a strike in the last frame can not be a strike if the first one is not a strike", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 6], - "roll_should_fail": 10 - }, { - "description": "second bonus roll after a strike in the last frame can not score than 10 points", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10], - "roll_should_fail": 11 - }, { - "description": "an unstarted game can not be scored", - "rolls": [], - "expected": {"error": "Score cannot be taken until the end of the game"} - }, { - "description": "an incomplete game can not be scored", - "rolls": [0, 0], - "expected": {"error": "Score cannot be taken until the end of the game"} - }, { - "description": "cannot roll if game already has ten frames", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - "roll_should_fail": 0 - }, { - "description": "bonus rolls for a strike in the last frame must be rolled before score can be calculated", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10], - "expected": {"error": "Score cannot be taken until the end of the game"} - }, { - "description": "both bonus rolls for a strike in the last frame must be rolled before score can be calculated", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10], - "expected": {"error": "Score cannot be taken until the end of the game"} - }, { - "description": "bonus roll for a spare in the last frame must be rolled before score can be calculated", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3], - "expected": {"error": "Score cannot be taken until the end of the game"} - }] - } + "cases": [{ + "description": "should be able to score a game with all zeros", + "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "expected": 0 + }, { + "description": "should be able to score a game with no strikes or spares", + "rolls": [3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6], + "expected": 90 + }, { + "description": "a spare followed by zeros is worth ten points", + "rolls": [6, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "expected": 10 + }, { + "description": "points scored in the roll after a spare are counted twice", + "rolls": [6, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "expected": 16 + }, { + "description": "consecutive spares each get a one roll bonus", + "rolls": [5, 5, 3, 7, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "expected": 31 + }, { + "description": "a spare in the last frame gets a one roll bonus that is counted once", + "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, 7], + "expected": 17 + }, { + "description": "a strike earns ten points in a frame with a single roll", + "rolls": [10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "expected": 10 + }, { + "description": "points scored in the two rolls after a strike are counted twice as a bonus", + "rolls": [10, 5, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "expected": 26 + }, { + "description": "consecutive strikes each get the two roll bonus", + "rolls": [10, 10, 10, 5, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "expected": 81 + }, { + "description": "a strike in the last frame gets a two roll bonus that is counted once", + "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 7, 1], + "expected": 18 + }, { + "description": "rolling a spare with the two roll bonus does not get a bonus roll", + "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 7, 3], + "expected": 20 + }, { + "description": "strikes with the two roll bonus do not get bonus rolls", + "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10], + "expected": 30 + }, { + "description": "a strike with the one roll bonus after a spare in the last frame does not get a bonus", + "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, 10], + "expected": 20 + }, { + "description": "all strikes is a perfect game", + "rolls": [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10], + "expected": 300 + }, { + "description": "rolls can not score negative points", + "rolls": [], + "roll_should_fail": -1 + }, { + "description": "a roll can not score more than 10 points", + "rolls": [], + "roll_should_fail": 11 + }, { + "description": "two rolls in a frame can not score more than 10 points", + "rolls": [5], + "roll_should_fail": 6 + }, { + "description": "bonus roll after a strike in the last frame can not score more than 10 points", + "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10], + "roll_should_fail": 11 + }, { + "description": "two bonus rolls after a strike in the last frame can not score more than 10 points", + "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 5], + "roll_should_fail": 6 + }, { + "description": "two bonus rolls after a strike in the last frame can score more than 10 points if one is a strike", + "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 6], + "expected": 26 + }, { + "description": "the second bonus rolls after a strike in the last frame can not be a strike if the first one is not a strike", + "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 6], + "roll_should_fail": 10 + }, { + "description": "second bonus roll after a strike in the last frame can not score than 10 points", + "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10], + "roll_should_fail": 11 + }, { + "description": "an unstarted game can not be scored", + "rolls": [], + "expected": {"error": "Score cannot be taken until the end of the game"} + }, { + "description": "an incomplete game can not be scored", + "rolls": [0, 0], + "expected": {"error": "Score cannot be taken until the end of the game"} + }, { + "description": "cannot roll if game already has ten frames", + "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "roll_should_fail": 0 + }, { + "description": "bonus rolls for a strike in the last frame must be rolled before score can be calculated", + "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10], + "expected": {"error": "Score cannot be taken until the end of the game"} + }, { + "description": "both bonus rolls for a strike in the last frame must be rolled before score can be calculated", + "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10], + "expected": {"error": "Score cannot be taken until the end of the game"} + }, { + "description": "bonus roll for a spare in the last frame must be rolled before score can be calculated", + "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3], + "expected": {"error": "Score cannot be taken until the end of the game"} + }] } From 1122fafaad1ab7e22a6c964a47ba26753ffe173b Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Tue, 21 Feb 2017 21:37:56 -0800 Subject: [PATCH 4/6] bowling: rename rolls to previous_rolls This makes clearer that the rolls are assumed to happen prior to the test at hand. --- exercises/bowling/canonical-data.json | 61 ++++++++++++++------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/exercises/bowling/canonical-data.json b/exercises/bowling/canonical-data.json index 138f9ebe48..2c72621d8e 100644 --- a/exercises/bowling/canonical-data.json +++ b/exercises/bowling/canonical-data.json @@ -3,8 +3,9 @@ "Students should implement roll and score methods.", "Roll should accept a single integer.", "Score should return the game's final score, when possible", - "For brevity the tests display all the rolls in an array;", - "each element of the rolls array should be passed to the roll method", + "For brevity the tests display all the previous rolls in an array;", + "each element of the previous_rolls array should be passed to the roll method", + "and each of those previous rolls is expected to succeed.", "", "If the `roll_should_fail` key is present", "Then after rolling the values in the `roll` key,", @@ -21,115 +22,115 @@ ], "cases": [{ "description": "should be able to score a game with all zeros", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "expected": 0 }, { "description": "should be able to score a game with no strikes or spares", - "rolls": [3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6], + "previous_rolls": [3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6], "expected": 90 }, { "description": "a spare followed by zeros is worth ten points", - "rolls": [6, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "previous_rolls": [6, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "expected": 10 }, { "description": "points scored in the roll after a spare are counted twice", - "rolls": [6, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "previous_rolls": [6, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "expected": 16 }, { "description": "consecutive spares each get a one roll bonus", - "rolls": [5, 5, 3, 7, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "previous_rolls": [5, 5, 3, 7, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "expected": 31 }, { "description": "a spare in the last frame gets a one roll bonus that is counted once", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, 7], + "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, 7], "expected": 17 }, { "description": "a strike earns ten points in a frame with a single roll", - "rolls": [10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "previous_rolls": [10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "expected": 10 }, { "description": "points scored in the two rolls after a strike are counted twice as a bonus", - "rolls": [10, 5, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "previous_rolls": [10, 5, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "expected": 26 }, { "description": "consecutive strikes each get the two roll bonus", - "rolls": [10, 10, 10, 5, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "previous_rolls": [10, 10, 10, 5, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "expected": 81 }, { "description": "a strike in the last frame gets a two roll bonus that is counted once", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 7, 1], + "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 7, 1], "expected": 18 }, { "description": "rolling a spare with the two roll bonus does not get a bonus roll", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 7, 3], + "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 7, 3], "expected": 20 }, { "description": "strikes with the two roll bonus do not get bonus rolls", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10], + "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10], "expected": 30 }, { "description": "a strike with the one roll bonus after a spare in the last frame does not get a bonus", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, 10], + "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, 10], "expected": 20 }, { "description": "all strikes is a perfect game", - "rolls": [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10], + "previous_rolls": [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10], "expected": 300 }, { "description": "rolls can not score negative points", - "rolls": [], + "previous_rolls": [], "roll_should_fail": -1 }, { "description": "a roll can not score more than 10 points", - "rolls": [], + "previous_rolls": [], "roll_should_fail": 11 }, { "description": "two rolls in a frame can not score more than 10 points", - "rolls": [5], + "previous_rolls": [5], "roll_should_fail": 6 }, { "description": "bonus roll after a strike in the last frame can not score more than 10 points", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10], + "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10], "roll_should_fail": 11 }, { "description": "two bonus rolls after a strike in the last frame can not score more than 10 points", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 5], + "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 5], "roll_should_fail": 6 }, { "description": "two bonus rolls after a strike in the last frame can score more than 10 points if one is a strike", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 6], + "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 6], "expected": 26 }, { "description": "the second bonus rolls after a strike in the last frame can not be a strike if the first one is not a strike", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 6], + "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 6], "roll_should_fail": 10 }, { "description": "second bonus roll after a strike in the last frame can not score than 10 points", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10], + "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10], "roll_should_fail": 11 }, { "description": "an unstarted game can not be scored", - "rolls": [], + "previous_rolls": [], "expected": {"error": "Score cannot be taken until the end of the game"} }, { "description": "an incomplete game can not be scored", - "rolls": [0, 0], + "previous_rolls": [0, 0], "expected": {"error": "Score cannot be taken until the end of the game"} }, { "description": "cannot roll if game already has ten frames", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "roll_should_fail": 0 }, { "description": "bonus rolls for a strike in the last frame must be rolled before score can be calculated", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10], + "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10], "expected": {"error": "Score cannot be taken until the end of the game"} }, { "description": "both bonus rolls for a strike in the last frame must be rolled before score can be calculated", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10], + "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10], "expected": {"error": "Score cannot be taken until the end of the game"} }, { "description": "bonus roll for a spare in the last frame must be rolled before score can be calculated", - "rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3], + "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3], "expected": {"error": "Score cannot be taken until the end of the game"} }] } From 8ddbd7ff6eb7d768596e5ecac585adfc9398009e Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Tue, 21 Feb 2017 21:49:17 -0800 Subject: [PATCH 5/6] bowling: Add score and roll properties The transformation is simple: Any case with a "roll_should_fail" turns into a "property": "roll" case with an appropriate error. All other cases turn into a "property": "score" case. --- exercises/bowling/canonical-data.json | 64 ++++++++++++++++++++------- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/exercises/bowling/canonical-data.json b/exercises/bowling/canonical-data.json index 2c72621d8e..cd51eaaa64 100644 --- a/exercises/bowling/canonical-data.json +++ b/exercises/bowling/canonical-data.json @@ -7,129 +7,163 @@ "each element of the previous_rolls array should be passed to the roll method", "and each of those previous rolls is expected to succeed.", "", - "If the `roll_should_fail` key is present", - "Then after rolling the values in the `roll` key,", - "attempting to roll the value in the `roll_should_fail` key should produce an error.", + "Two properties are tested:", "", - "If the `expected` key is present, it will either be an integer or an error object.", - "If it is an integer, all rolls should succeed, and the final score should equal that value.", - "If it is an error object, all rolls should succeed, but attempting to score the game should result in an error.", + "`score`: All rolls succeed, and taking the score gives the expected result.", + "The expected result may be an integer score or an error.", "", - "Each case will have exactly one of `roll_should_fail` or `expected`.", + "`roll`: All previous_rolls succeed, and rolling the number of pins in `roll` produces the expected result.", + "Currently, all cases of this type result in errors.", "", "In all error cases you should expect an error as is idiomatic for your language", "whether that be via exceptions, optional values, or otherwise." ], "cases": [{ "description": "should be able to score a game with all zeros", + "property": "score", "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "expected": 0 }, { "description": "should be able to score a game with no strikes or spares", + "property": "score", "previous_rolls": [3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6], "expected": 90 }, { "description": "a spare followed by zeros is worth ten points", + "property": "score", "previous_rolls": [6, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "expected": 10 }, { "description": "points scored in the roll after a spare are counted twice", + "property": "score", "previous_rolls": [6, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "expected": 16 }, { "description": "consecutive spares each get a one roll bonus", + "property": "score", "previous_rolls": [5, 5, 3, 7, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "expected": 31 }, { "description": "a spare in the last frame gets a one roll bonus that is counted once", + "property": "score", "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, 7], "expected": 17 }, { "description": "a strike earns ten points in a frame with a single roll", + "property": "score", "previous_rolls": [10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "expected": 10 }, { "description": "points scored in the two rolls after a strike are counted twice as a bonus", + "property": "score", "previous_rolls": [10, 5, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "expected": 26 }, { "description": "consecutive strikes each get the two roll bonus", + "property": "score", "previous_rolls": [10, 10, 10, 5, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "expected": 81 }, { "description": "a strike in the last frame gets a two roll bonus that is counted once", + "property": "score", "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 7, 1], "expected": 18 }, { "description": "rolling a spare with the two roll bonus does not get a bonus roll", + "property": "score", "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 7, 3], "expected": 20 }, { "description": "strikes with the two roll bonus do not get bonus rolls", + "property": "score", "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10], "expected": 30 }, { "description": "a strike with the one roll bonus after a spare in the last frame does not get a bonus", + "property": "score", "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, 10], "expected": 20 }, { "description": "all strikes is a perfect game", + "property": "score", "previous_rolls": [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10], "expected": 300 }, { "description": "rolls can not score negative points", + "property": "roll", "previous_rolls": [], - "roll_should_fail": -1 + "roll": -1, + "expected": {"error": "Negative roll is invalid"} }, { "description": "a roll can not score more than 10 points", + "property": "roll", "previous_rolls": [], - "roll_should_fail": 11 + "roll": 11, + "expected": {"error": "Pin count exceeds pins on the lane"} }, { "description": "two rolls in a frame can not score more than 10 points", + "property": "roll", "previous_rolls": [5], - "roll_should_fail": 6 + "roll": 6, + "expected": {"error": "Pin count exceeds pins on the lane"} }, { "description": "bonus roll after a strike in the last frame can not score more than 10 points", + "property": "roll", "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10], - "roll_should_fail": 11 + "roll": 11, + "expected": {"error": "Pin count exceeds pins on the lane"} }, { "description": "two bonus rolls after a strike in the last frame can not score more than 10 points", + "property": "roll", "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 5], - "roll_should_fail": 6 + "roll": 6, + "expected": {"error": "Pin count exceeds pins on the lane"} }, { "description": "two bonus rolls after a strike in the last frame can score more than 10 points if one is a strike", + "property": "score", "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 6], "expected": 26 }, { "description": "the second bonus rolls after a strike in the last frame can not be a strike if the first one is not a strike", + "property": "roll", "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 6], - "roll_should_fail": 10 + "roll": 10, + "expected": {"error": "Pin count exceeds pins on the lane"} }, { "description": "second bonus roll after a strike in the last frame can not score than 10 points", + "property": "roll", "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10], - "roll_should_fail": 11 + "roll": 11, + "expected": {"error": "Pin count exceeds pins on the lane"} }, { "description": "an unstarted game can not be scored", + "property": "score", "previous_rolls": [], "expected": {"error": "Score cannot be taken until the end of the game"} }, { "description": "an incomplete game can not be scored", + "property": "score", "previous_rolls": [0, 0], "expected": {"error": "Score cannot be taken until the end of the game"} }, { "description": "cannot roll if game already has ten frames", + "property": "roll", "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - "roll_should_fail": 0 + "roll": 0, + "expected": {"error": "Cannot roll after game is over"} }, { "description": "bonus rolls for a strike in the last frame must be rolled before score can be calculated", + "property": "score", "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10], "expected": {"error": "Score cannot be taken until the end of the game"} }, { "description": "both bonus rolls for a strike in the last frame must be rolled before score can be calculated", + "property": "score", "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10], "expected": {"error": "Score cannot be taken until the end of the game"} }, { "description": "bonus roll for a spare in the last frame must be rolled before score can be calculated", + "property": "score", "previous_rolls": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3], "expected": {"error": "Score cannot be taken until the end of the game"} }] From a3464972e3c1be2ec73b97f925dfcfb3f7cfb471 Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Tue, 21 Feb 2017 21:52:33 -0800 Subject: [PATCH 6/6] bowling: Add exercise and version; rename # to comments --- exercises/bowling/canonical-data.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/exercises/bowling/canonical-data.json b/exercises/bowling/canonical-data.json index cd51eaaa64..4bad8685a7 100644 --- a/exercises/bowling/canonical-data.json +++ b/exercises/bowling/canonical-data.json @@ -1,5 +1,7 @@ { - "#": [ + "exercise": "bowling", + "version": "1", + "comments": [ "Students should implement roll and score methods.", "Roll should accept a single integer.", "Score should return the game's final score, when possible",