Player::getName()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arp\DominoGame\Value;
6
7
use Arp\DominoGame\Exception\DominoGameException;
8
9
/**
10
 * @author  Alex Patterson <[email protected]>
11
 * @package Arp\DominoGame\Value
12
 */
13
class Player
14
{
15
    /**
16
     * @var string
17
     */
18
    private string $name;
19
20
    /**
21
     * @var DominoCollection
22
     */
23
    private DominoCollection $hand;
24
25
    /**
26
     * @param string $name
27
     */
28
    public function __construct(string $name)
29
    {
30
        $this->name = $name;
31
        $this->hand = new DominoCollection();
32
    }
33
34
    /**
35
     * @return string
36
     */
37
    public function getName(): string
38
    {
39
        return $this->name;
40
    }
41
42
    /**
43
     * @return DominoCollection
44
     */
45
    public function getHand(): DominoCollection
46
    {
47
        return $this->hand;
48
    }
49
50
    /**
51
     * @return int
52
     */
53
    public function getHandCount(): int
54
    {
55
        return $this->hand->count();
56
    }
57
58
    /**
59
     * Return the total value of the entire hand
60
     *
61
     * @return int
62
     */
63
    public function getHandValue(): int
64
    {
65
        return $this->hand->getValue();
66
    }
67
68
    /**
69
     * @param Domino $domino
70
     */
71
    public function addToHand(Domino $domino): void
72
    {
73
        $this->hand->add($domino);
74
    }
75
76
    /**
77
     * Remove a domino from the players hand.
78
     *
79
     * @param Domino $domino
80
     *
81
     * @return bool
82
     *
83
     * @throws DominoGameException If the $domino provided does not exist in the players hand
84
     */
85
    public function removeFromHand(Domino $domino): bool
86
    {
87
        if (!$this->hand->has($domino)) {
88
            throw new DominoGameException(
89
                sprintf(
90
                    'The player \'%s\' does not have the domino \'%s\' to remove',
91
                    $this->getName(),
92
                    $domino->getName()
93
                )
94
            );
95
        }
96
97
        return $this->hand->removeElement($domino);
98
    }
99
100
    /**
101
     * Find and return the domino that has the highest double in the hand. If there are no doubles then null is returned
102
     *
103
     * @return Domino|null
104
     */
105
    public function getHighestDouble(): ?Domino
106
    {
107
        if (0 === $this->hand->count()) {
108
            return null;
109
        }
110
111
        $hand = $this->hand->getElements();
112
        usort(
113
            $hand,
114
            static function (Domino $a, Domino $b) {
115
                if ($a->isDouble() && !$b->isDouble()) {
116
                    return 0;
117
                }
118
                if (!$a->isDouble() && $b->isDouble()) {
119
                    return 1;
120
                }
121
                return $b->getValue() <=> $a->getValue();
122
            }
123
        );
124
125
        return $hand[0] ?? null;
126
    }
127
128
    /**
129
     * Attempt to find domino that 'matches' the available tiles in the provided $board.
130
     *
131
     * There will be cases where we can match 0 tiles (not right or left provided by the board)
132
     * There will be cases where we can match 1 tiles (right or left)
133
     * There will be cases where we can match 2 tiles (right and left)
134
     *  - This can be with 1 tile
135
     *  - This can be with more than one tile (so multiple possible choices)
136
     *
137
     * In all above cases we aim to return a SINGLE domino that matches either left OR right if possible. If we end
138
     * up having to choose between more than one match, we will always choose the domino with the HIGHEST value, as
139
     * this would ultimately result in use having a lower overall value in our hand.
140
     *
141
     * @param Board $board
142
     *
143
     * @return Domino|null
144
     */
145
    public function getDominoWithMatchingTile(Board $board): ?Domino
146
    {
147
        // If there is an empty board, fetch our highest available double...
148
        if ($board->isEmpty()) {
149
            return $this->getHighestDouble();
150
        }
151
152
        $leftTile = $board->getLeftTile();
153
        $rightTile = $board->getRightTile();
154
155
        $matchesLeft = $this->hand->createCollectionWithMatchingTiles($leftTile);
156
        $matchesRight = $this->hand->createCollectionWithMatchingTiles($rightTile);
157
158
        // No matches found in our hand
159
        if ($matchesLeft->isEmpty() && $matchesRight->isEmpty()) {
160
            return null;
161
        }
162
163
        // If the left is the highest value, we will try to place our piece on the left
164
        if ($leftTile > $rightTile && !$matchesLeft->isEmpty()) {
165
            return $matchesLeft->getDominoWithHighestValue();
166
        }
167
168
        // If the right is the highest value, we will try to place our piece on the right
169
        if ($rightTile > $leftTile && !$matchesRight->isEmpty()) {
170
            return $matchesRight->getDominoWithHighestValue();
171
        }
172
173
        /**
174
         * Now we are left with both left and right matching so we can merge the collections
175
         * together and return the single largest valued domino.
176
         *
177
         * @var DominoCollection $mergedCollection
178
         */
179
        $mergedCollection = $matchesLeft->createMergedCollection($matchesRight);
180
181
        return $mergedCollection->getDominoWithHighestValue();
182
    }
183
184
    /**
185
     * @return string
186
     */
187
    public function __toString(): string
188
    {
189
        return $this->getName();
190
    }
191
}
192