Passed
Push — master ( 152a70...0c8393 )
by Jose
03:00
created

DomainLogic::getAdjacentNodes()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 8
c 1
b 0
f 0
dl 0
loc 17
ccs 9
cts 9
cp 1
rs 10
cc 4
nc 4
nop 1
crap 4
1
<?php
2
3
namespace JMGQ\AStar\Example\Terrain;
4
5
use JMGQ\AStar\DomainLogicInterface;
6
7
class DomainLogic implements DomainLogicInterface
8
{
9
    private TerrainCost $terrainCost;
10
    /** @var Position[][] */
11
    private array $positions;
12
13 15
    public function __construct(TerrainCost $terrainCost)
14
    {
15 15
        $this->terrainCost = $terrainCost;
16
17
        // We store all the Position objects in a matrix for efficiency, so that we don't need to create new Position
18
        // instances every time we call "getAdjacentNodes". If we created new positions with every call, the algorithm
19
        // would still work correctly but it would be a bit slower.
20 15
        $this->positions = $this->generatePositions($terrainCost);
21 15
    }
22
23
    /**
24
     * @param Position $node
25
     * @return Position[]
26
     */
27 10
    public function getAdjacentNodes(mixed $node): iterable
28
    {
29 10
        $adjacentNodes = [];
30
31 10
        [$startingRow, $endingRow, $startingColumn, $endingColumn] = $this->calculateAdjacentBoundaries($node);
32
33 10
        for ($row = $startingRow; $row <= $endingRow; $row++) {
34 10
            for ($column = $startingColumn; $column <= $endingColumn; $column++) {
35 10
                $adjacentNode = $this->positions[$row][$column];
36
37 10
                if (!$node->isEqualTo($adjacentNode)) {
38 10
                    $adjacentNodes[] = $adjacentNode;
39
                }
40
            }
41
        }
42
43 10
        return $adjacentNodes;
44
    }
45
46
    /**
47
     * @param Position $node
48
     * @param Position $adjacent
49
     * @return float|int
50
     */
51 8
    public function calculateRealCost(mixed $node, mixed $adjacent): float | int
52
    {
53 8
        if ($node->isAdjacentTo($adjacent)) {
54 7
            return $this->terrainCost->getCost($adjacent->getRow(), $adjacent->getColumn());
55
        }
56
57 1
        return TerrainCost::INFINITE;
58
    }
59
60
    /**
61
     * @param Position $fromNode
62
     * @param Position $toNode
63
     * @return float|int
64
     */
65 8
    public function calculateEstimatedCost(mixed $fromNode, mixed $toNode): float | int
66
    {
67 8
        return $this->euclideanDistance($fromNode, $toNode);
68
    }
69
70 8
    private function euclideanDistance(Position $a, Position $b): float
71
    {
72 8
        $rowFactor = ($a->getRow() - $b->getRow()) ** 2;
73 8
        $columnFactor = ($a->getColumn() - $b->getColumn()) ** 2;
74
75 8
        return sqrt($rowFactor + $columnFactor);
76
    }
77
78
    /**
79
     * @param Position $position
80
     * @return int[]
81
     */
82 10
    private function calculateAdjacentBoundaries(Position $position): array
83
    {
84 10
        if ($position->getRow() === 0) {
85 8
            $startingRow = 0;
86
        } else {
87 8
            $startingRow = $position->getRow() - 1;
88
        }
89
90 10
        if ($position->getRow() === $this->terrainCost->getTotalRows() - 1) {
91 5
            $endingRow = $position->getRow();
92
        } else {
93 9
            $endingRow = $position->getRow() + 1;
94
        }
95
96 10
        if ($position->getColumn() === 0) {
97 8
            $startingColumn = 0;
98
        } else {
99 8
            $startingColumn = $position->getColumn() - 1;
100
        }
101
102 10
        if ($position->getColumn() === $this->terrainCost->getTotalColumns() - 1) {
103 4
            $endingColumn = $position->getColumn();
104
        } else {
105 9
            $endingColumn = $position->getColumn() + 1;
106
        }
107
108 10
        return [$startingRow, $endingRow, $startingColumn, $endingColumn];
109
    }
110
111
    /**
112
     * @param TerrainCost $terrainCost
113
     * @return Position[][]
114
     */
115 15
    private function generatePositions(TerrainCost $terrainCost): array
116
    {
117 15
        $positions = [];
118
119 15
        for ($row = 0; $row < $terrainCost->getTotalRows(); $row++) {
120 15
            for ($column = 0; $column < $terrainCost->getTotalColumns(); $column++) {
121 15
                $positions[$row][$column] = new Position($row, $column);
122
            }
123
        }
124
125 15
        return $positions;
126
    }
127
}
128