<?xml version="1.0" encoding="windows-1252"?>
<node id="188754" title="Skrabbel 2" created="2002-08-08 17:47:51" updated="2005-08-11 22:08:20">
<type id="1748">
sourcecode</type>
<author id="961">
Anonymous Monk</author>
<data>
<field name="doctext">
Here's the actual script:
&lt;CODE&gt;
use strict;
use Tk;
use Tk::DialogBox;
use Tk::FileDialog;
use Tk::Checkbutton;
use Tk::OptionMenu;
use File::Glob;


#My custom class.
use Skrabbel;


#Our Important Globals
my $MW; #MainWindow
my $VERSION = '0.1';
my $scrabble; #Our class object that maintains the game
my $board_frame; #Frame for Scrabble Board
my $entry_frame; #Frame for all entries
my @square_frames; #1-D Array of Frames
my @squares; #2-D Array of Labels (15x15)
my $entry_word; #Word to enter onto board
my $x_pt; #X coordinate point on board
my $y_pt; #Y coordinate point on board
my $orientation; #Direction of new word (Horizontal,Vertical)
my $my_letters; #Current letters you have to play
my $num_results; #Number of results we want when finding best
my $value; #current value of word being shown
my $best_moves; #reference to array of best moves
my $preview_showing; #which preview in array we are showing
my $search_time; #time it took to run search


#Set up the mainwindow
$MW = MainWindow-&gt;new(
	-title =&gt; "Scrabbler - v$VERSION"
	);
$MW-&gt;resizable(0,0);
$MW-&gt;withdraw();


#Set up our board_frame
$board_frame = $MW-&gt;Frame(
	)-&gt;pack(
		-side =&gt; 'left',
		);
my $top_axis_frame = $board_frame-&gt;Frame(
	)-&gt;pack(
		-side =&gt; 'top',
		-fill =&gt; 'x'
		);
$top_axis_frame-&gt;Label(
	-text =&gt; " # ",
	-width =&gt; 2,
	-font =&gt; [
		-weight =&gt; 'bold',
		-size =&gt; 12
		],
	-relief =&gt; 'flat',
	-borderwidth =&gt; 1
	)-&gt;pack(
		-side =&gt; 'left',
		-padx =&gt; 1
		);
for (my $x = 0; $x &lt; 15; $x++) {
	$top_axis_frame-&gt;Label(
		-text =&gt; " $x ",
		-width =&gt; 2,
		-font =&gt; [
			-weight =&gt; 'bold',
			-size =&gt; 12
			],
		-relief =&gt; 'flat',
		-borderwidth =&gt; 1
		)-&gt;pack(
			-side =&gt; 'left',
			-padx =&gt; 1,
			-pady =&gt; 1
			);
}
for (my $x = 0; $x &lt; 15; $x++) {
	$square_frames[$x] = $board_frame-&gt;Frame(
	)-&gt;pack(
		-side =&gt; 'top',
		-fill =&gt; 'x',
		-expand =&gt; 1
		);
	$square_frames[$x]-&gt;Label(
		-text =&gt; " $x ",
		-width =&gt; 2,
		-font =&gt; [
			-weight =&gt; 'bold',
			-size =&gt; 12
			],
		-relief =&gt; 'flat',
		-borderwidth =&gt; 1
		)-&gt;pack(
			-side =&gt; 'left',
			-padx =&gt; 1
			);
	for (my $y = 0; $y &lt; 15; $y++) {
		$squares[$x][$y] = $square_frames[$x]-&gt;Label(
			-text =&gt; "   ",
			-width =&gt; 2,
			-font =&gt; [
				-weight =&gt; 'bold',
				-size =&gt; 12
				],
			-relief =&gt; 'sunken',
			-borderwidth =&gt; 1
			)-&gt;pack(
				-side =&gt; 'left',
				-padx =&gt; 1
				);
	}
}


#Color our squares on board_frame
#make the 3W (Triple Word) squares
foreach my $x (0,7,14) {
	foreach my $y (0,7,14) {
		$squares[$x][$y]-&gt;configure(
			-background =&gt; 'red'
			);
	}
}
#make the 2W (Double Word) squares
foreach my $x (1,2,3,4,7) {
	$squares[$x][$x]-&gt;configure(
			-background =&gt; 'orange'
			);
	$squares[$x][14 - $x]-&gt;configure(
			-background =&gt; 'orange'
			);
	$squares[14 - $x][$x]-&gt;configure(
			-background =&gt; 'orange'
			);
	$squares[14 - $x][14 - $x]-&gt;configure(
			-background =&gt; 'orange'
			);
}
#make the 3L (Triple Letter) squares
foreach my $x (5,9) {
	foreach my $y (5,9) {
		$squares[$x][$y]-&gt;configure(
			-background =&gt; 'blue'
			);
	}
}
foreach my $x (1,13) {
	foreach my $y (1,5) {
		$squares[$x][$y]-&gt;configure(
			-background =&gt; 'blue'
			);
		$squares[$x][14 - $y]-&gt;configure(
			-background =&gt; 'blue'
			);
	}
}
#make the 2L (Double Letter) squares
foreach my $x (0,7,14) {
	foreach my $y (3,11) {
		$squares[$x][$y]-&gt;configure(
			-background =&gt; 'yellow'
			);
		$squares[$y][$x]-&gt;configure(
			-background =&gt; 'yellow'
			);
	}
}
foreach my $x (2,12,6,8) {
	foreach my $y (6,8) {
		$squares[$x][$y]-&gt;configure(
			-background =&gt; 'yellow'
			);
		$squares[$y][$x]-&gt;configure(
			-background =&gt; 'yellow'
			);
	}
}


#set our Entries frame
$entry_frame = $MW-&gt;Frame(
	)-&gt;pack(
		-side =&gt; 'right',
		-fill =&gt; 'both',
		-expand =&gt; 1
		);


