Passed
Push — main ( 06b8a6...5a482c )
by Emil
05:26
created

BlackJack   A

Complexity

Total Complexity 40

Size/Duplication

Total Lines 274
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 63
c 1
b 1
f 0
dl 0
loc 274
rs 9.2
ccs 73
cts 73
cp 1
wmc 40

18 Methods

Rating   Name   Duplication   Size   Complexity  
A getNumOfPlayers() 0 3 1
A getDealer() 0 3 1
A drawUpdate() 0 10 3
A getDeck() 0 3 1
A __construct() 0 19 4
A setDeck() 0 3 1
A setPlayer() 0 8 3
A setPlayers() 0 6 2
A getPlayers() 0 3 1
A setDealer() 0 3 1
A stayPlayer() 0 11 4
A isPlayerBust() 0 8 3
A isPlayerBroke() 0 8 3
A hitPlayer() 0 12 4
A isDealerBust() 0 3 1
A stateOfGame() 0 3 1
A newGame() 0 3 1
A doubleDownPlayer() 0 18 5

How to fix   Complexity   

Complex Class

Complex classes like BlackJack often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use BlackJack, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace App\Game;
4
5
use App\Cards\DeckOfCards;
6
use App\Game\BlackJack\Dealer;
7
use App\Game\BlackJack\GameLogic;
8
use App\Game\BlackJack\Player;
9
10
/**
11
 * BlackJack.
12
 */
