Word Search Puzzle Solver

1. Introduction

Probably everyone spent a lot of time in childhood with solving Word Search Puzzle. I solved last one few months ago and now got an idea to create a Word Search Puzzle solver with HTML and JavaScript. This guide will show you how to create a puzzle solver without any knowledge of HTML or JavaScript. I won’t wrote complete guide what each line of the code means or how to open/create new file, .. , but I’ll document most important lines. Solving the puzzle is divided into two parts:

  • entering the words that need to be found and entering the puzzle
  • showing the puzzle and solving it
Word-search-puzzle

Word Search Puzzle example

2. What do I need?

Code can be written in any simple text editor / source code editor e.g. Windows Notepad, Notepad++, Vim, Emacs on Unix, … I am using Notepad++ in this tutorial.
Web browser to check the written code (e.g. Internet Explorer, Firefox, Google Chrome, Opera,… ).

3. Let’s begin!

We need to open source code editor and let’s start typing the HTML source code.

<html>
  <head>
    <title>Word Search Puzzle</title>
    <meta charset="UTF-8" />
    <!-- Load JavaScript libraries -->
    <script src="http://code.jquery.com/jquery-1.10.1.min.js" type="text/javascript"></script>
    <script src="js/puzzle.js" type="text/javascript"></script>
  </head>
  <body>
    <!-- Input content -->
    <div id="step1"></div>
    
    <!-- Result content -->
    <div id="step2" style="display:none"></div>
    
    <!-- JavaScript execute -->
    <script type="text/javascript">
    </script>
  </body>
</html>

Save the file and name it index.html. Then in same folder create new sub-folder names ‘js’ and create new file ‘puzzle.js’. In the puzzle.js we will write a JavaScript to create our puzzle and script to solve the puzzle.
Then open created index.html with your favorite browser. You will get “blank page”, because lines above are just preparation for our puzzle solver. On line 7 is loaded JavaScript jQuery library which will help us to show the Puzzle, solution, …

Let’s prepare two text area fields and a button to continue to the next step. First text area will be for words and second for our puzzle. Update or replace your <div id=”step1″> with the next code:

<!-- Input content -->
<div id="step1">
  <!-- Words to be found -->
  Words list: <textarea id="words"></textarea><br />
  <!-- the puzzle -->
  Puzzle: <textarea id="puzzleText"></textarea><br />
  <!-- Prepare the puzzle -->
  <input type="button" name="toStep2" value="Continue" id="toStep2" />
</div>

If you open or refresh index.html you should see something like on the picture below.

Word Search Puzzle Step 1

Step 1: Two text areas and continue button

Into the text area “Words list” we will type words that we need to find. Each word must be in separate line.Words must be written in uppercase. Into the “Puzzle” text area we need to write lines of the puzzle. Each line must start and end with a apostrophe ‘. Characters must be uppercase.

On the next image you will see how the data should be inserted. I used the puzzle from the first image on this guide. I need to find 7 words: apple, banana, cherry, grapes, lemon, orange and tomato. The puzzle has 7 lines, each start and ends with ‘. First line of the puzzle is ‘VNYBKGS’, seconds ‘RORANGE’, etc.

Step 1 - filled text areas

Step 1 – filled text areas

Now it’s time to prepare for the seconds part – show the puzzle. Copy or update code written below to the <div id=”step2″>. This will create a table (to show our puzzle), two buttons (go back to first step and solve the puzzle) and few small information’s (number of words, number of words found in the puzzle, list of not found words and our solution).

<!-- Result content -->
<div id="step2" style="display:none">
  <!-- display the puzzle in the table -->
  <table id="puzzle"></table>
  <!-- buttons to go back to step 1 and find the solution -->
  <input type="button" name="back" value="Go back to step 1" id="back" /><br />
  <input type="button" name="check" value="Check words" id="check" /><br />
  <!-- Satistics and the solution -->
  Found :<input type="text" name="wFound" value="0" id="wFound" /> / <span id="wTotal"></span><br />
  Not found: <div id="nFound"></div>
  Solution: <div id="pSolution"></div>
</div>

4. JavaScript – create the puzzle

