Skip to content

Instantly share code, notes, and snippets.

@drjamesj
Last active May 23, 2021 11:53
Show Gist options
  • Save drjamesj/0e407e69824a5a60154bc6bcdb530f39 to your computer and use it in GitHub Desktop.
Save drjamesj/0e407e69824a5a60154bc6bcdb530f39 to your computer and use it in GitHub Desktop.
Wordsearch Generator
<?php
class WordsearchGenerator
{
private array $matrix;
private array $letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
private int $dimension = 14;
private array $leftMargin = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
// 'Circular' wordsearch grid
// private array $leftMargin = [6, 3, 2, 1, 1, 1, 0, 0, 1, 1, 1, 2, 3, 6];
private array $movement = [
[0, 1], [1, 1], [1, 0], [1, -1],
[0, -1], [-1, -1], [-1, 0], [-1, 1],
];
private array $usedWords = [];
private array $unusedWords = [];
public function __construct($words = [])
{
$this->createWordMatrix(array_map(fn ($w) => strtoupper($w), $words));
}
public function getMatrix(): array
{
return $this->matrix;
}
public function getUsedWords(): array
{
return $this->usedWords;
}
public function getUnusedWords(): array
{
return $this->unusedWords;
}
/**
* Initiates the word matrix grid.
* # - denotes fillable cell
* * - denotes unfillable cell for padding
*/
private function initiateMatrix(): void
{
$matrix = [];
for ($i = 0; $i < $this->dimension; $i++) {
for ($j = 0; $j < $this->dimension; $j++) {
$matrix[$i][$j] = '#';
}
}
for ($i = 0; $i < $this->dimension; $i++) {
for ($j = 0; $j < $this->leftMargin[$i]; $j++) {
$matrix[$i][$j] = '*';
$matrix[$i][$this->dimension - $j - 1] = '*';
}
}
$this->matrix = $matrix;
}
/**
* Validates word against length requirements and removes word substrings
*/
private function validWord(string $word = ''): bool
{
if (strlen($word) < 2 || strlen($word) > 14) {
return false;
}
if (
count(
array_filter($this->usedWords, fn ($w) => strpos($w, $word) !== false)
)
) {
return false;
}
return true;
}
/**
* Used to validate a potential word position and direction in the current matrix
*/
private function validWordPosition($i, $j, $word, $direction): bool
{
$ii = $i;
$jj = $j;
for ($k = 0; $k < strlen($word); $k++) {
if ($ii < 0 || $ii >= $this->dimension) return false;
if ($jj < 0 || $jj >= $this->dimension) return false;
if ($this->matrix[$ii][$jj] == '*') return false;
if ($this->matrix[$ii][$jj] != $word[$k] && $this->matrix[$ii][$jj] != '#') return false;
$ii = $ii + $this->movement[$direction][0];
$jj = $jj + $this->movement[$direction][1];
}
return true;
}
private function addWordToMatrix($i, $j, $word, $direction): void
{
$ii = $i;
$jj = $j;
for ($k = 0; $k < strlen($word); $k++) {
$this->matrix[$ii][$jj] = $word[$k];
$ii = $ii + $this->movement[$direction][0];
$jj = $jj + $this->movement[$direction][1];
}
}
private function createWordMatrix($words = [])
{
$this->initiateMatrix();
foreach ($words as $word) {
if ($this->validWord($word)) {
$valid = false;
$positions = range(0, pow($this->dimension, 2));
shuffle($positions);
for ($t = 0; $t < pow($this->dimension, 2); $t++) {
$i = floor($positions[$t] / $this->dimension);
$j = $positions[$t] % $this->dimension;
$directions = range(0, 7);
shuffle($directions);
foreach ($directions as $direction) {
if ($this->validWordPosition($i, $j, $word, $direction)) {
$this->addWordToMatrix($i, $j, $word, $direction);
$valid = true;
break;
}
}
if ($valid) {
break;
}
}
if ($valid) {
$this->usedWords[] = $word;
} else {
$this->unusedWords[] = $word;
}
} else {
$this->unusedWords[] = $word;
}
}
for ($i = 0; $i < $this->dimension; $i++) {
for ($j = 0; $j < $this->dimension; $j++) {
if ($this->matrix[$i][$j] == '#') {
$this->matrix[$i][$j] = $this->letters[array_rand($this->letters)];
}
}
}
}
}
// Create 'fruit' wordsearch
$fruits = new WordsearchGenerator(['Banana', 'Apple', 'Orange', 'Pear', 'Lemon', 'Watermelon', 'Grape', 'Melon', 'Strawberry', 'Impossible_Word_To_Fit']);
foreach ($fruits->getMatrix() as $line) {
echo implode(' ', $line) . "\r\n";
}
echo 'Used words: ' . implode(', ', $fruits->getUsedWords()) . "\r\n";
echo 'Unused words: ' . implode(', ', $fruits->getUnusedWords());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment