├── LICENSE.txt ├── css └── crossword-puzzle.css ├── README.md ├── crossword-puzzle-demo.html └── javascript └── crossword-puzzle.js /LICENSE.txt: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Benjamin Tepolt 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /css/crossword-puzzle.css: -------------------------------------------------------------------------------- 1 | .puzzle { 2 | border-collapse: collapse; 3 | border-color:#000000; 4 | } 5 | 6 | .letter-cell { 7 | text-align:center; 8 | height:40px; 9 | width:40px; 10 | } 11 | 12 | .letter-text { 13 | font-family:helvetica; 14 | font-weight:bold; 15 | } 16 | 17 | .list-text { 18 | font-family:helvetica; 19 | } 20 | 21 | .blank-cell { 22 | background-color:#000000; 23 | } 24 | 25 | .clickable { 26 | cursor:pointer; 27 | } 28 | 29 | .linkable { 30 | cursor:pointer; 31 | color:blue; 32 | text-decoration:underline; 33 | } 34 | .background-text { 35 | position: absolute; 36 | top: 0; 37 | left: 0; 38 | bottom: 0; 39 | right: 0; 40 | z-index: -1; 41 | overflow: hidden; 42 | } 43 | 44 | .strikeout { 45 | position: relative; 46 | } 47 | 48 | .strikeout::after { 49 | border-bottom: 0.250em solid blue; 50 | content: ""; 51 | left: 0; 52 | margin-top: calc(0.125em / 2 * -1); 53 | position: absolute; 54 | right: 0; 55 | top: 50%; 56 | } 57 | 58 | .red-strikeout { 59 | position: relative; 60 | } 61 | 62 | .red-strikeout::after { 63 | border-bottom: 0.250em solid red; 64 | content: ""; 65 | left: 0; 66 | margin-top: calc(0.250em / 2 * -1); 67 | position: absolute; 68 | right: 0; 69 | top: 50%; 70 | } 71 | 72 | .crossword-grid-cell-number { 73 | float: left; 74 | font-family: tahoma; 75 | font-size: 0.6em; 76 | } 77 | 78 | .relative-position { 79 | position: relative; 80 | } 81 | 82 | #answer-form { 83 | display: none; 84 | border: 2px solid black; 85 | font-family: tahoma; 86 | position: fixed; 87 | top: 50%; 88 | left: 50%; 89 | width: 30em; 90 | height: 12em; 91 | margin-top: -9em; 92 | margin-left: -15em; 93 | border: 1px solid #CCCCCC; 94 | background-color: #F3F3F3; 95 | } 96 | 97 | .short-margin { 98 | margin: 20px; 99 | } 100 | 101 | .hidden { 102 | display: none; 103 | } 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 100% JQuery Crossword Puzzle Generator and Game 2 | 3 | Demo: 4 | 5 | http://www.earthfluent.com/crossword-puzzle-demo.html 6 | 7 | Example Usage: 8 | 9 | $(document).ready(function(event) { 10 | var puzzlewords = [ 11 | // word, clue 12 | ['Incomplete', 'Some of us are always meant to be this.'], 13 | ['Ecosystem', 'Any system where life can grow and thrive.'], 14 | // ... etc.. 15 | ]; 16 | crosswordPuzzle(puzzlewords); 17 | }); 18 | 19 | Fully supports internationalization. Examples in twelve different languages : 20 | 21 | * Spanish : https://www.earthfluent.com/spanish/nouns-concepts-part-35/play.php?action=CrosswordPuzzle&previousquizzes=10 22 | * French : https://www.earthfluent.com/french/nouns-concepts-part-35/play.php?action=CrosswordPuzzle&previousquizzes=10 23 | * Italian : https://www.earthfluent.com/italian/nouns-concepts-part-35/play.php?action=CrosswordPuzzle&previousquizzes=10 24 | * German : https://www.earthfluent.com/german/nouns-concepts-part-35/play.php?action=CrosswordPuzzle&previousquizzes=10 25 | * Japanese : https://www.earthfluent.com/japanese/nouns-concepts-part-35/play.php?action=CrosswordPuzzle&previousquizzes=10 26 | * Chinese : https://www.earthfluent.com/chinese/nouns-concepts-part-35/play.php?action=CrosswordPuzzle&previousquizzes=10 27 | * Hindi : https://www.earthfluent.com/hindi/nouns-concepts-part-35/play.php?action=CrosswordPuzzle&previousquizzes=10 28 | * Indonesian : https://www.earthfluent.com/indonesian/nouns-concepts-part-35/play.php?action=CrosswordPuzzle&previousquizzes=10 29 | * Dutch : https://www.earthfluent.com/dutch/nouns-concepts-part-35/play.php?action=CrosswordPuzzle&previousquizzes=10 30 | * Polish : https://www.earthfluent.com/polish/nouns-concepts-part-35/play.php?action=CrosswordPuzzle&previousquizzes=10 31 | * Portuguese : https://www.earthfluent.com/portuguese/nouns-concepts-part-35/play.php?action=CrosswordPuzzle&previousquizzes=10 32 | * Russian : https://www.earthfluent.com/russian/nouns-concepts-part-35/play.php?action=CrosswordPuzzle&previousquizzes=10 33 | * Korean : https://www.earthfluent.com/korean/nouns-concepts-part-35/play.php?action=CrosswordPuzzle&previousquizzes=10 34 | -------------------------------------------------------------------------------- /crossword-puzzle-demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 100% JQuery, Open-Source Crossword Puzzle Generator and Game 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 40 | 41 | 42 | 43 |
44 |
45 | 46 |
47 | 48 | 49 | 50 | 51 | 56 | 61 | 62 | 63 |
52 |
53 |

Across

54 |
55 |
57 |
58 |

Down

59 |
60 |
64 | 65 |
66 | 67 |
68 | 69 |
70 | 71 |

72 | 73 |

Answer :

74 | 75 | 76 | 77 |

78 | 79 |
80 | 81 |
82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /javascript/crossword-puzzle.js: -------------------------------------------------------------------------------- 1 | // Set Globals 2 | // -------------------------------------------- 3 | 4 | var crosswordclues = []; 5 | 6 | // Set Randomization Configs 7 | // -------------------------------------------- 8 | 9 | function areWeInGodMode() { 10 | return false; 11 | return true; 12 | } 13 | 14 | function areWeRandomizingPuzzleWords() { 15 | return true; 16 | } 17 | 18 | function areWeRandomizingPuzzlePieces() { 19 | return true; 20 | } 21 | 22 | function areWeRandomizingAcrossDownChoices() { 23 | return true; 24 | } 25 | 26 | function areWeRandomizingAcrossDownLists() { 27 | return true; 28 | } 29 | 30 | // Main() 31 | // -------------------------------------------- 32 | 33 | function crosswordPuzzle(puzzlewords) { 34 | var wordcount = puzzlewords.length; 35 | 36 | if(!puzzlewords || !wordcount) { 37 | console.log("Developer Error : Did you forget to load words?"); 38 | return false; 39 | } 40 | 41 | if(areWeRandomizingPuzzleWords()) { 42 | puzzlewords = shuffle(puzzlewords); 43 | } 44 | 45 | var crosswords = generateCrosswordBlockSources(puzzlewords); 46 | 47 | var crosswordblocks = crosswords['blocks']; 48 | var crosswordclues = crosswords['clues']; 49 | 50 | var graphs = buildCrosswordBlocks(crosswordblocks); 51 | graphs = compactCrosswordBlockSources(graphs); 52 | 53 | if(areWeRandomizingPuzzlePieces()) { 54 | graphs = shuffle(graphs); 55 | } 56 | 57 | if(!graphs || !graphs.length) { 58 | console.log("Developer Error : Your words could not be made into graphs."); 59 | return false; 60 | } 61 | 62 | var fullgraph = buildCrosswordBlockGraphs(graphs); 63 | var wordlists = buildCrosswordLists(fullgraph['matrixpositions']); 64 | 65 | showCrossWordPuzzle(fullgraph['matrix']); 66 | showCrossWordLists(wordlists, crosswordclues); 67 | showCrossWordOptions(); 68 | 69 | return true; 70 | } 71 | 72 | // User Form Actions 73 | // -------------------------------------------- 74 | 75 | function showCrossWordOptions() { 76 | var solvefunction = function() { 77 | $('#solution-answer').val(''); 78 | $('#answer-results').hide(); 79 | $('#answer-results').html(''); 80 | 81 | var word = $(this).attr('data-word'); 82 | var acrosstext = $(this).attr('data-across') == 'false' ? 'Down' : 'Across'; 83 | $('#position-and-clue').html('' + acrosstext + ' : ' + $(this).attr('data-clue')); 84 | $('#answer-form').show(); 85 | 86 | if($(this).children('span').attr('data-solved')) { 87 | $('#answer-button').attr('disabled', true); 88 | $('#reveal-answer-button').attr('disabled', true); 89 | 90 | $('#answer-results').show(); 91 | $('#answer-results').html('You have already solved this problem.'); 92 | 93 | $('#solution-answer').val(word); 94 | } else { 95 | $('#solution-answer').attr('maxlength', word.length); 96 | 97 | $('#answer-button').attr('data-word', word); 98 | $('#reveal-answer-button').attr('data-word', word); 99 | 100 | var datax = $(this).attr('data-x'); 101 | 102 | $('#answer-button').attr('data-x', datax); 103 | $('#reveal-answer-button').attr('data-x', datax); 104 | 105 | var datay = $(this).attr('data-y'); 106 | 107 | $('#answer-button').attr('data-y', datay); 108 | $('#reveal-answer-button').attr('data-y', datay); 109 | 110 | var across = $(this).attr('data-across'); 111 | 112 | $('#answer-button').attr('data-across', across); 113 | $('#reveal-answer-button').attr('data-across', across); 114 | 115 | $('#solution-answer').focus(); 116 | 117 | $('#answer-button').attr('disabled', false); 118 | $('#reveal-answer-button').attr('disabled', false); 119 | } 120 | 121 | return false; 122 | } 123 | 124 | var closesolvefunction = function() { 125 | $('#answer-results').hide(); 126 | $('#answer-form').hide(); 127 | return false; 128 | } 129 | 130 | var answerfunction = function() { 131 | var word = $(this).attr('data-word'); 132 | var answer = $('#solution-answer').val().toLowerCase(); 133 | 134 | if(answer == word) { 135 | var across = $(this).attr('data-across'); 136 | 137 | var x = parseInt($(this).attr('data-x'), 10); 138 | var y = parseInt($(this).attr('data-y'), 10); 139 | 140 | if(across && across != 'false') { 141 | for(var i = 0; i < answer.length; i++) { 142 | var newheight = y + i ; 143 | var letterposition = 'letter-position-' + x + '-' + newheight; 144 | $('#' + letterposition).text(answer[i]); 145 | } 146 | } else { 147 | for(var i = 0; i < answer.length; i++) { 148 | var newwidth = x + i ; 149 | var letterposition = 'letter-position-' + newwidth + '-' + y; 150 | $('#' + letterposition).text(answer[i]); 151 | } 152 | } 153 | 154 | $('#' + word + '-listing').addClass('strikeout'); 155 | $('#' + word + '-listing').attr('data-solved', true); 156 | 157 | $('#answer-form').hide(); 158 | } else { 159 | if(!$('#answer-results').is(':visible')) { 160 | $('#answer-results').show(); 161 | $('#answer-results').html('Incorrect Answer, Please Try Again'); 162 | } 163 | } 164 | 165 | return false; 166 | } 167 | 168 | var revealanswerfunction = function() { 169 | var word = $(this).attr('data-word'); 170 | var across = $(this).attr('data-across'); 171 | 172 | var x = parseInt($(this).attr('data-x'), 10); 173 | var y = parseInt($(this).attr('data-y'), 10); 174 | 175 | if(across && across != 'false') { 176 | for(var i = 0; i < word.length; i++) { 177 | var newheight = y + i ; 178 | var letterposition = 'letter-position-' + x + '-' + newheight; 179 | $('#' + letterposition).text(word[i]); 180 | } 181 | } else { 182 | for(var i = 0; i < word.length; i++) { 183 | var newwidth = x + i ; 184 | var letterposition = 'letter-position-' + newwidth + '-' + y; 185 | $('#' + letterposition).text(word[i]); 186 | } 187 | } 188 | 189 | $('#' + word + '-listing').addClass('red-strikeout'); 190 | $('#' + word + '-listing').attr('data-solved', true); 191 | 192 | $('#answer-form').hide(); 193 | } 194 | 195 | $('.word-clue').click(solvefunction); 196 | $('#cancel-button').click(closesolvefunction); 197 | $('#answer-button').click(answerfunction); 198 | $('#reveal-answer-button').click(revealanswerfunction); 199 | } 200 | 201 | // Show Crossword Lists 202 | // -------------------------------------------- 203 | 204 | function showCrossWordLists(wordlists, clues) { 205 | var acrosslist = wordlists['across']; 206 | var downlist = wordlists['down']; 207 | 208 | if(areWeRandomizingAcrossDownLists()) { 209 | acrosslist = shuffle(acrosslist); 210 | downlist = shuffle(downlist); 211 | } 212 | 213 | var acrosslistordered = fillInCrossWordNumbers(acrosslist); 214 | var downlistordered = fillInCrossWordNumbers(downlist, acrosslist, acrosslistordered); 215 | 216 | var acrosslistorderedelement = getViewableCrossWordList(acrosslistordered, clues, true); 217 | var downlistorderedelement = getViewableCrossWordList(downlistordered, clues, false); 218 | 219 | $('#left-list').append(acrosslistorderedelement); 220 | $('#right-list').append(downlistorderedelement); 221 | } 222 | 223 | function getViewableCrossWordList(listitems, clues, across) { 224 | var numbers = Object.keys(listitems); 225 | 226 | var element = '