Passed
Push — master ( 1fb483...35eebd )
by Roman
04:31
created

RowXScanner::doScan()   B

Complexity

Conditions 9
Paths 6

Size

Total Lines 32
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 0
Metric Value
cc 9
eloc 18
nc 6
nop 4
dl 0
loc 32
ccs 0
cts 19
cp 0
crap 90
rs 8.0555
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\Crossword\Features\Constructor\Scanner;
6
7
use App\Crossword\Features\Constructor\Scanner\Grid\Cell;
8
use App\Crossword\Features\Constructor\Scanner\Grid\Coordinate;
9
use App\Crossword\Features\Constructor\Scanner\Grid\Grid;
10
use App\Crossword\Features\Constructor\Scanner\Grid\Row;
11
12
final class RowXScanner
13
{
14
    /**
15
     * @return Row[]
16
     */
17
    public function scan(Grid $grid, int $length): array
18
    {
19
        $result = [];
20
21
        foreach ($grid as $cell) {
22
            if (!$cell->isLetter()) {
23
                continue;
24
            }
25
26
            $topLine = $this->doScan($grid, $cell, new Move(Move::TOP), $length);
27
            $downLine = $this->doScan($grid, $cell, new Move(Move::DOWN), $length);
28
29
            if (count($topLine) || count($downLine)) {
30
                $result[] = new Row(...array_reverse(array_merge(array_reverse($topLine), [$cell], $downLine)));
31
            }
32
        }
33
34
        return $result;
35
    }
36
37
    private function doScan(Grid $grid, Cell $cell, Move $move, int $length): array
38
    {
39
        $cell = $this->startCell($grid, $cell, $move);
40
41
        $line = [];
42
        for ($counter = 1; $counter <= $length; $counter++) {
43
            $coordinate = $cell->coordinate();
44
45
            if ($cell->isBlack() || !$coordinate->inFrame()) {
46
                return $line;
47
            }
48
49
            if (!$this->isFitRow($grid, $coordinate)) {
50
                return [];
51
            }
52
53
            $leftCell = $grid->shiftCell($coordinate->left());
54
            $rightCell = $grid->shiftCell($coordinate->right());
55
            if (!($leftCell->isEmpty() && $rightCell->isEmpty())) {
56
                array_pop($line);
57
58
                return $line;
59
            }
60
61
            $line[] = $cell;
62
            $cell = $this->nextCell($grid, $move, $coordinate);
63
            if ($length === $counter && $cell->isLetter()) {
64
                array_pop($line);
65
            }
66
        }
67
68
        return $line;
69
    }
70
71
    private function isFitRow(Grid $grid, Coordinate $coordinate): bool
72
    {
73
        $topCell = $grid->shiftCell($coordinate->top());
74
        $downCell = $grid->shiftCell($coordinate->down());
75
76
        if ($topCell->isBlack() && $downCell->isLetter()) {
77
            return false;
78
        }
79
80
        if ($topCell->letter() && $downCell->isLetter()) {
81
            return false;
82
        }
83
84
        if ($topCell->letter() && $downCell->isBlack()) {
85
            return false;
86
        }
87
88
        return true;
89
    }
90
91
    private function nextCell(Grid $grid, Move $move, Coordinate $coordinate): Cell
92
    {
93
        $topCell = $grid->shiftCell($coordinate->top());
94
        $downCell = $grid->shiftCell($coordinate->down());
95
96
        return match ((string) $move->getValue()) {
97
            Move::TOP => $topCell,
98
            Move::DOWN => $downCell,
99
        };
100
    }
101
102
    private function startCell(Grid $grid, Cell $cell, Move $move): Cell
103
    {
104
        $coordinate = $cell->coordinate();
105
106
        return match ((string) $move->getValue()) {
107
            Move::TOP => $grid->shiftCell($coordinate->top()),
108
            Move::DOWN => $grid->shiftCell($coordinate->down()),
109
        };
110
    }
111
}
112