Grid::addPlacedWord()   A
last analyzed

Complexity

Conditions 5
Paths 9

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 17
c 1
b 0
f 0
nc 9
nop 3
dl 0
loc 27
rs 9.3888
1
<?php
2
3
namespace CustomD\WordFinder;
4
5
use RuntimeException;
6
use Illuminate\Support\Str;
7
use CustomD\WordFinder\Word;
8
use Illuminate\Support\Collection;
9
use CustomD\WordFinder\Traits\HasWordCollection;
10
use CustomD\WordFinder\Facades\WordFinder as WordFinderFacade;
11
12
class Grid
13
{
14
15
    use HasWordCollection;
16
17
18
    /**
19
     * minimum word length for the puzzle
20
     */
21
    protected int $minWordLen;
22
23
    /**
24
     * maximium word length for the puzzle
25
     */
26
    protected int $maxWordLen;
27
28
    /**
29
     * grid side length
30
     */
31
    protected int $gridSize;
32
33
    /**
34
     * cells holding the collection of lettes
35
     */
36
    protected array $cells = [];
37
38
    /**
39
     * Words used within the puzzle
40
     */
41
    protected array $wordsList = [];
42
43
    /**
44
     * column mapping
45
     */
46
    protected array $columnArray = [];
47
48
    public function __construct(int $gridSize, int $minWordLen, int $maxWordLen, Collection $wordsCollection)
49
    {
50
        $this->minWordLen = $minWordLen;
51
        $this->maxWordLen = $maxWordLen;
52
53
        $this->setGridsize($gridSize)
54
            ->setWordsCollection($wordsCollection)
55
            ->initGrid();
56
    }
57
58
    protected function setGridSize(int $gridSize): self
59
    {
60
        if ($gridSize < $this->minWordLen) {
61
            throw new RuntimeException('size must be greater than '.$this->minWordLen);
62
        }
63
64
        $this->gridSize = $gridSize;
65
        //max word length cannot be greater than the grid size.
66
        $this->maxWordLen = min($gridSize, $this->maxWordLen);
67
68
        return $this;
69
    }
70
71
    protected function initGrid(): self
72
    {
73
        $this->cells = array_fill(0, $this->gridSize * $this->gridSize, null);
74
75
        for ($i = 0; $i < (2 * $this->gridSize * $this->gridSize); $i++) {
76
            $this->columnArray[$i]=$this->getColumnDefault($i);
77
        }
78
79
        return $this;
80
    }
81
82
    public function generate(): self
83
    {
84
        $blocks = $this->gridSize * $this->gridSize; //36
85
        $i=rand(0, $blocks-1);
86
87
        $complete=0;                   //0
88
        while ($complete < $blocks) {
89
            $this->placeWord($i);
90
            $complete++;
91
            $i++;
92
            if ($i==$blocks) {
93
                $i=0;
94
            }
95
        }
96
97
        return $this;
98
    }
99
100
    protected function addPlacedWord(Word $word, int $increment, int $len): void
101
    {
102
        $string = '';
103
        $flag=false;
104
105
        for ($i=$word->getStart(); $i<=$word->getEnd(); $i+=$increment) {
106
            if ($this->cells[$i] === null) {
107
                $string .= '_';
108
            } else {
109
                $string .= $this->cells[$i];
110
                $flag=true;
111
            }
112
        }
113
114
        if (! $flag) {
0 ignored issues
show
introduced by
The condition $flag is always false.
Loading history...
115
            $randomWord = $this->getRandomWord($len);
116
            $word->setLabel($randomWord);
117
            $this->addWord($word);
118
            return;
119
        }
120
121
        if (Str::contains($string, '_')===false) {
122
            return;
123
        }
124
125
        $word->setInversed(false)->setLabel($this->getWordLike($string));
126
        $this->addWord($word);
127
    }
128
129
    protected function placeWordHorizontally(Word $word, int $len): void
130
    {
131
        $inc = 1;
132
        $word->setEnd($word->getStart()+$len-1);
133
        while ($this->columnArray[$word->getEnd()] < $this->columnArray[$word->getStart()]) {
134
            $word->setStart($word->getStart()-1);
135
            $word->setEnd($word->getStart()+$len-1);
136
        }
137
138
        $this->addPlacedWord($word, $inc, $len);
139
    }
140
141
    protected function placeWordVertical(Word $word, int $len): void
142
    {
143
        $inc=$this->gridSize;
144
        $word->setEnd($word->getStart()+($len*$this->gridSize)-$this->gridSize);
145
        while ($word->getEnd()>($this->gridSize*$this->gridSize)-1) {
146
            $word->setStart($word->getStart()-$this->gridSize);
147
            $word->setEnd($word->getStart()+($len*$this->gridSize)-$this->gridSize);
148
        }
149
150
        $this->addPlacedWord($word, $inc, $len);
151
    }
152
153
    protected function placeWordDiagonallyLtr(Word $word, int $len): void
154
    {
155
        $inc=$this->gridSize+1;
156
        $word->setEnd($word->getStart()+($len*($this->gridSize+1))-($this->gridSize+1));
157
        while ($this->columnArray[$word->getEnd()] < $this->columnArray[$word->getStart()]) {
158
            $word->setStart($word->getStart()-1);
159
            $word->setEnd($word->getStart()+($len*($this->gridSize+1))-($this->gridSize+1));
160
        }
161
        while ($word->getEnd()>($this->gridSize*$this->gridSize)-1) {
162
            $word->setStart($word->getStart()-$this->gridSize);
163
            $word->setEnd($word->getStart()+($len*($this->gridSize+1))-($this->gridSize+1));
164
        }
165
        $this->addPlacedWord($word, $inc, $len);
166
    }
167
168
    protected function placeWordDiagonallyRtl(Word $word, int $len): void
169
    {
170
        $inc=$this->gridSize-1;
171
        $word->setEnd($word->getStart()+(($len-1)*($this->gridSize-1)));
172
        while ($this->columnArray[$word->getEnd()] > $this->columnArray[$word->getStart()]) {
173
            $word->setStart($word->getStart()+1);
174
            $word->setEnd($word->getStart()+(($len-1)*($this->gridSize-1)));
175
        }
176
        while ($word->getEnd()>($this->gridSize*$this->gridSize)-1) {
177
            $word->setStart($word->getStart()-$this->gridSize);
178
            $word->setEnd($word->getStart()+(($len-1)*($this->gridSize-1)));
179
        }
180
        $this->addPlacedWord($word, $inc, $len);
181
    }
182
183
    protected function placeWord($start): void
184
    {
185
        $len = $this->getRandomWordLength();
186
        if ($len === 0) {
187
            return;
188
        }
189
        $word = Word::createRandom($start);
190
191
        switch ($word->getOrientation()) {
192
            case Word::HORIZONTAL:
193
                $this->placeWordHorizontally($word, $len);
194
                return;
195
196
            case Word::VERTICAL:
197
                $this->placeWordVertical($word, $len);
198
                return;
199
200
            case Word::DIAGONAL_LEFT_TO_RIGHT:
201
                $this->placeWordDiagonallyLtr($word, $len);
202
                return;
203
204
            case Word::DIAGONAL_RIGHT_TO_LEFT:
205
                $this->placeWordDiagonallyRtl($word, $len);
206
                return;
207
        }
208
    }
209
210
    protected function getColumnDefault(int $x): int
211
    {
212
        return ($x % $this->gridSize)+1;
213
    }
214
215
    protected function addWord(Word $word): void
216
    {
217
        if ($word->getLabel() === null) {
218
            return;
219
        }
220
221
        $j=0;
222
        $incrementBy = 1;
223
        switch ($word->getOrientation()) {
224
            case Word::HORIZONTAL:
225
                $incrementBy=1;
226
                break;
227
228
            case Word::VERTICAL:
229
                $incrementBy=$this->gridSize;
230
                break;
231
232
            case Word::DIAGONAL_LEFT_TO_RIGHT:
233
                $incrementBy=$this->gridSize+1;
234
                break;
235
236
            case Word::DIAGONAL_RIGHT_TO_LEFT:
237
                $incrementBy=$this->gridSize-1;
238
                break;
239
        }
240
241
        for ($i = $word->getStart(); $j < Str::length($word->getLabel()); $i += $incrementBy) {
242
            $nchar = Str::substr($word->getLabel(), $j, 1);
243
            if ($this->cells[$i] !== $nchar
244
                && ! is_null($this->cells[$i])) {
245
                throw new RuntimeException("Null or Char required: {$this->cells[$i]} - {$nchar}");
246
            }
247
            $this->cells[$i] = Str::substr($word->getLabel(), $j, 1);
248
            $j++;
249
        }
250
251
        $this->wordsList[]=$word;
252
    }
253
254
    public function getTextGrid()
255
    {
256
        $r = '';
257
        foreach ($this->getGrid() as $idx => $row) {
258
            if ($idx > 0) {
259
                $r .= "\n";
260
            }
261
            $r .= implode(" ", $row);
262
        }
263
        return $r;
264
    }
265
266
    public function getGrid()
267
    {
268
269
        $charClass = resolve(config('word-finder.character_map'));
270
271
        $callback = method_exists($charClass, 'unmapChars') ? fn($str) => $charClass->unmapChars($str) : fn($str) => $str;
0 ignored issues
show
Bug introduced by
The method unmapChars() does not exist on Illuminate\Contracts\Foundation\Application. ( Ignorable by Annotation )

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

271
        $callback = method_exists($charClass, 'unmapChars') ? fn($str) => $charClass->/** @scrutinizer ignore-call */ unmapChars($str) : fn($str) => $str;

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
272
273
        $return = [];
274
        $column = 0;
275
        $row = 0;
276
        foreach ($this->cells as $letter) {
277
            $cell = $letter ?? WordFinderFacade::getRandomChar();
278
            $return[$row][$column] = $callback($cell);
279
            $column++;
280
            if ($column === $this->gridSize) {
281
                $row++;
282
                $column = 0;
283
            }
284
        }
285
286
        return $return;
287
    }
288
289
    public function getPuzzleWords()
290
    {
291
        return collect($this->wordsList)->map(function (Word $word) {
292
            $str = $word->getLabel(true);
293
             $charClass = resolve(config('word-finder.character_map'));
294
295
            return method_exists($charClass, 'unmapChars') ? $charClass->unmapChars($str) : $str;
296
        });
297
    }
298
299
    public function getGridSize()
300
    {
301
        return $this->gridSize;
302
    }
303
}
304