|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace App\Game; |
|
4
|
|
|
|
|
5
|
|
|
use App\Card\DeckOfCards; |
|
6
|
|
|
use App\Card\Card; |
|
7
|
|
|
|
|
8
|
|
|
class GameManager |
|
9
|
|
|
{ |
|
10
|
|
|
/** @var DeckOfCards $deck */ |
|
11
|
|
|
private $deck; |
|
12
|
|
|
|
|
13
|
|
|
/** @var ReceiverInterface $player */ |
|
14
|
|
|
private $player; |
|
15
|
|
|
|
|
16
|
|
|
/** @var BankerInterface $banker */ |
|
17
|
|
|
private $banker; |
|
18
|
|
|
|
|
19
|
|
|
/** @var bool $assistanceMode As true if assistance mode is turned on, otherwise false. */ |
|
20
|
|
|
private bool $assistanceMode = false; |
|
21
|
|
|
|
|
22
|
|
|
/** @var Card[] $removedCards All cards drawn in previous turns. */ |
|
23
|
|
|
private array $removedCards = []; |
|
24
|
|
|
|
|
25
|
|
|
/** @var int $hasWon As 0 if no one has won, 1 if player has won or -1 if banker has won. */ |
|
26
|
|
|
private int $hasWon = 0; |
|
27
|
|
|
|
|
28
|
|
|
/** @return int As 0 if no one has won, 1 if player has one or -1 if banker has won. */ |
|
29
|
6 |
|
public function getHasWon(): int |
|
30
|
|
|
{ |
|
31
|
6 |
|
return $this->hasWon; |
|
32
|
|
|
} |
|
33
|
|
|
|
|
34
|
|
|
/** @return bool As true if assistance mode is turned on, otherwise false. */ |
|
35
|
1 |
|
public function hasAssistanceMode(): bool |
|
36
|
|
|
{ |
|
37
|
1 |
|
return $this->assistanceMode; |
|
38
|
|
|
} |
|
39
|
|
|
|
|
40
|
|
|
/** |
|
41
|
|
|
* Move drawn cards to array of removed cards before also resetting player and banker. |
|
42
|
|
|
*/ |
|
43
|
|
|
public function reset(): void |
|
44
|
|
|
{ |
|
45
|
|
|
/** @var Card[] $cardsToRemove Cards drawn by the player and banker this turn */ |
|
46
|
|
|
$cardsToRemove = array_merge($this->player->getCards(), $this->banker->getCards()); |
|
47
|
|
|
|
|
48
|
|
|
$this->removedCards = array_merge($this->removedCards, $cardsToRemove); |
|
49
|
|
|
|
|
50
|
|
|
// Reset player and banker points and cards |
|
51
|
|
|
$this->player->reset(); |
|
52
|
|
|
$this->banker->reset(); |
|
53
|
|
|
|
|
54
|
|
|
$this->hasWon = 0; |
|
55
|
|
|
} |
|
56
|
|
|
|
|
57
|
|
|
/** @param bool $value As true if mode should be turned on, otherwise false. */ |
|
58
|
1 |
|
public function setAssistanceMode(bool $value): void |
|
59
|
|
|
{ |
|
60
|
1 |
|
$this->assistanceMode = $value; |
|
61
|
|
|
} |
|
62
|
|
|
|
|
63
|
|
|
/** @param DeckOfCards $deck */ |
|
64
|
8 |
|
public function setDeck(DeckOfCards $deck): void |
|
65
|
|
|
{ |
|
66
|
8 |
|
$this->deck = $deck; |
|
67
|
|
|
} |
|
68
|
|
|
|
|
69
|
|
|
/** @param ReceiverInterface $player */ |
|
70
|
14 |
|
public function setPlayer(ReceiverInterface $player): void |
|
71
|
|
|
{ |
|
72
|
14 |
|
$this->player = $player; |
|
73
|
|
|
} |
|
74
|
|
|
|
|
75
|
|
|
/** @param BankerInterface $banker */ |
|
76
|
15 |
|
public function setBanker(BankerInterface $banker): void |
|
77
|
|
|
{ |
|
78
|
15 |
|
$this->banker = $banker; |
|
79
|
|
|
} |
|
80
|
|
|
|
|
81
|
|
|
/** |
|
82
|
|
|
* Get the risk of the player scoring over 21 (bursting). |
|
83
|
|
|
* |
|
84
|
|
|
* @return float As the risk of bursting on drawing another card. |
|
85
|
|
|
*/ |
|
86
|
4 |
|
public function getBurstRisk(): float |
|
87
|
|
|
{ |
|
88
|
|
|
/** @var int $margin Number of points the player can score before bursting. */ |
|
89
|
4 |
|
$margin = 21 - $this->player->getPoints(); |
|
90
|
|
|
|
|
91
|
4 |
|
if ($margin > 13) { |
|
92
|
1 |
|
return 0; // No card will put he player over 21, return early |
|
93
|
|
|
} |
|
94
|
|
|
|
|
95
|
|
|
/** @var int[] $burstCards Values of all cards that will make the player burst. */ |
|
96
|
3 |
|
$burstCards = array_filter($this->deck->getValues(), function ($value) use ($margin) { |
|
97
|
3 |
|
return $value > $margin; |
|
98
|
3 |
|
}); |
|
99
|
|
|
|
|
100
|
|
|
/** @var float $burstRisk Risk of drawing over 21. */ |
|
101
|
3 |
|
$burstRisk = round(count($burstCards) / $this->deck->getCount(), 2); |
|
102
|
|
|
|
|
103
|
3 |
|
return $burstRisk; |
|
104
|
|
|
} |
|
105
|
|
|
|
|
106
|
|
|
/** |
|
107
|
|
|
* Deal player a card. |
|
108
|
|
|
*/ |
|
109
|
3 |
|
public function dealPlayer(): void |
|
110
|
|
|
{ |
|
111
|
3 |
|
$this->player->receive($this->deck->draw(1)[0]); |
|
112
|
3 |
|
$this->refillEmptyDeck(); |
|
113
|
|
|
} |
|
114
|
|
|
|
|
115
|
|
|
/** |
|
116
|
|
|
* Deal banker cards until they decide to stop hitting. |
|
117
|
|
|
*/ |
|
118
|
1 |
|
public function dealBanker(): void |
|
119
|
|
|
{ |
|
120
|
1 |
|
$this->informBanker(); |
|
121
|
|
|
|
|
122
|
1 |
|
$keepHitting = true; |
|
123
|
1 |
|
while ($keepHitting === true) { |
|
124
|
1 |
|
$this->banker->receive($this->deck->draw(1)[0]); |
|
125
|
|
|
|
|
126
|
1 |
|
$keepHitting = $this->banker->keepHitting(); |
|
127
|
|
|
|
|
128
|
1 |
|
$this->refillEmptyDeck(); |
|
129
|
|
|
} |
|
130
|
|
|
} |
|
131
|
|
|
|
|
132
|
|
|
/** |
|
133
|
|
|
* Method of informing banker about current game state. |
|
134
|
|
|
*/ |
|
135
|
1 |
|
private function informBanker(): void |
|
136
|
|
|
{ |
|
137
|
1 |
|
$this->banker->passInfo($this->removedCards, $this->player->getPoints()); |
|
138
|
|
|
} |
|
139
|
|
|
|
|
140
|
|
|
/** @return mixed[] As the current game state. */ |
|
141
|
4 |
|
public function getState(): array |
|
142
|
|
|
{ |
|
143
|
4 |
|
$state = [ |
|
144
|
4 |
|
'playerPoints' => $this->player->getPoints(), |
|
145
|
4 |
|
'playerCards' => $this->player->getCards(), |
|
146
|
4 |
|
'bankerPoints' => $this->banker->getPoints(), |
|
147
|
4 |
|
'bankerCards' => $this->banker->getCards(), |
|
148
|
4 |
|
'cardCount' => $this->deck->getCount(), |
|
149
|
4 |
|
'assistance' => $this->assistanceMode ? $this->getBurstRisk() * 100 : null, |
|
150
|
4 |
|
'hasWon' => $this->hasWon, |
|
151
|
4 |
|
]; |
|
152
|
|
|
|
|
153
|
4 |
|
return $state; |
|
154
|
|
|
} |
|
155
|
|
|
|
|
156
|
|
|
/** |
|
157
|
|
|
* Check if player won. |
|
158
|
|
|
* |
|
159
|
|
|
* @return int As who (if anyone) has won. |
|
160
|
|
|
*/ |
|
161
|
2 |
|
public function checkPlayerWon(): int |
|
162
|
|
|
{ |
|
163
|
2 |
|
if ($this->player->getPoints() === 21) { |
|
164
|
1 |
|
$this->hasWon = 1; |
|
165
|
1 |
|
} elseif ($this->player->getPoints() > 21) { |
|
166
|
1 |
|
$this->hasWon = -1; |
|
167
|
|
|
} |
|
168
|
2 |
|
return $this->hasWon; |
|
169
|
|
|
} |
|
170
|
|
|
|
|
171
|
|
|
/** |
|
172
|
|
|
* Check if banker won. |
|
173
|
|
|
* |
|
174
|
|
|
* @return int As who (if anyone) has won. |
|
175
|
|
|
*/ |
|
176
|
4 |
|
public function checkBankerWon(): int |
|
177
|
|
|
{ |
|
178
|
4 |
|
if ($this->banker->getPoints() > 21 || $this->player->getPoints() > $this->banker->getPoints()) { |
|
179
|
2 |
|
$this->hasWon = 1; |
|
180
|
2 |
|
} elseif ($this->banker->getPoints() >= $this->player->getPoints()) { |
|
181
|
2 |
|
$this->hasWon = -1; |
|
182
|
|
|
} |
|
183
|
4 |
|
return $this->hasWon; |
|
184
|
|
|
} |
|
185
|
|
|
|
|
186
|
|
|
/** |
|
187
|
|
|
* Fill deck with removed cards if empty. |
|
188
|
|
|
*/ |
|
189
|
4 |
|
private function refillEmptyDeck(): void |
|
190
|
|
|
{ |
|
191
|
4 |
|
if ($this->deck->getCount() > 0) { |
|
192
|
4 |
|
return; // Deck is not empty, do nothing and return early |
|
193
|
|
|
} |
|
194
|
|
|
|
|
195
|
|
|
// Add removed cards back in the deck |
|
196
|
|
|
foreach ($this->removedCards as $card) { |
|
197
|
|
|
$this->deck->addCard($card); |
|
198
|
|
|
} |
|
199
|
|
|
|
|
200
|
|
|
// Shuffle deck |
|
201
|
|
|
$this->deck->shuffleCards(); |
|
202
|
|
|
|
|
203
|
|
|
// The removed cards are back in the deck, inform banker about it |
|
204
|
|
|
$this->removedCards = []; |
|
205
|
|
|
$this->informBanker(); |
|
206
|
|
|
} |
|
207
|
|
|
} |
|
208
|
|
|
|