Because all this buttons won’t do anything alone, we have to do some JavaScript coding. Open /js/puzzle.js file and let’s prepare the Puzzle class and add two blank functions. Copy code below to your puzzle.js file:

function Puzzle() {
  this.rows = 0; // number of rows in the puzzle
  this.columns = 0; // number of columns in the puzzle
  this.puzzle = new Array(); // the puzzle
  this.words = new Array(); // words need to be found

  // hard coded, this could be solved with the CSS class
  this.foundCharacterColor = '#00FF00';
};

Puzzle.prototype = {
  doCreatePuzzle: function() { },
  goBack: function() { }
};

We will write function ‘doCreatePuzzle‘ which will:

  • fill the array “words” that need to be found from the text area (id=”words”)
  • create and show our puzzle from the text area (id=”puzzle”)
  • save number of columns and rows for this puzzle
  • hide step 1 and show step 2

and function ‘goBack‘ which will hide step 2 and show step 1 (text areas).

Here is the code for this two functions:

Puzzle.prototype = {
  doCreatePuzzle: function() {
    var i, j = 0; // indexes
    var rebuild = false; // rebuild puzzle (new columns, rows..)
    
    // hide input form
    $("div#step1").slideUp();
    
    // read words
    if (this.words.length != $("#words").val().split('\n').length) {
      this.words = $("#words").val().split('\n');
      $("span#wTotal").html(this.words.length);
      rebuild = true;
    }
    
    if (this.puzzle.length != $("#puzzleText").val().split('\n').length) {
      this.puzzle = $("#puzzleText").val().split('\'\n');
      rebuild = true;
      this.rows = this.puzzle.length;
      this.columns = (this.puzzle[0].length-1);
    }

    // create table
    if (true == rebuild) {
      $("table#puzzle").html("");
      for (i=0; i < this.rows; ++i) {
        $("table#puzzle").append("<tr id=\"r"+i+"\"></tr>");
        for (j=0; j < this.columns; ++j) {
          $("table#puzzle tr#r"+i).append("<td id=\"c"+(j+1)+"\" rel=\"E\">"+this.puzzle[i].charAt(j+1)+"</td>");
        }
      }
    }
    
    // show form
    $("div#step2").slideDown();
  },
  goBack: function() {
    // hide input form
    $("div#step1").slideDown();
    $("div#step2").slideUp();
  }
};

At this point we are still not ready to show the puzzle. We need to add few more lines of JavaScript to the index.html file. This will create an instance of our new Puzzle class.

<!-- JavaScript execute -->
<script type="text/javascript">
  var puzzle;
  $(document).ready(function() {
    puzzle = new Puzzle();
  });
</script>

and of course, we need to call the ‘doCreatePuzzle’ function to do something. Edit the ‘Continue’ button – add ‘on click’ event.

<input type="button" name="toStep2" value="Continue" id="toStep2" onclick="javascript:puzzle.doCreatePuzzle();" />

The application is ready now to show our puzzle and step 2 details. If you did everything right, you should see the puzzle if you click on the continue button – puzzle is written in the chapter 1. and 3.

the-puzzle

The puzzle

5. JavaScript – preparation for the step 2

We have to do the hardest part of this application – solve the puzzle. We will have to write a lot of new functions:

  • solveThePuzzle: this will be our main function which will search the words in the puzzle
  • getSolution: function will show our solution on the screen
  • findWordLeft: recursive function to search the word from right to left side (horizontal)
  • findWordRight: recursive function to search the word from left to right side (horizontal)
  • findWordUp: recursive function to search the word from bottom to top (vertical)
  • findWordDown: recursive function to search the word from top to bottom (vertical)
  • findWordUpLeft: recursive function to search the word from bottom-right to top-left (diagonal)
  • findWordUpRight: recursive function to search the word from bottom-left to top-right (diagonal)
  • findWordDownLeft: recursive function to search the word from top-right to bottom-left (diagonal)
  • findWordDownRight: recursive function to search the word from top-left to down-right (diagonal)

Here is the update code of our puzzle.js file:

