Skip to content

Update bowling test to match canonical_data.json, fixes #120 #122

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 19, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
243 changes: 233 additions & 10 deletions exercises/bowling/bowling_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,50 +16,273 @@ public function setUp()
$this->game = new Game();
}

public function testGutterGame()
public function testShouldBeAbleToScoreAGameWithAllZeros()
{
$this->rollMany(20, 0);

$this->assertEquals(0, $this->game->score());
}

public function testAllOnes()
public function testShouldBeAbleToScoreAGameWithNoStrikesOrSpares()
{
$this->markTestSkipped();
$this->rollMany(20, 1);
$this->game->roll(3);
$this->game->roll(6);
$this->game->roll(3);
$this->game->roll(6);
$this->game->roll(3);
$this->game->roll(6);
$this->game->roll(3);
$this->game->roll(6);
$this->game->roll(3);
$this->game->roll(6);
$this->game->roll(3);
$this->game->roll(6);
$this->game->roll(3);
$this->game->roll(6);
$this->game->roll(3);
$this->game->roll(6);
$this->game->roll(3);
$this->game->roll(6);
$this->game->roll(3);
$this->game->roll(6);

$this->assertEquals(20, $this->game->score());
$this->assertEquals(90, $this->game->score());
}

public function testASpareFollowedByZerosIsWorthTenPoints()
{
$this->markTestSkipped();
$this->game->roll(6);
$this->game->roll(4);
$this->rollMany(18, 0);

$this->assertEquals(10, $this->game->score());
}

public function testOneSpare()
public function testPointsScoredInTheRollAfterASpareAreCountedTwice()
{
$this->markTestSkipped();
$this->rollSpare();
$this->game->roll(6);
$this->game->roll(4);
$this->game->roll(3);
$this->rollMany(17, 0);

$this->assertEquals(16, $this->game->score());
}

public function testOneStrike()
public function testConsecutiveSparesEachGetAOneRollBonus()
{
$this->markTestSkipped();
$this->rollStrike();
$this->game->roll(5);
$this->game->roll(5);
$this->game->roll(3);
$this->game->roll(7);
$this->game->roll(4);
$this->rollMany(15, 0);

$this->assertEquals(31, $this->game->score());
}

public function testASpareInTheLastFrameGetsAOneRollBonusThatIsCountedOnce()
{
$this->markTestSkipped();
$this->rollMany(18, 0);
$this->game->roll(7);
$this->game->roll(3);
$this->game->roll(7);

$this->assertEquals(17, $this->game->score());
}

public function testAStrikeEarnsTenPointsInFrameWithASingleRoll()
{
$this->markTestSkipped();
$this->game->roll(10);
$this->rollMany(18, 0);

$this->assertEquals(10, $this->game->score());
}

public function testPointsScoredInTheTwoRollsAfterAStrikeAreCountedTwiceAsABonus()
{
$this->markTestSkipped();
$this->game->roll(10);
$this->game->roll(5);
$this->game->roll(3);
$this->rollMany(16, 0);

$this->assertEquals(24, $this->game->score());
$this->assertEquals(26, $this->game->score());
}

public function testConsecutiveStrikesEachGetTheTwoRollBonus()
{
$this->markTestSkipped();
$this->game->roll(10);
$this->game->roll(10);
$this->game->roll(10);
$this->game->roll(5);
$this->game->roll(3);
$this->rollMany(12, 0);

$this->assertEquals(81, $this->game->score());
}

public function testAStrikeInTheLastFrameGetsATwoRollBonusThatIsCountedOnce()
{
$this->markTestSkipped();
$this->rollMany(18, 0);
$this->game->roll(10);
$this->game->roll(7);
$this->game->roll(1);

$this->assertEquals(18, $this->game->score());
}

public function testRollingASpareWithTheTwoRollBonusDoesNotGetABonusRoll()
{
$this->markTestSkipped();
$this->rollMany(18, 0);
$this->game->roll(10);
$this->game->roll(10);
$this->game->roll(10);

$this->assertEquals(30, $this->game->score());
}

public function testAStrikeWithTheOneRollBonusAfterASpareInTheLastFrameDoesNotGetABonus()
{
$this->markTestSkipped();
$this->rollMany(18, 0);
$this->game->roll(7);
$this->game->roll(3);
$this->game->roll(10);

$this->assertEquals(20, $this->game->score());
}

public function testStrikesWithTheTwoRollBonusDoNotGetBonusRolls()
{
$this->markTestSkipped();
$this->rollMany(18, 0);
$this->game->roll(10);
$this->game->roll(7);
$this->game->roll(3);

$this->assertEquals(20, $this->game->score());
}

public function testPerfectGame()
public function testAllStrikesIsAPerfectGame()
{
$this->markTestSkipped();
$this->rollMany(12, 10);

$this->assertEquals(300, $this->game->score());
}

public function testRollsCanNotScoreNegativePoints()
{
$this->markTestSkipped();
$this->expectException(Exception::class);

$this->game->roll(-1);
}

public function testARollCanNotScoreMoreThan10Points()
{
$this->markTestSkipped();
$this->expectException(Exception::class);
$this->game->roll(11);
$this->rollMany(19, 0);

$this->game->score();
}

