Row::withCell()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 3
dl 0
loc 9
ccs 0
cts 6
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\Crossword\Features\Constructor\Scanner\Grid;
6
7
use App\Crossword\Features\Constructor\Scanner\Exception\CellNotFoundException;
8
use App\Crossword\Features\Constructor\Scanner\Exception\SearchMaskIsShortException;
9
use ArrayIterator;
10
use IteratorAggregate;
11
12
/**
13
 * @psalm-immutable
14
 */
15
final class Row implements IteratorAggregate
16
{
17
    /**
18
     * @var Cell[]
19
     */
20
    private array $cells;
21
22
    public function __construct(Cell ...$cells)
23
    {
24
        $this->cells = $cells;
25
    }
26
27
    public static function withRandomRow(): self
28
    {
29
        $coordinateX = intdiv(20, 2) - random_int(1, 5);
30
        $coordinateY = intdiv(20, 2) - random_int(1, 5);
31
        $coordinate = new Coordinate($coordinateX, $coordinateY);
32
33
        return new self(new Cell($coordinate, null));
0 ignored issues
show
Bug introduced by
null of type null is incompatible with the type App\Crossword\Features\C...canner\Grid\null|string expected by parameter $letter of App\Crossword\Features\C...rid\Cell::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

33
        return new self(new Cell($coordinate, /** @scrutinizer ignore-type */ null));
Loading history...
34
    }
35
36
    public static function withCell(Row $row, int $position, Cell $cell): self
37
    {
38
        $row = clone $row;
39
        $cells = $row->cells();
40
        if (array_key_exists($position, $row->cells())) {
41
            $cells[$position] = $cell;
42
        }
43
44
        return new self(...$cells);
45
    }
46
47
    public function cells(): array
48
    {
49
        return $this->cells;
50
    }
51
52
    /**
53
     * @throws CellNotFoundException
54
     */
55 1
    public function cell(int $index): Cell
56
    {
57 1
        if (array_key_exists($index, $this->cells)) {
58 1
            return $this->cells[$index];
59
        }
60
61
        throw new CellNotFoundException();
62
    }
63
64 1
    public function remove(int $index): self
65
    {
66 1
        if (array_key_exists($index, $this->cells)) {
67 1
            $row = new self(...$this->cells);
68 1
            unset($row->cells[$index]);
69 1
            $row->cells = array_values($row->cells);
70
71 1
            return $row;
72
        }
73
74
        return $this;
75
    }
76
77
    /**
78
     * @throws SearchMaskIsShortException
79
     *
80
     * @example
81
     *  _ _ _ _ _ a  ===>  _ _ _ _ _ a.*{0,6}
82
     *  _ _ _ _ a _  ===>  _ _ _ _ a .*{0,6}
83
     *  _ _ _ a _ _  ===>  _ _ _ a .*{0,6}
84
     *  _ _ _ _ _ _  ===>  .*{0,6}
85
     *  a _ _ _ _ _  ===>  a.*{0,6}
86
     *  _ a _ _ _ _  ===>  _ a .*{0,6}
87
     *   _ _a _  _ _ ===>  _ _ a .*{0,6}
88
     */
89 8
    public function mask(): RowMask
90
    {
91 8
        $mask = array_map(static fn (Cell $cell): string => $cell->letter() ?: '.', $this->cells);
92 8
        $mask = implode('', $mask);
93 8
        while (substr($mask, -1) === '.') {
94 6
            $mask = substr($mask, 0, -1);
95
        }
96
97 8
        return new RowMask(sprintf('%s.*{0,%d}', $mask, count($this->cells)));
98
    }
99
100
    public function getIterator(): ArrayIterator
101
    {
102
        return new ArrayIterator($this->cells);
103
    }
104
}
105