Puzzle.prototype = {
  doCreatePuzzle: function() { ... },
  goBack: function() { ... },
  solveThePuzzle: function() { },
  getSolution: function() { },
  findWordLeft: function(word, posW, wordL, j, k) { },
  findWordRight: function(word, posW, wordL, j, k) { },
  findWordUp: function(word, posW, wordL, j, k) { },
  findWordDown: function (word, posW, wordL, j, k) { },
  findWordUpLeft: function(word, posW, wordL, j, k) { },
  findWordUpRight: function(word, posW, wordL, j, k) { },
  findWordDownLeft: function(word, posW, wordL, j, k) { },
  findWordDownRight: function(word, posW, wordL, j, k) { }
};

6. JavaScript – Fill the content of new functions

6.1 function: solveThePuzzle (part 1)

Function has to check all words listed in the words variable. Script will compare first character of the word with the character in the puzzle. If characters will compare then script will start checking in all 8 directions for the word. If the word is not found, then script will continue with comparing (searching) first character of the word. When the word is found, then script will change background color of the characters and mark them with a relationship attribute (rel=”X”).

Update your solveThePuzzle function with the code below:

solveThePuzzle: function() {
  var wFound = new Array(); // list of words that were found
  var res = false; // was word found in any direction
  var wL = this.words.length;
  var i=0; // index for the words
  var j=0; // index for the puzzle rows
  var k=0; // index for the puzzle columns
  var fChar = ''; // first character
  
  for (i=0; i < wL; ++i) {
    // search all words
    fChar = this.words[i].charAt(0); // get first char and find beginning
    
    wordFound:
    for (j=0; j < this.rows; ++j) {
      for (k=0; k < this.columns; ++k) {
        if (fChar == this.puzzle[j].charAt(k+1)) {
          // first character found
          
          // check left
          res = this.findWordLeft(this.words[i], 1, this.words[i].length, j, k+1);
          if (false !== res) {
            $("table#puzzle tr#r"+j+" td#c"+(k+1)).css('background-color', this.foundCharacterColor).attr('rel', 'X');
            break wordFound; // word found, break loops
          } 
          
          // check right
          res = this.findWordRight(this.words[i], 1, this.words[i].length, j, k+1);
          if (false !== res) {
            $("table#puzzle tr#r"+j+" td#c"+(k+1)).css('background-color', this.foundCharacterColor).attr('rel', 'X');
            break wordFound; // word found, break loops
          }
          
          // check top
          res = this.findWordUp(this.words[i], 1, this.words[i].length, j, k+1);
          if (false !== res) {
            $("table#puzzle tr#r"+j+" td#c"+(k+1)).css('background-color', this.foundCharacterColor).attr('rel', 'X');
            break wordFound; // word found, break loops
          }
          
          // check bottom
          res = this.findWordDown(this.words[i], 1, this.words[i].length, j, k+1);
          if (false !== res) {
            $("table#puzzle tr#r"+j+" td#c"+(k+1)).css('background-color', this.foundCharacterColor).attr('rel', 'X');
            break wordFound; // word found, break loops
          }
          
          // check up-left
          res = this.findWordUpLeft(this.words[i], 1, this.words[i].length, j, k+1);
          if (false !== res) {
            $("table#puzzle tr#r"+j+" td#c"+(k+1)).css('background-color', this.foundCharacterColor).attr('rel', 'X');
            break wordFound; // word found, break loops
          }
          
          // check up-right
          res = this.findWordUpRight(this.words[i], 1, this.words[i].length, j, k+1);
          if (false !== res) {
            $("table#puzzle tr#r"+j+" td#c"+(k+1)).css('background-color', this.foundCharacterColor).attr('rel', 'X');
            break wordFound; // word found, break loops
          }
          
          // check down-left
          res = this.findWordDownLeft(this.words[i], 1, this.words[i].length, j, k+1);
          if (false !== res) {
            $("table#puzzle tr#r"+j+" td#c"+(k+1)).css('background-color', this.foundCharacterColor).attr('rel', 'X');
            break wordFound; // word found, break loops
          }
          
          // check down-right
          res = this.findWordDownRight(this.words[i], 1, this.words[i].length, j, k+1);
          if (false !== res) {
            $("table#puzzle tr#r"+j+" td#c"+(k+1)).css('background-color', this.foundCharacterColor).attr('rel', 'X');
            break wordFound; // word found, break loops
          }
        }
      }
    }
    
    ++wFound;
  }
  $("input#wFound").val(wFound);
  
  $("#pSolution").html(this.getSolution());
},

