Completed
Push — master ( 0c443a...572f35 )
by Guillermo
01:33
created

SecretSanta::isValidSecretSanta()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 0
cts 6
cp 0
rs 9.2
c 0
b 0
f 0
cc 4
eloc 5
nc 3
nop 2
crap 20
1
<?php
2
3
namespace SecretSanta;
4
5
use SecretSanta\Exceptions\PlayerException;
6
use SecretSanta\Exceptions\SecretSantaException;
7
8
/**
9
 * Class SecretSanta
10
 * @package SecretSanta
11
 */
12
class SecretSanta
13
{
14
    /** @var PlayersCollection */
15
    private $players;
16
    /** @var  array */
17
    private $combination;
18
19
    /**
20
     * SecretSanta constructor.
21
     */
22 20
    public function __construct()
23
    {
24 20
        $this->players = new PlayersCollection();
25 20
    }
26
27
    /**
28
     * Add single player to the game
29
     * @param string $name
30
     * @param string $email
31
     * @return SecretSanta
32
     */
33
    public function addPlayer(string $name, string $email)
34
    {
35
        $this->players->addPlayer(Player::create($name, $email));
36
37
        return $this;
38
    }
39
40
    /**
41
     * Add two excluded players to the game
42
     * @param string $name
43
     * @param string $email
44
     * @param string $coupleName
45
     * @param string $coupleEmail
46
     * @return SecretSanta
47
     */
48
    public function addCouple(string $name, string $email, string $coupleName, string $coupleEmail)
49
    {
50
        $this->players->addCouple(
51
            Player::create($name, $email),
52
            Player::create($coupleName, $coupleEmail)
53
        );
54
55
        return $this;
56
    }
57
58
    /**
59
     * Add multiple exclusive players to the game
60
     * @param array $playersData
61
     * @return $this
62
     * @throws SecretSantaException
63
     */
64 7
    public function addExclusivePlayers(...$playersData)
65
    {
66 7
        if (count($playersData) < 2) {
67 2
            throw new SecretSantaException('Number of players must be grater or equal than two.');
68
        }
69
70
        try {
71 5
            $players = [];
72 5
            foreach ($playersData as $playerData) {
73 5
                list($name, $email) = array_values($playerData);
74 5
                $players[] = Player::create($name, $email);
75 4
            }
76
77 4
            $this->players->addExclusivePlayers($players);
78 5
        } catch (PlayerException $exception) {
79 1
            throw new SecretSantaException('One o more players are invalids.', $exception);
80 1
        } catch (\Exception $exception) {
81 1
            throw new SecretSantaException('An error has occurred.', $exception);
82
        }
83
84 3
        return $this;
85
    }
86
87
    /**
88
     * @return Player[]
89
     * @throws SecretSantaException
90
     */
91 1
    public function play()
92
    {
93
        try {
94 1
            $this->combinePlayers();
95
96
            return $this->associatePlayers();
97 1
        } catch (SecretSantaException $exception) {
98 1
            throw  $exception;
99
        } catch (\Exception $exception) {
100
            throw new SecretSantaException('Error during play, impossible to find secret santa, try again', $exception);
101
        }
102
    }
103
104
    /**
105
     * @throws SecretSantaException
106
     */
107 1
    private function combinePlayers()
108
    {
109 1
        if (count($this->players) < 3) {
110 1
            throw new SecretSantaException("Not enough players to play, at least 3 players are required");
111
        }
112
113
        $retry = count($this->players) + $this->players->countExclusivePlayers();
114
115
        while (!$this->tryMatchSecretSantaPlayers() && $retry > 0 ) {
116
            $retry--;
117
        }
118
119
        if (!$this->isValidCombination()) {
120
            throw new SecretSantaException("Not enough players to play");
121
        }
122
    }
123
124
    /**
125
     * @return bool
126
     */
127
    private function tryMatchSecretSantaPlayers()
128
    {
129
        $this->combination = [];
130
        $secretPlayers = $this->players->shufflePlayers();
131
        foreach ($this->players->players() as $playerId => $player) {
132
            foreach ($secretPlayers as $secretPlayer) {
133
                if ($this->isValidSecretSanta($player, $secretPlayer)) {
134
                    $this->combination[$player->id()] = $secretPlayer->id();
135
                    unset ($secretPlayers[$secretPlayer->id()]);
136
                    break;
137
                }
138
            }
139
        }
140
141
        return $this->isValidCombination();
142
    }
143
144
    /**
145
     * @param Player $player
146
     * @param Player $secretPlayer
147
     * @return bool
148
     */
149
    private function isValidSecretSanta(Player $player, Player $secretPlayer)
150
    {
151
        if ($player->id() != $secretPlayer->id() && !$this->players->areExclusive($player, $secretPlayer)) {
152
            if (!in_array($secretPlayer->id(), $this->combination)) {
153
                return true;
154
            }
155
        }
156
157
        return false;
158
    }
159
160
    /**
161
     * @return bool
162
     */
163
    private function isValidCombination()
164
    {
165
        return count($this->combination) > 0 && count($this->combination) == count($this->players);
166
    }
167
168
    private function associatePlayers()
169
    {
170
        foreach ($this->combination as $playerId => $secretSantaId) {
171
            $this->players->player($playerId)->setSecretSanta(
172
                $this->players->player($secretSantaId)
173
            );
174
        }
175
176
        return $this->players->players();
177
    }
178
}
179