#!/usr/bin/perl
use strict;
use warnings;
use Tree::Simple;

my ($start_word, $destination_word, $dictionary_file) = @ARGV;
die "You must specify two words of equal length" unless defined
$start_word and defined $destination_word and length $start_word ==
length $destination_word;

$dictionary_file ||= "/usr/share/dict/web2";
open (FH, $dictionary_file) or die "Couldn't open $dictionary_file: $!";

$start_word = lc $start_word;
$destination_word = lc $destination_word;

my %dict;
while (<FH>) {
    chomp;
    $dict{lc $_} = 1 if length $_ == length $start_word and $_ !~ /[^a-zA-Z]/;
}
close(FH);
die "$start_word is not in dictionary" unless exists $dict{$start_word};
die "$destination_word is not in dictionary" unless exists
$dict{$destination_word};

print "$start_word\n" and exit if $start_word eq $destination_word;

my ($list, $next_nodes);
{
    my $top = Tree::Simple->new($start_word);
    my $bottom = Tree::Simple->new($destination_word);

    $list = [{$start_word => $top}, {$destination_word => $bottom}];
    $next_nodes = [[$top],[$bottom]];
}

for (my $x = 0; ; $x = !$x) {
    $next_nodes->[$x] = find_next_nodes($next_nodes->[$x], $x);
}

sub find_next_nodes {
    my ($nodes, $x) = @_;
    my @next_nodes;
    for my $node (@$nodes) {
        my $orig_word = $node->getNodeValue();
        for (my $i = 0; $i < length $orig_word; $i++) {
            my $word = $orig_word;
            for my $char ('a'..'z') {
                next if $char eq substr $orig_word, $i, 1;
                substr $word, $i, 1, $char;

# if word is in other list, we're done

                success($x ? ($list->[!$x]->{$word}, $node) : ($node,
$list->[!$x]->{$word})) if exists $list->[!$x]->{$word};
                if (exists $dict{$word} and !exists $list->[$x]->{$word}) {
                    my $child = Tree::Simple->new($word);
                    $node->addChild($child); 
                    push @next_nodes, $child;
                    $list->[$x]->{$word} = $child;
                }
            }
        }
        prune($node) if $node->isLeaf;
    }
    return \@next_nodes;
}

sub success {
    my ($node1, $node2) = @_;
    print join "\n", (reverse word_chain($node1)), word_chain($node2), '';
    exit;
}

sub word_chain {
    my $node = shift;
    my @words;
    while (1) {
        push @words, $node->getNodeValue();
        last if $node->isRoot;
        $node = $node->getParent;
    }
    return @words;
}


sub prune {
    my $node = shift;
    while ($node->isLeaf) {
        die "No path exists" if $node->isRoot;
        my $parent = $node->getParent;
        $parent->removeChild($node);
        $node = $parent;
    }
}