6.2 function: findWordLeft

All findWord functions will be recursive, return boolean value (true – word found: false – word not found), mark the characters in the puzzle if the word is found and have same input parameters:

  • word (String): word that we are searching (example: APPLE)
  • posW (int): character position that we are searching (example: 4 (L))
  • wordL (int): word length (example: 5)
  • j (int): coordinate in the puzzle (column)
  • k (int): coordinate int he puzzle (row)

The code below will search the word from right to left. If we are searching the word APPLE in the puzzle should be ELPPA.

findWordLeft: function(word, posW, wordL, j, k) {
    var result = false;
    if (posW == wordL) { return true; } // check if all characters were found
    
    if (0 < k && word.charAt(posW) == this.puzzle[j].charAt(k-1)) {
      result = this.findWordLeft(word, posW+1, wordL, j, k-1);
      if (result !== false) {
        $("table#puzzle tr#r"+j+" td#c"+(k-1)).css('background-color', this.foundCharacterColor).attr('rel', 'X');
        return new Array(j, k-1);
      }
    }
    return result;
  },

6.3 function: findWordRight

The code below will search the word from left to right. If we are searching the word APPLE in the puzzle should be APPLE.

findWordRight: function(word, posW, wordL, j, k) {
    var result = false;
    if (posW == wordL) { return true; }
    
    if (this.columns > k && word.charAt(posW) == this.puzzle[j].charAt(k+1)) {
      result = this.findWordRight(word, posW+1, wordL, j, k+1);
      if (result !== false) {
        $("table#puzzle tr#r"+j+" td#c"+(k+1)).css('background-color', this.foundCharacterColor).attr('rel', 'X');
        return new Array(j, k+1);
      }
    }
    return result;
  },

6.4 function: findWordUp

The code below will search the word from bottom to up.

findWordUp: function(word, posW, wordL, j, k) {
  var result = false;
  if (posW == wordL) { return true; }
  
  if (0 <= (j-1) && word.charAt(posW) == this.puzzle[j-1].charAt(k)) {
    result = this.findWordUp(word, posW+1, wordL, j-1, k);
    if (result !== false) {
      $("table#puzzle tr#r"+(j-1)+" td#c"+k).css('background-color', this.foundCharacterColor).attr('rel', 'X');
      return new Array(j-1, k);
    }
  }
  return result;
},

6.5 function: findWordDown

The code below will search the word from top to bottom.

findWordDown: function (word, posW, wordL, j, k) {
  var result = false;
  if (posW == wordL) { return true; }
  
  if (this.rows > (j+1) && word.charAt(posW) == this.puzzle[j+1].charAt(k)) {
    result = this.findWordDown(word, posW+1, wordL, j+1, k);
    if (result !== false) {
      $("table#puzzle tr#r"+(j+1)+" td#c"+k).css('background-color', this.foundCharacterColor).attr('rel', 'X');
      return new Array(j+1, k);
    }
  }
  return result;
},

6.6 function: findWordUpLeft

The code below will search for the word from bottom-right to top-left.

findWordUpLeft: function(word, posW, wordL, j, k) {
  var result = false;
  if (posW == wordL) { return true; }
  
  if (0 < k && 0 < j && word.charAt(posW) == this.puzzle[j-1].charAt(k-1)) {
    result = this.findWordUpLeft(word, posW+1, wordL, j-1, k-1);
    if (result !== false) {
      $("table#puzzle tr#r"+(j-1)+" td#c"+(k-1)).css('background-color', this.foundCharacterColor).attr('rel', 'X');
      return new Array(j-1, k-1);
    }
  }
  return result;
},

6.7 function: findWordUpRight

The code below will search for the word from bottom-left to top-right.

