Completed
Push — master ( 3a1491...416016 )
by Rémi
02:59
created

Hangman::apply()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 4
cts 5
cp 0.8
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 1
crap 2.032
1
<?php
2
3
namespace Hangman\Entity;
4
5
use Broadway\EventSourcing\EventSourcedAggregateRoot;
6
use Hangman\Event\HangmanGameCreatedEvent;
7
use Hangman\Event\HangmanGameFailedStartingEvent;
8
use Hangman\Event\HangmanGameLostEvent;
9
use Hangman\Event\HangmanGameStartedEvent;
10
use Hangman\Event\HangmanPlayerCreatedEvent;
11
use Hangman\Event\HangmanPlayerFailedCreatingEvent;
12
use Hangman\Event\HangmanPlayerLostEvent;
13
use Hangman\Event\HangmanPlayerProposedInvalidAnswerEvent;
14
use Hangman\Event\HangmanPlayerTriedPlayingDuringAnotherPlayerTurnEvent;
15
use Hangman\Event\HangmanPlayerTriedPlayingInactiveGameEvent;
16
use Hangman\Event\HangmanPlayerTurnEvent;
17
use Hangman\Event\HangmanPlayerWinEvent;
18
use Hangman\Exception\HangmanException;
19
use Hangman\Exception\HangmanPlayerOptionsException;
20
use Hangman\Move\Answer;
21
use Hangman\Move\Proposition;
22
use Hangman\Options\HangmanPlayerOptions;
23
use Hangman\Result\HangmanBadProposition;
24
use Hangman\Result\HangmanGoodProposition;
25
use Hangman\Result\HangmanLost;
26
use Hangman\Result\HangmanWon;
27
use MiniGame\Entity\MiniGame;
28
use MiniGame\Entity\MiniGameId;
29
use MiniGame\Entity\Player;
30
use MiniGame\Entity\PlayerId;
31
use MiniGame\Entity\PlayTrait;
32
use MiniGame\GameResult;
33
use MiniGame\PlayerOptions;
34
35
class Hangman extends EventSourcedAggregateRoot implements MiniGame
36
{
37
    use PlayTrait;
38
39
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
40
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
41
    //////////////////////////////////////////////   CONSTANTS   ///////////////////////////////////////////////////
42
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
43
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
44
45
    const STATE_UNINITIALIZED = 'uninitialized';
46
    const STATE_READY = 'ready';
47
    const STATE_STARTED = 'started';
48
    const STATE_OVER = 'over';
49
50
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
51
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
52
    /////////////////////////////////////////////   PROPERTIES   ///////////////////////////////////////////////////
53
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
54
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
55
56
    /**
57
     * @var MiniGameId
58
     */
59
    private $id;
60
61
    /**
62
     * @var string
63
     */
64
    private $word;
65
66
    /**
67
     * @var HangmanPlayer[]
68
     **/
69
    private $players;
70
71
    /**
72
     * @var array
73
     */
74
    protected $gameOrder;
75
76
    /**
77
     * @var HangmanPlayer
78
     **/
79
    private $currentPlayer;
80
81
    /**
82
     * @var string
83
     */
84
    private $state;
85
86
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
87
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
88
    /////////////////////////////////////////   PRIVATE CONSTRUCTOR   //////////////////////////////////////////////
89
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
90
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
91
92
    /**
93
     * Constructor
94
     */
95 111
    private function __construct()
96
    {
97 111
        $this->state = self::STATE_UNINITIALIZED;
98 111
    }
99
100
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
101
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
102
    //////////////////////////////////////////////   ACCESSORS   ///////////////////////////////////////////////////
103
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
104
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
105
106
    /**
107
     * Returns the id of the game
108
     *
109
     * @return MiniGameId
110
     */
111 93
    public function getId()
112
    {
113 93
        return $this->id;
114
    }
115
116
    /**
117
     * Returns the aggregate id
118
     *
119
     * @return MiniGameId
120
     */
121 111
    public function getAggregateRootId()
122
    {
123 111
        return $this->id;
124
    }
125
126
    /**
127
     * Returns the name of the mini-game
128
     *
129
     * @return string
130
     */
131 3
    public static function getName()
132
    {
133 3
        return 'HANGMAN';
134
    }
135
136
    /**
137
     * Get the player identified by PlayerId
138
     *
139
     * @param PlayerId $playerId
140
     *
141
     * @return HangmanPlayer
142
     */
143 57
    public function getPlayer(PlayerId $playerId = null)
144
    {
145 57
        if ($playerId === null) {
146 3
            return null;
147
        }
148
149 54
        return isset($this->players[(string)$playerId]) ? $this->players[(string)$playerId] : null;
150
    }
151
152
    /**
153
     * Returns the player who can play
154
     *
155
     * @return Player
156
     */
157 3
    public function getCurrentPlayer()
158
    {
159 3
        return $this->currentPlayer;
160
    }
161
162
    /**
163
     * Get the players
164
     *
165
     * @return Player[]
166
     */
167 111
    public function getPlayers()
168
    {
169 111
        return array_values($this->players);
170
    }
171
172
    /**
173
     * Is game started?
174
     *
175
     * @return bool
176
     */
177 36
    public function isGameStarted()
178
    {
179 36
        return $this->state === self::STATE_STARTED;
180
    }
181
182
    /**
183
     * Is it the player's turn?
184
     *
185
     * @param PlayerId $playerId
186
     *
187
     * @return bool
188
     */
189 36
    public function canPlayerPlay(PlayerId $playerId)
190
    {
191 36
        return $this->currentPlayer && $this->currentPlayer->getId()->equals($playerId);
192
    }
193
194
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
195
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
196
    ///////////////////////////////////////////   DOMAIN METHODS   /////////////////////////////////////////////////
197
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
198
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
199
200
    /**
201
     * Starts the game
202
     *
203
     * @param PlayerId $playerId
204
     *
205
     * @return GameResult
206
     */
207 51
    public function startGame(PlayerId $playerId)
208
    {
209 51
        if ($this->state !== self::STATE_READY) {
210 3
            $event = new HangmanGameFailedStartingEvent(
211 3
                $this->id,
212 2
                $playerId,
213 1
                HangmanGameFailedStartingEvent::BAD_STATE
214 2
            );
215 3
            $this->apply($event);
216 3
            return $event;
217
        }
218
219 51
        if (count($this->players) === 0) {
220 3
            $event = new HangmanGameFailedStartingEvent(
221 3
                $this->id,
222 2
                $playerId,
223 1
                HangmanGameFailedStartingEvent::NO_PLAYER
224 2
            );
225 3
            $this->apply($event);
226 3
            return $event;
227
        }
228
229 48
        $event = new HangmanGameStartedEvent($this->id, $playerId);
230 48
        $this->apply($event);
231
232 48
        $this->setNextPlayer($playerId);
233
234 48
        return $event;
235
    }
236
237
    /**
238
     * Adds a player to the game
239
     *
240
     * @param PlayerOptions $playerOptions
241
     *
242
     * @throws HangmanPlayerOptionsException
243
     * @throws HangmanException
244
     *
245
     * @return GameResult
246
     */
247 75
    public function addPlayerToGame(PlayerOptions $playerOptions)
248
    {
249 75
        if (! $playerOptions instanceof HangmanPlayerOptions) {
250 3
            throw new HangmanPlayerOptionsException(
251 3
                $playerOptions->getPlayerId(),
252 3
                $this->getId(),
253 1
                'Options are not recognized'
254 2
            );
255
        }
256
257 75
        if ($this->state !== self::STATE_READY) {
258 3
            $event = new HangmanPlayerFailedCreatingEvent(
259 3
                $this->id,
260 3
                $playerOptions->getPlayerId(),
261 3
                $playerOptions->getExternalReference()
262 2
            );
263 3
            $this->apply($event);
264 3
            return $event;
265
        }
266
267 75
        $event = new HangmanPlayerCreatedEvent(
268 75
            $this->id,
269 75
            $playerOptions->getPlayerId(),
270 75
            $playerOptions->getName(),
271 75
            $playerOptions->getLives(),
272 75
            $playerOptions->getExternalReference()
273 50
        );
274 75
        $this->apply($event);
275 75
        return $event;
276
    }
277
278
    /**
279
     * A player leaves the game
280
     *
281
     * @param PlayerId $playerId
282
     *
283
     * @return GameResult
284
     */
285 9
    public function leaveGame(PlayerId $playerId)
286
    {
287 9
        switch ($this->state) {
288 9
            case self::STATE_STARTED:
289 3
                $player = $this->getPlayer($playerId);
290 3
                return $player ? $this->playerLoses($player) : null;
291 6
            case self::STATE_OVER:
292 3
                break;
293 2
            default:
294 3
                if (isset($this->players[(string) $playerId])) {
295 3
                    unset($this->players[(string) $playerId]);
296 2
                }
297 3
                break;
298 4
        }
299 6
        return null;
300
    }
301
302
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
303
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
304
    //////////////////////////////////////////   PRIVATE METHODS   /////////////////////////////////////////////////
305
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
306
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
307
308
    /**
309
     * Initialize the game
310
     *
311
     * @param MiniGameId             $id
312
     * @param string                 $word
313
     */
314 111
    private function initialize(MiniGameId $id, $word)
315
    {
316 111
        $this->apply(new HangmanGameCreatedEvent($id, $word));
317 111
    }
318
319
    /**
320
     * Player proposes a letter
321
     *
322
     * @param PlayerId $playerId
323
     * @param Proposition $move
324
     *
325
     * @return GameResult
326
     */
327 21
    private function playProposition(PlayerId $playerId, Proposition $move)
328
    {
329 21
        if ($errorEvent = $this->ensurePlayerCanPlay($playerId)) {
330 6
            $this->apply($errorEvent);
331 6
            return $errorEvent;
332
        }
333
334 15
        return $this->currentPlayerProposeLetter($move->getText());
335
    }
336
337
    /**
338
     * Player tries an answer
339
     *
340
     * @param PlayerId $playerId
341
     * @param Answer $move
342
     *
343
     * @return GameResult
344
     */
345 15
    private function playAnswer(PlayerId $playerId, Answer $move)
346
    {
347 15
        if ($errorEvent = $this->ensurePlayerCanPlay($playerId)) {
348 3
            $this->apply($errorEvent);
349 3
            return $errorEvent;
350
        }
351
352
        try {
353 12
            return $this->currentPlayerProposeAnswer($move->getText());
354 3
        } catch (HangmanException $e) {
355 3
            $event = new HangmanPlayerProposedInvalidAnswerEvent(
356 3
                $this->getId(),
357 2
                $playerId,
358
                $move
359 2
            );
360 3
            $this->apply($event);
361 3
            return $event;
362
        }
363
    }
364
365
    /**
366
     * Returns an error event if player cannot play
367
     *
368
     * @param PlayerId $playerId
369
     *
370
     * @return GameResult
371
     */
372 36
    private function ensurePlayerCanPlay(PlayerId $playerId)
373
    {
374 36
        if (!$this->isGameStarted()) {
375 3
            $event = new HangmanPlayerTriedPlayingInactiveGameEvent(
376 3
                $this->getId(),
377
                $playerId
378 2
            );
379 3
            return $event;
380
        }
381
382 33
        if (!$this->canPlayerPlay($playerId)) {
383 6
            $event = new HangmanPlayerTriedPlayingDuringAnotherPlayerTurnEvent(
384 6
                $this->getId(),
385
                $playerId
386 4
            );
387 6
            return $event;
388
        }
389
390 27
        return null;
391
    }
392
393
    /**
394
     * Propose a letter
395
     *
396
     * @param string $letter
397
     *
398
     * @return HangmanBadProposition | HangmanGoodProposition
399
     */
400 15
    private function currentPlayerProposeLetter($letter)
401
    {
402 15
        $capLetter = strtoupper($letter);
403 15
        $letterPresent = $this->wordContains($capLetter);
404
405 15
        $result =  (!$letterPresent)
406 13
                   ? $this->currentPlayerBadProposition($letter) // remove a life
407 15
                   : $this->currentPlayerGoodProposition($letter); // yay!
408
409 15
        return $result;
410
    }
411
412
    /**
413
     * Propose an answer
414
     *
415
     * @param string $answer
416
     *
417
     * @return HangmanLost | HangmanWon
418
     */
419 12
    private function currentPlayerProposeAnswer($answer)
420
    {
421 12
        $this->checkAnswerIsValid($answer);
422
423 9
        if ($this->isTheAnswer(strtoupper($answer))) {
424 3
            return $this->playerWins($this->currentPlayer); // you win
425
        } else {
426 6
            return $this->playerLoses($this->currentPlayer); // you lose
427
        }
428
    }
429
430
    /**
431
     * Function to call when a bad proposition has been made
432
     *
433
     * @param string $letter
434
     *
435
     * @return HangmanBadProposition | HangmanLost
436
     */
437 9
    private function currentPlayerBadProposition($letter)
438
    {
439 9
        $player = $this->currentPlayer;
440
441 9
        $event = $player->playBadLetter($letter, 1);
442
443 9
        if ($event->getRemainingLives() === 0) {
444 6
            return $this->playerLoses($player);
445
        }
446
447 3
        $this->setNextPlayer($this->getNextPlayerId());
448
449 3
        return $event;
450
    }
451
452
    /**
453
     * Function to call after a good proposition of letter has been made
454
     *
455
     * @param string $letter
456
     *
457
     * @return HangmanGoodProposition | HangmanWon
458
     */
459 6
    private function currentPlayerGoodProposition($letter)
460
    {
461 6
        $player = $this->currentPlayer;
462
463 6
        $event = $player->playGoodLetter($letter);
464
465 6
        if ($this->isAllLettersFoundForPlayer($player)) {
466 3
            return $this->playerWins($player);
467
        }
468
469 3
        $this->setNextPlayer($this->getNextPlayerId());
470
471 3
        return $event;
472
    }
473
474
    /**
475
     * Function to call when game is won by a player
476
     *
477
     * @param HangmanPlayer $player
478
     *
479
     * @return HangmanWon
480
     */
481 6
    private function playerWins(HangmanPlayer $player)
482
    {
483 6
        $playerId = $player->getId();
484
485 6
        $playedLetters = $this->getPlayedLettersForPlayer($playerId);
486 6
        $remainingLives = $this->getRemainingLives($playerId);
487
488 6
        $event = new HangmanPlayerWinEvent(
489 6
            $this->id,
490 4
            $playerId,
491 4
            $playedLetters,
492 4
            $remainingLives,
493 6
            $this->word
494 4
        );
495 6
        $this->apply($event);
496
497 6
        foreach ($this->players as $otherPlayer) {
498 6
            if ($otherPlayer->equals($player) || $otherPlayer->hasLost()) {
499 6
                continue;
500
            }
501 6
            $otherPlayer->lose($this->word);
502 4
        }
503
504 6
        return $event;
505
    }
506
507
    /**
508
     * Function to call when game is lost by a player
509
     *
510
     * @param HangmanPlayer $player
511
     *
512
     * @return hangmanLost | HangmanGameLostEvent
513
     */
514 15
    private function playerLoses(HangmanPlayer $player)
515
    {
516 15
        $nextPlayerId = $this->getNextPlayerId();
517
518 15
        $event = $player->lose($this->word);
519
520 15
        if (count($this->gameOrder) > 0 &&
521 15
            $this->currentPlayer &&
522 13
            $player->equals($this->currentPlayer)
523 10
        ) {
524 12
            $this->setNextPlayer($nextPlayerId);
525 12
            return $event;
526
        }
527
528 3
        $event = new HangmanGameLostEvent(
529 3
            $this->id,
530 3
            $player->getId(),
531 3
            $this->word
532 2
        );
533 3
        $this->apply($event);
534
535 3
        return $event;
536
    }
537
538
    /**
539
     * Sets the next player
540
     *
541
     * @param PlayerId $id
542
     */
543 48
    private function setNextPlayer(PlayerId $id = null)
544
    {
545 48
        if ($id === null || ($this->currentPlayer && $this->currentPlayer->getId()->equals($id))) {
546
            return;
547
        }
548
549 48
        $this->apply(
550 48
            new HangmanPlayerTurnEvent($this->getId(), $id)
551 32
        );
552 48
    }
553
554
    /**
555
     * Returns the next player in line
556
     *
557
     * @return PlayerId
558
     */
559 21
    private function getNextPlayerId()
560
    {
561 21
        if ($this->currentPlayer === null) {
562
            return null;
563
        }
564
565 21
        $nbPlayers = count($this->gameOrder);
566 21
        $currentPlayerId = (string)$this->currentPlayer->getId();
567 21
        $nextPlayerPosition = (array_search($currentPlayerId, $this->gameOrder) + 1) % $nbPlayers;
568
569 21
        $pos = $nextPlayerPosition;
570
        do {
571 21
            $id = PlayerId::create($this->gameOrder[$pos]);
572 21
            $player = $this->getPlayer($id);
573
574 21
            if ($player->getState() === HangmanPlayer::STATE_IN_GAME) {
575 21
                return $id;
576
            }
577
578
            $pos = ($pos + 1) % $nbPlayers;
579
        } while ($pos !== $nextPlayerPosition);
580
581
        return null;
582
    }
583
584
    /**
585
     * Returns the list of played letters
586
     *
587
     * @param PlayerId $playerId
588
     *
589
     * @return array
590
     */
591 6
    private function getPlayedLettersForPlayer(PlayerId $playerId)
592
    {
593 6
        return $this->getPlayer($playerId)->getPlayedLetters();
594
    }
595
596
    /**
597
     * Gets the remaining lives for the player
598
     *
599
     * @param PlayerId $playerId
600
     *
601
     * @return int
602
     */
603 6
    private function getRemainingLives(PlayerId $playerId)
604
    {
605 6
        return $this->getPlayer($playerId)->getRemainingLives();
606
    }
607
608
    /**
609
     * Returns the indexes of the letter in the word
610
     *
611
     * @param string $letter
612
     *
613
     * @return boolean
614
     */
615 15
    private function wordContains($letter)
616
    {
617 15
        return strpos(strtoupper($this->word), strtoupper($letter)) !== false;
618
    }
619
620
    /**
621
     * Get the letters of the word
622
     *
623
     * @return string[]
624
     */
625 42
    private function getLettersFromWord()
626
    {
627 42
        return array_unique(str_split(strtoupper($this->word)));
628
    }
629
630
    /**
631
     * Build the word from played letters
632
     *
633
     * @param string[] $playedLetters
634
     *
635
     * @return string
636
     */
637 42
    public function buildWord($playedLetters)
638
    {
639 42
        $wordLetters = $this->getLettersFromWord();
640
641 42
        $goodLetters = array_intersect($wordLetters, $playedLetters);
642
643 42
        $splitWord = str_split(strtoupper($this->word));
644 42
        $word = '';
645 42
        foreach ($splitWord as $letter) {
646 42
            $word .= (in_array($letter, $goodLetters) ? $letter : '_') . ' ';
647 28
        }
648
649 42
        return trim($word);
650
    }
651
652
    /**
653
     * Checks if all letters for the word have been found
654
     *
655
     * @param HangmanPlayer $player
656
     *
657
     * @return bool
658
     */
659 6
    private function isAllLettersFoundForPlayer(HangmanPlayer $player)
660
    {
661 6
        $wordLetters = $this->getLettersFromWord();
662 6
        $playerLetters = $player->getPlayedLetters();
663 6
        return count(array_intersect($wordLetters, $playerLetters)) == count($wordLetters);
664
    }
665
666
    /**
667
     * Checks if the answer is valid
668
     * If it's not, ends player turn and throws an HangmanException
669
     *
670
     * @param string $answer
671
     *
672
     * @throws HangmanException
673
     */
674 12
    private function checkAnswerIsValid($answer)
675
    {
676 12
        if (strlen($answer) !== strlen($this->word)) {
677 3
            throw new HangmanException(sprintf('"%s" is not a valid answer!', $answer));
678
        }
679 9
    }
680
681
    /**
682
     * Checks if the word is the same as the solution
683
     *
684
     * @param string $word
685
     *
686
     * @return bool
687
     */
688 9
    private function isTheAnswer($word)
689
    {
690 9
        return ($this->word === strtoupper($word));
691
    }
692
693
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
694
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
695
    ////////////////////////////////////////////   APPLY EVENTS   //////////////////////////////////////////////////
696
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
697
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
698
699
    /**
700
     * Apply the game created event
701
     *
702
     * @param HangmanGameCreatedEvent $event
703
     *
704
     * @return void
705
     */
706 111
    protected function applyHangmanGameCreatedEvent(HangmanGameCreatedEvent $event)
707
    {
708 111
        $this->id = $event->getGameId();
709 111
        $this->word = strtoupper($event->getWord());
710 111
        $this->players = [];
711 111
        $this->gameOrder = [];
712 111
        $this->state = self::STATE_READY;
713 111
    }
714
715
    /**
716
     * Apply the player created event
717
     *
718
     * @param HangmanPlayerCreatedEvent $event
719
     *
720
     * @return void
721
     */
722 75
    protected function applyHangmanPlayerCreatedEvent(HangmanPlayerCreatedEvent $event)
723
    {
724 75
        $player = new HangmanPlayer(
725 75
            $event->getPlayerId(),
726 75
            $event->getPlayerName(),
727 75
            $event->getLives(),
728 50
            $this,
729 75
            $event->getExternalReference()
730 50
        );
731
732 75
        $this->gameOrder[] = (string)$player->getId();
733 75
        $this->players[(string)$player->getId()] = $player;
734 75
    }
735
736
    /**
737
     * Apply the game created event
738
     */
739 48
    protected function applyHangmanGameStartedEvent()
740
    {
741 48
        $this->state = self::STATE_STARTED;
742 48
    }
743
744
    /**
745
     * Apply the player turn event
746
     *
747
     * @param HangmanPlayerTurnEvent $event
748
     */
749 48
    protected function applyHangmanPlayerTurnEvent(HangmanPlayerTurnEvent $event)
750
    {
751 48
        $this->currentPlayer = $this->getPlayer($event->getPlayerId());
752 48
    }
753
754
    /**
755
     * Apply the hangman player lost event
756
     *
757
     * @param HangmanPlayerLostEvent $event
758
     */
759 21
    protected function applyHangmanPlayerLostEvent(HangmanPlayerLostEvent $event)
760
    {
761 21
        $this->state = self::STATE_OVER;
762 21
        unset($this->gameOrder[array_search((string) $event->getPlayerId(), $this->gameOrder)]);
763 21
    }
764
765
    /**
766
     * Apply the hangman player win event
767
     *
768
     * @return void
769
     */
770 6
    protected function applyHangmanPlayerWinEvent()
771
    {
772 6
        $this->currentPlayer = null;
773 6
        $this->state = self::STATE_OVER;
774 6
    }
775
776
    /**
777
     * Apply the hangman lost by all event
778
     *
779
     * @return void
780
     */
781 3
    protected function applyHangmanGameLostEvent()
782
    {
783 3
        $this->currentPlayer = null;
784 3
        $this->state = self::STATE_OVER;
785 3
    }
786
787
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
788
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
789
    ////////////////////////////////////////////   EVENT SOURCED   /////////////////////////////////////////////////
790
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
791
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
792
793
    /**
794
     * @return Player[]
795
     */
796 111
    protected function getChildEntities()
797
    {
798 111
        return $this->getPlayers();
799
    }
800
801
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
802
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
803
    /////////////////////////////////////////   STATIC CONSTRUCTOR   ///////////////////////////////////////////////
804
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
805
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
806
807
    /**
808
     * Create a new instance
809
     *
810
     * @param MiniGameId $id
811
     * @param string     $word
812
     *
813
     * @return Hangman
814
     */
815 111
    public static function createGame(MiniGameId $id, $word)
816
    {
817 111
        $hangman = new self();
818 111
        $hangman->initialize($id, $word);
819
820 111
        return $hangman;
821
    }
822
823
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
824
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
825
    ///////////////////////////////////////////   RECONSTITUTION   /////////////////////////////////////////////////
826
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
827
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
828
829
    /**
830
     * Static construction method for reconstitution
831
     *
832
     * @return Hangman
833
     */
834 3
    public static function instantiateForReconstitution()
835
    {
836 3
        return new self();
837
    }
838
839
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
840
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
841
    /////////////////////////////////////////   APPLY RESTRICTIONS   ///////////////////////////////////////////////
842
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
843
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
844
845
846
    /**
847
     * @param mixed $event
848
     */
849 111
    public function apply($event)
850
    {
851 111
        if (! $this->isSupportedEvent($event)) {
852
            return;
853
        }
854
855 111
        parent::apply($event);
856 111
    }
857
858
    /**
859
     * @param mixed $event
860
     *
861
     * @return bool
862
     */
863 111
    private function isSupportedEvent($event)
864
    {
865
        return (
866 111
            $event instanceof GameResult &&
867 111
            ($this->id === null || $this->id == $event->getGameId())
868 74
        );
869
    }
870
}
871