Skip to content

Instantly share code, notes, and snippets.

@laracasts
Created January 3, 2020 16:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save laracasts/70d59758574c6828e743e23fe9eb15f0 to your computer and use it in GitHub Desktop.
Save laracasts/70d59758574c6828e743e23fe9eb15f0 to your computer and use it in GitHub Desktop.
Bowling Game Kata
<?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];
}
}
<?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