findWordUpRight: function(word, posW, wordL, j, k) {
  var result = false;
  if (posW == wordL) { return true; }
  
  if (this.columns > k && 0 < j && word.charAt(posW) == this.puzzle[j-1].charAt(k+1)) {
    result = this.findWordUpRight(word, posW+1, wordL, j-1, k+1);
    if (result !== false) {
      $("table#puzzle tr#r"+(j-1)+" td#c"+(k+1)).css('background-color', this.foundCharacterColor).attr('rel', 'X');
      return new Array(j-1, k+1);
    }
  }
  return result;
},

6.8 function: findWordDownLeft

The code below will search for the word from top-right to bottom-left.

findWordDownLeft: function(word, posW, wordL, j, k) {
  var result = false;
  if (posW == wordL) { return true; } 

  if (this.rows > (j+1) && 0 < k  && word.charAt(posW) == this.puzzle[j+1].charAt(k-1)) {
    result = this.findWordDownLeft(word, posW+1, wordL, j+1, k-1);
    if (result !== false) {
      $("table#puzzle tr#r"+(j+1)+" td#c"+(k-1)).css('background-color', this.foundCharacterColor).attr('rel', 'X');
      return new Array(j+1, k-1);
    }
  }
  return result;
},

6.9 function: findWordDownRight

The code below will search for the word from top-leftto bottom-right.

findWordDownRight: function(word, posW, wordL, j, k) {
  var result = false;
  if (posW == wordL) { return true; }
  
  if (this.rows > (j+1) && this.columns > k   && word.charAt(posW) == this.puzzle[j+1].charAt(k+1)) {
    result = this.findWordDownRight(word, posW+1, wordL, j+1, k+1);
    if (result !== false) {
      $("table#puzzle tr#r"+(j+1)+" td#c"+(k+1)).css('background-color', this.foundCharacterColor).attr('rel', 'X');
      return new Array(j+1, k+1);
    }
  }
  return result;
}

7. Final touches

We almost completed our journey of this guide. We just have to write a function which will return our solution (solveThePuzzle), add two events on the buttons and few CSS styles to make our puzzle beautiful.

So, here is the code for solveThePuzzle. It will search for all table cells where relationship attribute is ‘E’.

getSolution: function() {
  var result = '';
  $("#puzzle td[rel='E']").each(function(i) {
    result += $(this).html();
  });
  return result;
},

The puzzle will not solve alone, so we have to add two on click events on the buttons. First on click event is ‘goBack’ and the second is ‘solveThePuzzle’.

<!-- Result content -->
<div id="step2" style="display:none">
  <!-- display the puzzle in the table -->
  <table id="puzzle"></table>
  <!-- buttons to go back to step 1 and find the solution -->
  <input type="button" name="back" value="Go back to step 1" id="back" onclick="javascript:puzzle.goBack();" /><br />
  <input type="button" name="check" value="Check words" id="check" onclick="javascript:puzzle.solveThePuzzle();" /><br />
  <!-- Satistics and the solution -->
  Found :<input type="text" name="wFound" value="0" id="wFound" /> / <span id="wTotal"></span><br />
  Not found: <div id="nFound"></div>
  Solution: <div id="pSolution"></div>
</div>

And the last part of this guide, CSS styles to make our puzzle looking better. Update your ‘head’ with lines 7-10.

<head>
  <title>Word Search Puzzle</title>
  <meta charset="UTF-8" />
  <!-- Load JavaScript libraries -->
  <script src="http://code.jquery.com/jquery-1.10.1.min.js" type="text/javascript"></script>
  <script src="js/puzzle.js" type="text/javascript"></script>
  <style type="text/css">
    table#puzzle { text-align:center;border-collapse:collapse; padding:0px }
    table#puzzle td { width:20px; border: 1px solid }
  </style>
</head>

We reached the end of this guide. I hope you like it.

8. Example

Example is available at word search puzzle example page.

9. Download

All source files with the example are available at my GitHub repository:
https://github.com/JercSi/Word-Search-Puzzle

10. Resources

Image in the Introduction paragraph was taken at http://syndicate.yoogi.com/word-search/.
Word search puzzle at the example was taken from the bicikel.com forum.

Leave a Reply