javascripthtmlrecursionwordsearch

Why is recursive wordsearch solver missing five words yet finding the other 47?


I'm creating a recursive wordsearch app which appears (to my amateur eye) correct yet is missing five of the fifty three words. The words may be hidden left to right, right to left, up, down or diagonally. I want the console to output the found word, x and y coordinates in the puzzle, and direction. I have looked through the code tirelessly and can't seem to find the problem. Please help!

The missing words are:

2: Backup

29: LCD

40: Power Supply

44: Scanner

53: Wireless

Here's the code:

I'm loading the puzzle and word list from textareas:

<textarea name="wordsearch" id="wordsearch" style="display:none">
TPIRCSAVAJLEXIPIGE
LIAMEMORYMMOUSENIL
CRABKSATXINUYHSTFG
DNDIRECTORYETAOEOO
POWERSUPPLYNIRFRLO
UCOASAEVASCCRETNDG
KIROPKTYPSHRUWWEEL
CDDECPREEAHYCAATRM
ANRIMALLTDRPERREAT
BOLENMEIEKETSEEPHH
RCKIPRAFCVRIIRSULM
EEBEIARRIABOOTMBOR
NSTWRAPRGRTNWBINGO
NOOSGNDLOODINTIOIS
ANGMAKAULARAOTEANR
CAEASPTLTAIPONRNDU
SNFIREWALLWREIKOOC
TFDPRDHTOOTEULBYTE</textarea>

<textarea name="wordlist" id="wordlist" style="display:none">
Application
Backup
Binary
Bluetooth
Boot
Byte
Chat
Click
Cookie
Cursor
Data
Defragment
Directory
Disk drive
DOS
Drag
Email
Encryption
File
Firewall
Folder
GIF
Google
HTML
Icon
Internet
JavaScript
Kernal
LCD
Login
Memory
Monitor
Mouse
Nanosecond
Network
Partition
Paste
PDF
Pixel
Power Supply
Programmer
Router
Save As
Scanner
Security
ShareWare
Software
Spam
Taskbar
Thumbnail
UNIX
Wallpaper
Wireless</textarea>
<script src="wordsearch.js" type="text/javascript"></script> 

And the javascript:

(function() {
    var puzzle = [];
    var rows = 0;
    var columns = 0;
    var words = [];
    var foundWordsArray = [];
    var foundWordsObj = {
        word: "",
        coordinates: {
            x: 0,
            y: 0
        },
        direction: ""
    };

window.onload = function() {
    puzzle = document.getElementById("wordsearch").value.split("\n").map(function(p) {
        return p.toUpperCase();
    });
    words = document.getElementById("wordlist").value.split("\n").map(function(w) {
        return w.toUpperCase().replace(/\s/g, '');
    });

    rows = puzzle.length;
    columns = puzzle[0].length;

    solvePuzzle();
};

function solvePuzzle() {
    var wFound = []; // list of words that were found
    var res = false; // was word found in any direction
    var wL = words.length;
    // console.log(wL);
    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) {
        fChar = words[i].charAt(0);
        wordFound:
            for (j = 0; j < rows; ++j) {
                for (k = 0; k < columns; ++k) {
                    if (fChar == puzzle[j].charAt(k + 1)) {
                        // found first character


                        //left
                        res = findWordLeft(words[i], 1, words[i].length, j, k + 1);
                        if (false !== res) {

                            // console.log(i + 1, words[i], (k + 2), (j + 1), "RL");
                            break wordFound; // found a word, break to wordFound
                        }

                        //Right
                        res = findWordRight(words[i], 1, words[i].length, j, k + 1);
                        if (false !== res) {

                            // console.log(i + 1, words[i], (k + 2), (j + 1), "LR");
                            break wordFound; // found a word, break to wordFound
                        }

                        //Up
                        res = findWordUp(words[i], 1, words[i].length, j, k + 1);
                        if (false !== res) {

                            // console.log(i + 1, words[i], (k + 2), (j + 1), "U");
                            break wordFound; // found a word, break to wordFound
                        }

                        //Down
                        res = findWordDown(words[i], 1, words[i].length, j, k + 1);
                        if (false !== res) {

                            // console.log(i + 1, words[i], (k + 2), (j + 1), "D");
                            break wordFound; // found a word, break to wordFound
                        }

                        //UpLeft
                        res = findWordUpLeft(words[i], 1, words[i].length, j, k + 1);
                        if (false !== res) {

                            // console.log(i + 1, words[i], (k + 2), (j + 1), "DUL");
                            break wordFound; // found a word, break to wordFound
                        }

                        //UpRight
                        res = findWordUpRight(words[i], 1, words[i].length, j, k + 1);
                        if (false !== res) {

                            // console.log(i + 1, words[i], (k + 2), (j + 1), "DUR");
                            break wordFound; // found a word, break to wordFound
                        }

                        //DownLeft
                        res = findWordDownLeft(words[i], 1, words[i].length, j, k + 1);
                        if (false !== res) {

                            // console.log(i + 1, words[i], (k + 2), (j + 1), "DDL");
                            break wordFound; // found a word, break to wordFound
                        }

                        //DownRight
                        res = findWordDownRight(words[i], 1, words[i].length, j, k + 1);
                        if (false !== res) {

                            // console.log(i + 1, words[i], (k + 2), (j + 1), "DDR");
                            break wordFound; // found a word, break to wordFound
                        }
                    }
                }
            }

    }
}

function findWordLeft(word, posW, wordL, j, k) {
    var result = false;
    if (posW == wordL) { // check if all characters were found
        return true;
    }

    if (k > 0 && word.charAt(posW) == puzzle[j].charAt(k - 1)) {
        result = findWordLeft(word, posW + 1, wordL, j, k - 1);
        if (result !== false) {
            return new Array(j, k - 1);
        }
    }
    return result;
}

function findWordRight(word, posW, wordL, j, k) {

    var result = false;
    if (posW == wordL) { // check if all characters were found
        return true;
    } 

    if (k < columns && word.charAt(posW) == puzzle[j].charAt(k + 1)) {
        result = findWordRight(word, posW + 1, wordL, j, k + 1);
        if (result !== false) {
            return new Array(j, k + 1);
        }
    }
    return result;
}

function findWordUp(word, posW, wordL, j, k) {
    var result = false;
    if (posW == wordL) { // check if all characters were found
        return true;
    } 

    if (0 <= (j - 1) && word.charAt(posW) == puzzle[j - 1].charAt(k)) {
        result = findWordUp(word, posW + 1, wordL, j - 1, k);
        if (result !== false) {

            return new Array(j - 1, k);
        }
    }
    return result;
}

function findWordDown(word, posW, wordL, j, k) {
    var result = false;
    if (posW == wordL) { // check if all characters were found
        return true;
    } 

    if (rows > (j + 1) && word.charAt(posW) == puzzle[j + 1].charAt(k)) {
        result = findWordDown(word, posW + 1, wordL, j + 1, k);
        if (result !== false) {

            return new Array(j + 1, k);
        }
    }
    return result;
}

function findWordUpLeft(word, posW, wordL, j, k) {
    var result = false;
    if (posW == wordL) { // check if all characters were found
        return true;
    } 

    if (0 < k && 0 < j && word.charAt(posW) == puzzle[j - 1].charAt(k - 1)) {
        result = findWordUpLeft(word, posW + 1, wordL, j - 1, k - 1);
        if (result !== false) {

            return new Array(j - 1, k - 1);
        }
    }
    return result;
}

function findWordUpRight(word, posW, wordL, j, k) {
    var result = false;
    if (posW == wordL) { // check if all characters were found
        return true;
    } 

    if (columns > k && 0 < j && word.charAt(posW) == puzzle[j - 1].charAt(k + 1)) {
        result = findWordUpRight(word, posW + 1, wordL, j - 1, k + 1);
        if (result !== false) {

            return new Array(j - 1, k + 1);
        }
    }
    return result;
}

function findWordDownLeft(word, posW, wordL, j, k) {
    var result = false;
    if (posW == wordL) { // check if all characters were found
        return true;
    }

    if (rows > (j + 1) && 0 < k && word.charAt(posW) == puzzle[j + 1].charAt(k - 1)) {
        result = findWordDownLeft(word, posW + 1, wordL, j + 1, k - 1);
        if (result !== false) {

            return new Array(j + 1, k - 1);
        }
    }
    return result;
}

function findWordDownRight(word, posW, wordL, j, k) {
    var result = false;
    if (posW == wordL) {  // check if all characters were found
        return true;
    }

    if (rows > (j + 1) && columns > k && word.charAt(posW) == puzzle[j + 1].charAt(k + 1)) {
        result = findWordDownRight(word, posW + 1, wordL, j + 1, k + 1);
        if (result !== false) {

            return new Array(j + 1, k + 1);
        }
    }
    return result;
}

})();