#Our "Place Move" title
$entry_frame-&gt;Label(
	-text =&gt; "Place Move",
	-font =&gt; [
		-size =&gt; 12,
		-weight =&gt; 'bold',
		-underline =&gt; 1
		]
	)-&gt;pack(
		-side =&gt; 'top'
		);


#Our Word-Entry frame
my $word_frame = $entry_frame-&gt;Frame(
	)-&gt;pack(
		-side =&gt; 'top',
		-fill =&gt; 'x',
		-pady =&gt; 5
		);
my $word_entry = $word_frame-&gt;Entry(
	-textvariable =&gt; \$entry_word,
	-width =&gt; 18,
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold'
		],
	)-&gt;pack(
		-side =&gt; 'right'
		);
$word_frame-&gt;Label(
	-text =&gt; "Word: ",
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold'
		],
	)-&gt;pack(
		-side =&gt; 'right'
		);


#Our Coordinate Entry Frame
my $location_frame = $entry_frame-&gt;Frame(
	)-&gt;pack(
		-side =&gt; 'top',
		-fill =&gt; 'x',
		-pady =&gt; 5
		);
$location_frame-&gt;Entry(
	-textvariable =&gt; \$x_pt,
	-width =&gt; 2,
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold'
		],
	)-&gt;pack(
		-side =&gt; 'right'
		);
$location_frame-&gt;Label(
	-text =&gt; " col:",
	-font =&gt; [
		-size =&gt; 10
		]
	)-&gt;pack(
		-side =&gt; 'right'
		);
$location_frame-&gt;Entry(
	-textvariable =&gt; \$y_pt,
	-width =&gt; 2,
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold'
		],
	)-&gt;pack(
		-side =&gt; 'right'
		);
$location_frame-&gt;Label(
	-text =&gt; "row:",
	-font =&gt; [
		-size =&gt; 10
		]
	)-&gt;pack(
		-side =&gt; 'right'
		);
$location_frame-&gt;Label(
	-text =&gt; "Starting Point: ",
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold'
		],
	)-&gt;pack(
		-side =&gt; 'right'
		);


#Our Direction Entry Frame
my $direction_frame = $entry_frame-&gt;Frame(
	)-&gt;pack(
		-side =&gt; 'top',
		-fill =&gt; 'x',
		-pady =&gt; 5
		);
$direction_frame-&gt;Optionmenu(
	-variable =&gt; \$orientation,
	-options =&gt; [
		"Horizontal",
		"Vertical"
		],
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold'
		]
	)-&gt;pack(
		-side =&gt; 'right'
		);
$direction_frame-&gt;Label(
	-text =&gt; "Orientation: ",
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold'
		]
	)-&gt;pack(
		-side =&gt; 'right'
		);


#Our button to activate place-move
$entry_frame-&gt;Button(
	-text =&gt; "Add Word",
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold'
		],
	-command =&gt; [\&amp;add_word],
	-background =&gt; 'orange',
	-activebackground =&gt; 'green'
	)-&gt;pack(
		-side =&gt; 'top',
		-anchor =&gt; 'e',
		-pady =&gt; 5
		);



#A little separator
$entry_frame-&gt;Frame(
	)-&gt;pack(
		-side =&gt; 'top',
		-fill =&gt; 'x',
		-pady =&gt; 10
		);


#Our "Find Best Move" title
$entry_frame-&gt;Label(
	-text =&gt; "Find Best Move(s)",
	-font =&gt; [
		-size =&gt; 12,
		-weight =&gt; 'bold',
		-underline =&gt; 1
		],
	)-&gt;pack(
		-side =&gt; 'top'
		);


#our "My Letters" frame
my $letters_frame = $entry_frame-&gt;Frame(
	)-&gt;pack(
		-side =&gt; 'top',
		-fill =&gt; 'x',
		-pady =&gt; 5
		);
$letters_frame-&gt;Entry(
	-textvariable =&gt; \$my_letters,
	-width =&gt; 15,
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold'
		]
	)-&gt;pack(
		-side =&gt; 'right',
		-padx =&gt; 5
		);
$letters_frame-&gt;Label(
	-text =&gt; "My Letters: ",
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold'
		]
	)-&gt;pack(
		-side =&gt; 'right',
		-padx =&gt; 5
		);

#Our "Number of Results" frame
my $results_frame = $entry_frame-&gt;Frame(
	)-&gt;pack(
		-side =&gt; 'top',
		-fill =&gt; 'x',
		-pady =&gt; 5
		);
$results_frame-&gt;Entry(
	-textvariable =&gt; \$num_results,
	-width =&gt; 3,
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold'
		]
	)-&gt;pack(
		-side =&gt; 'right',
		-padx =&gt; 5
		);
$results_frame-&gt;Label(
	-text =&gt; "Num of Results: ",
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold'
		]
	)-&gt;pack(
		-side =&gt; 'right',
		-padx =&gt; 5
		);


#Our frame for controlling scrolling results
my $scroll_frame = $entry_frame-&gt;Frame(
	)-&gt;pack(
		-side =&gt; 'top',
		-fill =&gt; 'x',
		-pady =&gt; 5
		);
$scroll_frame-&gt;Button(
	-text =&gt; "next",
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold'
		],
	-activebackground =&gt; 'green',
	-command =&gt; sub {
			$preview_showing++ if ($preview_showing &lt; scalar(@{$best_moves}) - 1);
			preview($$best_moves[$preview_showing]);
		}
	)-&gt;pack(
		-side =&gt; 'right',
		-padx =&gt; 5
		);
