wordsearch-generator
Version:
Module for creating wordsearch puzzles
250 lines (230 loc) • 7.6 kB
JavaScript
'use strict';
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var LANGUAGES = {
'en': 'abcdefghijklmnopqrstuvwxyz'.split('').map(function (letter) {
return letter.toUpperCase();
}),
'es': 'abcdefghijklmnñopqrstuvwxyz'.split('').map(function (letter) {
return letter.toUpperCase();
})
}; /*
* File: index.js
* Project: word search generator
* Date: September 2018
*
* Author: Todd Hibbs
*
* Description: Generates a word search puzzle given a grid size and a list of words
* The words can appear forward or backward in either a horizontal, vertical, or diagonal
* direction.
*/
var printGrid = function printGrid(grid, space) {
var lines = [];
_lodash2.default.each(grid, function (row) {
var r = row.join(' ');
if (space) {
r = r.replace(/0/gi, ' ');
}
lines.push(r);
});
return lines;
};
var initializeGrid = function initializeGrid(width, height) {
var grid = [];
for (var i = 0; i < height; i++) {
grid.push(_lodash2.default.fill(Array(width), 0));
}
return grid;
};
var cloneGrid = function cloneGrid(grid) {
var _grid = [];
grid.forEach(function (row) {
return _grid.push(row.slice(0));
});
return _grid;
};
var hideWords = function hideWords(grid, languageCode) {
var alphabet = LANGUAGES[languageCode];
var _grid = cloneGrid(grid);
for (var i = 0; i < _grid.length; i++) {
for (var j = 0; j < _grid[i].length; j++) {
if (_grid[i][j] == 0) {
var alphabetIndex = getRandomIntInclusive(0, alphabet.length - 1);
var letter = alphabet[alphabetIndex];
// console.log('alphabetIndex', alphabetIndex, 'letter', letter)
_grid[i][j] = letter;
}
}
}
return _grid;
};
var reverseWord = function reverseWord(word) {
return _lodash2.default.reverse(word.split('')).join('');
};
var cleanWord = function cleanWord(word) {
return word.split(' ').join('');
};
var cleanWordsList = function cleanWordsList(words) {
return words.map(cleanWord).map(function (w) {
return w.toUpperCase();
});
};
var sortWordsByLength = function sortWordsByLength(words) {
var _words = words;
return _words.sort(function (w1, w2) {
return w2.length - w1.length;
});
};
var minColumnIndex = function minColumnIndex() {
return 0;
};
var maxColumnIndex = function maxColumnIndex(grid, word) {
// maximum starting column index
var wordLength = word.length;
var gridWidth = grid[0].length;
if (wordLength > gridWidth) {
throw "Grid Width is too narrow for the given word: " + word;
}
return gridWidth - wordLength;
};
var minRowIndex = function minRowIndex() {
return 0;
};
var maxRowIndex = function maxRowIndex(grid, word) {
// maximum starting row index
var wordLength = word.length;
var gridHeight = grid.length;
if (wordLength > gridHeight) {
throw "Grid Height is too short for the given word: " + word;
}
return gridHeight - wordLength;
};
var minimumGridSize = function minimumGridSize(wordList) {
return cleanWordsList(wordList).map(function (w) {
return w.length;
}).reduce(function (a, c) {
return Math.max(a, c);
});
};
var getRandomInt = function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
};
var getRandomIntInclusive = function getRandomIntInclusive(min, max) {
var _min = Math.ceil(min);
var _max = Math.floor(max);
return Math.floor(Math.random() * (_max - _min + 1)) + _min;
};
var getRandomBool = function getRandomBool() {
return !!getRandomInt(2);
};
var getRandomDirection = function getRandomDirection() {
var directions = ['horizontal', 'vertical', 'diagonal'];
return directions[getRandomIntInclusive(0, 2)];
};
var canPlaceWord = function canPlaceWord(grid, word, row, column, direction) {
var _grid = cloneGrid(grid);
for (var i = 0; i < word.length; i++) {
var r = row;
var c = column;
if (direction == 'vertical' || direction == 'diagonal') {
r += i;
}
if (direction == 'horizontal' || direction == 'diagonal') {
c += i;
}
var cell = _grid[r][c];
if (cell != 0 && word[i] != cell) {
//collision detected
return false;
}
// _grid[r][c] = word[i]
}
//made it to the end with no collisions
return true;
};
var placeWord = function placeWord(grid, word, row, column, direction) {
//returns a new grid with the word placed in the specified location
var _grid = grid;
for (var i = 0; i < word.length; i++) {
var r = row;
var c = column;
if (direction == 'vertical' || direction == 'diagonal') {
r += i;
}
if (direction == 'horizontal' || direction == 'diagonal') {
c += i;
}
_grid[r][c] = word[i];
}
return _grid;
};
/* This function is potentially called recursively until a valid location for the word
* within the grid is located. To prevent stack overflow situations, arbitrary limites are set
* on the number of times it can be called
*/
var placeWordRandom = function placeWordRandom(grid, word, counter) {
// console.log('placeWordRandom', 'word', word, 'counter', counter)
if (counter > 1000) {
throw 'Unable to find any location for this word. Try increasing grid size';
}
var isBackward = getRandomBool();
var rowIndex = getRandomIntInclusive(minRowIndex(), maxRowIndex(grid, word));
var columnIndex = getRandomIntInclusive(minColumnIndex(), maxColumnIndex(grid, word));
var direction = getRandomDirection();
var _grid = cloneGrid(grid);
var _word = isBackward ? reverseWord(word) : word;
// NOTE: it might be smarter to try to find overlaps. this implementation simply brute forces a place
// to put the word by checking many possible locations. The smaller the grid, the more difficult this is
var wordPlaced = false;
var tryCount = 0;
while (!wordPlaced && tryCount < 1000) {
if (canPlaceWord(_grid, _word, rowIndex, columnIndex, direction)) {
_grid = placeWord(_grid, _word, rowIndex, columnIndex, direction);
wordPlaced = true;
}
tryCount++;
}
if (!wordPlaced) {
// console.log('trying in different random position:', word)
_grid = placeWordRandom(_grid, word, ++counter);
}
return _grid;
};
var createPuzzle = function createPuzzle(width, height, languageCode, words) {
var _wordList = sortWordsByLength(cleanWordsList(words));
var minimumGrid = minimumGridSize(_wordList);
//expand grid if needed
// let gridWidth = minimumGrid > gridWidth ? minimumGrid * 2 : gridWidth
// let gridHeight = minimumGrid > gridHeight ? minimumGrid * 2 : gridHeight
var grid = initializeGrid(width, height);
_wordList.forEach(function (w) {
grid = placeWordRandom(grid, w, 0);
});
return grid;
};
module.exports = {
createPuzzle: createPuzzle,
printGrid: printGrid,
initializeGrid: initializeGrid,
cloneGrid: cloneGrid,
hideWords: hideWords,
reverseWord: reverseWord,
cleanWord: cleanWord,
cleanWordsList: cleanWordsList,
sortWordsByLength: sortWordsByLength,
minColumnIndex: minColumnIndex,
maxColumnIndex: maxColumnIndex,
minRowIndex: minRowIndex,
maxRowIndex: maxRowIndex,
minimumGridSize: minimumGridSize,
getRandomInt: getRandomInt,
getRandomIntInclusive: getRandomIntInclusive,
getRandomBool: getRandomBool,
getRandomDirection: getRandomDirection,
canPlaceWord: canPlaceWord,
placeWord: placeWord,
placeWordRandom: placeWordRandom,
LANGUAGES: LANGUAGES
};