Completed
Push — master ( 676495...4dafdb )
by Guillermo
9s
created

SecretSanta::combinePlayers()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5.025

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 9
cts 10
cp 0.9
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 8
nc 5
nop 0
crap 5.025
1
<?php
2
3
namespace SecretSanta;
4
5
use SecretSanta\Exceptions\SecretSantaException;
6
7
/**
8
 * Class SecretSanta
9
 * @package SecretSanta
10
 */
11
class SecretSanta
12
{
13
    /** @var PlayersCollection */
14
    private $players;
15
    /** @var  array */
16
    private $combination;
17
18
    /**
19
     * SecretSanta constructor.
20
     */
21 12
    public function __construct()
22
    {
23 12
        $this->players = new PlayersCollection();
24 12
    }
25
26
    /**
27
     * @param string $name
28
     * @param string $email
29
     * @return SecretSanta
30
     */
31 5
    public function addPlayer($name, $email)
32
    {
33 5
        $this->players->addPlayer(Player::create($name, $email));
34
35 5
        return $this;
36
    }
37
38
    /**
39
     * @param string $name
40
     * @param string $email
41
     * @param string $coupleName
42
     * @param string $coupleEmail
43
     * @return SecretSanta
44
     */
45 11
    public function addCouple($name, $email, $coupleName, $coupleEmail)
46
    {
47 11
        $this->players->addCouple(
48 11
            Player::create($name, $email),
49 11
            Player::create($coupleName, $coupleEmail)
50 11
        );
51
52 10
        return $this;
53
    }
54
55
    /**
56
     * @return Player[]
57
     * @throws SecretSantaException
58
     */
59 9
    public function play()
60
    {
61
        try {
62 9
            $this->combinePlayers();
63
64 8
            return $this->associatePlayers();
65 1
        } catch (SecretSantaException $exception) {
66 1
            throw  $exception;
67
        } catch (\Exception $exception) {
68
            throw new SecretSantaException('Error during play, impossible to find secret santa, try again');
69
        }
70
    }
71
72
    /**
73
     * @throws SecretSantaException
74
     */
75 9
    private function combinePlayers()
76
    {
77 9
        if (count($this->players) < 4) {
78 1
            throw new SecretSantaException("Not enough players to play, at least 4 players are required");
79
        }
80
81 8
        $retry = count($this->players) + $this->players->countExcludePlayers();
82
83 8
        while (!$this->tryMatchSecretSantaPlayers() && $retry > 0 ) {
84 1
            $retry--;
85 1
        }
86
87 8
        if (!$this->isValidCombination()) {
88
            throw new SecretSantaException("Not enough players to play");
89
        }
90 8
    }
91
92
    /**
93
     * @return bool
94
     */
95 8
    private function tryMatchSecretSantaPlayers()
96
    {
97 8
        $this->combination = [];
98 8
        $secretPlayers = $this->players->shufflePlayers();
99 8
        foreach ($this->players->players() as $playerId => $player) {
100 8
            foreach ($secretPlayers as $secretPlayer) {
101 8
                if ($this->isValidSecretSanta($player, $secretPlayer)) {
102 8
                    $this->combination[$player->id()] = $secretPlayer->id();
103 8
                    unset ($secretPlayers[$secretPlayer->id()]);
104 8
                    break;
105
                }
106 8
            }
107 8
        }
108
109 8
        return $this->isValidCombination();
110
    }
111
112
    /**
113
     * @param Player $player
114
     * @param Player $secretPlayer
115
     * @return bool
116
     */
117 8
    private function isValidSecretSanta($player, $secretPlayer)
118
    {
119 8
        if ($player->id() != $secretPlayer->id() && !$this->players->areExclude($player, $secretPlayer)) {
120 8
            if (!in_array($secretPlayer->id(), $this->combination)) {
121 8
                return true;
122
            }
123
        }
124
125 6
        return false;
126
    }
127
128
    /**
129
     * @return bool
130
     */
131 8
    private function isValidCombination()
132
    {
133 8
        return count($this->combination) > 0 && count($this->combination) == count($this->players);
134
    }
135
136 8
    private function associatePlayers()
137
    {
138 8
        foreach ($this->combination as $playerId => $secretSantaId) {
139 8
            $this->players->player($playerId)->setSecretSanta(
140 8
                $this->players->player($secretSantaId)
141 8
            );
142 8
        }
143
144 8
        return $this->players->players();
145
    }
146
}
147