Hangman::apply()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 8
ccs 5
cts 5
cp 1
rs 9.4285
cc 2
eloc 4
nc 2
nop 1
crap 2
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\Exception\HangmanException;
18
use Hangman\Exception\HangmanPlayerOptionsException;
19
use Hangman\Move\Answer;
20
use Hangman\Move\Proposition;
21
use Hangman\Options\HangmanPlayerOptions;
22
use Hangman\PlayersCollection;
23
use Hangman\Result\HangmanBadProposition;
24
use Hangman\Result\HangmanGoodProposition;
25
use Hangman\Result\HangmanLost;
26
use Hangman\Result\HangmanWon;
27
use Hangman\Word;
28
use MiniGame\Entity\MiniGame;
29
use MiniGame\Entity\MiniGameId;
30
use MiniGame\Entity\Player;
31
use MiniGame\Entity\PlayerId;
32
use MiniGame\Entity\PlayTrait;
33
use MiniGame\GameResult;
34
use MiniGame\PlayerOptions;
35
36
class Hangman extends EventSourcedAggregateRoot implements MiniGame
37
{
38
    use PlayTrait;
39
40
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
41
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
42
    //////////////////////////////////////////////   CONSTANTS   ///////////////////////////////////////////////////
43
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
44
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
45
46
    const STATE_UNINITIALIZED = 'uninitialized';
47
    const STATE_READY = 'ready';
48
    const STATE_STARTED = 'started';
49
    const STATE_OVER = 'over';
50
51
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
52
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
53
    /////////////////////////////////////////////   PROPERTIES   ///////////////////////////////////////////////////
54
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
55
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
56
57
    /**
58
     * @var MiniGameId
59
     */
60
    private $id;
61
62
    /**
63
     * @var Word
64
     */
65
    private $word;
66
67
    /**
68
     * @var PlayersCollection
69
     **/
70
    private $players;
71
72
    /**
73
     * @var string
74
     */
75
    private $state;
76
77
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
78
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
79
    /////////////////////////////////////////   PRIVATE CONSTRUCTOR   //////////////////////////////////////////////
80
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
81
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
82
83
    /**
84
     * Constructor
85
     */
86 114
    private function __construct()
87
    {
88 114
        $this->state = self::STATE_UNINITIALIZED;
89 114
    }
90
91
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
92
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
93
    //////////////////////////////////////////////   ACCESSORS   ///////////////////////////////////////////////////
94
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
95
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
96
97
    /**
98
     * Returns the id of the game
99
     *
100
     * @return MiniGameId
101
     */
102 87
    public function getId()
103 12
    {
104 87
        return $this->id;
105
    }
106
107
    /**
108
     * Returns the aggregate id
109
     *
110
     * @return string
111
     */
112 114
    public function getAggregateRootId()
113
    {
114 114
        return (string) $this->id;
115
    }
116
117
    /**
118
     * Returns the name of the mini-game
119
     *
120
     * @return string
121
     */
122 3
    public static function getName()
123
    {
124 3
        return 'HANGMAN';
125
    }
126
127
    /**
128
     * Get the player identified by PlayerId
129
     *
130
     * @param PlayerId $playerId
131
     *
132
     * @return HangmanPlayer
133
     */
134 12
    public function getPlayer(PlayerId $playerId = null)
135
    {
136 12
        if ($playerId === null) {
137 3
            return null;
138
        }
139
140 9
        return $this->players->get((string)$playerId);
141
    }
142
143
    /**
144
     * Returns the player who can play
145
     *
146
     * @return Player
147
     */
148 3
    public function getCurrentPlayer()
149
    {
150 3
        return $this->players->getCurrentPlayer();
151
    }
152
153
    /**
154
     * Get the players
155
     *
156
     * @return Player[]
157
     */
158 114
    public function getPlayers()
159
    {
160 114
        return $this->players->toArray();
161
    }
162
163
    /**
164
     * Is game started?
165
     *
166
     * @return bool
167
     */
168 36
    public function isGameStarted()
169
    {
170 36
        return $this->state === self::STATE_STARTED;
171
    }
172
173
    /**
174
     * Is it the player's turn?
175
     *
176
     * @param PlayerId $playerId
177
     *
178
     * @return bool
179
     */
180 36
    public function canPlayerPlay(PlayerId $playerId)
181
    {
182 36
        return $this->players->canPlay($playerId);
183
    }
184
185
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
186
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
187
    ///////////////////////////////////////////   DOMAIN METHODS   /////////////////////////////////////////////////
188
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
189
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
190
191
    /**
192
     * Starts the game
193
     *
194
     * @param PlayerId $playerId
195
     *
196
     * @return GameResult
197
     */
198 51
    public function startGame(PlayerId $playerId)
199
    {
200 51
        if (! $this->isGameReady()) {
201 3
            return $this->failStarting($playerId, HangmanGameFailedStartingEvent::BAD_STATE);
202
        }
203
204 51
        if (! $this->players->hasPlayers()) {
205 3
            return $this->failStarting($playerId, HangmanGameFailedStartingEvent::NO_PLAYER);
206
        }
207
208 48
        $event = new HangmanGameStartedEvent($this->id, $playerId);
209 48
        $this->apply($event);
210
211 48
        $this->setNextPlayer($playerId);
212
213 48
        return $event;
214
    }
215
216
    /**
217
     * Adds a player to the game
218
     *
219
     * @param PlayerOptions $playerOptions
220
     *
221
     * @throws HangmanPlayerOptionsException
222
     * @throws HangmanException
223
     *
224
     * @return GameResult
225
     */
226 66
    public function addPlayerToGame(PlayerOptions $playerOptions)
227
    {
228 66
        if (! $playerOptions instanceof HangmanPlayerOptions) {
229 3
            throw new HangmanPlayerOptionsException(
230 3
                $playerOptions->getPlayerId(),
231 3
                $this->getId(),
232 1
                'Options are not recognized'
233 2
            );
234
        }
235
236 63
        if (! $this->isGameReady()) {
237 3
            $event = new HangmanPlayerFailedCreatingEvent(
238 3
                $this->id,
239 3
                $playerOptions->getPlayerId(),
240 3
                $playerOptions->getExternalReference()
241 2
            );
242 3
            $this->apply($event);
243 3
            return $event;
244
        }
245
246 63
        $event = new HangmanPlayerCreatedEvent(
247 63
            $this->id,
248 63
            $playerOptions->getPlayerId(),
249 63
            $playerOptions->getName(),
250 63
            $playerOptions->getLives(),
251 63
            $playerOptions->getExternalReference()
252 42
        );
253 63
        $this->apply($event);
254 63
        return $event;
255
    }
256
257
    /**
258
     * A player leaves the game
259
     *
260
     * @param PlayerId $playerId
261
     *
262
     * @return GameResult
263
     */
264 9
    public function leaveGame(PlayerId $playerId)
265
    {
266 9
        switch ($this->state) {
267 9
            case self::STATE_STARTED:
268 3
                $player = $this->getPlayer($playerId);
269 3
                return $player ? $this->playerLoses($player) : null;
270 6
            case self::STATE_OVER:
271 3
                break;
272 2
            default:
273 3
                $this->players->remove((string) $playerId);
274 3
                break;
275 4
        }
276 6
        return null;
277 4
    }
278
279
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
280
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
281
    //////////////////////////////////////////   PRIVATE METHODS   /////////////////////////////////////////////////
282
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
283
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
284
285
    /**
286
     * Initialize the game
287
     *
288
     * @param MiniGameId             $id
289
     * @param string                 $word
290
     */
291 114
    private function initialize(MiniGameId $id, $word)
292
    {
293 114
        $this->apply(new HangmanGameCreatedEvent($id, $word));
294 114
    }
295
296
    /**
297
     * @return bool
298
     */
299 66
    private function isGameReady()
300
    {
301 66
        return $this->state === self::STATE_READY;
302
    }
303
304
    /**
305
     * @param PlayerId $playerId
306
     * @param string   $reason
307
     *
308
     * @return HangmanGameFailedStartingEvent
309
     */
310 6
    private function failStarting(PlayerId $playerId, $reason)
311
    {
312 6
        $event = new HangmanGameFailedStartingEvent(
313 6
            $this->id,
314 4
            $playerId,
315
            $reason
316 4
        );
317 6
        $this->apply($event);
318 6
        return $event;
319
    }
320
321
    /**
322
     * Player proposes a letter
323
     *
324
     * @param PlayerId $playerId
325
     * @param Proposition $move
326
     *
327
     * @return GameResult
328
     */
329 21
    private function playProposition(PlayerId $playerId, Proposition $move)
330
    {
331 21
        if ($errorEvent = $this->ensurePlayerCanPlay($playerId)) {
332 6
            $this->apply($errorEvent);
333 6
            return $errorEvent;
334
        }
335
336 15
        return $this->currentPlayerProposeLetter($move->getText());
337
    }
338
339
    /**
340
     * Player tries an answer
341
     *
342
     * @param PlayerId $playerId
343
     * @param Answer $move
344
     *
345
     * @return GameResult
346
     */
347 15
    private function playAnswer(PlayerId $playerId, Answer $move)
348
    {
349 15
        if ($errorEvent = $this->ensurePlayerCanPlay($playerId)) {
350 3
            $this->apply($errorEvent);
351 3
            return $errorEvent;
352
        }
353
354
        try {
355 12
            return $this->currentPlayerProposeAnswer($move->getText());
356 3
        } catch (HangmanException $e) {
357 3
            $event = new HangmanPlayerProposedInvalidAnswerEvent(
358 3
                $this->getId(),
359 2
                $playerId,
360
                $move
361 2
            );
362 3
            $this->apply($event);
363 3
            return $event;
364
        }
365
    }
366
367
    /**
368
     * Returns an error event if player cannot play
369
     *
370
     * @param PlayerId $playerId
371
     *
372
     * @return GameResult
373
     */
374 36
    private function ensurePlayerCanPlay(PlayerId $playerId)
375
    {
376 36
        if (!$this->isGameStarted()) {
377 3
            $event = new HangmanPlayerTriedPlayingInactiveGameEvent(
378 3
                $this->getId(),
379
                $playerId
380 2
            );
381 3
            return $event;
382
        }
383
384 33
        if (!$this->canPlayerPlay($playerId)) {
385 6
            $event = new HangmanPlayerTriedPlayingDuringAnotherPlayerTurnEvent(
386 6
                $this->getId(),
387
                $playerId
388 4
            );
389 6
            return $event;
390
        }
391
392 27
        return null;
393
    }
394
395
    /**
396
     * Propose a letter
397
     *
398
     * @param string $letter
399
     *
400
     * @return HangmanBadProposition | HangmanGoodProposition
401
     */
402 15
    private function currentPlayerProposeLetter($letter)
403
    {
404 15
        $result =  (!$this->word->contains($letter))
405 13
                   ? $this->currentPlayerBadProposition($letter) // remove a life
406 15
                   : $this->currentPlayerGoodProposition($letter); // yay!
407
408 15
        return $result;
409
    }
410
411
    /**
412
     * Propose an answer
413
     *
414
     * @param string $answer
415
     *
416
     * @return HangmanLost | HangmanWon
417
     */
418 12
    private function currentPlayerProposeAnswer($answer)
419
    {
420 12
        $this->checkAnswerIsValid($answer);
421
422 9
        if (! $this->word->equals($answer)) {
423 6
            return $this->playerLoses($this->players->getCurrentPlayer()); // you lose
424
        }
425
426 3
        return $this->playerWins($this->players->getCurrentPlayer()); // you win
427
    }
428
429
    /**
430
     * Function to call when a bad proposition has been made
431
     *
432
     * @param string $letter
433
     *
434
     * @return HangmanBadProposition | HangmanLost
435
     */
436 9
    private function currentPlayerBadProposition($letter)
437
    {
438 9
        $player = $this->players->getCurrentPlayer();
439
440 9
        $event = $player->playBadLetter($letter, 1);
441
442 9
        if ($event->getRemainingLives() === 0) {
443 6
            return $this->playerLoses($player);
444
        }
445
446 3
        $this->setNextPlayer($this->players->getNextPlayerId());
447
448 3
        return $event;
449
    }
450
451
    /**
452
     * Function to call after a good proposition of letter has been made
453
     *
454
     * @param string $letter
455
     *
456
     * @return HangmanGoodProposition | HangmanWon
457
     */
458 6
    private function currentPlayerGoodProposition($letter)
459
    {
460 6
        $player = $this->players->getCurrentPlayer();
461
462 6
        $event = $player->playGoodLetter($letter);
463
464 6
        if ($this->isAllLettersFoundForPlayer($player)) {
465 3
            return $this->playerWins($player);
466
        }
467
468 3
        $this->setNextPlayer($this->players->getNextPlayerId());
469
470 3
        return $event;
471
    }
472
473
    /**
474
     * Function to call when game is won by a player
475
     *
476
     * @param HangmanPlayer $player
477
     *
478
     * @return HangmanWon
479
     */
480 6
    private function playerWins(HangmanPlayer $player)
481
    {
482 6
        $event = $player->win($this->word);
483
484 6
        foreach ($this->players as $otherPlayer) {
485 6
            if ($otherPlayer->equals($player) || $otherPlayer->hasLost()) {
486 6
                continue;
487
            }
488 6
            $otherPlayer->lose($this->word);
489 4
        }
490
491 6
        return $event;
492
    }
493
494
    /**
495
     * Function to call when game is lost by a player
496
     *
497
     * @param HangmanPlayer $player
498
     *
499
     * @return hangmanLost | HangmanGameLostEvent
500
     */
501 15
    private function playerLoses(HangmanPlayer $player)
502
    {
503 15
        $event = $player->lose($this->word);
504
505 15
        if ($this->players->hasAtLeastOneActivePlayer() &&
506 13
            $this->players->isCurrentPlayer($player->getId())
507 10
        ) {
508 12
            $this->setNextPlayer($this->players->getNextPlayerId());
509 12
            return $event;
510
        }
511
512 3
        $event = new HangmanGameLostEvent(
513 3
            $this->id,
514 3
            $player->getId(),
515 3
            (string) $this->word
516 2
        );
517 3
        $this->apply($event);
518
519 3
        return $event;
520
    }
521
522
    /**
523
     * Sets the next player
524
     *
525
     * @param PlayerId $id
526
     */
527 48
    private function setNextPlayer(PlayerId $id = null)
528
    {
529 48
        if ($id === null || $this->players->isCurrentPlayer($id)) {
530 48
            return;
531
        }
532
533 18
        $this->apply(
534 18
            new HangmanPlayerTurnEvent($this->getId(), $id)
535 12
        );
536 18
    }
537
538
    /**
539
     * Build the word from played letters
540
     *
541
     * @param string[] $playedLetters
542
     *
543
     * @return string
544
     */
545 42
    public function buildWord($playedLetters)
546
    {
547 42
        return $this->word->buildWord($playedLetters);
548
    }
549
550
    /**
551
     * Checks if all letters for the word have been found
552
     *
553
     * @param HangmanPlayer $player
554
     *
555
     * @return bool
556
     */
557 6
    private function isAllLettersFoundForPlayer(HangmanPlayer $player)
558
    {
559 6
        $wordLetters = $this->word->getLetters();
560 6
        $playerLetters = $player->getPlayedLetters();
561 6
        return count(array_intersect($wordLetters, $playerLetters)) == count($wordLetters);
562
    }
563
564
    /**
565
     * Checks if the answer is valid
566
     * If it's not, ends player turn and throws an HangmanException
567
     *
568
     * @param string $answer
569
     *
570
     * @throws HangmanException
571
     */
572 12
    private function checkAnswerIsValid($answer)
573
    {
574 12
        if (! $this->word->isValid($answer)) {
575 3
            throw new HangmanException(sprintf('"%s" is not a valid answer!', $answer));
576
        }
577 9
    }
578
579
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
580
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
581
    ////////////////////////////////////////////   APPLY EVENTS   //////////////////////////////////////////////////
582
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
583
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
584
585
    /**
586
     * Apply the game created event
587
     *
588
     * @param HangmanGameCreatedEvent $event
589
     *
590
     * @return void
591
     */
592 114
    protected function applyHangmanGameCreatedEvent(HangmanGameCreatedEvent $event)
593
    {
594 114
        $this->id = $event->getGameId();
595 114
        $this->word = new Word($event->getWord());
596 114
        $this->players = new PlayersCollection();
597 114
        $this->state = self::STATE_READY;
598 114
    }
599
600
    /**
601
     * Apply the player created event
602
     *
603
     * @param HangmanPlayerCreatedEvent $event
604
     *
605
     * @return void
606
     */
607 63
    protected function applyHangmanPlayerCreatedEvent(HangmanPlayerCreatedEvent $event)
608
    {
609 63
        $this->players->add(
610 63
            new HangmanPlayer(
611 63
                $event->getPlayerId(),
612 63
                $event->getPlayerName(),
613 63
                $event->getLives(),
614 42
                $this,
615 63
                $event->getExternalReference()
616 42
            )
617 42
        );
618 63
    }
619
620
    /**
621
     * Apply the game created event
622
     */
623 48
    protected function applyHangmanGameStartedEvent()
624
    {
625 48
        $this->state = self::STATE_STARTED;
626 48
    }
627
628
    /**
629
     * Apply the player turn event
630
     *
631
     * @param HangmanPlayerTurnEvent $event
632
     */
633 18
    protected function applyHangmanPlayerTurnEvent(HangmanPlayerTurnEvent $event)
634
    {
635 18
        $this->players->setCurrentPlayer($event->getPlayerId());
636 18
    }
637
638
    /**
639
     * Apply the hangman player lost event
640
     *
641
     * @param HangmanPlayerLostEvent $event
642
     */
643 21
    protected function applyHangmanPlayerLostEvent(HangmanPlayerLostEvent $event)
0 ignored issues
show
Unused Code introduced by
The parameter $event is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
644
    {
645 21
        $this->state = self::STATE_OVER;
646 21
    }
647
648
    /**
649
     * Apply the hangman player win event
650
     *
651
     * @return void
652
     */
653 6
    protected function applyHangmanPlayerWinEvent()
654
    {
655 6
        $this->players->setCurrentPlayer(null);
656 6
        $this->state = self::STATE_OVER;
657 6
    }
658
659
    /**
660
     * Apply the hangman lost by all event
661
     *
662
     * @return void
663
     */
664 3
    protected function applyHangmanGameLostEvent()
665
    {
666 3
        $this->players->setCurrentPlayer(null);
667 3
        $this->state = self::STATE_OVER;
668 3
    }
669
670
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
671
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
672
    ////////////////////////////////////////////   EVENT SOURCED   /////////////////////////////////////////////////
673
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
674
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
675
676
    /**
677
     * @return Player[]
678
     */
679 114
    protected function getChildEntities()
680
    {
681 114
        return $this->getPlayers();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getPlayers(); (MiniGame\Entity\Player[]) is incompatible with the return type of the parent method Broadway\EventSourcing\E...eRoot::getChildEntities of type Broadway\EventSourcing\EventSourcedEntity[].

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
682
    }
683
684
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
685
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
686
    /////////////////////////////////////////   STATIC CONSTRUCTOR   ///////////////////////////////////////////////
687
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
688
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
689
690
    /**
691
     * Create a new instance
692
     *
693
     * @param MiniGameId $id
694
     * @param string     $word
695
     *
696
     * @return Hangman
697
     */
698 114
    public static function createGame(MiniGameId $id, $word)
699
    {
700 114
        $hangman = new self();
701 114
        $hangman->initialize($id, $word);
702
703 114
        return $hangman;
704
    }
705
706
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
707
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
708
    ///////////////////////////////////////////   RECONSTITUTION   /////////////////////////////////////////////////
709
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
710
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
711
712
    /**
713
     * Static construction method for reconstitution
714
     *
715
     * @return Hangman
716
     */
717 3
    public static function instantiateForReconstitution()
718
    {
719 3
        return new self();
720
    }
721
722
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
723
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
724
    /////////////////////////////////////////   APPLY RESTRICTIONS   ///////////////////////////////////////////////
725
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
726
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
727
728
    /**
729
     * @param mixed $event
730
     *
731
     * @throws HangmanException
732
     */
733 114
    public function apply($event)
734
    {
735 114
        if (! $this->isSupportedEvent($event)) {
736 3
            throw new HangmanException('You cannot apply a non hangman event.');
737
        }
738
739 114
        parent::apply($event);
740 114
    }
741
742
    /**
743
     * @param mixed $event
744
     *
745
     * @return bool
746
     */
747 114
    private function isSupportedEvent($event)
748
    {
749
        return (
750 114
            $event instanceof GameResult &&
751 114
            ($this->id === null || $this->id == $event->getGameId())
752 76
        );
753
    }
754
}
755