UNPKG

wordsearch-generator

Version:

Module for creating wordsearch puzzles

250 lines (230 loc) 7.6 kB
'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 };