public function testTwoRollsInAFrameCanNotScoreMoreThan10Points()
{
$this->markTestSkipped();

$this->expectException(Exception::class);
$this->game->roll(5);
$this->game->roll(6);
$this->rollMany(18, 0);

$this->game->score();
}

public function testTwoBonusRollsAfterAStrikeInTheLastFrameCanNotScoreMoreThan10Points()
{
$this->markTestSkipped();
$this->expectException(Exception::class);

$this->rollMany(18, 0);
$this->game->roll(10);
$this->game->roll(5);
$this->game->roll(6);

$this->game->score();
}

public function testAnUnstartedGameCanNotBeScored()
{
$this->markTestSkipped();

$this->expectException(Exception::class);

$this->game->score();
}

public function testAnIncompleteGameCanNotBeScored()
{
$this->markTestSkipped();
$this->expectException(Exception::class);
$this->game->roll(0);
$this->game->roll(0);

$this->game->score();
}

public function testAGameWithMoreThanTenFramesCanNotBeScored()
{
$this->markTestSkipped();
$this->expectException(Exception::class);
$this->rollMany(21, 0);

$this->game->score();
}

public function testBonusRollsForAStrikeInTheLastFrameMustBeRolledBeforeScoreCanBeCalculated()
{
$this->markTestSkipped();
$this->expectException(Exception::class);
$this->rollMany(18, 0);
$this->game->roll(10);

$this->game->score();
}

public function testBothBonusRollsForAStrikeInTheLastFrameMustBeRolledBeforeScoreCanBeCalculated()
{
$this->markTestSkipped();
$this->expectException(Exception::class);
$this->rollMany(18, 0);
$this->game->roll(10);
$this->game->roll(10);

$this->game->score();
}

public function testBonusRollForASpareInTheLastFrameMustBeRolledBeforeScoreCanBeCalculated()
{
$this->markTestSkipped();
$this->expectException(Exception::class);
$this->rollMany(18, 0);
$this->game->roll(7);
$this->game->roll(3);

$this->game->score();
}

private function rollStrike()
{
$this->game->roll(10);
Expand Down
55 changes: 49 additions & 6 deletions exercises/bowling/example.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,22 @@
*/
class Game
{
private $rolls = array();
private $currentRoll = 0;
private $rolls = [];

public function roll($pins)
{
$this->rolls[$this->currentRoll++] = $pins;
if ($pins < 0 || $pins > 10) {
throw new Exception('Pins must be between 0 and 10');
}
$this->rolls[] = $pins;
}

public function score()
{
$score = 0;
$frameIndex = 0;
for ($frame = 0; $frame < 10; $frame++) {
foreach (range(1, 10) as $frame) {
$this->guardAgainstIncompleteGame($frameIndex);
if ($this->isStrike($frameIndex)) {
$score += 10 + $this->strikeBonus($frameIndex);
$frameIndex++;
Expand All @@ -30,6 +33,7 @@ public function score()
$frameIndex += 2;
}
}
$this->guardAgainstTooManyFrames($frameIndex);

return $score;
}
Expand All @@ -41,7 +45,16 @@ private function isStrike($frameIndex)

private function strikeBonus($frameIndex)
{
return $this->rolls[$frameIndex + 1] + $this->rolls[$frameIndex + 2];
if (!isset($this->rolls[$frameIndex + 1]) || !isset($this->rolls[$frameIndex + 2])) {
throw new Exception('Incomplete game');
}
$bonus1 = $this->rolls[$frameIndex + 1];
$bonus2 = $this->rolls[$frameIndex + 2];
$sum = $bonus1 + $bonus2;
if ($sum > 10 && $frameIndex = 10 && $bonus1 != 10) {
throw new Exception('Cannot score more than 10');
}
return $sum;
}

private function isSpare($frameIndex)
Expand All @@ -51,11 +64,41 @@ private function isSpare($frameIndex)

private function spareBonus($frameIndex)
{
if (!isset($this->rolls[$frameIndex + 2])) {
throw new Exception('Incomplete game');
}
return $this->rolls[$frameIndex + 2];
}

private function sumOfBallsInFrame($frameIndex)
{
return $this->rolls[$frameIndex] + $this->rolls[$frameIndex + 1];
$sum = $this->rolls[$frameIndex] + $this->rolls[$frameIndex + 1];
if ($sum > 10) {
throw new Exception('Cannot score more than ten in single frame');
}
return $sum;
}

private function guardAgainstIncompleteGame($frameIndex)
{
if (!isset($this->rolls[$frameIndex])) {
throw new Exception('Incomplete game');
}
}

private function guardAgainstTooManyFrames($frameIndex)
{
$rollsCount = count($this->rolls);
if ($this->isStrike($frameIndex - 1)) {
if ($rollsCount > $frameIndex + 2) {
throw new Exception('Too many frames in game');
}
} elseif ($this->isSpare($frameIndex - 2)) {
if ($rollsCount > $frameIndex + 1) {
throw new Exception('Too many frames in game');
}
} elseif ($rollsCount > $frameIndex) {
throw new Exception('Too many frames in game');
}
}
}