$scroll_frame-&gt;Button(
	-text =&gt; "prev",
	-height =&gt; .5,
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold'
		],
	-activebackground =&gt; 'green',
	-command =&gt; sub {
			$preview_showing-- if ($preview_showing &gt; 0);
			preview($$best_moves[$preview_showing]);
		}
	)-&gt;pack(
		-side =&gt; 'right',
		-padx =&gt; 5
		);
$scroll_frame-&gt;Label(
	-text =&gt; "Value: ",
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold',
		]
	)-&gt;pack(
		-side =&gt; 'left',
		-padx =&gt; 2
		);
$scroll_frame-&gt;Label(
	-textvariable =&gt; \$value,
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold'
		]
	)-&gt;pack(
		-side =&gt; 'left'
		);


#Our buttons for finding or clearing results
my $clear_frame = $entry_frame-&gt;Frame(
	)-&gt;pack(
		-side =&gt; 'top',
		-fill =&gt; 'x',
		-pady =&gt; 5
		);
my $find_button = $clear_frame-&gt;Button(
	-text =&gt; "Find",
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold'
		],
	-activebackground =&gt; 'green',
	-command =&gt; [\&amp;find_results]
	)-&gt;pack(
		-side =&gt; 'right',
		-padx =&gt; 5
		);
$clear_frame-&gt;Button(
	-text =&gt; "Clear",
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold'
		],
	-activebackground =&gt; 'green',
	-command =&gt; [\&amp;clear_results]
	)-&gt;pack(
		-side =&gt; 'right',
		-padx =&gt; 5
		);
$clear_frame-&gt;Label(
	-text =&gt; "Time:",
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold'
		]
	)-&gt;pack(
		-side =&gt; 'left',
		-padx =&gt; 3
		);
$clear_frame-&gt;Label(
	-textvariable =&gt; \$search_time,
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold',
		],
	-width =&gt; 6
	)-&gt;pack(
		-side =&gt; 'left',
		-padx =&gt; 3
		);


#our bottom button frame
my $button_frame = $entry_frame-&gt;Frame(
	)-&gt;pack(
		-side =&gt; 'bottom',
		-fill =&gt; 'x',
		-pady =&gt; 5
		);
$button_frame-&gt;Button(
	-text =&gt; "Save Board",
	-command =&gt; [\&amp;save_select],
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold'
		],
	-background =&gt; 'orange',
	-activebackground =&gt; 'green'
	)-&gt;pack(
		-side =&gt; 'right',
		-padx =&gt; 5
		);
$button_frame-&gt;Button(
	-text =&gt; "Load Board",
	-command =&gt; [\&amp;load_select],
	-font =&gt; [
		-size =&gt; 10,
		-weight =&gt; 'bold'
		],
	-background =&gt; 'orange',
	-activebackground =&gt; 'green'
	)-&gt;pack(
		-side =&gt; 'right',
		-padx =&gt; 5
		);


#configure file browsing object
my $dictionary_window = $MW-&gt;FileDialog(
	-Title =&gt; 'Select Word List',
	-SelHook =&gt; \&amp;start,
	-ShowAll =&gt; 1,
	-Create =&gt; 0
	);
my $load_window = $MW-&gt;FileDialog(
	-Title =&gt; 'Select Board to Load',
	-SelHook =&gt; \&amp;load_board,
	-ShowAll =&gt; 1,
	-Create =&gt; 0
	);
my $save_window = $MW-&gt;FileDialog(
	-Title =&gt; 'Where to Save Board',
	-SelHook =&gt; \&amp;save_board,
	-ShowAll =&gt; 1,
	-Create =&gt; 1
	);


#Pop up our window to get word list
$dictionary_window-&gt;Show();


#This function creates our scrabble class
#and gets things started
sub start($) {
	my ($word_list) = @_;
	my $dir = $word_list;
	while (chop($dir) ne '/') {}


	#bring down this window
	$dictionary_window-&gt;destroy();
	$load_window-&gt;configure(
		-Path =&gt; $dir
		);
	$save_window-&gt;configure(
		-Path =&gt; $dir
		);



	#load module and board
	unless ($scrabble = Skrabbel-&gt;new($word_list)) {
		pop_message("Constructor Failed",1);
	}
	$MW-&gt;geometry('+100+100');
	$MW-&gt;Popup();
	MainLoop();
	exit;
}


#This just updates the values of our displayed board
#based on the 2-D array-ref argument
sub update_board($) {
	my ($new_board) = @_;
	for (my $x = 0; $x &lt; 15; $x++) {
		for (my $y = 0; $y &lt; 15; $y++) {
			my $txt = $$new_board[$x][$y];
			$squares[$x][$y]-&gt;configure(
				-text =&gt; "$txt"
				);
		}
	}
}