Here is what I get from the console:

1 "APPLICATION" 4 6 "DDR"
3 "BINARY" 3 12 "DUR"
4 "BLUETOOTH" "RL" 15 18
5 "BOOT" 11 12 "LR"
6 "BYTE" 15 18 "LR"
7 "CHAT" 12 6 "DDL"
8 "CLICK" 2 11 "DUR"
9 "COOKIE" "RL" 18 17
10 "CURSOR" 18 17 "U"
11 "DATA" 11 14 "DDL"
12 "DEFRAGMENT" 10 9 "DDL"
13 "DIRECTORY" 3 4 "LR"
14 "DISKDRIVE" 3 18 "DUR"
15 "DOS" 3 8 "DUR"
16 "DRAG" 6 18 "DUL"
17 "EMAIL" "RL" 5 2
18 "ENCRYPTION" 12 4 "D"
19 "FILE" 8 11 "U"
20 "FIREWALL" 3 17 "LR"
21 "FOLDER" 17 3 "D"
22 "GIF" 17 1 "D"
23 "GOOGLE" 18 6 "U"
24 "HTML" 18 10 "U"
25 "ICON" 2 7 "U"
26 "INTERNET" 16 1 "D"
27 "JAVASCRIPT" "RL" 10 1
28 "KERNAL" 3 11 "DDR"
30 "LOGIN" 17 11 "D"
31 "MEMORY" 4 2 "LR"
32 "MONITOR" 18 11 "DDL"
33 "MOUSE" 11 2 "LR"
34 "NANOSECOND" 2 17 "U"
35 "NETWORK" 16 16 "DUL"
36 "PARTITION" 9 7 "DDR"
37 "PASTE" 6 16 "DUL"
38 "PDF" "RL" 4 18
39 "PIXEL" "RL" 15 1
41 "PROGRAMMER" 12 16 "DUL"
42 "ROUTER" 10 13 "DDL"
43 "SAVEAS" "RL" 10 6
45 "SECURITY" 13 10 "U"
46 "SHAREWARE" 14 2 "D"
47 "SOFTWARE" 15 3 "D"
48 "SPAM" 15 11 "DUR"
49 "TASKBAR" "RL" 8 3
50 "THUMBNAIL" 18 9 "DDL"
51 "UNIX" "RL" 12 3
52 "WALLPAPER" 11 17 "DUL"

