Crate   A
last analyzed

Complexity

Total Complexity 20

Size/Duplication

Total Lines 119
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 41
c 1
b 0
f 0
dl 0
loc 119
rs 10
wmc 20

12 Methods

Rating   Name   Duplication   Size   Complexity  
A id() 0 3 1
A movesUntilOutOfBounds() 0 12 2
A __construct() 0 10 1
A coordinates() 0 3 1
A canMoveInDirectionOf() 0 4 4
A canPush() 0 12 3
A after() 0 5 1
A canMoveUnhindered() 0 10 3
A wouldMoveOutOfBounds() 0 6 1
A __toString() 0 3 1
A fromIdAndRectangle() 0 9 1
A fromCoordinates() 0 7 1
1
<?php declare(strict_types=1);
2
3
namespace Stratadox\PuzzleSolver\Puzzle\SlidingCrates;
4
5
final class Crate
6
{
7
    /** @var string */
8
    private $id;
9
    /** @var Rectangle */
10
    private $rectangle;
11
    /** @var bool */
12
    private $canSlideHorizontally;
13
    /** @var bool */
14
    private $canSlideVertically;
15
16
    public function __construct(
17
        string $id,
18
        Rectangle $rectangle,
19
        bool $canSlideHorizontally,
20
        bool $canSlideVertically
21
    ) {
22
        $this->id = $id;
23
        $this->rectangle = $rectangle;
24
        $this->canSlideHorizontally = $canSlideHorizontally;
25
        $this->canSlideVertically = $canSlideVertically;
26
    }
27
28
    public static function fromCoordinates(
29
        string $id,
30
        array ...$coordinates
31
    ): self {
32
        return self::fromIdAndRectangle(
33
            $id,
34
            Rectangle::fromCoordinates(...$coordinates)
35
        );
36
    }
37
38
    public static function fromIdAndRectangle(
39
        string $id,
40
        Rectangle $rectangle
41
    ): self {
42
        return new self(
43
            $id,
44
            $rectangle,
45
            $rectangle->isHorizontal(),
46
            $rectangle->isVertical()
47
        );
48
    }
49
50
    public function after(Push $push): self
51
    {
52
        $new = clone $this;
53
        $new->rectangle = $new->rectangle->after($push);
54
        return $new;
55
    }
56
57
    public function id(): string
58
    {
59
        return $this->id;
60
    }
61
62
    public function wouldMoveOutOfBounds(
63
        Push $push,
64
        int $width,
65
        int $height
66
    ): bool {
67
        return $this->rectangle->after($push)->isOutOfBounds($width, $height);
68
    }
69
70
    public function movesUntilOutOfBounds(
71
        Push $push,
72
        int $width,
73
        int $height
74
    ): int {
75
        $i = 0;
76
        $rectangle = $this->rectangle;
77
        while (!$rectangle->isOutOfBounds($width, $height)) {
78
            ++$i;
79
            $rectangle = $rectangle->after($push);
80
        }
81
        return $i;
82
    }
83
84
    public function canPush(
85
        Push $push,
86
        Crates $obstacles,
87
        int $width,
88
        int $height
89
    ): bool {
90
        if (!$this->canMoveInDirectionOf($push)) {
91
            return false;
92
        }
93
        $rectangle = $this->rectangle->after($push);
94
        return !$rectangle->isOutOfBounds($width, $height)
95
            && $this->canMoveUnhindered($rectangle, $obstacles->remove($this));
96
    }
97
98
    public function coordinates(): array
99
    {
100
        return $this->rectangle->coordinates();
101
    }
102
103
    public function __toString(): string
104
    {
105
        return "{$this->id} @ {$this->rectangle}";
106
    }
107
108
    private function canMoveInDirectionOf(Push $push): bool
109
    {
110
        return (!$push->isHorizontal() || $this->canSlideHorizontally)
111
            && (!$push->isVertical() || $this->canSlideVertically);
112
    }
113
114
    private function canMoveUnhindered(
115
        Rectangle $movedRectangle,
116
        Crates $obstacles
117
    ): bool {
118
        foreach ($obstacles as $obstacle) {
119
            if ($obstacle->rectangle->collidesWith($movedRectangle)) {
120
                return false;
121
            }
122
        }
123
        return true;
124
    }
125
}
126