package bowling.acceptance; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import static org.junit.Assert.fail; import org.junit.Test; /** * Sub-class this and override the protected methods to make it work for your implementation */ public abstract class SingleGameScoring { // 2.1.1 A game of tenpins consists of ten frames. A player delivers two balls in each of the first // nine frames unless a strike is scored. In the tenth frame, a player delivers three balls if a // strike or spare is scored. Every frame must be completed by each player bowling in regular // order. // We'll come back to the three frames thing because we don't know what a strike is yet // and we only support one player for now @Test public void aGameOfTenPinsConsistsOfTenFrames() { assertThat(numberOfFrames(), is(10)); } @Test public void aPlayerDeliversTwoBallsInEachFrame() { for(int frame = 0; frame < 10; frame++) { deliverBall(0); deliverBall(0); } assertThat(gameOver(), is(true)); } @Test(expected=IllegalArgumentException.class) public void shouldThrowAnExceptionIfTooManyPins() { deliverBall(11); } @Test(expected=IllegalArgumentException.class) public void shouldThrowAnExceptionIfPinCountIsNegative() { deliverBall(-1); } // 2.1.2 Except when a strike is scored, the number of pins knocked down by the player’s first delivery // is to be marked in the small square in the upper left-hand corner of that frame, and // the number of pins knocked down by the player’s second delivery is to be marked in the // upper right-hand corner. If none of the standing pins are knocked down by the second delivery // in the frame, the score sheet shall be marked with a (-). The count for the two deliveries // in the frame shall be recorded immediately. @Test public void theNumberofPinsKnockedDownByTheFirstDeliveryIsToBeMarkedInTheUpperLeftCorner() { deliverBall(3); assertThat(firstBallInFrame(1), is("3")); } @Test public void theNumberofPinsKnockedDownByTheSecondDeliveryIsToBeMarkedInTheUpperRightCorner() { deliverBall(3); deliverBall(4); assertThat(secondBallInFrame(1), is("4")); } @Test public void ifNoPinsAreKnockedDownByFirstBallMarkWithDash() { // The rules don't say what to do for the first gutter ball, but I am guessing it's a dash deliverBall(0); assertThat(firstBallInFrame(1), is("-")); } @Test public void ifNoPinsAreKnockedDownBySecondBallMarkWithDash() { deliverBall(3); deliverBall(0); assertThat(secondBallInFrame(1), is("-")); } @Test public void theCountForTheTwoDeliveriesShallBeShownImmediately() { deliverBall(3); deliverBall(6); assertThat(scoreForFrame(1), is("9")); } /// ...and conversely... @Test public void theCountShouldNotBeShownUntilTheFrameIsComplete() { deliverBall(3); assertThat(scoreForFrame(1), is("")); } // the rules don't say to accumulate the score for each frame, but the diagram does @Test public void everyFrameShouldShowTheCumulativeScore() { deliverBall(3); deliverBall(6); deliverBall(1); deliverBall(2); assertThat(scoreForFrame(1), is("9")); assertThat(scoreForFrame(2), is("12")); } // We should probably show the running total too @Test public void updateTheGameScoreAfterEveryDelivery() { deliverBall(3); assertThat(scoreForGame(), is("3")); deliverBall(6); assertThat(scoreForGame(), is("9")); deliverBall(1); assertThat(scoreForGame(), is("10")); } // still don't know what a strike is... // 2.1.3 A strike is made when a full setup of pins is knocked down with the first delivery in a // frame. It is marked by an (X) in the small square in the upper left-hand corner of the frame // where it was made. The count for one strike is 10 plus the number of pins knocked down // on the player’s next two deliveries. // ...ah - strikes. We have some catching up to do @Test public void aStrikeIsTenPinsDownWithTheFirstBall() { deliverBall(10); assertThat(firstBallInFrame(1), is("X")); } @Test public void aStrikeScoresTenPlusTheNextTwoDeliveries() { deliverBall(10); deliverBall(6); deliverBall(3); assertThat(scoreForFrame(1), is("19")); } // the rules don't say where to put the other pins but we can clarify with a test @Test public void thereIsOnlyOneBallInStrikeFrames() { deliverBall(10); deliverBall(6); deliverBall(3); assertThat(firstBallInFrame(1), is("X")); assertThat(secondBallInFrame(1), is("")); assertThat(firstBallInFrame(2), is("6")); assertThat(secondBallInFrame(2), is("3")); } // the rules don't say when to update the frame score after a strike but we can clarify with a test @Test public void dontUpdateFrameScoreUntilAfterAdditionalDeliveries() { deliverBall(10); assertThat(scoreForFrame(1), is("")); deliverBall(6); deliverBall(3); assertThat(scoreForFrame(1), is("19")); } // 2.1.4 Two consecutive strikes is a double. The count for the first strike is 20 plus the number of // pins knocked down with the first delivery following the second strike. // No new requirements here, but it provides an additional test @Test public void twoConsecutiveStrikesIsADouble() { deliverBall(10); deliverBall(10); deliverBall(3); assertThat(scoreForFrame(1), is("23")); } // 2.1.5 Three successive strikes is a triple. The count for the first strike is 30. To bowl the maximum // score of 300, the player must bowl 12 strikes in succession. // Also not a new requirement // If I had David Saff's Popper, I'd write an @Theory for the maximum score @Test public void threeSuccessiveStrikesIsATriple() { deliverBall(10); deliverBall(10); deliverBall(10); assertThat(scoreForFrame(1), is("30")); } @Test public void twelveStrikesScoresThreeHundred() { for(int frame = 0; frame < 12; frame++) { deliverBall(10); } assertThat(scoreForFrame(10), is("300")); } // 2.1.6 A spare is scored when pins left standing after the first delivery are knocked down with the // second delivery in that frame. It is marked by a (/) in the small square in the upper righthand // corner of the frame. The count for a spare is 10 plus the number of pins knocked // down by the player’s next delivery.} @Test public void aSpareIsScoredWhenRemainingPinsAreKnockedDownWithTheSecondBall() { deliverBall(7); deliverBall(3); assertThat(firstBallInFrame(1), is("7")); assertThat(secondBallInFrame(1), is("/")); } @Test public void aSpareScoresTenPlusTheNextDelivery() { deliverBall(7); deliverBall(3); deliverBall(4); assertThat(scoreForFrame(1), is("14")); } // It's time to revisit the last frame special cases // 2.1.1 In the tenth frame, a player delivers three balls if a // strike or spare is scored. @Test public void tenthFrameHasThreeBallsIfStrikeIsScored() { for(int frame = 0; frame < 10; frame++) { deliverBall(10); } deliverBall(4); deliverBall(3); assertThat(firstBallInFrame(10), is("X")); assertThat(secondBallInFrame(10), is("4")); assertThat(thirdBallInFrame(10), is("3")); } protected int numberOfFrames() { notImplementedYet("numberOfFrames"); return -1; } protected boolean gameOver() { notImplementedYet("gameOver"); return false; } protected void deliverBall(int i) { notImplementedYet("deliverBall"); } protected String firstBallInFrame(int frameIndex) { notImplementedYet("firstBallInFrame"); return null; } protected String secondBallInFrame(int frameIndex) { notImplementedYet("secondBallInFrame"); return null; } protected String thirdBallInFrame(int frameIndex) { notImplementedYet("thirdBallInFrame"); return null; } protected String scoreForFrame(int frameIndex) { notImplementedYet("scoreForFrame"); return null; } protected String scoreForGame() { notImplementedYet("scoreForGame"); return null; } protected void notImplementedYet(String feature) { fail(feature + " is not implemented yet"); } }