From a933620de96564c272128f73e846f142753e5642 Mon Sep 17 00:00:00 2001 From: GuilleGF Date: Wed, 23 Nov 2016 16:10:47 +0100 Subject: [PATCH] Add multiple exclusive players --- README.md | 34 +++++- src/Exceptions/PlayerException.php | 5 +- src/Exceptions/PlayersCollectionException.php | 5 +- src/Exceptions/SecretSantaException.php | 5 +- src/PlayersCollection.php | 55 +++++++--- src/SecretSanta.php | 38 ++++++- tests/PlayersCollectionTest.php | 59 ++++++++++- tests/SecretSantaTest.php | 100 ++++++++++++++++++ 8 files changed, 264 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 7c5f3ca..93b0351 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![License](https://poser.pugx.org/guillegf/secret-santa/license)](https://packagist.org/packages/guillegf/secret-santa) * Repository: https://github.com/GuilleGF/SecretSantaPHP -* Version: 1.1.1 +* Version: 1.2.0 * License: MIT, see [LICENSE](LICENSE) ## Description @@ -31,29 +31,53 @@ documentation. To add this dependency using the command, run the following from within your project directory: ``` -composer require guillegf/secret-santa "~1.1" +composer require guillegf/secret-santa "~1.2" ``` Alternatively, add the dependency directly to your `composer.json` file: ```json "require": { - "guillegf/secret-santa": "~1.1" + "guillegf/secret-santa": "~1.2" } ``` ## Usage +In this example in total we add 10 players, 2 singles players, 2 couples (exclusive) and 4 exclusive players. + +**The couples and exclusive players never match together.** + ```php addPlayer('Player', 'player@email.com') +$secretSanta->addPlayer('Player1', 'player1@email.com') ->addPlayer('Player2', 'player2@email.com') ->addCouple('Player3', 'player3@email.com', 'Couple3', 'couple3@email.com') - ->addCouple('Player4', 'player4@email.com', 'Couple4', 'couple4@email.com'); + ->addCouple('Player4', 'player4@email.com', 'Couple4', 'couple4@email.com') + ->addExclusivePlayers( + ['Player5', 'player5@email.com'], + ['Player6', 'player6@email.com'], + ['Player7', 'player7@email.com'], + ['Player8', 'player8@email.com'] + ); foreach ($secretSanta->play() as $player) { echo ("{$player->name()} ({$player->email()}): {$player->secretSanta()->name()}\n"); } ``` +The above example will output: + +```php +Player1 (player1@email.com): Player5 +Player2 (player2@email.com): Player7 +Player3 (player3@email.com): Player2 +Couple3 (couple3@email.com): Player8 +Player4 (player4@email.com): Player3 +Couple4 (couple4@email.com): Player6 +Player5 (player5@email.com): Player4 +Player6 (player6@email.com): Player1 +Player7 (player7@email.com): Couple3 +Player8 (player8@email.com): Couple4 +``` ## License diff --git a/src/Exceptions/PlayerException.php b/src/Exceptions/PlayerException.php index 11e415a..fb46689 100644 --- a/src/Exceptions/PlayerException.php +++ b/src/Exceptions/PlayerException.php @@ -11,11 +11,10 @@ class PlayerException extends \Exception /** * PlayerException constructor. * @param string $message - * @param int $code * @param \Exception|null $previous */ - public function __construct($message, $code = 0, \Exception $previous = null) + public function __construct($message, \Exception $previous = null) { - parent::__construct($message, $code, $previous); + parent::__construct($message, 0, $previous); } } diff --git a/src/Exceptions/PlayersCollectionException.php b/src/Exceptions/PlayersCollectionException.php index 1dc8f72..2a2a8f2 100644 --- a/src/Exceptions/PlayersCollectionException.php +++ b/src/Exceptions/PlayersCollectionException.php @@ -11,11 +11,10 @@ class PlayersCollectionException extends \Exception /** * PlayersCollectionException constructor. * @param string $message - * @param int $code * @param \Exception|null $previous */ - public function __construct($message, $code = 0, \Exception $previous = null) + public function __construct($message, \Exception $previous = null) { - parent::__construct($message, $code, $previous); + parent::__construct($message, 0, $previous); } } \ No newline at end of file diff --git a/src/Exceptions/SecretSantaException.php b/src/Exceptions/SecretSantaException.php index bf17a0b..e114541 100644 --- a/src/Exceptions/SecretSantaException.php +++ b/src/Exceptions/SecretSantaException.php @@ -11,11 +11,10 @@ class SecretSantaException extends \Exception /** * SecretSantaException constructor. * @param string $message - * @param int $code * @param \Exception|null $previous */ - public function __construct($message, $code = 0, \Exception $previous = null) + public function __construct($message, \Exception $previous = null) { - parent::__construct($message, $code, $previous); + parent::__construct($message, 0, $previous); } } diff --git a/src/PlayersCollection.php b/src/PlayersCollection.php index b06d562..a5a9597 100644 --- a/src/PlayersCollection.php +++ b/src/PlayersCollection.php @@ -13,7 +13,7 @@ class PlayersCollection implements \Countable /** @var Player[] */ private $players = []; /** @var array */ - private $excludePlayers = []; + private $exclusivePlayers = []; /** * @param Player $player @@ -33,18 +33,37 @@ public function addPlayer(Player $player) */ public function addCouple(Player $player, Player $couple) { - if (!$this->areDifferentPlayers($player, $couple)) { - throw new PlayersCollectionException('The couple can not be the same player'); + if (!$this->areDifferentPlayers([$player, $couple])) { + throw new PlayersCollectionException('The couple can not be the same player'); } if (!$this->isDuplicatePlayer($player) && !$this->isDuplicatePlayer($couple) ) { $this->players[$player->id()] = $player; $this->players[$couple->id()] = $couple; - $this->excludePlayers($player, $couple); + $this->exclusivePlayers([$player, $couple]); } } + /** + * @param Player[] $players + * @throws PlayersCollectionException + */ + public function addExclusivePlayers($players) + { + if (!$this->areDifferentPlayers($players)) { + throw new PlayersCollectionException('The players must be different'); + } + + foreach ($players as $player) { + if (!$this->isDuplicatePlayer($player)) { + $this->players[$player->id()] = $player; + } + } + + $this->exclusivePlayers($players); + } + /** * @return Player[] */ @@ -77,16 +96,16 @@ public function player($id) /** - * @param Player[] ...$players + * @param Player[] $players */ - private function excludePlayers(...$players) + private function exclusivePlayers($players) { foreach ($players as $mainPlayer) { foreach ($players as $player) { if ($mainPlayer->id() == $player->id()){ continue; } - $this->excludePlayers[$mainPlayer->id()][] = $player->id(); + $this->exclusivePlayers[$mainPlayer->id()][] = $player->id(); } } } @@ -96,10 +115,10 @@ private function excludePlayers(...$players) * @param Player $player2 * @return bool */ - public function areExclude(Player $player, Player $player2) + public function areExclusive(Player $player, Player $player2) { - if (array_key_exists($player->id(), $this->excludePlayers) - && in_array($player2->id(), $this->excludePlayers[$player->id()]) + if (array_key_exists($player->id(), $this->exclusivePlayers) + && in_array($player2->id(), $this->exclusivePlayers[$player->id()]) ) { return true; } @@ -118,9 +137,9 @@ public function count() /** * @return int */ - public function countExcludePlayers() + public function countExclusivePlayers() { - return count($this->excludePlayers); + return count($this->exclusivePlayers); } @@ -139,13 +158,17 @@ private function isDuplicatePlayer(Player $player) } /** - * @param Player $player - * @param Player $otherPlayer + * @param Player[] $players * @return bool */ - private function areDifferentPlayers(Player $player, Player $otherPlayer) + private function areDifferentPlayers($players) { - return $player->id() != $otherPlayer->id(); + $uniqueIds = []; + foreach ($players as $player) { + $uniqueIds[] = $player->id(); + } + + return count($players) == count(array_unique($uniqueIds)); } /** diff --git a/src/SecretSanta.php b/src/SecretSanta.php index 3211be6..f7e345b 100644 --- a/src/SecretSanta.php +++ b/src/SecretSanta.php @@ -2,6 +2,7 @@ namespace SecretSanta; +use SecretSanta\Exceptions\PlayerException; use SecretSanta\Exceptions\SecretSantaException; /** @@ -24,6 +25,7 @@ public function __construct() } /** + * Add single player to the game * @param string $name * @param string $email * @return SecretSanta @@ -36,6 +38,7 @@ public function addPlayer($name, $email) } /** + * Add two excluded players to the game * @param string $name * @param string $email * @param string $coupleName @@ -52,6 +55,35 @@ public function addCouple($name, $email, $coupleName, $coupleEmail) return $this; } + /** + * Add multiple exclusive players to the game + * @param array $playersData + * @return $this + * @throws SecretSantaException + */ + public function addExclusivePlayers(...$playersData) + { + if (count($playersData) < 2) { + throw new SecretSantaException('Number of players must be grater or equal than two.'); + } + + try { + $players = []; + foreach ($playersData as $playerData) { + list($name, $email) = array_values($playerData); + $players[] = Player::create($name, $email); + } + + $this->players->addExclusivePlayers($players); + } catch (PlayerException $exception) { + throw new SecretSantaException('One o more players are invalids.', $exception); + } catch (\Exception $exception) { + throw new SecretSantaException('An error has occurred.', $exception); + } + + return $this; + } + /** * @return Player[] * @throws SecretSantaException @@ -65,7 +97,7 @@ public function play() } catch (SecretSantaException $exception) { throw $exception; } catch (\Exception $exception) { - throw new SecretSantaException('Error during play, impossible to find secret santa, try again'); + throw new SecretSantaException('Error during play, impossible to find secret santa, try again', $exception); } } @@ -78,7 +110,7 @@ private function combinePlayers() throw new SecretSantaException("Not enough players to play, at least 4 players are required"); } - $retry = count($this->players) + $this->players->countExcludePlayers(); + $retry = count($this->players) + $this->players->countExclusivePlayers(); while (!$this->tryMatchSecretSantaPlayers() && $retry > 0 ) { $retry--; @@ -116,7 +148,7 @@ private function tryMatchSecretSantaPlayers() */ private function isValidSecretSanta($player, $secretPlayer) { - if ($player->id() != $secretPlayer->id() && !$this->players->areExclude($player, $secretPlayer)) { + if ($player->id() != $secretPlayer->id() && !$this->players->areExclusive($player, $secretPlayer)) { if (!in_array($secretPlayer->id(), $this->combination)) { return true; } diff --git a/tests/PlayersCollectionTest.php b/tests/PlayersCollectionTest.php index 0309302..2b1ab73 100644 --- a/tests/PlayersCollectionTest.php +++ b/tests/PlayersCollectionTest.php @@ -96,7 +96,7 @@ public function testShufflePlayers() $this->assertNotEquals(array_keys($playersCollection->players()), array_keys($shufflePlayers)); } - public function testExcludePlayers() + public function testExclusivePlayers() { $expectedSinglePlayer = Player::create('nameSingle', 'emailSingle@email.com'); $expectedPlayer = Player::create('name', 'email@email.com'); @@ -105,8 +105,59 @@ public function testExcludePlayers() $playersCollection = new PlayersCollection(); $playersCollection->addCouple($expectedPlayer, $expectedCouple); - $this->assertTrue($playersCollection->areExclude($expectedPlayer, $expectedCouple)); - $this->assertFalse($playersCollection->areExclude($expectedSinglePlayer, $expectedPlayer)); - $this->assertSame(2, $playersCollection->countExcludePlayers()); + $this->assertTrue($playersCollection->areExclusive($expectedPlayer, $expectedCouple)); + $this->assertFalse($playersCollection->areExclusive($expectedSinglePlayer, $expectedPlayer)); + $this->assertSame(2, $playersCollection->countExclusivePlayers()); + } + + /** + * @expectedException \SecretSanta\Exceptions\PlayersCollectionException + */ + public function AddExclusivePlayersEmpty() + { + $playersCollection = new PlayersCollection(); + $playersCollection->addExclusivePlayers([]); + } + + /** + * @expectedException \SecretSanta\Exceptions\PlayersCollectionException + */ + public function AddExclusivePlayersOnlyOnePlayer() + { + $expectedPlayer = Player::create('name', 'email@email.com'); + + $playersCollection = new PlayersCollection(); + $playersCollection->addExclusivePlayers( + [$expectedPlayer] + ); + } + + /** + * @expectedException \SecretSanta\Exceptions\PlayersCollectionException + */ + public function AddExclusivePlayersOnlyTwoPlayer() + { + $expectedPlayer = Player::create('name', 'email@email.com'); + $expectedPlayer2 = Player::create('name2', 'email2@email.com'); + + $playersCollection = new PlayersCollection(); + $playersCollection->addExclusivePlayers([ + $expectedPlayer, + $expectedPlayer2 + ]); + } + + public function AddExclusivePlayersThreePlayer() + { + $expectedPlayer = Player::create('name', 'email@email.com'); + $expectedPlayer2 = Player::create('name2', 'email2@email.com'); + $expectedPlayer3 = Player::create('name3', 'email3@email.com'); + + $playersCollection = new PlayersCollection(); + $playersCollection->addExclusivePlayers([ + $expectedPlayer, + $expectedPlayer2, + $expectedPlayer3 + ]); } } \ No newline at end of file diff --git a/tests/SecretSantaTest.php b/tests/SecretSantaTest.php index ce29c80..be6feb7 100644 --- a/tests/SecretSantaTest.php +++ b/tests/SecretSantaTest.php @@ -240,4 +240,104 @@ public function testFiftyCouples() $this->assertSame(100 , count($combination)); } + + /** + * @expectedException \SecretSanta\Exceptions\SecretSantaException + */ + public function testExclusivePlayersEmpty() + { + $secretSanta = new SecretSanta(); + $secretSanta->addExclusivePlayers([]); + } + + /** + * @expectedException \SecretSanta\Exceptions\SecretSantaException + */ + public function testExclusivePlayersOnlyOnePlayer() + { + $secretSanta = new SecretSanta(); + $secretSanta->addExclusivePlayers( + ['Player', 'player@email.com'] + ); + } + + /** + * @expectedException \SecretSanta\Exceptions\SecretSantaException + */ + public function testExclusivePlayersSamePlayer() + { + $secretSanta = new SecretSanta(); + $secretSanta->addExclusivePlayers( + ['Player', 'player@email.com'], + ['Player', 'player@email.com'] + ); + } + + /** + * @expectedException \SecretSanta\Exceptions\SecretSantaException + */ + public function testExclusivePlayersInvalidPlayer() + { + $secretSanta = new SecretSanta(); + $secretSanta->addExclusivePlayers( + ['', 'player@email.com'], + ['Player', 'player@email.com'] + ); + } + + /** + * @expectedException \SecretSanta\Exceptions\SecretSantaException + */ + public function testExclusivePlayersTwoPlayers() + { + $secretSanta = new SecretSanta(); + $secretSanta->addExclusivePlayers( + ['Player', 'player@email.com'], + ['Player2', 'player2@email.com'] + ); + + $secretSanta->play(); + } + + public function testFourExclusivePlayersAndFourSinglePlayers() + { + $secretSanta = new SecretSanta(); + $secretSanta->addExclusivePlayers( + ['Player', 'player@email.com'], + ['Player2', 'player2@email.com'], + ['Player3', 'player3@email.com'], + ['Player4', 'player4@email.com'] + ); + $secretSanta->addPlayer('Player5', 'player5@email.com'); + $secretSanta->addPlayer('Player6', 'player6@email.com'); + $secretSanta->addPlayer('Player7', 'player7@email.com'); + $secretSanta->addPlayer('Player8', 'player8@email.com'); + + $combination = $secretSanta->play(); + + $this->assertSame(8 , count($combination)); + } + + public function testFourExclusivePlayersAndFourSinglePlayersAndFourCouples() + { + $secretSanta = new SecretSanta(); + $secretSanta->addExclusivePlayers( + ['Player', 'player@email.com'], + ['Player2', 'player2@email.com'], + ['Player3', 'player3@email.com'], + ['Player4', 'player4@email.com'] + ) + ->addPlayer('Player5', 'player5@email.com') + ->addPlayer('Player6', 'player6@email.com') + ->addPlayer('Player7', 'player7@email.com') + ->addPlayer('Player8', 'player8@email.com') + ->addCouple('Player9', 'player9@email.com', 'Couple9', 'couple9@email.com') + ->addCouple('Player10', 'player10@email.com', 'Couple10', 'couple10@email.com') + ->addCouple('Player11', 'player11@email.com', 'Couple11', 'couple11@email.com') + ->addCouple('Player12', 'player12@email.com', 'Couple12', 'couple12@email.com'); + + $combination = $secretSanta->play(); + + $this->assertSame(16 , count($combination)); + } }