Completed
Push — master ( 2e3d39...70ea86 )
by Dan
01:47
created

SevenCard::isStraight()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 10
cts 10
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 11
nc 4
nop 1
crap 4
1
<?php
2
3
namespace Cysha\Casino\Holdem\Cards\Evaluators;
4
5
use Cysha\Casino\Cards\Card;
6
use Cysha\Casino\Cards\CardCollection;
7
use Cysha\Casino\Cards\Contracts\CardEvaluator;
8
use Cysha\Casino\Cards\Contracts\CardResults;
9
use Cysha\Casino\Cards\Hand;
10
use Cysha\Casino\Cards\HandCollection;
11
use Cysha\Casino\Cards\ResultCollection;
12
use Cysha\Casino\Holdem\Cards\Results\SevenCardResult;
13
use Cysha\Casino\Holdem\Cards\SevenCardResultCollection;
14
use Illuminate\Support\Collection;
15
16
class SevenCard implements CardEvaluator
17
{
18
    /**
19
     * @param CardCollection $board
20
     * @param Hand           $hand
21
     *
22
     * @return CardResults
23
     */
24 30
    public static function evaluate(CardCollection $board, Hand $hand): CardResults
25
    {
26 30
        $cards = $board->merge($hand->cards());
27
28 30
        if (($result = static::royalFlush($cards)) !== false) {
29 3
            return SevenCardResult::createRoyalFlush($result, $hand);
0 ignored issues
show
Bug introduced by
It seems like $result defined by static::royalFlush($cards) on line 28 can also be of type boolean; however, Cysha\Casino\Holdem\Card...ult::createRoyalFlush() does only seem to accept object<Cysha\Casino\Cards\CardCollection>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
30
        }
31
32 28
        if (($result = static::straightFlush($cards)) !== false) {
33 2
            return SevenCardResult::createStraightFlush($result, $hand);
0 ignored issues
show
Bug introduced by
It seems like $result defined by static::straightFlush($cards) on line 32 can also be of type boolean; however, Cysha\Casino\Holdem\Card...::createStraightFlush() does only seem to accept object<Cysha\Casino\Cards\CardCollection>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
34
        }
35
36 27
        if (($result = static::fourOfAKind($cards)) !== false) {
37 4
            return SevenCardResult::createFourOfAKind($result, $hand);
38
        }
39
40 23
        if (($result = static::fullHouse($cards)) !== false) {
41 2
            return SevenCardResult::createFullHouse($result, $hand);
0 ignored issues
show
Bug introduced by
It seems like $result defined by static::fullHouse($cards) on line 40 can also be of type boolean; however, Cysha\Casino\Holdem\Card...sult::createFullHouse() does only seem to accept object<Cysha\Casino\Cards\CardCollection>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
42
        }
43
44 21
        if (($result = static::flush($cards)) !== false) {
45 2
            return SevenCardResult::createFlush($result, $hand);
0 ignored issues
show
Bug introduced by
It seems like $result defined by static::flush($cards) on line 44 can also be of type boolean; however, Cysha\Casino\Holdem\Card...rdResult::createFlush() does only seem to accept object<Cysha\Casino\Cards\CardCollection>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
46
        }
47
48 19
        if (($result = static::straight($cards)) !== false) {
49 6
            return SevenCardResult::createStraight($result, $hand);
0 ignored issues
show
Bug introduced by
It seems like $result defined by static::straight($cards) on line 48 can also be of type boolean; however, Cysha\Casino\Holdem\Card...esult::createStraight() does only seem to accept object<Cysha\Casino\Cards\CardCollection>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
50
        }
51
52 15
        if (($result = static::threeOfAKind($cards)) !== false) {
53 2
            return SevenCardResult::createThreeOfAKind($result, $hand);
0 ignored issues
show
Bug introduced by
It seems like $result defined by static::threeOfAKind($cards) on line 52 can also be of type boolean; however, Cysha\Casino\Holdem\Card...t::createThreeOfAKind() does only seem to accept object<Cysha\Casino\Cards\CardCollection>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
54
        }
55
56 13
        if (($result = static::twoPair($cards)) !== false) {
57 6
            return SevenCardResult::createTwoPair($result, $hand);
0 ignored issues
show
Bug introduced by
It seems like $result defined by static::twoPair($cards) on line 56 can also be of type boolean; however, Cysha\Casino\Holdem\Card...Result::createTwoPair() does only seem to accept object<Cysha\Casino\Cards\CardCollection>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
58
        }
59
60 9
        if (($result = static::onePair($cards)) !== false) {
61 6
            return SevenCardResult::createOnePair($result, $hand);
62
        }
63
64 5
        return SevenCardResult::createHighCard(static::highCard($cards), $hand);
65
    }
66
67
    /**
68
     * @param CardCollection $board
69
     * @param HandCollection $playerHands
70
     *
71
     * @return ResultCollection
72
     */
73 17
    public function evaluateHands(CardCollection $board, HandCollection $playerHands): ResultCollection
74
    {
75
        $playerHands = $playerHands
76
        // evaluate hands
77
        ->map(function (Hand $hand) use ($board) {
78
            return static::evaluate($board, $hand);
79 17
        })
80 17
81
        // sort the hands by their hand rank
82
            ->sortByDesc(function (SevenCardResult $result) {
83
                return [$result->rank(), $result->value()];
84 17
            })
85 17
86
        // group by the hand rank
87
            ->groupBy(function (SevenCardResult $result) {
88
                return $result->rank();
89 17
            })
90 17
        ;
91
92
        // if all hands in the first collection are equal
93
        $handsAreEqual = $playerHands
94 17
            ->first()
95 17
            ->groupBy(function (SevenCardResult $result) {
96
                return array_sum($result->value());
97
            })
98
        ;
99
100 17
        $winningResults = SevenCardResultCollection::make($handsAreEqual->first()->toArray());
101
102 17
        return $winningResults;
103 17
    }
104
105
    /**
106 17
     * @param CardCollection $cards
107
     *
108 17
     * @return bool|CardCollection
109
     */
110
    public static function royalFlush(CardCollection $cards)
111
    {
112
        // check for straight flush
113
        if (!static::straightFlush($cards)) {
114
            return false;
115
        }
116 33
117
        // make sure that TJQKA exist in hand
118
        $royalFlushHand = $cards
119 33
            ->switchAceValue()
120 28
            ->filter(function (Card $card) {
121
                return $card->isFaceCard() || $card->value() === 10;
122
            });
123
124
        if ($royalFlushHand->count() < 5) {
125 6
            return false;
126
        }
127 6
128 6
        return $royalFlushHand->sortByValue();
129
    }
130 6
131 3
    /**
132
     * @param CardCollection $cards
133
     *
134 4
     * @return bool|CardCollection
135
     */
136
    public static function straightFlush(CardCollection $cards)
137
    {
138
        // check for flush
139
        if (($flushCards = static::flush($cards)) === false) {
140
            return false;
141
        }
142 37
143
        // check for straight, using the flush cards
144
        if (($straight = static::straight($flushCards)) === false) {
0 ignored issues
show
Bug introduced by
It seems like $flushCards defined by static::flush($cards) on line 139 can also be of type boolean; however, Cysha\Casino\Holdem\Card...s\SevenCard::straight() does only seem to accept object<Cysha\Casino\Cards\CardCollection>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
145 37
            return false;
146 26
        }
147
148
        return $straight;
149
    }
150 12
151 5
    /**
152
     * @param CardCollection $cards
153
     *
154 7
     * @return CardCollection|false
155
     */
156
    public static function fourOfAKind(CardCollection $cards)
157
    {
158
        $judgedHand = self::nNumberOfCardsInSet($cards, 4);
159
160
        if ($judgedHand === null) {
161
            return false;
162 29
        }
163
164 29
        $highCard = self::highCard($cards->diff($judgedHand))->last();
165
166 29
        return $judgedHand
167 24
            ->push($highCard)
168
            ->switchAceValue()
169
            ->sortByValue();
170 5
    }
171
172
    /**
173 5
     * @param CardCollection $cards
174 5
     *
175 5
     * @return bool|CardCollection
176
     */
177
    public static function fullHouse(CardCollection $cards)
178
    {
179
        $threeOfAKind = self::nNumberOfCardsInSet($cards, 3);
180
        $twoOfAKind = self::nNumberOfCardsInSet($cards->diff($threeOfAKind), 2);
181
182
        if ($threeOfAKind === null || $twoOfAKind === null) {
183 25
            return false;
184
        }
185 25
186 25
        return $threeOfAKind->merge($twoOfAKind);
187
    }
188 25
189 22
    /**
190
     * @param CardCollection $cards
191
     *
192 3
     * @return CardCollection|bool
193
     */
194
    public static function flush(CardCollection $cards)
195
    {
196
        $groupedBySuit = $cards
197
            ->switchAceValue()
198
            ->groupBy(function (Card $card) {
199
                return $card->suit()->name();
200 39
            })
201
            ->sortByDesc(function ($group) {
202
                return count($group);
203 39
            });
204
        if ($groupedBySuit->first()->count() < 5) {
205 39
            return false;
206 39
        }
207
208 39
        return $groupedBySuit
209 39
            ->first()
210 39
            ->sortByValue()
211 26
            ->take(-5)
212
            ->values();
213
    }
214
215 14
    /**
216 14
     * @param CardCollection $cardCollection
217 14
     *
218 14
     * @return bool|CardCollection
219
     */
220
    public static function straight(CardCollection $cardCollection)
221
    {
222
        // a straight has to have a 5 or 10 in
223
        if ($cardCollection->whereValue(5)->count() === 0 && $cardCollection->whereValue(10)->count() === 0) {
224
            return false;
225
        }
226 34
227
        // we only care about the first instance of a card number
228
        $cardCollection = $cardCollection->uniqueByValue();
229 34
230 4
        // check with ace == 1
231
        $check = static::checkForStraight($cardCollection->sortByValue()->unique());
0 ignored issues
show
Bug introduced by
Since checkForStraight() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of checkForStraight() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
232
        if ($check !== false) {
233
            return $check;
234 31
        }
235 31
236 14
        // check with ace == 14
237
        $check = static::checkForStraight($cardCollection->switchAceValue()->sortByValue()->unique());
0 ignored issues
show
Bug introduced by
Since checkForStraight() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of checkForStraight() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
238
        if ($check !== false) {
239
            return $check;
240 19
        }
241 19
242 1
        return false;
243
    }
244
245 18
    /**
246
     * @param CardCollection $cards
247
     *
248
     * @return CardCollection|bool
249
     */
250
    public static function threeOfAKind(CardCollection $cards)
251
    {
252
        $judgedHand = self::nNumberOfCardsInSet($cards, 3);
253 16
254
        if ($judgedHand === null) {
255 16
            return false;
256
        }
257 16
258 13
        $highCards = $cards->diff($judgedHand)->sortByValue()->reverse()->take(2);
259
260
        return $judgedHand
261 3
            ->merge($highCards)
262
            ->switchAceValue()
263
            ->sortByValue();
264 3
    }
265 3
266 3
    /**
267
     * @param CardCollection $cards
268
     *
269
     * @return CardCollection|bool
270
     */
271
    public static function twoPair(CardCollection $cards)
272
    {
273
        $pairOne = self::nNumberOfCardsInSet($cards, 2);
274 15
        $pairTwo = self::nNumberOfCardsInSet($cards->diff($pairOne), 2);
275
276 15
        if ($pairTwo === null) {
277 15
            return false;
278
        }
279 15
280 10
        $pairs = $pairOne->merge($pairTwo);
281
282
        $highCard = self::highCard($cards->diff($pairs))->last();
283 7
284
        return $pairs
285 7
            ->push($highCard)
286
            ->switchAceValue();
287
    }
288 7
289 7
    /**
290
     * @param CardCollection $cards
291
     *
292
     * @return CardCollection|false
293
     */
294
    public static function onePair(CardCollection $cards)
295
    {
296
        $cards = $cards->switchAceValue();
297 11
        $pair = self::nNumberOfCardsInSet($cards, 2);
298
299 11
        if ($pair === null) {
300 11
            return false;
301
        }
302 11
303 6
        $otherCardsInResult = $cards->diff($pair)
304
            ->sortByValue()
305
            ->reverse()
306 7
            ->take(3)
307 7
        ;
308 7
309 7
        return $pair->merge($otherCardsInResult);
310
    }
311
312 7
    /**
313
     * @param CardCollection $cards
314
     *
315
     * @return CardCollection
316
     */
317
    public static function highCard(CardCollection $cards): CardCollection
318
    {
319
        return $cards
320 19
            ->switchAceValue()
321
            ->sortByValue()
322
            ->reverse()
323 19
            ->take(5)
324 19
            ->reverse()
325 19
            ->values();
326 19
    }
327 19
328 19
    /**
329
     * @param CardCollection $cards
330
     *
331
     * @return bool|CardCollection
332
     */
333
    private static function checkForStraight(CardCollection $cards)
334
    {
335
        // check 2-6
336 31
        $cardsToCheck = static::isStraight($cards->only(range(2, 6)));
0 ignored issues
show
Bug introduced by
Since isStraight() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of isStraight() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
337
        if ($cardsToCheck !== false) {
338
            return $cardsToCheck;
339 31
        }
340 31
341 4
        // check 1-5
342
        $cardsToCheck = static::isStraight($cards->only(range(1, 5)));
0 ignored issues
show
Bug introduced by
Since isStraight() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of isStraight() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
343
        if ($cardsToCheck !== false) {
344
            return $cardsToCheck;
345 29
        }
346 29
347 4
        // check 0-4
348
        $cardsToCheck = static::isStraight($cards->only(range(0, 4)));
0 ignored issues
show
Bug introduced by
Since isStraight() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of isStraight() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
349
        if ($cardsToCheck !== false) {
350
            return $cardsToCheck;
351 28
        }
352 28
353 10
        return false;
354
    }
355
356 19
    /**
357
     * @author Derecho
358
     *
359
     * @param CardCollection $cards
360
     *
361
     * @return bool|CardCollection
362
     */
363
    private static function isStraight(CardCollection $cards)
364
    {
365
        if ($cards->count() !== 5) {
366 31
            return false;
367
        }
368 31
369 10
        $uniqueCards = $cards->map->value()->unique();
370
        if ($cards->count() !== $uniqueCards->count()) {
371
            return false;
372
        }
373 31
374 31
        if ($cards->sumByValue() === array_sum(range(
375 31
            $cards->sortByValue()->first()->value(),
376 13
            $cards->sortByValue()->last()->value()
377
        ))) {
378
            return $cards->sortByValue()->values();
379 27
        }
380 27
381 27
        return false;
382
    }
383 15
384
    /**
385
     * @param CardCollection $cards
386 18
     * @param int            $numberOfCardsOfType
387
     *
388
     * @return CardCollection
389
     */
390
    private static function nNumberOfCardsInSet(CardCollection $cards, int $numberOfCardsOfType)
391
    {
392
        $judgedHand = $cards
393
            ->groupBy(function (Card $card) {
394
                return $card->value();
395 36
            })
396
            ->filter(function (CardCollection $group) use ($numberOfCardsOfType) {
397
                return $group->count() === $numberOfCardsOfType;
398
            })
399 36
            ->sortBy(function (CardCollection $group) {
400 36
                return $group->count();
401
            })
402 36
            ->values()
403 36
            ->last()
404 36
        ;
405 28
406 36
        return $judgedHand;
407 36
    }
408
}
409