Skip to content

Instantly share code, notes, and snippets.

@laracasts
Created January 3, 2020 16:10
Show Gist options
  • Select an option

  • Save laracasts/70d59758574c6828e743e23fe9eb15f0 to your computer and use it in GitHub Desktop.

Select an option

Save laracasts/70d59758574c6828e743e23fe9eb15f0 to your computer and use it in GitHub Desktop.

Revisions

  1. Laracasts created this gist Jan 3, 2020.
    126 changes: 126 additions & 0 deletions Game.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,126 @@
    <?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];
    }
    }
    111 changes: 111 additions & 0 deletions GameTest.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,111 @@
    <?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());
    }
    }