Solution

  • There seems to be something off with the way you indexing or the way you check for the boundaries. To be honest, I can't exactly tell you what the problem is, but I have a working version which is much shorter and with less code repetition. Maybe you can take something from that.

    function solvePuzzle() {
      // list of words that were found
      var wFound = [];
      console.log("Number of words:", words.length);
      // loop over words
      for (var i = 0; i < words.length; ++i) {
        var fChar = words[i].charAt(0);
        wordFound:
        // loop over characters
        for (var j = 0; j < rows; ++j) {
          for (var k = 0; k < columns; ++k) {
            // check first character
            if (fChar == puzzle[j].charAt(k)) {
              // iterate over all 9 directions
              for (var dj = -1; dj <= 1; dj++) {
                for (var dk = -1; dk <= 1; dk++) {
                  // skip invalid direction
                  if (dj == 0 && dk == 0) continue;
                  // try to find word
                  if (findWord(words[i], 0, j, k, dj, dk)) {
                    console.log(i + 1, words[i], k + 1, j + 1, getDirectionString(dj, dk));
                    // found a word
                    wFound.push(words[i]);
                    break wordFound;
                  }
                }
              }
            }
          }
        }
      }
      console.log("Number of words found:", wFound.length);
    }
    
    function findWord(word, posW, j, k, dj, dk) {
      // check if all characters were found
      if (posW == word.length) return true;
      // check if position is outside the boundaries
      if (j < 0 || j >= rows || k < 0 || k >= columns) return false;
      // check if current character fails to match the word
      if (word.charAt(posW) != puzzle[j].charAt(k)) return false;
      // check next character
      return findWord(word, posW + 1, j + dj, k + dk, dj, dk);
    }
    
    function getDirectionString(dj, dk) {
      switch (dj) {
        case -1: switch (dk) { case -1: return "DUL"; case 0: return "U"; case 1: return "DUR"; } break;
        case 0: switch (dk) { case -1: return "RL"; case 1: return "LR"; } break;
        case 1: switch (dk) { case -1: return "DDL"; case 0: return "D"; case 1: return "DDR"; } break;
      }
      return "invalid";
    }
    

    The output is:

    Number of words: 53
    1 "APPLICATION" 4 6 "DDR"
    2 "BACKUP" 1 10 "U"
    3 "BINARY" 3 12 "DUR"
    4 "BLUETOOTH" 15 18 "RL"
    5 "BOOT" 11 12 "LR"
    6 "BYTE" 15 18 "LR"
    7 "CHAT" 12 6 "DDL"
    8 "CLICK" 2 11 "DUR"
    9 "COOKIE" 18 17 "RL"
    10 "CURSOR" 18 17 "U"
    11 "DATA" 11 14 "DDL"
    12 "DEFRAGMENT" 10 9 "DDL"
    13 "DIRECTORY" 3 4 "LR"
    14 "DISKDRIVE" 3 18 "DUR"
    15 "DOS" 3 8 "DUR"
    16 "DRAG" 6 18 "DUL"
    17 "EMAIL" 5 2 "RL"
    18 "ENCRYPTION" 12 4 "D"
    19 "FILE" 8 11 "U"
    20 "FIREWALL" 3 17 "LR"
    21 "FOLDER" 17 3 "D"
    22 "GIF" 17 1 "D"
    23 "GOOGLE" 18 6 "U"
    24 "HTML" 18 10 "U"
    25 "ICON" 2 7 "U"
    26 "INTERNET" 16 1 "D"
    27 "JAVASCRIPT" 10 1 "RL"
    28 "KERNAL" 3 11 "DDR"
    29 "LCD" 1 2 "D"
    30 "LOGIN" 17 11 "D"
    31 "MEMORY" 4 2 "LR"
    32 "MONITOR" 18 11 "DDL"
    33 "MOUSE" 11 2 "LR"
    34 "NANOSECOND" 2 17 "U"
    35 "NETWORK" 16 16 "DUL"
    36 "PARTITION" 9 7 "DDR"
    37 "PASTE" 6 16 "DUL"
    38 "PDF" 4 18 "RL"
    39 "PIXEL" 15 1 "RL"
    40 "POWERSUPPLY" 1 5 "LR"
    41 "PROGRAMMER" 12 16 "DUL"
    42 "ROUTER" 10 13 "DDL"
    43 "SAVEAS" 10 6 "RL"
    44 "SCANNER" 1 17 "U"
    45 "SECURITY" 13 10 "U"
    46 "SHAREWARE" 14 2 "D"
    47 "SOFTWARE" 15 3 "D"
    48 "SPAM" 15 11 "DUR"
    49 "TASKBAR" 8 3 "RL"
    50 "THUMBNAIL" 18 9 "DDL"
    51 "UNIX" 12 3 "RL"
    52 "WALLPAPER" 11 17 "DUL"
    Number of words found: 52