A while ago I posted the beginnings of a boggle solver to SoPW. It used depth first search and recursion to go through the board finding all the possible strings. After some discussion, abigail-II posted a working boggle solver that used the depth first search and some preprocessing to find the words in the board. After a brief perl sabbatical, I went back through my old posts and decided to redo some of them. This is part of that effort.

As opposed to dfs, this use an iterative approached based on the location of characters, and saves time by finding words while reading from the dictionary. First it splits $input into @data and pushes those values into @board, which is really a copy of one of the boards in %layout. While it does this it is creating %letter_position, where the key is the character and the value is an array holding the positions where that character can be found. Then the dictionary is opened, the word is validated and split into @word, and the search for the word begins.

First you start a for loop that goes through the index of @word; inside this loop you foreach through the co-ordinates of the character $word[$index] (foreach (@{ $letter_position{$word[$index]} }) {). At the beginning of that loop you add that position ($x_one, $y_one) to %path to prevent the reuse of a character later . Inside that loop, you begin another foreach through the co-ordinates of the next character $word[$index+1]. If the second position ($x_two, $y_two) isnt touching the first ($x_one, $y_one) it is added to %invalid for that particular index i.e. the character in that position cant possibly be used to represent $word[$index+1]. If that position is touching the first, you jump to the next index.

Wow that is a bad explanation. Running this with the debugger will give you a better idea of how it works. Here is an example of how it finds the word fink in a board:


board:
    0 1 2 3
    -------
  0|a b c i
  1|e f i h
  2|i j k l
  3|m n o p

word:
  fink

index: 1
  letter_one: f
  x_one: 1
  y_one: 1
    letter_two: i
    x_two: 3
    y_two: 0
      letter_two not touching letter_one
      3 . 0 added to %invalid for index 2
      next letter_two
    letter_two: i
    x_two: 2
    y_two: 1
      letter_two touching letter_one
      index++
      next index

index: 2
  letter_one: i
  x_one: 3
  y_one: 0
    3 . 0 invalid for index 2
  x_one: 2
  y_one: 1
    letter_two: n
    x_two: 1
    y_two: 3
      letter_two not touching letter_one
      1 . 3 added to %invalid for index 3
      next letter_two
    no more n's
    2 . 1 added to invalid for index 2
    index--
    next index

index: 1
  letter_one: f
  x_one: 1
  y_one: 1
  letter_two: i
    x_two: 3
    y_two: 0
      3 . 0 invalid for index 2
    x_two: 2
    y_two: 1
      2 . 1 invalid for inde 2
    x_two: 0
    y_two: 2
      letter_two touching letter_one
      index++
      next index

index: 2
  letter_one: i
  x_one: 3
  y_one: 0
    3 . 0 invalid at index 2
  x_one: 2
  y_one: 1
    2 . 1 invalid at index 2
  x_one: 0
  y_one: 2
    letter_two: n
    x_two: 1
    y_two: 3
      letter_two touching letter_one
      index++
      next index

index: 4
  letter_one: n
  x_one: 1
  y_one: 3
    letter_two: k
    x_two: 2
    y_two: 2
      letter_two touching letter_one
      index++
      next index

This example skips over some of the little details, and doesnt explain exactly how it is implemented, but it should be enough to understand the code below. There are some places for performance improvement. When you validate the the word from the dictionary you could make sure that $input contains the number of letters in the word i.e. skip the word if the input has only one a and the word is arab. Also, you could create a hash of invalid prefixs as you find invalid words and skip those words i.e. if you cant find child, there is no reason to search for childhood. This isn't yet finished code (no way to chose layout or provide input from the command line), but the core of the program is there. Anyway... here is the code:

#!/usr/bin/perl -w use strict; ##### # Layouts of boggle/word racer boards ##### my (%layout) = ( "word_racer_1" => [ [1,1,1,1], [1,1,1,1], [1,1,1,1], [1,1,1,1] ], "word_racer_2" => [ [0,0,1,1,0,0], [0,1,1,1,1,0], [1,1,1,1,1,1], [1,1,1,1,1,1], [0,1,1,1,1,0], [0,0,1,1,0,0] ], "word_racer_3" => [ [1,1,1,1,0,0], [1,1,1,1,0,0], [1,1,1,1,1,1], [1,1,1,1,1,1], [0,0,1,1,1,1], [0,0,1,1,1,1] ], "word_racer_4" => [ [1,1,1,1,1,1], [1,1,1,1,1,1], [1,1,0,0,1,1], [1,1,0,0,1,1], [1,1,1,1,1,1], [1,1,1,1,1,1] ] ); ##### # get/check configuration ##### my ($sqrt_2) = sqrt(2); my ($min_length) = 3; my ($which_layout) = "word_racer_1"; die "No such layout $which_layout\n" unless exists $layout{ $which_lay +out }; my (@board) = @{$layout{ $which_layout }}; my ($input) = "tnlehrayvsrtjesn"; die "Malformed input\n" if $input =~ /[^a-z]/; my (@data) = split //, $input; ##### # Initialize board ##### my ($x, $y, %letter_position); for ($y = 0; $y <= $#board; $y++) { for ($x = 0; $x <= $#{$board[$y]}; $x++) { die "Data smaller then layout\n" if $#data == -1; next if $board[$y][$x] == 0; $board[$y][$x] = shift @data; push @{ $letter_position{ $board[$y][$x] } }, [$x, $y]; } } die "Data longer then layout\n" unless $#data == -1; ##### # Open the dictionary ##### my ($file_dict) = "/usr/share/dict/words_processed"; open (DICT, "<", $file_dict) or die "Can't open $file_dict: $!\n"; ##### # Check wether the word is in the board ##### my (@word, $index, %invalid, %notouch, %path); while (<DICT>) { chomp; $_ = lc; next if length() < $min_length or /[^$input]/; @word = split //; %invalid = (); %path = (); INDEX: for ($index = 0; $index <= $#word; $index++) { if ($index == $#word) { print join '', @word; print "\n"; last; } foreach (@{ $letter_position{$word[$index]} }) { my ($x_one, $y_one) = @{$_}; last if exists $invalid{$index}{$x_one . $y_one}; $path{$x_one . $y_one} = 1; foreach (@{ $letter_position{$word[$index+1]} }) { my ($x_two, $y_two) = @{$_}; next if exists $notouch{$x_one . $y_one . $x_two . $y_t +wo}; next if exists $invalid{$index+1}{$x_two . $y_two}; next if exists $path{$x_two . $y_two}; my ($dist) = sqrt( (($x_one - $x_two)**2) + (($y_one - +$y_two)**2) ); unless ($dist == 1 or $dist == $sqrt_2) { $notouch{$x_one . $y_one . $x_two . $y_two} = 1; next; } next INDEX; } $invalid{$index}{$x_one . $y_one} = 1; } last; } } ##### # Cleanup ##### close (DICT);
Update 2/24/02 21:22: escaped html entities and changed mechanism used to tell if letter_one and letter_two are touching from exists $moves{($x_one - $x_two) . ($y_one - $y_two)} to calculating the distance between the points.
Update 2/24/02 22:50: There is a problem that is either caused by the last update or the program in general. It is missing a couple words on some boards, and adding a ton of words that arent there. Hopefully it will be fixed soon.
Update 2/25/02 01:25: I fixed part of the program, and cleaned up the code, but there are still some kinks to work out, and I havent updated the example above.

In reply to Boggle again by smgfc

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.