13
class BlackJack
14
{
15
    public const MAX_PLAYERS = 7;
16
    public const MINIMUM_BET = 50;
17
18
    private GameLogic $gameLogic;
19
    private int $numOfPlayers;
20
    /**
21
     * @var array<Player> Is an array that contains BlackJackPlayer objects
22
     */
23
    private array $players;
24
    private Dealer $dealer;
25
    private DeckOfCards $deck;
26
27
    /**
28
     * __construct.
29
     *
30
     * Constructor of the class
31
     *
32
     * @return void
33
     */
34 22
    public function __construct(int $numOfPlayers = 1)
35
    {
36 22
        if ($numOfPlayers < 1) {
37 1
            throw new \RuntimeException("Can't have less then one player in Black Jack");
38
        }
39
40 21
        if ($numOfPlayers > self::MAX_PLAYERS) {
41 1
            throw new \RuntimeException('Maximum of '.self::MAX_PLAYERS.' players in Black Jack');
42
        }
43
44 20
        $this->numOfPlayers = $numOfPlayers;
45 20
        $this->deck = new DeckOfCards();
46 20
        $this->dealer = new Dealer();
47 20
        for ($i = 0; $i < $this->numOfPlayers; ++$i) {
48 20
            $this->players[$i] = new Player();
49
        }
50
51
        // Needs to be last or can create error if accessing any BlackJack properties before set
52 20
        $this->gameLogic = new GameLogic($this);
53
    }
54
55
    /**
56
     * drawUpdate.
57
     */
58 4
    private function drawUpdate(int $index): void
59
    {
60 4
        if ($this->players[$index]->isBust()) {
61 2
            $this->players[$index]->setGameState(Player::DEALER_WIN);
62 2
            $this->players[$index]->resetBet();
63 2
            $this->gameLogic->checkIfDealersTurn();
64
        }
65
66 4
        if (Player::DOUBLE_DOWN === $this->players[$index]->getGameState()) {
67 2
            $this->gameLogic->checkIfDealersTurn();
68
        }
69
    }
70
71
    /**
72
     * getNumOfPlayers.
73
     */
74 19
    public function getNumOfPlayers(): int
75
    {
76 19
        return $this->numOfPlayers;
77
    }
78
79
    /**
80
     * getPlayers.
81
     *
82
     * @return array<Player>
83
     */
84 19
    public function getPlayers(): array
85
    {
86 19
        return $this->players;
87
    }
88
89
    /**
90
     * getDealer.
91
     */
92 19
    public function getDealer(): Dealer
93
    {
94 19
        return $this->dealer;
95
    }
96
97
    /**
98
     * getDeck.
99
     */
100 19
    public function getDeck(): DeckOfCards
101
    {
102 19
        return $this->deck;
103
    }
104
105
    /**
106
     * setPlayers.
107
     *
108
     * @param array<Player> $players
109
     */
110 19
    public function setPlayers(array $players): void
111
    {
112
        // If not to many players
113 19
        if (self::MAX_PLAYERS >= count($players)) {
114 19
            $this->players = $players;
115 19
            $this->numOfPlayers = count($players);
116
        }
117
    }
118
119
    /**
120
     * setPlayer.
121
     */
122 11
    public function setPlayer(int $index, Player $player): void
123
    {
124
        // If index is out of bounds
125 11
        if ($index < 0 or $index >= $this->numOfPlayers) {
126 1
            return;
127
        }
128
129 11
        $this->players[$index] = $player;
130
    }
131
132
    /**
133
     * setDealer.
134
     */
135 19
    public function setDealer(Dealer $dealer): void
136
    {
137 19
        $this->dealer = $dealer;
138
    }
139
140
    /**
141
     * setDeck.
142
     */
143 19
    public function setDeck(DeckOfCards $deck): void
144
    {
145 19
        $this->deck = $deck;
146
    }
147
148
    /**
149
     * isPlayerBust.
150
     *
151
     * Returns if the player's hand value is over 21
152
     */
153 5
    public function isPlayerBust(int $index = 0): bool
154
    {
155
        // If index is out of bounds
156 5
        if ($index < 0 or $index >= $this->numOfPlayers) {
157 1
            return true;
158
        }
159
160 5
        return $this->players[$index]->isBust();
161
    }
162
163
    /**
164
     * isPlayerBroke.
165
     *
166
     * Returns if the player is broke
167
     */
168 2
    public function isPlayerBroke(int $index = 0): bool
169
    {
170
        // If index is out of bounds
171 2
        if ($index < 0 or $index >= $this->numOfPlayers) {
172 1
            return true;
173
        }
174
175 2
        return $this->players[$index]->isBroke();
176
    }
177
178
    /**
179
     * isDealerBust.
180
     *
181
     * Returns if the dealer's hand value is over 21
182
     */
183 2
    public function isDealerBust(): bool
184
    {
185 2
        return $this->dealer->isBust();
186
    }
187
188
    /**
189
     * newGame.
190
     *
191
     * Sets up a new game
192
     *
193
     * @param array<int, int> $bets
194
     */
195 11
    public function newGame(array $bets = []): void
196
    {
197 11
        $this->gameLogic->newGame($bets);
198
    }
199
200
    /**
201
     * stateOfGame.
202
     *
203
     * Returns the current game state.
204
     *
205
     * @return array
206
     *               Descriptive list of array contents:
207
     *               - numOfPlayers (int)
208
     *               - playersNames (array<string>)
209
     *               - playersCards (array<int, array<string>>)
210
     *               - playersHandValue (array<string>)
211
     *               - playersCredits (array<string>)
212
     *               - playersBets (array<string>)
213
     *               - dealerCards (array<string>)
214
     *               - dealerHandValue (string)
215
     *               - gameStates (array<string>)
216
     *
217
     * @phpstan-return array{
218
     *   numOfPlayers: int,
219
     *   playersNames: array<string>,
220
     *   playersCards: array<int, array<string>>,
221
     *   playersHandValue: array<string>,
222
     *   playersCredits: array<string>,
223
     *   playersBets: array<string>,
224
     *   dealerCards: array<string>,
225
     *   dealerHandValue: string,
226
     *   gameStates: array<string>
227
     * }
228
     */
229 5
    public function stateOfGame(): array
230
    {
231 5
        return $this->gameLogic->stateOfGame();
232
    }
233
234
    /**
235
     * stayPlayer.
236
     */
237 3
    public function stayPlayer(int $index = 0): void
238
    {
239
        // If index is out of bounds
240 3
        if ($index < 0 or $index >= $this->numOfPlayers) {
241 1
            return;
242
        }
243
244
        // If game not over
245 3
        if (Player::UNDECIDED === $this->players[$index]->getGameState()) {
246 3
            $this->players[$index]->setGameState(Player::STAYED);
247 3
            $this->gameLogic->checkIfDealersTurn();
248
        }
249
    }
250
251
    /**
252
     * hitPlayer.
253
     */
254 3
    public function hitPlayer(int $index = 0): void
255
    {
256
        // If index is out of bounds
257 3
        if ($index < 0 or $index >= $this->numOfPlayers) {
258 1
            return;
259
        }
260
261
        // Stops drawing new cards if game is over
262 3
        if (Player::UNDECIDED === $this->players[$index]->getGameState()) {
263 3
            $this->players[$index]->addCard($this->deck->drawCard());
264
265 3
            $this->drawUpdate($index);
266
        }
267
    }
268
269 2
    public function doubleDownPlayer(int $index = 0): void
270
    {
271
        // If index is out of bounds
272 2
        if ($index < 0 or $index >= $this->numOfPlayers) {
273 1
            return;
274
        }
275
276
        // Can't double down if game is over
277 2
        if (Player::UNDECIDED === $this->players[$index]->getGameState() and 2 === count($this->players[$index]->getString())) {
278
            // Double bet
279 2
            $currentBet = $this->players[$index]->getBet();
280 2
            $this->players[$index]->increaseBet($currentBet);
281
282 2
            $this->players[$index]->addCard($this->deck->drawCard());
283
284 2
            $this->players[$index]->setGameState(Player::DOUBLE_DOWN);
285
286 2
            $this->drawUpdate($index);
287
        }
288
    }
289
}
290