Passed
Push — main ( 6814eb...9309d6 )
by Peter
05:49
created

BlackjackHandManagerProj::checkForBlackjack()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 4.074

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 4
eloc 5
c 1
b 0
f 1
nc 6
nop 0
dl 0
loc 10
ccs 5
cts 6
cp 0.8333
crap 4.074
rs 10
1
<?php
2
3
namespace App\GameProj;
4
5
use App\Card\DeckProj;
6
use App\Card\Card;
7
use InvalidArgumentException;
8
9
/**
10
 * Manages the hands in a Blackjack game.
11
 */
12
class BlackjackHandManagerProj
13
{
14
    /** @var array[] The player hands */
15
    private $hands = [];
16
17
    /** @var Card[] The dealer hand */
18
    private $dealerHand = [];
19
20
    /** @var int The index of the current hand being played */
21
    private $currentHandIndex = 0;
22
23
    /** @var BlackjackHandHelperProj */
24
    private $helper;
25
26
    /**
27
     * BlackjackHandManager constructor.
28
     *
29
     * @param int $numberOfHands The number of hands to manage
30
     * @param float $totalBet The total bet amount for all hands
31
     * @throws InvalidArgumentException If the number of hands is invalid
32
     */
33 16
    public function __construct(int $numberOfHands, float $totalBet)
34
    {
35 16
        if ($numberOfHands < 1 || $numberOfHands > 3) {
36 1
            throw new InvalidArgumentException("Number of hands must be between 1 and 3");
37
        }
38
39 15
        $betPerHand = $totalBet / $numberOfHands;
40 15
        for ($i = 0; $i < $numberOfHands; $i++) {
41 15
            $this->hands[] = [
42 15
                'hand' => [],
43 15
                'status' => 'playing',
44 15
                'bet' => $betPerHand
45 15
            ];
46
        }
47
48 15
        $this->helper = new BlackjackHandHelperProj();
49
    }
50
51
    /**
52
     * Deal initial cards to all hands.
53
     *
54
     * @param DeckProj $deck The deck to draw cards from
55
     */
56 7
    public function dealInitialCards(DeckProj $deck): void
57
    {
58 7
        foreach ($this->hands as &$hand) {
59 7
            $hand['hand'][] = $deck->drawCard();
60 7
            $hand['hand'][] = $deck->drawCard();
61
        }
62 7
        $this->dealerHand[] = $deck->drawCard();
63
    }
64
65
    /**
66
     * Draw a card for a specific hand.
67
     *
68
     * @param int $handIndex The index of the hand to hit
69
     * @param DeckProj $deck The deck to draw cards from
70
     * @throws InvalidArgumentException If the hand index is invalid
71
     */
72 4
    public function hitHand(int $handIndex, DeckProj $deck): void
73
    {
74 4
        if ($handIndex < 0 || $handIndex >= count($this->hands)) {
75
            throw new InvalidArgumentException("Invalid hand index");
76
        }
77
78 4
        $this->hands[$handIndex]['hand'][] = $deck->drawCard();
79 4
        if ($this->getHandValue($this->hands[$handIndex]['hand']) > 21) {
80 3
            $this->hands[$handIndex]['status'] = 'busted';
81
        }
82
    }
83
84
    /**
85
     * Stand a specific hand.
86
     *
87
     * @param int $handIndex The index of the hand to stand
88
     * @throws InvalidArgumentException If the hand index is invalid
89
     */
90 4
    public function standHand(int $handIndex): void
91
    {
92 4
        if ($handIndex < 0 || $handIndex >= count($this->hands)) {
93 1
            throw new InvalidArgumentException("Invalid hand index");
94
        }
95
96 4
        $this->hands[$handIndex]['status'] = 'standing';
97
    }
98
99
    /**
100
     * Draw a card for the dealer.
101
     *
102
     * @param DeckProj $deck The deck to draw cards from
103
     */
104 2
    public function hitDealer(DeckProj $deck): void
105
    {
106 2
        $this->dealerHand[] = $deck->drawCard();
107
    }
108
109
    /**
110
     * Get all player hands.
111
     *
112
     * @return array[] An array of player hands
113
     */
114 7
    public function getPlayerHands(): array
115
    {
116 7
        return $this->hands;
117
    }
118
119
    /**
120
     * Get dealer hand.
121
     *
122
     * @return Card[] The dealer hand
123
     */
124 3
    public function getDealerHand(): array
125
    {
126 3
        return $this->dealerHand;
127
    }
128
129
    /**
130
     * Calculate the total value of a hand.
131
     *
132
     * @param Card[] $hand The hand to calculate
133
     * @return int The value of the hand
134
     */
135 5
    public function getHandValue(array $hand): int
136
    {
137 5
        return $this->helper->getHandValue($hand);
138
    }
139
140
    /**
141
     * Check if the dealer must draw more cards.
142
     *
143
     * @return bool True if dealer hand value is below 17
144
     */
145 1
    public function dealerMustDraw(): bool
146
    {
147 1
        return $this->getHandValue($this->dealerHand) < 17;
148
    }
149
150
    /**
151
     * Check if a specific hand is busted.
152
     *
153
     * @param int $handIndex The index of the hand to check
154
     * @return bool True if the hand is busted
155
     */
156 3
    public function isHandBusted(int $handIndex): bool
157
    {
158 3
        return $this->hands[$handIndex]['status'] === 'busted';
159
    }
160
161
    /**
162
     * Check if the game is over.
163
     *
164
     * @param int|null $computerHandIndex The index of the computer hand, or null if not used
165
     * @return bool True if the game is over
166
     */
167 1
    public function isGameOver(?int $computerHandIndex): bool
168
    {
169 1
        foreach ($this->hands as $index => $hand) {
170 1
            if ($hand['status'] === 'playing' && $index !== $computerHandIndex) {
171 1
                return false;
172
            }
173
        }
174 1
        return true;
175
    }
176
177
    /**
178
     * Get the probability of busting for a specific hand.
179
     *
180
     * @param int $handIndex The index of the hand to check
181
     * @param DeckProj $deck The deck to calculate probabilities from
182
     * @return float The probability of busting
183
     */
184 2
    public function getBustProbability(int $handIndex, DeckProj $deck): float
185
    {
186 2
        return $this->helper->getBustProbability($this->hands[$handIndex]['hand'], $deck);
187
    }
188
189
    /**
190
     * Get the index of the current hand being played.
191
     *
192
     * @return int|null The index of the current hand, or null if no hands are playing
193
     */
194 2
    public function getCurrentHand(): ?int
195
    {
196 2
        $totalHands = count($this->hands);
197
198 2
        while ($this->currentHandIndex < $totalHands) {
199 2
            if ($this->hands[$this->currentHandIndex]['status'] === 'playing') {
200 2
                return $this->currentHandIndex;
201
            }
202 1
            $this->currentHandIndex++;
203
        }
204 1
        return null;
205
    }
206
207
    /**
208
     * Move to the next hand.
209
     */
210 2
    public function nextHand(): void
211
    {
212 2
        $this->currentHandIndex++;
213 2
        if ($this->currentHandIndex >= count($this->hands)) {
214 2
            $this->currentHandIndex = 0;
215
        }
216
    }
217
218
    /**
219
     * Play the computer hand according to its strategy.
220
     *
221
     * @param int|null $computerHandIndex The index of the computer hand
222
     * @param string $computerStrategy The strategy used by the computer player
223
     * @param DeckProj $deck The deck to draw cards from
224
     */
225 2
    public function playComputerHand(?int $computerHandIndex, string $computerStrategy, DeckProj $deck): void
226
    {
227 2
        if ($computerHandIndex === null) {
228 1
            return;
229
        }
230
    
231 1
        $hand = &$this->hands[$computerHandIndex];
232 1
        if ($hand['status'] !== 'playing') {
233
            return;
234
        }
235
    
236 1
        while ($hand['status'] === 'playing') {
237 1
            $handValue = $this->getHandValue($hand['hand']);
238
    
239 1
            if ($this->helper->shouldComputerHit($computerStrategy, $handValue, $hand['hand'])) {
240 1
                $hand['hand'][] = $deck->drawCard();
241 1
                if ($this->getHandValue($hand['hand']) > 21) {
242 1
                    $hand['status'] = 'busted';
243
                }
244
            } else {
245 1
                $hand['status'] = 'standing';
246
            }
247
        }
248
    }
249
250
    /**
251
     * Check for blackjack in all hands.
252
     */
253 1
    public function checkForBlackjack(): void
254
    {
255 1
        foreach ($this->hands as &$hand) {
256 1
            if ($this->hasBlackjack($hand['hand'])) {
257 1
                $hand['status'] = 'blackjack';
258
            }
259
        }
260
261 1
        if ($this->hasBlackjack($this->dealerHand)) {
262
            $this->dealerHand['status'] = 'blackjack';
263
        }
264
    }
265
266
    /**
267
     * Check if a hand has blackjack.
268
     *
269
     * @param Card[] $hand The hand to check
270
     * @return bool True if the hand has blackjack
271
     */
272 1
    public function hasBlackjack(array $hand): bool
273
    {
274 1
        return $this->helper->hasBlackjack($hand);
275
    }
276
277
    /**
278
     * Settle all bets.
279
     *
280
     * @return float The total adjustment to the player bank
281
     */
282 1
    public function settleBets(): float
283
    {
284 1
        return $this->helper->settleBets($this->hands, $this->dealerHand);
285
    }
286
287
    /**
288
     * For testing, set a specific player hand.
289
     *
290
     * @param array $hand The hand to set
291
     * @param int $index The index of the hand to set
292
     */
293 4
    public function setPlayerHand(array $hand, int $index = 0)
294
    {
295 4
        $this->hands[$index]['hand'] = $hand;
296
    }
297
298
    /**
299
     * For testing, set a specific dealer hand.
300
     *
301
     * @param array $hand The hand to set
302
     */
303
    public function setDealerHand(array $hand)
304
    {
305
        $this->dealerHand = $hand;
306
    }
307
}
308