One of the things that grieves me about playing scrabble is "I wonder
if I missed a bingo?", particularly if I have a bunch of
likely-looking letters (or a blank). I did some searching, finding a
Perl one-liner (I like Perl) that searched a dictionary (like the Unix
/usr/dict/words) using regular expressions. But that didn't work when
I had multiples of a single letter, since regular expressions (without
some tweaking) don't help you count *how many* matches you have. It
seemed to be a natural application for hashes (associative
arrays). So, during a subway ride, I was able to put together such a
program.
First, the word list. I'm running Linux, so I have
/usr/share/dict/words, which has a whole bunch of words, not all legal
for Scrabble, one per line. Then I found an old Scrabble word list on
the web, likewise one word per line, which is what I ended up using.
The observation that a hash was the thing led to the following
subroutine for turning a word into a hash:
sub makehash {
my ($s)=@_;
$s=uc($s); # my dictionary has words in uppercase, but user will
# probably type lowercase
my @s=split //, $s; # divide word into array of individual chars
my %h=();
for my $i (@s) {
$h{$i}++;
}
# for example, $h{'A'} is the number of A's in the word
return %h;
}
Now, I'm looking for bingos (to use up all the letters from my rack,
plus maybe one on the board), so I need any candidate word to have at
least as many of each letter as I do on my rack (plus any letters I'm
planning to use on the board). Conversely, if the dictionary word has
*fewer* of any letter than I have on my rack, I can reject it right
away.
I put in one more refinement: I might have a blank on my rack, so as
well as inputting to the program the letters I would like to use up
(my rack plus anything on the board), I also enter the length of word
I would like to find. That way, any dictionary words not that length
can be rejected without even making a hash for them.
With that in hand, the strategy is:
- read the desired letters and word length from the command line
- turn those letters into a hash
- for each word in word list:
- reject if wrong length;
- turn into a hash
- if examination of the hash reveals that the dictionary word has
fewer of any letters than the input does, that dictionary word
can be rejected.
- if dictionary word is not rejected, print it out
which leads to the following main program:
my $letters=shift;
my $length=shift;
$length+=2; # one is the line-ending newline, but I don't know what
# the other is
%l=makehash($letters);
open IN, "TWL06.txt"; # or die....
WORD: while (<IN>) { # the dictionary word is in $_
next unless length==$length;
my %m=makehash($_);
for my $l (keys %l) {
next WORD if $l{$l}>$m{$l};
}
print;
}
As an example, I had INGESTI on my rack. I had seen that this makes
IGNITES, but there was no place to put it. There was, however, an open
N. Any help? Could I make an 8-letter bingo?
ken@ken-laptop:~/words$ perl search.pl ingestin 8
GINNIEST
Woo hoo!
Another example: suppose I have ROTANE plus a blank. What 7 letter
bingos do I have? Quite a few:
ken@ken-laptop:~/words$ perl search.pl rotane 7
ANOTHER
ATONERS
BARONET
ENACTOR
NEGATOR
OPERANT
OUTEARN
PRONATE
PROTEAN
REBOANT
SANTERO
SENATOR
TONEARM
TREASON
No comments:
Post a Comment