Created
January 3, 2020 16:10
-
-
Save laracasts/70d59758574c6828e743e23fe9eb15f0 to your computer and use it in GitHub Desktop.
Bowling Game Kata
This file contains hidden or 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
<?php | |
namespace App; | |
class Game | |
{ | |
/** | |
* The number of frames in a game. | |
*/ | |
const FRAMES_PER_GAME = 10; | |
/** | |
* All rolls for the game. | |
* | |
* @var array | |
*/ | |
protected array $rolls = []; | |
/** | |
* Roll the ball. | |
* | |
* @param int $pins | |
* @return void | |
*/ | |
public function roll(int $pins): void | |
{ | |
$this->rolls[] = $pins; | |
} | |
/** | |
* Calculate the final score. | |
* | |
* @return int | |
*/ | |
public function score() | |
{ | |
$score = 0; | |
$roll = 0; | |
foreach (range(1, self::FRAMES_PER_GAME) as $frame) { | |
if ($this->isStrike($roll)) { | |
$score += $this->pinCount($roll) + $this->strikeBonus($roll); | |
$roll += 1; | |
continue; | |
} | |
$score += $this->defaultFrameScore($roll); | |
if ($this->isSpare($roll)) { | |
$score += $this->spareBonus($roll); | |
} | |
$roll += 2; | |
} | |
return $score; | |
} | |
/** | |
* Determine if the current roll was a strike. | |
* | |
* @param int $roll | |
* @return bool | |
*/ | |
protected function isStrike(int $roll): bool | |
{ | |
return $this->pinCount($roll) === 10; | |
} | |
/** | |
* Determine if the current frame was a spare. | |
* | |
* @param int $roll | |
* @return bool | |
*/ | |
protected function isSpare(int $roll): bool | |
{ | |
return $this->defaultFrameScore($roll) === 10; | |
} | |
/** | |
* Calculate the score for the frame. | |
* | |
* @param int $roll | |
* @return int | |
*/ | |
protected function defaultFrameScore(int $roll): int | |
{ | |
return $this->pinCount($roll) + $this->pinCount($roll + 1); | |
} | |
/** | |
* Get the bonus for a strike. | |
* | |
* @param int $roll | |
* @return int | |
*/ | |
protected function strikeBonus(int $roll): int | |
{ | |
return $this->pinCount($roll + 1) + $this->pinCount($roll + 2); | |
} | |
/** | |
* Get the bonus for a spare. | |
* | |
* @param int $roll | |
* @return int | |
*/ | |
protected function spareBonus(int $roll): int | |
{ | |
return $this->pinCount($roll + 2); | |
} | |
/** | |
* Get the number of pins knocked down for the given roll. | |
* | |
* @param int $roll | |
* @return int | |
*/ | |
protected function pinCount(int $roll): int | |
{ | |
return $this->rolls[$roll]; | |
} | |
} |
This file contains hidden or 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
<?php | |
use App\Game; | |
use PHPUnit\Framework\TestCase; | |
class GameTest extends TestCase | |
{ | |
/** @test */ | |
function it_scores_a_gutter_game_as_zero() | |
{ | |
$game = new Game(); | |
foreach (range(1, 20) as $roll) { | |
$game->roll(0); | |
} | |
$this->assertSame(0, $game->score()); | |
} | |
/** @test */ | |
function it_scores_all_ones() | |
{ | |
$game = new Game(); | |
foreach (range(1, 20) as $roll) { | |
$game->roll(1); | |
} | |
$this->assertSame(20, $game->score()); | |
} | |
/** @test */ | |
function it_awards_a_one_roll_bonus_for_every_spare() | |
{ | |
$game = new Game(); | |
$game->roll(5); | |
$game->roll(5); // spare | |
$game->roll(8); | |
foreach (range(1, 17) as $roll) { | |
$game->roll(0); | |
} | |
$this->assertSame(26, $game->score()); | |
} | |
/** @test */ | |
function it_awards_a_two_roll_bonus_for_every_strike() | |
{ | |
$game = new Game(); | |
$game->roll(10); // strike | |
$game->roll(5); | |
$game->roll(2); | |
foreach (range(1, 16) as $roll) { | |
$game->roll(0); | |
} | |
$this->assertSame(24, $game->score()); | |
} | |
/** @test */ | |
function a_spare_on_the_final_frame_grants_one_extra_ball() | |
{ | |
$game = new Game(); | |
foreach (range(1, 18) as $roll) { | |
$game->roll(0); | |
} | |
$game->roll(5); | |
$game->roll(5); // spare | |
$game->roll(5); | |
$this->assertSame(15, $game->score()); | |
} | |
/** @test */ | |
function a_strike_on_the_final_frame_grants_two_extra_balls() | |
{ | |
$game = new Game(); | |
foreach (range(1, 18) as $roll) { | |
$game->roll(0); | |
} | |
$game->roll(10); // strike | |
$game->roll(10); | |
$game->roll(10); | |
$this->assertSame(30, $game->score()); | |
} | |
/** @test */ | |
function it_scores_a_perfect_game() | |
{ | |
$game = new Game(); | |
foreach (range(1, 12) as $roll) { | |
$game->roll(10); | |
} | |
$this->assertSame(300, $game->score()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment