<?xml version="1.0" encoding="windows-1252"?>
<node id="181348" title="Scrabbler" created="2002-07-12 13:14:15" updated="2005-08-11 08:35:48">
<type id="1748">
sourcecode</type>
<author id="178565">
dcpve</author>
<data>
<field name="doctext">
&lt;CODE&gt;
#!/usr/bin/perl -w
#scrabbler.pl - A program to find suitable words while playing Scrabble
#By Collin Winter d_cove@hotmail.com
#
#Options:
#	-f : Means that this is the first turn.
#	     This will skip the request for base letters (see below),
#	     since there will be none.

use strict;

#Replace /usr/share/dict/linux.words with the path to the words file on your system.
open WORDLIST, "/usr/share/dict/linux.words" or die "Unable to open word list: $!\n";
my @wordlist=&lt;WORDLIST&gt;;
close WORDLIST;

#Find out what letters the player has, and make a hash of their frequency
print "Your letters: ";
chomp(my $master_letters=&lt;STDIN&gt;);

#Removes any non-alphacharacters that may have been used to separate the letters;
$master_letters=~s/[^A-Za-z]//g;

my $base_letters;
my %letter_freq;
#If this is the first turn of the game, then there be no base letters to use, so we need to obtain one
if($ARGV[0] eq '-f'){
	$base_letters=substr($master_letters,0,1);
	$master_letters=substr($master_letters,1,length($master_letters)-1);
}

for (split(//,$master_letters)){
	$letter_freq{$_}++;
}

#"Base letters" are tiles that are already out on the board and available for to build words on.
#One base letter will be added at a time to $letters, and valid words looked up for that combination.
if(!$ARGV[0] || $ARGV[0] ne '-f'){
	print "Base letters: ";
	chomp($base_letters=&lt;STDIN&gt;);
	$base_letters=~s/[^A-Za-z]//g;
}

#"Initial omission level" is the number of characters from $letters that maybe left out of a word.
#A value of 0 will return only words that contain all of the characters in $letters.
#Setting it to 1 will return all words that contain every letter and all words with all letters except one, etc
print "Initial omission level: ";
chomp(my $master_om_level=&lt;STDIN&gt;);

my @possible_words;
my @valid_words;

foreach my $base (split(//,$base_letters)){
	my $om_level=$master_om_level;

	#Add the current base letter to the string and to the letter frequency hash
	my $letters=$master_letters.$base;

	$letter_freq{$base}++;
	my $do_it_again=1;

	#Repeat the word-evaluation until a word is found.
	while($do_it_again==1){
		
		#This is done like it is so that $max_length and $min_length can be modified later.
		my $max_length=length($letters)+1;
		my $min_length=length($letters)-$om_level+1;
		
		#Thanks to mt2k for suggesting this kind of for loop.
		for my $i ($min_length .. $max_length){
			push(@possible_words,grep(length($_)==$i,@wordlist));
		}
		
		foreach my $word (@possible_words){
			my %this_freq=%letter_freq;
			my $this_word=$word;
			my $key_count=0;
			my $remaining_oms=$om_level;
		
			#Repeat until either:
			# a) there are no more letters to check for,
			# b) all keys in %letter_freq have been used, or
			# c) the word has too many omissions to be used.
			while((length($this_word)&gt;1) &amp;&amp; ($key_count&lt;=(scalar keys %letter_freq)-1) &amp;&amp; ($remaining_oms&gt;=0)){
				my $key=(keys %letter_freq)[$key_count];
		
				#If the current letter isn't in the word, then
				#There is one less omission remaining for this word.
				if($this_word!~/$key/i){
					$remaining_oms--;
				}
		
				if(($this_freq{$key} &gt; 0) &amp;&amp; ($this_word=~/$key/i)){
			
					if($this_word!~/$key/i){
						$this_freq{$key}--;
					}
			
					while($this_freq{$key}&gt;0){	
						$this_freq{$key}--;
						$this_word=~s/$key//i;
					}
				}
				$key_count++;
			}
			
			if(length($this_word)==1){
				push(@valid_words,$word);
			}
		}
		
		#If no words are found at your current omission level, the search
		#will be automatically enlarged until at least one word is returned.
		print $#valid_words+1," results with $om_level allowed omissions for base \'$base\'";
		
		if(!$valid_words[0]){
			@possible_words=();
			print "; Expanding search...\n";
			$om_level++;
			$max_length=$min_length--;
		}
		if($valid_words[0]){
			print ":\n",@valid_words;
			@valid_words=();
			$do_it_again=0;
		}
	}

	#Remove the current base from the letter frequency hash
	$letter_freq{$base}--;
}
exit;
&lt;/CODE&gt;</field>
<field name="codedescription">
Finds suitable Scrabble words based on your current tiles, the tiles on the board that are available for word building, and how many of your current tiles you are willing to omit to form a word.</field>
<field name="codecategory">
Fun Stuff</field>
<field name="codeauthor">
Collin Winter aka dcpve&lt;br&gt;
d_cove@hotmail.com</field>
</data>
</node>
