diff --git a/README.md b/README.md index 4ab5833..84bfd5b 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Includes: - **Bob**: returns different responses based on input. - **Bowling**: calculate the score for a game of bowling. - **Grade School**: given students' names along with the grade that they are in, create a roster for the school. +- **Rock, paper, scissors** - **Roman numerals**: convert a number into its roman numeral value. [exercism.io]: https://exercism.io diff --git a/src/RockPaperScissorsGame.php b/src/RockPaperScissorsGame.php new file mode 100644 index 0000000..78d8a38 --- /dev/null +++ b/src/RockPaperScissorsGame.php @@ -0,0 +1,77 @@ + [self::MOVE_ROCK, self::MOVE_PAPER], + 'winner' => self::MOVE_PAPER, + ], + [ + 'moves' => [self::MOVE_ROCK, self::MOVE_SCISSORS], + 'winner' => self::MOVE_SCISSORS, + ], + [ + 'moves' => [self::MOVE_PAPER, self::MOVE_SCISSORS], + 'winner' => self::MOVE_SCISSORS, + ], + ]; + + private Collection $moves; + + public function __construct() + { + $this->moves = new Collection(); + } + + public function firstMove(string $move): self + { + $this->validateMoveIsValid($move); + $this->moves[0] = $move; + + return $this; + } + + public function secondMove(string $move): self + { + $this->validateMoveIsValid($move); + $this->moves[1] = $move; + + return $this; + } + + public function getWinner(): ?string + { + if ($this->moves[0] == $this->moves[1]) { + return null; + } + + $move = (new Collection(self::$possibleMoves)) + ->first(function (array $move): bool { + // Determine if the played moves match this combination of + // possible moves. + return $this->moves->diff($move['moves'])->isEmpty(); + }); + + return $move['winner']; + } + + private function validateMoveIsValid(string $move): void + { + Assert::that($move) + ->notEmpty('You must specify a move.') + ->inArray( + [self::MOVE_ROCK, self::MOVE_PAPER, self::MOVE_SCISSORS], + 'You must enter a valid move. %s given.' + ); + } +} diff --git a/tests/RockPaperScissorsTest.php b/tests/RockPaperScissorsTest.php new file mode 100644 index 0000000..9c4ed2d --- /dev/null +++ b/tests/RockPaperScissorsTest.php @@ -0,0 +1,72 @@ +game = new App\RockPaperScissorsGame(); +}); + +it('throws an exception for an empty first move', function () { + $this->game->firstMove(''); +})->throws(AssertionFailedException::class, 'You must specify a move.'); + +it('throws an exception for an empty second move', function () { + $this->game->secondMove(''); +})->throws(AssertionFailedException::class, 'You must specify a move.'); + +it('throws an exception if an invalid first move is entered', function () { + $this->game->firstMove('banana'); +})->throws(AssertionFailedException::class, 'You must enter a valid move. banana given.'); + +it('throws an exception if an invalid second move is entered', function () { + $this->game->secondMove('apple'); +})->throws(AssertionFailedException::class, 'You must enter a valid move. apple given.'); + +it('returns the result of two different valid moves', function ( + string $firstMove, + string $secondMove, + string $winner +) { + $this->game + ->firstMove($firstMove) + ->secondMove($secondMove); + + assertSame($winner, $this->game->getWinner()); +})->with([ + [ + 'firstMove' => 'rock', + 'secondMove' => 'paper', + 'winner' => 'paper', + ], + [ + 'firstMove' => 'paper', + 'secondMove' => 'rock', + 'winner' => 'paper', + ], + [ + 'firstMove' => 'rock', + 'secondMove' => 'scissors', + 'winner' => 'scissors', + ], + [ + 'firstMove' => 'scissors', + 'secondMove' => 'rock', + 'winner' => 'scissors', + ], + [ + 'firstMove' => 'scissors', + 'secondMove' => 'paper', + 'winner' => 'scissors', + ], + [ + 'firstMove' => 'paper', + 'secondMove' => 'scissors', + 'winner' => 'scissors', + ], +]); + +test('it returns null if there is a tie', function (string $move) { + $this->game->firstMove($move)->secondMove($move); + + assertNull($this->game->getWinner()); +})->with(['rock', 'paper', 'scissors']);