#This function previews an additional word on our board
sub preview($) {
	my ($move) = @_;
	my $val_buffer = $$move{'value'};
	$value = $val_buffer;


	#breaking word into array of chars
	my @word_array = split(//,uc($$move{'word'}));


	#first update to actual board
	update_board($scrabble-&gt;curr_board());
	#if horiz we preview here
	if($$move{'orientation'} eq "Horizontal") {
		for(my ($x,$tmp_x) = (0,$$move{'x_pt'}); $x &lt; length($$move{'word'}); $x++,$tmp_x++) {
			$squares[$$move{'y_pt'}][$tmp_x]-&gt;configure(
				-text =&gt; "$word_array[$x]"
				);
		}
	}
	#if vertical then here
	elsif($$move{'orientation'} eq "Vertical") {
		for(my ($y,$tmp_y) = (0,$$move{'y_pt'}); $y &lt; length($$move{'word'}); $y++,$tmp_y++) {
			$squares[$tmp_y][$$move{'x_pt'}]-&gt;configure(
				-text =&gt; "$word_array[$y]"
				);
		}
	}
}


#this function just clears all our fields
#and resets our board
sub clear_results() {
	$num_results = '';
	$my_letters = '';
	$value = '';
	update_board($scrabble-&gt;curr_board());
}


#This function gathers the data in entry_frame
#And tries to add a word to our board!
sub add_word() {
	#Some basic error-checking
	if ($x_pt =~ /\D/ || $y_pt =~ /\D/ || $x_pt &gt; 14 || $x_pt &lt; 0 ||
		$y_pt &gt; 14 || $y_pt &lt; 0 || $y_pt eq "" || $x_pt eq "") {
		pop_message("Coordinates Are Improper",0);
		return 0;
	}


	#have our object add this word
	if($scrabble-&gt;add_word($entry_word, $x_pt, $y_pt, $orientation)) {
		update_board($scrabble-&gt;curr_board());
		return 1;
	}
	else {
		pop_message("New Word Won't Fit On Board",0);
		return 0;
	}
}


#Here we pop our filedialog to select file to load
sub load_select() {
	$load_window-&gt;raise();
	$load_window-&gt;Show();
}


#Here we load the board in the functions first and only argument
sub load_board($) {
	my ($file) = @_;


	#let's have our filedialog pop up in this dir next time
	my $dir = $file;
	while (chop($dir) ne "/") {};
	$load_window-&gt;configure(
		-Path =&gt; $dir
		);


	#Now we call our class and get it done
	if($scrabble-&gt;load_board($file)) {
		update_board($scrabble-&gt;curr_board());
		return 1;
	}
	else {
		pop_message("Couldn't Load Board At: $file",0);
		return 0;
	}
}


#Here we pop up our filedialog to select file to save board to
sub save_select() {
	$save_window-&gt;raise();
	$save_window-&gt;Show();
}


#Here we save our board to the file in the functions first
#and only argument
sub save_board($) {
	my ($file) = @_;


	#let's have our filedialog pop up in this dir next time
	my $dir = $file;
	while (chop($dir) ne "/") {};
	$save_window-&gt;configure(
		-Path =&gt; $dir
		);


	#Now we call our class and get it done
	if($scrabble-&gt;save_board($file)) {
		pop_message("Board Saved Successfully",0);
		return 1;
	}
	else {
		pop_message("Couldn't Save Board To: $file",0);
		return 0;
	}
}


#Here we ask our object to do it's thing and we find
#the top results.
sub find_results() {
	my $time = time();
	$best_moves = $scrabble-&gt;find_results($my_letters, $num_results);
	$time = time() - $time;
	my $min = int($time/60);
	my $sec = $time - ($min * 60);
	$search_time = "$min" . "m " . "$sec" . "s";
	if($best_moves) {
		preview($$best_moves[0]);
		$preview_showing = 0;
		pop_message("Move Search Complete",0);
	}
	else {
		pop_message("Find Request Failed",0);
	}
}


#This function replaces our Win32 MsgBox's.  We pop
#up message and then whether it's a program ender
#or just a message.
sub pop_message($$) {
	my ($msg,$death) = @_;


	#Create our toplevel object
	my $PW = $MW-&gt;Toplevel(
		-title =&gt; "Message For Ya",
		-takefocus =&gt; 1
		);
	$PW-&gt;resizable(0,0);
	$PW-&gt;protocol('WM_DELETE_WINDOW',sub {;});
	$PW-&gt;withdraw();


	#If it's the end we take away MW
	if ($death) {
		$MW-&gt;withdraw();
	}


	#Now make our label and button
	$PW-&gt;Label(
		-text =&gt; "$msg",
		-font =&gt; [
			-size =&gt; 10,
			-weight =&gt; 'bold'
			]
		)-&gt;pack(
			-side =&gt; 'top',
			-pady =&gt; 5
			);
	my $button = $PW-&gt;Button(
		-text =&gt; "Ok",
		-font =&gt; [
			-size =&gt; 10,
			-weight =&gt; 'bold'
			],
		-activebackground =&gt; 'green',
		-width =&gt; 10,
		-command =&gt; [ sub {
				if ($death) {
					$MW-&gt;destroy();
				}
				else {
					$PW-&gt;destroy();
				}
			} ]
		)-&gt;pack(
			-side =&gt; 'top',
			-pady =&gt; 5
			);


	#Pop up our window
	$PW-&gt;Popup();
	$button-&gt;focus();
}
&lt;/CODE&gt;
This is the module.  It should become Skrabbel.pm
&lt;CODE&gt;
package Skrabbel;


use strict;


#our letter values
my %letter_values = (
	"A" =&gt; 1,
	"B" =&gt; 3,
	"C" =&gt; 3,
	"D" =&gt; 2,
	"E" =&gt; 1,
	"F" =&gt; 4,
	"G" =&gt; 2,
	"H" =&gt; 4,
	"I" =&gt; 1,
	"J" =&gt; 8,
	"K" =&gt; 5,
	"L" =&gt; 1,
	"M" =&gt; 3,
	"N" =&gt; 1,
	"O" =&gt; 1,
	"P" =&gt; 3,
	"Q" =&gt; 10,
	"R" =&gt; 1,
	"S" =&gt; 1,
	"T" =&gt; 1,
	"U" =&gt; 1,
	"V" =&gt; 4,
	"W" =&gt; 4,
	"X" =&gt; 8,
	"Y" =&gt; 4,
	"Z" =&gt; 10
	);


#our premium squares
my @premium_squares;
for(my $x = 0; $x &lt; 15; $x++) {
	for(my $y = 0; $y &lt; 15; $y++) {
		$premium_squares[$x][$y] = "  ";
	}
}


#make the 3W (Triple Word) squares
foreach my $x (0,7,14) {
	foreach my $y (0,7,14) {
		$premium_squares[$x][$y] = "3W";
	}
}


#make the 2W (Double Word) squares
foreach my $x (1,2,3,4,7) {
	$premium_squares[$x][$x] = "2W";
	$premium_squares[$x][14 - $x] = "2W";
	$premium_squares[14 - $x][$x] = "2W";
	$premium_squares[14 - $x][14 - $x] = "2W";
}


#make the 3L (Triple Letter) squares
foreach my $x (5,9) {
	foreach my $y (5,9) {
		$premium_squares[$x][$y] = "3L";
	}
}
foreach my $x (1,13) {
	foreach my $y (1,5) {
		$premium_squares[$x][$y] = "3L";
		$premium_squares[$x][14 - $y] = "3L";
	}
}


#make the 2L (Double Letter) squares
foreach my $x (0,7,14) {
	foreach my $y (3,11) {
		$premium_squares[$x][$y] = "2L";
		$premium_squares[$y][$x] = "2L";
	}
}
foreach my $x (2,12,6,8) {
	foreach my $y (6,8) {
		$premium_squares[$x][$y] = "2L";
		$premium_squares[$y][$x] = "2L";
	}
}
1;


#################################################
#CLIENT FUNCTIONS
#################################################

#Our constructor.  Takes a file location
#and loads the word-list in it
sub new($$) {
	my ($self, $word_file) = @_;


	#Load our word list, if we have one
	my @word_list = ();
	if($word_file) {
		open(LST, '&lt;', $word_file)
			or return 0;
		@word_list = &lt;LST&gt;;
		close(LST);
	}
	for(my $x = 0; $x &lt; scalar(@word_list); $x++) {
		chomp($word_list[$x]);
		$word_list[$x] =~ s/\s+//g;
		$word_list[$x] = lc($word_list[$x]);
	}


	#Create our current board
	my @current_board;
	for(my $x = 0; $x &lt; 15; $x++) {
		for(my $y = 0; $y &lt; 15; $y++) {
			$current_board[$x][$y] = ' ';
		}
	}


	#Instantiate our object
	bless {
		word_list =&gt; \@word_list,
		current_board =&gt; \@current_board
	}, $self;
}


#This function takes a file-location
#and loads the formatted board in it
sub load_board($$) {
	my ($self, $file) = @_;
	open(BRD, '&lt;', $file)
		or return 0;
	chomp(my @current_board = &lt;BRD&gt;);
	close(BRD);
	for(my $x = 0; $x &lt; 15; $x++) {
		$current_board[$x] = [ split('-',$current_board[$x]) ];
	}
	$self-&gt;{current_board} = \@current_board;


	return 1;
}


#This function saves our curr_board to the file
#location in argument.
sub save_board($$) {
	my ($self,$file) = @_;
	my $current_board = $self-&gt;curr_board();


	#Open our file
	open(BRD, '&gt;', $file)
		or return 0;


	#Now we print it out
	for(my $y = 0; $y &lt; 14; $y++) {
		for(my $x = 0; $x &lt; 14; $x++) {
			print BRD $$current_board[$y][$x],"-";
		}
		print BRD $$current_board[$y][14],"\n";
	}
	for(my $x = 0; $x &lt; 14; $x++) {
		print BRD $$current_board[14][$x],"-";
	}
	print BRD $$current_board[14][14];


	#close our file
	close(BRD);


	#and return our success
	return 1;
}


#This function takes all data for adding
#a new word to the board
sub add_word($$$$$) {
	my ($self, $new_word, $x_pt, $y_pt, $orientation) = @_;
	my $current_board = $self-&gt;curr_board();


	#Properly format our word
	$new_word = uc($new_word);
	$new_word =~ s/\s+//g;


	#Break our word up into an array of chars.
	my @word_array = split(//,$new_word);


	#checking that new addition is a valid move
	my $connection = 0; #whether the word is touching another
	#if true it must be our first move
	if ($self-&gt;board_empty()) {
		$connection = 1;
	}
	elsif($orientation eq "Horizontal") {
		for(my ($x,$tmp_x) = (0,$x_pt); $x &lt; scalar(@word_array); $x++,$tmp_x++) {
			return 0 if ($tmp_x &gt; 14);
			if($$current_board[$y_pt][$tmp_x] =~ /\S/) {
				return 0 if ($$current_board[$y_pt][$tmp_x] ne $word_array[$x]);
				$connection = 1;
			}
		}
	}
	elsif($orientation eq "Vertical") {
		my $tmp_y = $y_pt;
		for(my ($y,$tmp_y) = (0,$y_pt); $y &lt; scalar(@word_array); $y++,$tmp_y++) {
			return 0 if ($tmp_y &gt; 14);
			if($$current_board[$tmp_y][$x_pt] =~ /\S/) {
				return 0 if ($$current_board[$tmp_y][$x_pt] ne $word_array[$y]);
				$connection = 1;
			}
		}
	}
	else {
		return 0;
	}
	return 0 unless ($connection);

	#If we're still here than it must be valid so we add it
	if($orientation eq "Horizontal") {
		for(my ($x,$tmp_x) = (0,$x_pt); $x &lt; scalar(@word_array); $x++,$tmp_x++) {
			$$current_board[$y_pt][$tmp_x] = $word_array[$x];
		}
	}
	elsif($orientation eq "Vertical") {
		for(my ($y,$tmp_y) = (0,$y_pt); $y &lt; scalar(@word_array); $y++,$tmp_y++) {
			$$current_board[$tmp_y][$x_pt] = $word_array[$y];
		}
	}
	else {
		return 0;
	}


	#Got here without an error so we return success!
	return 1;
}


#This master function tries to find the best moves possible
sub find_results($$$) {
	my ($self, $letters, $num) = @_;
	$letters = uc($letters);
	$letters =~ s/\s//g;


	#Verify our arguments to be valid
	unless($letters =~ /\S/) {
		return 0;
	}
	unless($num =~ /\d/) {
		return 0;
	}


	#Here's our array of hash-references of best moves
	#which we'll fill, sort, shorten, and finally return
	my @best_moves;


	#Let's look for horizontal entry positions first
	for(my $y = 0; $y &lt; 15; $y++) {
		unless($self-&gt;row_empty($y)) {
			my $tmp_letters = $letters . $self-&gt;row_letters($y);
			my $words = $self-&gt;words_with($tmp_letters);
			foreach my $word (@{$words}) {
				for(my $x = 0; $x &lt; 15; $x++) {
					my $tmp_move = $self-&gt;validate($letters, $word, $y, $x, "Horizontal");
					if($tmp_move) {
						my $value = $self-&gt;value($word, $y, $x, "Horizontal");
						my %tmp_move = (
							"word" =&gt; $word,
							"orientation" =&gt; "Horizontal",
							"value" =&gt; $value,
							"y_pt" =&gt; $y,
							"x_pt" =&gt; $x
							);
						push(@best_moves, \%tmp_move);
					}
				}
			}
			@best_moves = sort {$$b{"value"} &lt;=&gt; $$a{"value"}} @best_moves;
			while(scalar(@best_moves) &gt; $num) {
				pop(@best_moves);
			}
		}
	}
	#now lets look for vertical entry positions
	for(my $x = 0; $x &lt; 15; $x++) {
		unless($self-&gt;col_empty($x)) {
			my $tmp_letters = $letters . $self-&gt;col_letters($x);
			my $words = $self-&gt;words_with($tmp_letters);
			foreach my $word (@{$words}) {
				for(my $y = 0; $y &lt; 15; $y++) {
					my $tmp_move = $self-&gt;validate($letters, $word, $y, $x, "Vertical");
					if($tmp_move) {
						my $value = $self-&gt;value($word, $y, $x, "Vertical");
						my %tmp_move = (
							"word" =&gt; uc($word),
							"orientation" =&gt; "Vertical",
							"value" =&gt; $value,
							"y_pt" =&gt; $y,
							"x_pt" =&gt; $x
							);
						push(@best_moves, \%tmp_move);
					}
				}
			}
			@best_moves = sort {$$b{"value"} &lt;=&gt; $$a{"value"}} @best_moves;
			while(scalar(@best_moves) &gt; $num) {
				pop(@best_moves);
			}
		}
	} close(BUG);


	return \@best_moves;
}


#This function returns the value of the move
#given in the arguments
sub value($$$$$) {
	my ($self, $word, $y_pt, $x_pt, $orientation) = @_;
	my $current_board = $self-&gt;curr_board();
	my $word = uc($word);


	#Time to break our word into array of chars.
	my @word_array = split(//,$word);


	#Here's our full value we're going to add on to
	my $full_value = 0;
	my $double = 0; #whether to double our final value
	my $triple = 0; #whether to triple our final value
	my $letters_used = 0; #if 7 we can add all letters bonus


	#Time to value if it's horizontal
	if($orientation eq "Horizontal") {
		for(my ($x,$tmp_x) = (0,$x_pt); $x &lt; scalar(@word_array); $x++,$tmp_x++) {
			#if letter already here we just add it's normal value
			if($$current_board[$y_pt][$tmp_x] =~ /\S/) {
				$full_value += $letter_values{$word_array[$x]};
				next;
			}
			else{
				if($premium_squares[$y_pt][$tmp_x] eq "  ") {
					$full_value += $letter_values{$word_array[$x]};
				}
				else{
					if($premium_squares[$y_pt][$tmp_x] eq "2L") {
						$full_value += ($letter_values{$word_array[$x]}) * 2;
					}
					elsif($premium_squares[$y_pt][$tmp_x] eq "3L") {
						$full_value += ($letter_values{$word_array[$x]}) * 3;
					}
					elsif($premium_squares[$y_pt][$tmp_x] eq "2W") {
						$full_value += $letter_values{$word_array[$x]};
						$double = 1;
					}
					elsif($premium_squares[$y_pt][$tmp_x] eq "3W") {
						$full_value += $letter_values{$word_array[$x]};
						$triple = 1;
					}
				}
				$letters_used++;
			}
			#now if there's a letter above or below we want to add that
			if(($$current_board[$y_pt-1][$tmp_x] =~ /\S/ &amp;&amp; $y_pt &gt; 0)
			|| ($$current_board[$y_pt+1][$tmp_x] =~ /\S/ &amp;&amp; $y_pt &lt; 14)) {
				my $p = $y_pt - 1;
				while($$current_board[$p][$tmp_x] =~ /\S/ &amp;&amp; $p &gt;= 0) {
					$p--;
				}
				$p++;
				while($p &lt; $y_pt) {
					$full_value += $letter_values{$$current_board[$p][$tmp_x]};
					$p++;
				}
				$full_value += $letter_values{$word_array[$x]};
				$p++;
				while($$current_board[$p][$tmp_x] =~ /\S/ &amp;&amp; $p &lt; 15) {
					$full_value += $letter_values{$$current_board[$p][$tmp_x]};
					$p++;
				}
			}
		}
	}
	#now time to do it vertically
	elsif($orientation eq "Vertical") {
		for(my ($y,$tmp_y) = (0,$y_pt); $y &lt; scalar(@word_array); $y++,$tmp_y++) {
			#if letter already here we just add it's normal value
			if($$current_board[$tmp_y][$x_pt] =~ /\S/) {
				$full_value += $letter_values{$word_array[$y]};
				next;
			}
			else{
				 if($premium_squares[$tmp_y][$x_pt] eq "  ") {
					$full_value += $letter_values{$word_array[$y]};
				}
				else{
					if($premium_squares[$tmp_y][$x_pt] eq "2L") {
						$full_value += ($letter_values{$word_array[$y]}) * 2;
					}
					elsif($premium_squares[$tmp_y][$x_pt] eq "3L") {
						$full_value += ($letter_values{$word_array[$y]}) * 3;
					}
					elsif($premium_squares[$tmp_y][$x_pt] eq "2W") {
						$full_value += $letter_values{$word_array[$y]};
						$double = 1;
					}
					elsif($premium_squares[$tmp_y][$x_pt] eq "3W") {
						$full_value += $letter_values{$word_array[$y]};
						$triple = 1;
					}
				}
				$letters_used++;
			}
			#now if there's a letter behind or in front we add this too
			if(($$current_board[$tmp_y][$x_pt-1] =~ /\S/ &amp;&amp; $x_pt &gt; 0)
			|| ($$current_board[$tmp_y][$x_pt+1] =~ /\S/ &amp;&amp; $x_pt &lt; 14)) {
				my $p = $x_pt - 1;
				while($$current_board[$tmp_y][$p] =~ /\S/ &amp;&amp; $p &gt;= 0) {
					$p--;
				}
				$p++;
				while($p &lt; $x_pt) {
					$full_value += $letter_values{$$current_board[$tmp_y][$p]};
					$p++;
				}
				$full_value += $letter_values{$word_array[$y]};
				$p++;
				while($$current_board[$tmp_y][$p] =~ /\S/ &amp;&amp; $p &lt; 15) {
					$full_value += $letter_values{$$current_board[$tmp_y][$p]};
					$p++;
				}
			}

		}
	}


	#now whether to double or triple or boost our results
	$full_value *= 2 if ($double);
	$full_value *= 3 if ($triple);
	$full_value += 50 if ($letters_used == 7);


	#and now to return our result
	return $full_value;
}

#This function decides whether a move is legal
#Using the current word-list.
sub validate($$$$$$) {
	my ($self, $letters, $word, $y_pt, $x_pt, $orientation) = @_;
	my $current_board = $self-&gt;curr_board();
	$word = uc($word);
	$letters = uc($letters);


	#Break our word up into an array of chars.
	my @word_array = split(//,$word);


	my $connection = 0; #whether the word is touching another
	my $use_letter = 0; #whether we use any of our letters
	#time to validate if horizontal
	if($orientation eq "Horizontal") {
		return 0 if ($x_pt &gt; (15 - length($word)));
		#now to see if we made a runon-word with one behind or in front of us
		return 0 if (($$current_board[$y_pt][$x_pt-1] =~ /\S/ &amp;&amp; $x_pt &gt; 0) ||
		($$current_board[$y_pt][$x_pt+length($word)] =~ /\S/ &amp;&amp; $x_pt &lt; (15 - length($word))));
		for(my ($x,$tmp_x) = (0,$x_pt); $x &lt; scalar(@word_array); $x++,$tmp_x++) {
			#if there's a letter here we make sure it matches
			if($$current_board[$y_pt][$tmp_x] =~ /\S/) {
				return 0 if ($$current_board[$y_pt][$tmp_x] ne $word_array[$x]);
				$connection = 1;
			}
			#if no letter here we make sure we have this letter to place
			else {
				return 0 unless ($letters =~ s/$word_array[$x]//);
				$use_letter = 1;
			}
		}
		return 0 unless ($connection &amp;&amp; $use_letter);
		#now we look up and down to see if we're touching, at each spot
		for(my ($x,$tmp_x) = (0,$x_pt); $x &lt; scalar(@word_array); $x++,$tmp_x++) {
			next if ($$current_board[$y_pt][$tmp_x] =~ /\S/);
			if(($$current_board[$y_pt-1][$tmp_x] =~ /\S/ &amp;&amp; $y_pt &gt; 0)
			|| ($$current_board[$y_pt+1][$tmp_x] =~ /\S/ &amp;&amp; $y_pt &lt; 14)) {
				my $vert_word;
				my $p = $y_pt - 1;
				while($$current_board[$p][$tmp_x] =~ /\S/ &amp;&amp; $p &gt;= 0) {
					$p--;
				}
				$p++;
				while($p &lt; $y_pt) {
					$vert_word .= $$current_board[$p][$tmp_x];
					$p++;
				}
				$vert_word .= $word_array[$x];
				$p++;
				while($$current_board[$p][$tmp_x] =~ /\S/ &amp;&amp; $p &lt; 15) {
					$vert_word .= $$current_board[$p][$tmp_x];
					$p++;
				}
				return 0 unless $self-&gt;word_in_dictionary($vert_word);
			}
		}
	}
	#now time to validate if vertical
	elsif($orientation eq "Vertical") {
		return 0 if ($y_pt &gt; (15 - length($word)));
		#now to see if we made a runon-word with one below or above us
		return 0 if (($$current_board[$y_pt-1][$x_pt] =~ /\S/ &amp;&amp; $y_pt &gt; 0) ||
		($$current_board[$y_pt+length($word)][$x_pt] =~ /\S/ &amp;&amp; $y_pt &lt; (15 - length($word))));
		for(my ($y,$tmp_y) = (0,$y_pt); $y &lt; scalar(@word_array); $y++,$tmp_y++) {
			#if there's a letter here we make sure it matches
			if($$current_board[$tmp_y][$x_pt] =~ /\S/) {
				return 0 if ($$current_board[$tmp_y][$x_pt] ne $word_array[$y]);
				$connection = 1;
			}
			#if no letter here we make sure we have this letter to place
			else {
				return 0 unless ($letters =~ s/$word_array[$y]//);
				$use_letter = 1;
			}
		}
		return 0 unless ($connection &amp;&amp; $use_letter);
		#now we look left and right to see if we're touching, at each spot
		for(my ($y,$tmp_y) = (0,$y_pt); $y &lt; scalar(@word_array); $y++,$tmp_y++) {
			next if ($$current_board[$tmp_y][$x_pt] =~ /\S/);
			if(($$current_board[$tmp_y][$x_pt-1] =~ /\S/ &amp;&amp; $x_pt &gt; 0)
			|| ($$current_board[$tmp_y][$x_pt+1] =~ /\S/ &amp;&amp; $x_pt &lt; 14)) {
				my $horiz_word;
				my $p = $x_pt - 1;
				while($$current_board[$tmp_y][$p] =~ /\S/ &amp;&amp; $p &gt;= 0) {
					$p--;
				}
				$p++;
				while($p &lt; $x_pt) {
					$horiz_word .= $$current_board[$tmp_y][$p];
					$p++;
				}
				$horiz_word .= $word_array[$y];
				$p++;
				while($$current_board[$tmp_y][$p] =~ /\S/ &amp;&amp; $p &lt; 15) {
					$horiz_word .= $$current_board[$tmp_y][$p];
					$p++;
				}
				return 0 unless $self-&gt;word_in_dictionary($horiz_word);
			}
		}
	}
	else {
		return 0;
	}


	#Well it must be good then, so we return true
	return 1;
}


#This function returns a reference to an array
#of all words with given letters in our dictionary
sub words_with($$) {
	my ($self, $letters) = @_;
	my $word_list = $self-&gt;word_list();


	#now to build our array to return
	my @words;
	foreach my $word (@{$word_list}) {
		next unless (length($word) &gt; 2);
		if ($self-&gt;match($letters, $word)) {
			push(@words, $word);
		}
	}


	#Now to return reference to our array
	return \@words;
}


#This bool function returns whether the second argument
#string can be built with the first arguments letters
sub match($$$) {
	my ($self, $letters, $word) = @_;
	return 0 if (length($word) &gt; length($letters));
	$letters = lc($letters);
	$word = lc($word);


	#Now we build our hashes
	my (%letter_hash);
	while($letters) {
		$letter_hash{chop($letters)}++;
	}
	while($word) {
		$letter_hash{chop($word)}--;
	}


	#Now to analyze
	my ($key,$value);
	while(($key,$value) = each(%letter_hash)) {
		return 0 if ($value &lt; 0);
	}


	#Must be good
	return 1;
}


#This bool function just returns whether
#given word is in dictionary.
sub word_in_dictionary($$) {
	my ($self, $new_word) = @_;
	$new_word = lc($new_word);
	my $word_list = $self-&gt;word_list();


	#now to check each word for equality
	foreach my $word (@{$word_list}) {
		return 1 if ($new_word eq $word);
	}
	return 0;
}


#This bool function returns whether or not
#the given row on the board is empty.
sub row_empty($$) {
	my ($self, $row) = @_;
	my $current_board = $self-&gt;curr_board();


	#Now to check this row
	for(my $x = 0; $x &lt; 15; $x++) {
		return 0 if ($$current_board[$row][$x] =~ /\S/);
	}


	#Well if they were all empty we return true
	return 1;
}


#This bool function returns whether or not
#the given column on the board is empty.
sub col_empty($$) {
	my ($self, $col) = @_;
	my $current_board = $self-&gt;curr_board();


	#Now to check this column
	for(my $y = 0; $y &lt; 15; $y++) {
		return 0 if ($$current_board[$y][$col] =~ /\S/);
	}


	#Well if all empty we return true
	return 1;
}


#This function returns all the letters on a given row
sub row_letters($$) {
	my ($self, $row) = @_;
	my $current_board = $self-&gt;curr_board();


	#So let's get all our letters
	my $row_letters;
	for(my $x = 0; $x &lt; 15; $x++) {
		if($$current_board[$row][$x] =~ /\S/) {
			$row_letters .= $$current_board[$row][$x];
		}
	}


	#And we return our letters
	return $row_letters;
}


#This function returns all the letters on a given column
sub col_letters($$) {
	my ($self, $col) = @_;
	my $current_board = $self-&gt;curr_board();


	#So let's get all our letters
	my $col_letters;
	for(my $y = 0; $y &lt; 15; $y++) {
		if($$current_board[$y][$col] =~ /\S/) {
			$col_letters .= $$current_board[$y][$col];
		}
	}


	#And we return our letters
	return $col_letters;
}


#This bool function just tells whether board is empty
sub board_empty($) {
	my ($self) = @_;
	my $current_board = $self-&gt;curr_board();
	for(my $y = 0; $y &lt; 15; $y++) {
		for(my $x = 0; $x &lt; 15; $x++) {
			return 0 if ($$current_board[$y][$x] =~ /\S/);
		}
	}
	return 1;
}


#This function just returns our object's
#Current_board reference
sub curr_board($) {
	my ($self) = @_;
	return $self-&gt;{current_board};
}


#This just returns our word list
sub word_list($) {
	my ($self) = @_;
	return $self-&gt;{word_list};
}
&lt;/CODE&gt;</field>
<field name="codedescription">
This is the fruition of my labors to build a program to find all the best moves in a scrabble game!  Right now all the algorithms remain in Perl so, depending on your dictionary size and the number of moves on the board, search time can vary from 30seconds to 7minutes.  So once you click "Find" remember to be patient.  A pop up window saying "Search complete" will appear when it is done.  I plan on porting some of the more time consuming routines to Inline::C to speed things up greatly.  You can get all the files for this program, including a windows executable built with perl2exe, a dictionary massaged to include only useful scrabble words, and a sample board file at: http://filebox.vt.edu/users/jubishop/Scrabble/</field>
<field name="codecategory">
Fun Stuff</field>
<field name="codeauthor">
Justin Bishop
mrbishop@vt.edu</field>
<field name="reputation">
13</field>
</data>
</node>
