Passed
Push — main ( 8bc290...60b955 )
by Peter
04:33
created

BlackjackHandManagerProj::playComputerHand()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5.0144

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 5
eloc 11
c 1
b 0
f 1
nc 5
nop 3
dl 0
loc 19
ccs 11
cts 12
cp 0.9167
crap 5.0144
rs 9.6111
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
        while ($hand['status'] === 'playing') {
233 1
            $handValue = $this->getHandValue($hand['hand']);
234
235 1
            if ($this->helper->shouldComputerHit($computerStrategy, $handValue, $hand['hand'])) {
236 1
                $hand['hand'][] = $deck->drawCard();
237 1
                if ($this->getHandValue($hand['hand']) > 21) {
238
                    $hand['status'] = 'busted';
239
                }
240 1
                continue;
241
            }
242
243 1
            $hand['status'] = 'standing';
244
        }
245
    }
246
247
    /**
248
     * Check for blackjack in all hands.
249
     */
250 1
    public function checkForBlackjack(): void
251
    {
252 1
        foreach ($this->hands as &$hand) {
253 1
            if ($this->hasBlackjack($hand['hand'])) {
254 1
                $hand['status'] = 'blackjack';
255
            }
256
        }
257
258 1
        if ($this->hasBlackjack($this->dealerHand)) {
259
            $this->dealerHand['status'] = 'blackjack';
260
        }
261
    }
262
263
    /**
264
     * Check if a hand has blackjack.
265
     *
266
     * @param Card[] $hand The hand to check
267
     * @return bool True if the hand has blackjack
268
     */
269 1
    public function hasBlackjack(array $hand): bool
270
    {
271 1
        return $this->helper->hasBlackjack($hand);
272
    }
273
274
    /**
275
     * Settle all bets.
276
     *
277
     * @return float The total adjustment to the player bank
278
     */
279 1
    public function settleBets(): float
280
    {
281 1
        return $this->helper->settleBets($this->hands, $this->dealerHand);
282
    }
283
284
    /**
285
     * For testing, set a specific player hand.
286
     *
287
     * @param array $hand The hand to set
288
     * @param int $index The index of the hand to set
289
     */
290 4
    public function setPlayerHand(array $hand, int $index = 0)
291
    {
292 4
        $this->hands[$index]['hand'] = $hand;
293
    }
294
295
    /**
296
     * For testing, set a specific dealer hand.
297
     *
298
     * @param array $hand The hand to set
299
     */
300
    public function setDealerHand(array $hand)
301
    {
302
        $this->dealerHand = $hand;
303
    }
304
}
305