view release on metacpan or search on metacpan
lib/AI/Pathfinding/SMAstar.pm view on Meta::CPAN
use AI::Pathfinding::SMAstar::Path;
use Scalar::Util;
use Carp;
my $DEBUG = 0;
##################################################
# SMAstar constructor
##################################################
sub new {
my $invocant = shift;
my $class = ref($invocant) || $invocant;
my $self = {
_priority_queue => AI::Pathfinding::SMAstar::PriorityQueue->new(),
_state_eval_func => undef,
_state_goal_p_func => undef,
_state_num_successors_func => undef,
_state_successors_iterator => undef,
_show_prog_func => undef,
_state_get_data_func => undef,
@_, # attribute override
};
return bless $self, $class;
}
sub state_eval_func {
my $self = shift;
if (@_) { $self->{_state_eval_func} = shift }
return $self->{_state_eval_func};
}
sub state_goal_p_func {
my $self = shift;
if (@_) { $self->{_state_goal_p_func} = shift }
return $self->{_state_goal_p_func};
}
sub state_num_successors_func {
my $self = shift;
if (@_) { $self->{_state_num_successors_func} = shift }
return $self->{_state_num_successors_func};
}
sub state_successors_iterator {
my $self = shift;
if (@_) { $self->{_state_successors_iterator} = shift }
return $self->{_state_successors_iterator};
}
sub state_get_data_func {
my $self = shift;
if (@_) { $self->{_state_get_data_func} = shift }
return $self->{_state_get_data_func};
}
sub show_prog_func {
my $self = shift;
if (@_) { $self->{_show_prog_func} = shift }
return $self->{_show_prog_func};
}
###################################################################
#
# Add a state from which to begin the search. There can
# be multiple start-states.
#
###################################################################
sub add_start_state
{
my ($self, $state) = @_;
my $state_eval_func = $self->{_state_eval_func};
my $state_goal_p_func = $self->{_state_goal_p_func};
my $state_num_successors_func = $self->{_state_num_successors_func},
my $state_successors_iterator = $self->{_state_successors_iterator},
my $state_get_data_func = $self->{_state_get_data_func};
lib/AI/Pathfinding/SMAstar.pm view on Meta::CPAN
# add this node to the queue
$self->{_priority_queue}->insert($state_obj);
}
###################################################################
#
# start the SMAstar search process
#
###################################################################
sub start_search
{
my ($self,
$log_function,
$str_function,
$max_states_in_queue,
$max_cost,
) = @_;
if(!defined($str_function)){
croak "SMAstar start_search: str_function is not defined.\n";
lib/AI/Pathfinding/SMAstar.pm view on Meta::CPAN
#################################################################
#
# SMAstar search
# Memory-bounded A* search
#
#
#################################################################
sub sma_star_tree_search
{
my ($priority_queue,
$goal_p,
$successors_func,
$eval_func,
$backup_func,
$log_function, # debug string func; represent state object as a string.
$str_function,
$prog_function,
lib/AI/Pathfinding/SMAstar.pm view on Meta::CPAN
# removing and re-inserting in queue changes temporal ordering,
# and we don't want to do that unless the node will be
# placed in a new cost-bucket/tree.
# 2) then backup fvals
# 3) then re-insert best and all antecedents back on queue.
# Check if need for backup fvals
$best->check_need_fval_change();
my $cmp_func = sub {
my ($str) = @_;
return sub{
my ($obj) = @_;
my $obj_path_str = $str_function->($obj);
if($obj_path_str eq $str){
return 1;
}
else{
return 0;
}
lib/AI/Pathfinding/SMAstar.pm view on Meta::CPAN
#
# If best's descendants are all in memory, mark best as completed.
#
if($best->all_in_memory()) {
if(!($best->is_completed())){
$best->is_completed(1);
}
my $cmp_func = sub {
my ($str) = @_;
return sub{
my ($obj) = @_;
my $obj_str = $str_function->($obj);
if($obj_str eq $str){
return 1;
}
else{
return 0;
}
lib/AI/Pathfinding/SMAstar.pm view on Meta::CPAN
}
print "\n\nreturning unsuccessfully. iteration: $iteration\n";
return;
}
}
sub max
{
my ($n1, $n2) = @_;
return ($n1 > $n2 ? $n1 : $n2);
}
sub fp_compare {
my ($a, $b, $dp) = @_;
my $a_seq = sprintf("%.${dp}g", $a);
my $b_seq = sprintf("%.${dp}g", $b);
if($a_seq eq $b_seq){
return 0;
}
elsif($a_seq lt $b_seq){
lib/AI/Pathfinding/SMAstar/AVLQueue.pm view on Meta::CPAN
use Tree::AVL;
use AI::Pathfinding::SMAstar::PairObj;
use Carp;
use strict;
##################################################
# AVLQueue constructor
##################################################
sub new {
my $invocant = shift;
my $class = ref($invocant) || $invocant;
my $self = {
_key => undef, # for comparisons with other queues, etc.
_avltree => Tree::AVL->new(fcompare => \&AI::Pathfinding::SMAstar::AVLQueue::compare_obj_counters,
fget_key => \&AI::Pathfinding::SMAstar::AVLQueue::obj_counter,
fget_data => \&AI::Pathfinding::SMAstar::AVLQueue::obj_value),
_counter => 0,
lib/AI/Pathfinding/SMAstar/AVLQueue.pm view on Meta::CPAN
};
return bless $self, $class;
}
##############################################
# accessor
##############################################
sub key
{
my $self = shift;
if (@_) { $self->{_key} = shift }
return $self->{_key};
}
#############################################################################
#
# other methods
#
#############################################################################
sub get_keys_iterator
{
my ($self) = @_;
return $self->{_obj_counts_tree}->get_keys_iterator();
}
sub compare_obj_counters{
my ($obj, $arg_obj) = @_;
if ($arg_obj){
my $arg_key = $arg_obj->{_queue_counter};
my $key = $obj->{_queue_counter};
if($arg_key > $key){
return(-1);
}
elsif($arg_key == $key){
lib/AI/Pathfinding/SMAstar/AVLQueue.pm view on Meta::CPAN
elsif($arg_key < $key){
return(1);
}
}
else{
croak "AVLQueue::compare_obj_counters: error: null argument object\n";
}
}
sub obj_counter{
my ($obj) = @_;
return $obj->{_queue_counter};
}
sub obj_value{
my ($obj) = @_;
return $obj->{_value};
}
sub compare {
my ($self, $arg_obj) = @_;
if ($arg_obj){
my $arg_key = $arg_obj->{_key};
my $key = $self->{_key};
if($arg_key > $key){
return(-1);
}
elsif($arg_key == $key){
lib/AI/Pathfinding/SMAstar/AVLQueue.pm view on Meta::CPAN
}
elsif($arg_key < $key){
return(1);
}
}
else{
croak "AVLQueue::compare error: null argument object\n";
}
}
sub lookup {
my ($self, $obj) = @_;
my $found_obj = $self->{_avltree}->lookup_obj($obj);
if(!$found_obj){
croak "AVLQueue::lookup: did not find obj in queue\n";
return;
}
return $found_obj;
}
sub lookup_by_key {
my ($self, $key) = @_;
my $pair = AI::Pathfinding::SMAstar::PairObj->new(
_queue_counter => $key,
);
my $found_obj = $self->{_avltree}->lookup_obj($pair);
if(!$found_obj){
croak "AVLQueue::lookup: did not find obj in queue\n";
return;
}
return $found_obj;
}
sub remove {
my ($self, $obj, $compare_func) = @_;
my $found_obj;
$found_obj = $self->{_avltree}->remove($obj);
if(!$found_obj){
croak "AVLQueue::remove: did not find obj in queue\n";
return;
}
lib/AI/Pathfinding/SMAstar/AVLQueue.pm view on Meta::CPAN
my $pairobj = AI::Pathfinding::SMAstar::PairObj->new(_key => $count,
_value => $count);
$self->{_obj_counts_tree}->remove($pairobj);
return $found_obj;
}
sub is_empty
{
my ($self) = @_;
if($self->{_avltree}->is_empty()){
return 1;
}
return 0;
}
sub insert
{
my ($self,
$obj) = @_;
my $count = $self->{_counter};
$obj->{_queue_counter} = $count;
$self->{_avltree}->insert($obj);
lib/AI/Pathfinding/SMAstar/AVLQueue.pm view on Meta::CPAN
_value => $count);
$self->{_obj_counts_tree}->insert($pairobj);
$self->{_counter} = $self->{_counter} + 1;
return;
}
sub pop_top
{
my ($self) = @_;
my $top = $self->{_avltree}->pop_smallest();
my $count = $top->{_queue_counter};
my $pairobj = AI::Pathfinding::SMAstar::PairObj->new(_key => $count,
_value => $count);
$self->{_obj_counts_tree}->remove($pairobj);
return $top;
}
sub top
{
my ($self) = @_;
my $top = $self->{_avltree}->smallest();
return $top;
}
sub get_list{
my ($self) = @_;
return $self->{_avltree}->get_list();
}
sub get_size{
my ($self) = @_;
my $avltree = $self->{_avltree};
my $size = $avltree->get_size();
return $size;
}
sub print{
my ($self, $delim) = @_;
my @tree_elts = $self->{_avltree}->get_list();
foreach my $obj (@tree_elts){
print $obj->{_start_word} . ", " . $obj->{_phrase} . ", " . $obj->{_queue_counter} . "\n";
}
print "\n\nobj_counts_tree:\n";
$self->{_obj_counts_tree}->print("*");
lib/AI/Pathfinding/SMAstar/Examples/PalUtils.pm view on Meta::CPAN
use Tree::AVL;
use AI::Pathfinding::SMAstar::Examples::WordObj;
package AI::Pathfinding::SMAstar::Examples::PalUtils;
my $max_nodes_in_mem = 0;
sub length_no_spaces
{
my ($w) = @_;
$w =~ s/\ //g;
return length($w);
}
sub get_word_number_of_letters_that_have_repeats
{
my ($word) = @_;
my @letters = split('', $word);
my %letters_hash = ();
foreach my $element (@letters) { $letters_hash{$element}++ }
my $repeated_letters = 0;
foreach my $element (keys %letters_hash){
if($letters_hash{$element} > 1){
lib/AI/Pathfinding/SMAstar/Examples/PalUtils.pm view on Meta::CPAN
}
return $repeated_letters;
}
#
# finds the number of times each letter appears within
# an entire list of words. returns a hash of the letters
#
sub find_letter_frequencies
{
my (@words) = @_;
my %letters_freq;
foreach my $w (@words)
{
@letters = split('', $w);
foreach my $l (@letters)
{
$letters_freq{$l}++;
}
}
return %letters_freq;
}
sub collisions_per_length
{
my ($w, $phrase) = @_;
if(!$w){ $w = "" }
if(!$phrase){ $phrase = "" }
my $length = length($w);
$phrase =~ s/ //g;
my @letters = split('', $w);
lib/AI/Pathfinding/SMAstar/Examples/PalUtils.pm view on Meta::CPAN
}
}
my $val = $collisions/$length;
return $val;
}
sub get_word_sparsity
{
my ($word) = @_;
my $length = length($word);
my $num_letters = num_chars_in_word_memo($word);
my $sparseness = $length - $num_letters;
return $sparseness;
}
{ my %memo_cache;
sub get_word_sparsity_memo
{
my ($word) = @_;
if($memo_cache{$word}){
return $memo_cache{$word};
}
else{
my $length = length($word);
my $num_letters = num_chars_in_word_memo($word);
lib/AI/Pathfinding/SMAstar/Examples/PalUtils.pm view on Meta::CPAN
$memo_cache{$word} = $sparseness;
return $sparseness;
}
}
}
# get the highest number of times a letter
# is repeated within a word.
sub get_word_highest_frequency
{
my ($word) = @_;
my @letters = split('', $word);
my %letters_hash = ();
foreach my $element (@letters) { $letters_hash{$element}++ }
my $most_frequent_letter_freq = 0;
foreach my $element (keys %letters_hash){
if($letters_hash{$element} > $most_frequent_letter_freq){
$most_frequent_letter_freq = $letters_hash{$element};
}
}
return $most_frequent_letter_freq;
}
sub get_letters
{
my ($word) = @_;
my @letter_set = ();
my %letters_hash = ();
my @letters = split('', $word);
foreach my $element (@letters) { $letters_hash{$element}++ }
foreach my $element (keys %letters_hash)
{
push(@letter_set, $element);
}
return @letter_set;
}
{ my %memo_cache;
sub word_collision_memo
{
my ($word,
$sorted_letters_seen) = @_;
my $sorted_letters_seen_str = join('', @$sorted_letters_seen);
my $memo_key = $word . $sorted_letters_seen_str;
#print "sorted_letters_seen_str: $sorted_letters_seen_str\n";
if($memo_cache{$memo_key}){
lib/AI/Pathfinding/SMAstar/Examples/PalUtils.pm view on Meta::CPAN
$memo_cache{$memo_key} = \@answer;
return ($intersect_num, @difference);
}
}
}
sub word_collision{
my ($word,
$letters_seen) = @_;
my @letters = split('', $word);
my @difference = ();
my %letters_hash = ();
my %letters_seen_hash = ();
my $intersect_num = 0;
lib/AI/Pathfinding/SMAstar/Examples/PalUtils.pm view on Meta::CPAN
else{
push(@difference, $element);
}
}
return ($intersect_num, @difference);
}
sub get_cands_from_left
{
my ($word,
$dictionary,
$dictionary_rev) = @_;
my @cands = get_cands_memo($word, $dictionary_rev);
foreach my $c (@cands){
$c = reverse($c);
}
my @sorted_cands = sort(@cands);
return @sorted_cands;
}
sub get_cands_from_right
{
my ($word,
$dictionary,
$dictionary_rev) = @_;
my $rev_word = reverse($word);
my @cands = get_cands_memo($rev_word, $dictionary);
my @sorted_cands = sort(@cands);
return @sorted_cands;
}
{my $memo_hash_ref = {};
sub get_cands_memo
{
my ($word, $dictionary_rev) = @_;
my $cand = AI::Pathfinding::SMAstar::Examples::WordObj->new(
_word => $word
);
my $cache_key = $word . $dictionary_rev;
my $cached_vals = $memo_hash_ref->{$cache_key};
if($cached_vals){
lib/AI/Pathfinding/SMAstar/Examples/PalUtils.pm view on Meta::CPAN
\&AI::Pathfinding::SMAstar::Examples::WordObj::compare_up_to,
\&AI::Pathfinding::SMAstar::Examples::WordObj::compare);
my @cands = (@substr_cands, @superstr_cands);
# these are all the words in the dictionary that could end this pal.
$memo_hash_ref->{$cache_key} = \@cands;
return @cands;
}
}
}
sub get_cands
{
my ($word, $dictionary_rev) = @_;
my $cand = AI::Pathfinding::SMAstar::Examples::WordObj->new(
_word => $word
);
my @substr_cands = get_substrs_memo($word, $dictionary_rev);
my @superstr_cands = $dictionary_rev->acc_lookup($cand,
\&AI::Pathfinding::SMAstar::Examples::WordObj::compare_up_to,
\&AI::Pathfinding::SMAstar::Examples::WordObj::compare);
my @cands = (@substr_cands, @superstr_cands);
# these are all the words in the dictionary that could end this pal.
return @cands;
}
sub match_remainder
{
my ($word1, $word2) = @_;
$word1 =~ s/\ //g;
$word2 =~ s/\ //g;
my $len1 = length($word1);
my $len2 = length($word2);
if(index($word1, $word2) != 0)
lib/AI/Pathfinding/SMAstar/Examples/PalUtils.pm view on Meta::CPAN
my $remainder_word = substr($word1, $len2);
return $remainder_word;
}
#
# memoized version of get_substrs- for speed
#
{my $memo_hash_ref = {};
sub get_substrs_memo
{
my ($word, $dictionary) = @_;
my @words;
my @matches;
my $cache_key = $word . $dictionary;
my $cached_vals = $memo_hash_ref->{$cache_key};
if($cached_vals1){
lib/AI/Pathfinding/SMAstar/Examples/PalUtils.pm view on Meta::CPAN
}
#print "no hashed value yet, creating one.\n";
$memo_hash_ref->{$cache_key} = \@matches;
return @matches;
}
}
}
sub get_substrs
{
my ($word, $dictionary) = @_;
my @words;
my @matches;
for(my $i = 1; $i < length($word); $i++){
push(@words, substr($word, 0, $i));
}
lib/AI/Pathfinding/SMAstar/Examples/PalUtils.pm view on Meta::CPAN
push(@matches, $match_word);
}
}
return @matches;
}
# randomize an array. Accepts a reference to an array.
sub fisher_yates_shuffle {
my ($array) = @_;
my $i;
for ($i = @$array; --$i; ) {
my $j = int rand ($i+1);
next if $i == $j;
@$array[$i,$j] = @$array[$j,$i];
}
}
sub process_words
{
my ($words) = @_;
my @word_objs;
for(my $i = 0; $i < @$words; $i++)
{
my $word = $words->[$i];
chomp($word);
$word_objs[$i] = AI::Pathfinding::SMAstar::Examples::WordObj->new(
_word => $word,
);
}
return @word_objs;
}
sub process_words_by_density
{
my ($words,
$max_score # 0: no repeats, 1: 1 repeat, etc.
) = @_;
my @word_objs;
my $i = 0;
foreach my $word (@$words)
{
lib/AI/Pathfinding/SMAstar/Examples/PalUtils.pm view on Meta::CPAN
);
$i++;
}
}
return @word_objs;
}
sub process_rev_words
{
my ($words) = @_;
my @word_objs;
for(my $i = 0; $i < @$words; $i++)
{
my $word = $words->[$i];
chomp($word);
my $rev_word = reverse($word);
$word_objs[$i] = AI::Pathfinding::SMAstar::Examples::WordObj->new(
_word => $rev_word,
);
}
return @word_objs;
}
sub process_rev_words_by_density
{
my ($words,
$max_score # 0: no repeats, 1: 1 repeat, etc.
) = @_;
my @word_objs;
my $i = 0;
foreach my $word (@$words)
{
lib/AI/Pathfinding/SMAstar/Examples/PalUtils.pm view on Meta::CPAN
$word_objs[$i] = AI::Pathfinding::SMAstar::Examples::WordObj->new(
_word => $rev_word,
);
$i++;
}
}
return @word_objs;
}
sub is_palindrome
{
my ($candidate) = @_;
if(!$candidate){
return 0;
}
$candidate =~ s/\ //g;
return($candidate eq reverse($candidate));
}
sub join_strings
{
my ($strings) = @_;
my $candidate = join(' ', @$strings);
return $candidate;
}
sub make_one_word
{
my ($phrase) = @_;
$phrase =~ s/\s//g;
return $phrase;
}
sub num_chars_in_word
{
my ($word) = @_;
my %hash;
if(!$word) { return 0; }
@hash{ split '', $word} = 1;
my $num_keys = keys(%hash);
return $num_keys;
}
{my %memo_cache;
sub num_chars_in_word_memo
{
my ($word) = @_;
if($memo_cache{$word}){
return $memo_cache{$word};
}
else{
my %hash;
@hash{ split '', $word} = 1;
my $num_keys = keys(%hash);
$memo_cache{$word} = $num_keys;
return $num_keys;
}
}
}
{my %memo_cache;
sub num_chars_in_pal
{
my ($pal) = @_;
my $num_keys;
$pal =~ s/\ //g;
my $length = length($pal);
my $first_half = substr($pal, 0, $length/2 + 1);
if($memo_cache{$first_half}){
lib/AI/Pathfinding/SMAstar/Examples/PalUtils.pm view on Meta::CPAN
my %hash;
@hash{ split '', $first_half } = 1;
$num_keys = keys(%hash);
$memo_cache{$pal} = $num_keys;
return $num_keys;
}
}
}
sub read_dictionary
{
my ($in_file) = @_;
unless(open(READF, "+<$in_file")){
return;
}
my @lines = <READF>;
close(READF);
return @lines;
}
sub read_dictionary_filter_by_density
{
my ($in_file, $max_score) = @_;
unless(open(READF, "+<$in_file")){
return;
}
my @lines = <READF>;
my $num_lines = @lines;
lib/AI/Pathfinding/SMAstar/Examples/PalUtils.pm view on Meta::CPAN
if($sparsity <= $max_score){
$filtered_words[$i] = $word;
$i++;
}
}
return ($num_lines, @filtered_words);
}
sub read_dictionary_filter_by_density_rev
{
my ($in_file, $max_score) = @_;
unless(open(READF, "+<$in_file")){
return;
}
my @lines = <READF>;
my $num_lines = @lines;
lib/AI/Pathfinding/SMAstar/Examples/PalUtils.pm view on Meta::CPAN
$filtered_words[$i] = $rev_word;
$i++;
}
}
return ($num_lines, @filtered_words);
}
sub flush {
my $h = select($_[0]); my $a=$|; $|=1; $|=$a; select($h);
}
{my $spinny_thing = "-";
my $call_num = 0;
my $state;
sub show_progress {
$call_num++;
$state = $call_num % 4;
if($state == 0){
$spinny_thing = "-";
}
elsif($state == 1){
$spinny_thing = "\\";
}
elsif($state == 2){
$spinny_thing = "|";
lib/AI/Pathfinding/SMAstar/Examples/PalUtils.pm view on Meta::CPAN
my $percent = sprintf("%.2f", $progress*100);
$percent = $percent >= 100 ? '100.00%' : $percent.'%';
print("\r$stars $spinny_thing $percent.");
flush(STDOUT);
}
}
sub show_search_depth_and_percentage {
my ($depth, $so_far, $total) = @_;
my $stars = '*' x int($depth);
my $amount_completed = $so_far/$total;
my $percentage = sprintf("%0.2f", $amount_completed*100);
print("\r$stars depth: $depth. completed: $percentage %");
flush(STDOUT);
}
sub show_search_depth_and_num_states {
my ($depth, $states) = @_;
my $stars = '*' x int($depth);
my $num_states = @$states;
print("\rdepth: $depth. num_states: $num_states");
flush(STDOUT);
}
{my $LINES=`tput lines`; # number of rows in current terminal window
my $COLUMNS=`tput cols`; # number of columns in current terminal window
sub show_progress_so_far {
my ($iteration, $num_states, $str, $opt_datum, $opt_datum2) = @_;
my $stars = '*' x int($iteration);
# print "\e[H"; # Put the cursor on the first line
# print "\e[J"; # Clear from cursor to end of screen
# print "\e[H\e[J"; # Clear entire screen (just a combination of the above)
# print "\e[K"; # Clear to end of current line (as stated previously)
# print "\e[m"; # Turn off character attributes (eg. colors)
# printf "\e[%dm", $N; # Set color to $N (for values of 30-37, or 100-107)
lib/AI/Pathfinding/SMAstar/Examples/PalUtils.pm view on Meta::CPAN
print "\e[J"; #clears to end of screen
print "string: $str\e[J";
flush(STDOUT);
}
}
sub show_search_depth_and_num_states_debug {
}
{my $LINES=`tput lines`; # number of rows in current terminal window
my $COLUMNS=`tput cols`; # number of columns in current terminal window
sub show_progress_so_far_debug {
my ($depth, $prog, $num_states, $str, $num_successors) = @_;
my $stars = '*' x int($depth);
print "depth: $depth, string: $str, num_successors: $num_successors\n";
flush(STDOUT);
}
}
lib/AI/Pathfinding/SMAstar/Examples/Phrase.pm view on Meta::CPAN
}
use vars qw($d $max_forgotten_nodes); # used to debug destroy method for accounting purposes
$d = 0;
$max_forgotten_nodes = 0;
##################################################
## the Phrase constructor
##################################################
sub new {
my $invocant = shift;
my $class = ref($invocant) || $invocant;
my $self = {
_word_list => undef,
_words_w_cands_list => undef,
_dictionary => undef,
_dictionary_rev => undef,
_start_word => undef, # remainder on cand for antecedent of this obj
_word => undef,
_cand => undef, # cand found for the antecedent of this obj
lib/AI/Pathfinding/SMAstar/Examples/Phrase.pm view on Meta::CPAN
}
##############################################
## methods to access per-object data
##
## With args, they set the value. Without
## any, they only retrieve it/them.
##############################################
sub start_word {
my $self = shift;
if (@_) { $self->{_start_word} = shift }
return $self->{_start_word};
}
sub word {
my $self = shift;
if (@_) { $self->{_word} = shift }
return $self->{_word};
}
sub cand {
my $self = shift;
if (@_) { $self->{_cand} = shift }
return $self->{_cand};
}
sub antecedent{
my $self = shift;
if (@_) { $self->{_predecessor} = shift }
return $self->{_predecessor};
}
sub dir{
my $self = shift;
if (@_) { $self->{_dir} = shift }
return $self->{_dir};
}
sub match_remainder_left{
my $self = shift;
if (@_) { $self->{_match_remainder_left} = shift }
return $self->{_match_remainder_left};
}
sub match_remainder_right {
my $self = shift;
if (@_) { $self->{_match_remainder_right} = shift }
return $self->{_match_remainder_right};
}
sub intersect_threshold {
my $self = shift;
if (@_) { $self->{_intersect_threshold} = shift }
return $self->{_intersect_threshold};
}
sub max_collisions{
my $self = shift;
if (@_) { $self->{_max_collisions} = shift }
return $self->{_max_collisions};
}
sub letters_seen{
my $self = shift;
if (@_) { $self->{_letters_seen} = shift }
return $self->{_letters_seen};
}
sub f_cost{
my $self = shift;
if (@_) { $self->{_f_cost} = shift }
return $self->{_f_cost};
}
sub depth{
my $self = shift;
if (@_) { $self->{_depth} = shift }
return $self->{_depth};
}
sub is_completed{
my $self = shift;
if (@_) { $self->{_is_completed} = shift }
return $self->{_is_completed};
}
sub is_on_queue{
my $self = shift;
if (@_) { $self->{_is_on_queue} = shift }
return $self->{_is_on_queue};
}
sub descendants_deleted{
my $self = shift;
if (@_) { $self->{_descendants_deleted} = shift }
return $self->{_descendants_deleted};
}
sub need_fval_change{
my $self = shift;
if (@_) { $self->{_need_fcost_change} = shift }
return $self->{_need_fcost_change};
}
sub compare
{
my ($min_letters) = @_;
return sub{
my ($self, $arg_obj) = @_;
my $self_eval_func = evaluate($min_letters);
my $argobj_eval_func = evaluate($min_letters);
my $self_eval = $self->$self_eval_func;
my $arg_obj_eval = $arg_obj->$argobj_eval_func;
return $self_eval - $arg_obj_eval;
}
}
sub compare_by_depth
{
my ($self, $arg_obj) = @_;
my $self_depth = $self->{_depth};
my $argobj_depth = $arg_obj->{_depth};
my $result = $self_depth - $argobj_depth;
return $result;
}
# compare_phrase_word_strings
#
# usage: $phrase_obj->compare_phrase_word_strings($other_word_obj)
#
# Accepts another Phrase object as an argument.
# Returns 1 if greater than argument, 0 if equal, and -1 if
# less than argument
sub compare_phrase_word_strings
{
my ($self, $arg_obj) = @_;
my $arg_phrase_plus_word = $arg_obj->{_phrase} . $arg_obj->{_word};
my $phrase_plus_word = $self->{_phrase} . $self->{_word};
if($arg_phrase_plus_word gt $phrase_plus_word){
return -1;
}
elsif($arg_phrase_plus_word eq $phrase_plus_word){
lib/AI/Pathfinding/SMAstar/Examples/Phrase.pm view on Meta::CPAN
#----------------------------------------------------------------------------
# evaluation function f(n) = g(n) + h(n) where
#
# g(n) = cost of path through this node
# h(n) = distance from this node to goal (optimistic)
#
# used for A* search.
#
sub evaluate
{
my ($min_num_letters) = @_;
return sub{
my ($self) = @_;
# if fcost has already been calculated (or reassigned during a backup)
# then return it. otherwise calculate it
my $fcost = $self->{_f_cost};
if(defined($fcost)){
lib/AI/Pathfinding/SMAstar/Examples/Phrase.pm view on Meta::CPAN
my $distance_from_goal = $min_num_letters - ($num_chars_so_far + $num_new_chars); #1 optimistic/admissible
my $evaluation = $total_cost + $distance_from_goal;
$self->{_f_cost} = $evaluation;
return $evaluation;
}
}
#-----------------------------------------------------------------------------
sub phrase_is_palindrome_min_num_chars
{
my ($min_num_chars) = @_;
return sub{
my ($self) = @_;
my $phrase = $self->{_phrase};
if(AI::Pathfinding::SMAstar::Examples::PalUtils::is_palindrome($phrase) &&
(AI::Pathfinding::SMAstar::Examples::PalUtils::num_chars_in_pal($phrase) >= $min_num_chars)){
lib/AI/Pathfinding/SMAstar/Examples/Phrase.pm view on Meta::CPAN
}
else{
return 0;
}
}
}
#----------------------------------------------------------------------------
sub letters_seen_so_far
{
my ($self) = @_;
my $num_letters_seen = $self->{_num_chars_so_far};
return $num_letters_seen;
}
lib/AI/Pathfinding/SMAstar/Examples/Phrase.pm view on Meta::CPAN
#-----------------------------------------------------------------------------
# Get descendants iterator function.
# Generate the next descendant of a phrase object. Each descendant adds
# another word to the phrase that could possibly lead to a palindrome
#
#-----------------------------------------------------------------------------
sub get_descendants_iterator
{
my ($phrase_obj) = @_;
if(!$phrase_obj){
return;
}
my $words = $phrase_obj->{_word_list};
my $words_w_cands = $phrase_obj->{_words_w_cands_list};
my $dictionary = $phrase_obj->{_dictionary};
my $dictionary_rev = $phrase_obj->{_dictionary_rev};
lib/AI/Pathfinding/SMAstar/Examples/Phrase.pm view on Meta::CPAN
}
}
}
#-----------------------------------------------------------------------------
# Return the number of successors of this phrase
#-----------------------------------------------------------------------------
sub get_num_successors
{
my ($self) = @_;
my $num_successors = 0;
my $iterator = $self->get_descendants_num_iterator();
while(my $next_descendant = $iterator->()){
$num_successors++;
}
lib/AI/Pathfinding/SMAstar/Examples/Phrase.pm view on Meta::CPAN
#-----------------------------------------------------------------------------
# Get descendants number function.
#
#
#
#-----------------------------------------------------------------------------
sub get_descendants_number
{
my ($phrase_obj) = @_;
if(!$phrase_obj){
return;
}
my $words = $phrase_obj->{_word_list};
my $words_w_cands = $phrase_obj->{_words_w_cands_list};
my $dictionary = $phrase_obj->{_dictionary};
my $dictionary_rev = $phrase_obj->{_dictionary_rev};
lib/AI/Pathfinding/SMAstar/Examples/Phrase.pm view on Meta::CPAN
}
#-----------------------------------------------------------------------------
# Get descendants iterator function.
# Generate the next descendant of a phrase object. Each descendant adds
# another word to the phrase that could possibly lead to a palindrome
#
#-----------------------------------------------------------------------------
sub get_descendants_num_iterator
{
my ($phrase_obj) = @_;
if(!$phrase_obj){
return;
}
my $words = $phrase_obj->{_word_list};
my $words_w_cands = $phrase_obj->{_words_w_cands_list};
my $dictionary = $phrase_obj->{_dictionary};
my $dictionary_rev = $phrase_obj->{_dictionary_rev};
lib/AI/Pathfinding/SMAstar/Examples/Phrase.pm view on Meta::CPAN
#-----------------------------------------------------------------------------
# traverse from candidate phrase-object back up to start word, building up the
# phrase string. iterative version.
#-----------------------------------------------------------------------------
sub roll_up_phrase
{
my ($pobj, $phrase, $depth) = @_; # depth == depth of recursion
if(!$depth){
$depth = 0;
}
while($pobj){
if(!$pobj->{_cand} && $depth == 0){
# top-level call to roll_up_phrase is called on a root node.
lib/AI/Pathfinding/SMAstar/Examples/Phrase.pm view on Meta::CPAN
$depth++;
} # end while($pobj);
return $phrase;
}
sub roll_up_phrase_plus_word
{
my ($self) = @_;
my $phrase = $self->{_phrase};
my $word = $self->{_start_word};
my $phrase_plus_cand = $phrase . ": " . $word;
return $phrase_plus_cand;
}
sub DESTROY
{
my ($self) = @_;
my $antecedent;
my $ant_phrase;
my ($pkg, $filename, $line_num) = caller();
if($self->{_predecessor}){
$antecedent = $self->{_predecessor};
lib/AI/Pathfinding/SMAstar/Examples/WordObj.pm view on Meta::CPAN
package AI::Pathfinding::SMAstar::Examples::WordObj;
use strict;
##################################################
## the object constructor (simplistic version) ##
##################################################
sub new {
my $invocant = shift;
my $class = ref($invocant) || $invocant;
my $self = {
_word => undef,
@_, # Override previous attributes
};
return bless $self, $class;
}
##############################################
## methods to access per-object data ##
## ##
## With args, they set the value. Without ##
## any, they only retrieve it/them. ##
##############################################
sub word {
my $self = shift;
if (@_) { $self->{_word} = shift }
return $self->{_word};
}
# compare
#
# usage: $word_obj->compare($other_word_obj)
#
# Accepts another WordObj object as an argument.
# Returns 1 if greater than argument, 0 if equal, and -1 if
# less than argument
sub compare{
my ($self,$arg_wordobj) = @_;
my $arg_word = $arg_wordobj->{_word};
my $word = $self->{_word};
if($arg_word gt $word){
return -1;
}
elsif($arg_word eq $word){
return 0;
lib/AI/Pathfinding/SMAstar/Examples/WordObj.pm view on Meta::CPAN
# compare_up_to
#
# usage: $word_obj->compare_up_to($other_word_obj)
#
# Accepts another WordObj object as an argument.
# Returns 1 if greater than argument, 0 if $other_word_obj
# is a substring of $word_obj
# that appears at the beginning of $word_obj
# and -1 if less than argument $other_word_obj
sub compare_up_to{
my $self = shift;
if (@_){
my $arg_wordobj = shift;
my $arg_word = $arg_wordobj->{_word};
my $word = $self->{_word};
# perl's index function works like: index($string, $substr);
if(index($word, $arg_word) == 0){
return(0);
}
lib/AI/Pathfinding/SMAstar/Examples/WordObj.pm view on Meta::CPAN
# compare_up_to
#
# usage: $word_obj->compare_down_to($other_word_obj)
#
# Returns 0 if $word_obj is a substring of
# $other_word_obj, that appears at the beginning
# of $other_word_obj.
#
sub compare_down_to{
my $self = shift;
if (@_){
my $arg_wordobj = shift;
my $arg_word = $arg_wordobj->{_word};
my $word = $self->{_word};
# perl's index function works like: index($string, $substr);
if(index($arg_word, $word) == 0){
return(0);
}
lib/AI/Pathfinding/SMAstar/PairObj.pm view on Meta::CPAN
package AI::Pathfinding::SMAstar::PairObj;
use strict;
##################################################
# PairObj constructor
##################################################
sub new {
my $invocant = shift;
my $class = ref($invocant) || $invocant;
my $self = {
_key => undef,
_value => undef,
@_, # Override previous attributes
};
return bless $self, $class;
}
##############################################
# accessors
##############################################
sub value {
my $self = shift;
if (@_) { $self->{_value} = shift }
return $self->{_value};
}
sub val {
my $self = shift;
if (@_) { $self->{_value} = shift }
return $self->{_value};
}
sub key {
my $self = shift;
if (@_) { $self->{_key} = shift }
return $self->{_key};
}
# compare_vals
#
# usage: $pair_obj->compare($other_pair_obj)
#
# Accepts another PairObj object as an argument.
# Returns 1 if greater than argument, 0 if equal, and -1 if
# less than argument
sub compare_vals{
my ($self,$arg_obj) = @_;
my $arg_value = $arg_obj->{_value};
my $value = $self->{_value};
if($arg_value gt $value){
return -1;
}
elsif($arg_value eq $value){
return 0;
lib/AI/Pathfinding/SMAstar/PairObj.pm view on Meta::CPAN
}
# compare_keys
#
# usage: $pair_obj->compare($other_pair_obj)
#
# Accepts another PairObj object as an argument.
# Returns 1 if greater than argument, 0 if equal, and -1 if
# less than argument
sub compare_keys{
my ($self,$arg_obj) = @_;
my $arg_key = $arg_obj->{_key};
my $key = $self->{_key};
if($arg_key gt $key){
return -1;
}
elsif($arg_key eq $key){
return 0;
}
return 1;
}
sub compare_keys_numeric{
my ($self,$arg_obj) = @_;
my $arg_key = $arg_obj->{_key};
my $key = $self->{_key};
if($arg_key > $key){
return -1;
}
elsif($self->fp_equal($arg_key, $key, 10)){
return 0;
}
return 1;
}
sub fp_equal {
my ($self, $A, $B, $dp) = @_;
return sprintf("%.${dp}g", $A) eq sprintf("%.${dp}g", $B);
}
1; # so the require or use succeeds
lib/AI/Pathfinding/SMAstar/Path.pm view on Meta::CPAN
}
use vars qw($d $max_forgotten_nodes); # used to debug destroy method for accounting purposes
$d = 0;
$max_forgotten_nodes = 0;
##################################################
# Path constructor
##################################################
sub new {
my $invocant = shift;
my $class = ref($invocant) || $invocant;
my $self = {
_state => undef, # node in the search space
_eval_func => undef,
_goal_p_func => undef,
_num_successors_func => undef,
_successors_iterator => undef,
_get_data_func => undef,
lib/AI/Pathfinding/SMAstar/Path.pm view on Meta::CPAN
};
return bless $self, $class;
}
##############################################
# accessors
##############################################
sub state{
my $self = shift;
if (@_) { $self->{_state} = shift }
return $self->{_state};
}
sub antecedent{
my $self = shift;
if (@_) { $self->{_antecedent} = shift }
return $self->{_antecedent};
}
sub f_cost{
my $self = shift;
if (@_) { $self->{_f_cost} = shift }
return $self->{_f_cost};
}
sub depth{
my $self = shift;
if (@_) { $self->{_depth} = shift }
return $self->{_depth};
}
sub is_completed{
my $self = shift;
if (@_) { $self->{_is_completed} = shift }
return $self->{_is_completed};
}
sub is_on_queue{
my $self = shift;
if (@_) { $self->{_is_on_queue} = shift }
return $self->{_is_on_queue};
}
sub descendants_deleted{
my $self = shift;
if (@_) { $self->{_descendants_deleted} = shift }
return $self->{_descendants_deleted};
}
sub need_fval_change{
my $self = shift;
if (@_) { $self->{_need_fcost_change} = shift }
return $self->{_need_fcost_change};
}
# new version 8
sub remember_forgotten_nodes_fcost
{
my ($self, $node) = @_;
my $fcost = $node->{_f_cost};
my $index = $node->{_descendant_index};
$self->{_forgotten_node_fcosts}->[$index] = $fcost;
return;
}
lib/AI/Pathfinding/SMAstar/Path.pm view on Meta::CPAN
#----------------------------------------------------------------------------
# evaluation function f(n) = g(n) + h(n) where
#
# g(n) = cost of path through this node
# h(n) = distance from this node to goal (optimistic)
#
# used for A* search.
#
sub fcost
{
my ($self) = @_;
my $fcost = $self->{_f_cost};
if(defined($fcost)){
return $fcost;
}
my $eval_func = $self->{_eval_func};
my $result = $eval_func->($self->{_state});
$self->{_f_cost} = $result;
return $result;
}
sub is_goal
{
my ($self) = @_;
my $goal_p_func = $self->{_goal_p_func};
my $result = $goal_p_func->($self->{_state});
return $result;
}
sub get_num_successors
{
my ($self) = @_;
my $num_successors_func = $self->{_num_successors_func};
my $result = $num_successors_func->($self->{_state});
return $result;
}
sub get_successors_iterator
{
my ($self) = @_;
my $successors_iterator = $self->{_successors_iterator};
my $iterator = $successors_iterator->($self->{_state});
return $iterator;
}
#-----------------------------------------------------------------------------------------------
#
# Check whether we need to backup the fvals for a node when it is completed (recursive)
# Sets flags throughout path object's lineage, indicating whether fvals need to be updated.
#
#-----------------------------------------------------------------------------------------------
sub check_need_fval_change
{
my ($self, $descendant_fcost, $descendant_ind) = @_;
my $descendant_index = $self->{_descendant_index};
if(!$self->is_completed()){
# node not completed. no need to update fcost.
$self->need_fval_change(0);
return;
}
my $fcost = $self->{_f_cost};
my $least_fcost2 = 99;
my $min = sub {
my ($n1, $n2) = @_;
return ($n1 < $n2 ? $n1 : $n2);
};
if($self->{_forgotten_nodes_num} != 0){
foreach my $ind (keys %{$self->{_forgotten_nodes_offsets}}){
my $cost = $self->{_forgotten_node_fcosts}->[$ind];
if($cost != -1 && $cost < $least_fcost2){
$least_fcost2 = $cost;
}
lib/AI/Pathfinding/SMAstar/Path.pm view on Meta::CPAN
#-----------------------------------------------------------------------------------------------
#
# Backup the fvals for a node when it is completed.
#
#-----------------------------------------------------------------------------------------------
sub backup_fvals
{
my ($self) = @_;
while($self){
if(!$self->is_completed()){
# node not completed, return
return;
}
my $fcost = $self->{_f_cost};
my $least_fcost = 99;
my $min = sub {
my ($n1, $n2) = @_;
return ($n1 < $n2 ? $n1 : $n2);
};
if($self->{_forgotten_nodes_num} != 0){
foreach my $ind (keys %{$self->{_forgotten_nodes_offsets}}){
my $cost = $self->{_forgotten_node_fcosts}->[$ind];
if($cost != -1 && $cost < $least_fcost){
$least_fcost = $cost;
}
lib/AI/Pathfinding/SMAstar/Path.pm view on Meta::CPAN
#
# return 1 if all descendants of this path are in
# memory, return 0 otherwise.
#
sub all_in_memory
{
my ($self) = @_;
my $is_completed = $self->is_completed();
my $num_successors_in_mem = $self->{_num_successors_in_mem};
my $num_successors = $self->{_num_successors};
my $num_forgotten_fcosts = @{$self->{_forgotten_node_fcosts}};
if($is_completed || $num_successors == 0){
if($num_successors == $num_successors_in_mem){
lib/AI/Pathfinding/SMAstar/Path.pm view on Meta::CPAN
return 0;
}
return 0;
}
#
# return 1 if *any* descendants are in memory
#
sub has_descendants_in_memory
{
my ($self) = @_;
my $num_descendants_on_queue = $self->{_descendants_on_queue};
if($num_descendants_on_queue){
return $num_descendants_on_queue;
}
return;
lib/AI/Pathfinding/SMAstar/Path.pm view on Meta::CPAN
#-----------------------------------------------------------------------------
# Get descendants iterator function, for for SMA* search. Returns one new
# node at a time.
#
# The SMA* algorithm must handle "forgotten" nodes.
#
# Generate the next descendant of a path object. Each descendant adds
# another node on the path that may lead to the goal.
#
#-----------------------------------------------------------------------------
sub get_descendants_iterator_smastar
{
my ($self) = @_;
my $depth = $self->{_depth};
my $iterator;
my $num_successors = 0;
my $next_descendant;
# if we haven't counted the number of successors yet,
# count and record the number, so we only have to do
lib/AI/Pathfinding/SMAstar/Path.pm view on Meta::CPAN
}
else{
# if number of successors has already been recorded, update
# num_successors variable with stored value.
$num_successors = $self->{_num_successors};
}
return sub{
my $i = 0;
# entering get_descendants_iterator_smastar() sub
$iterator = $self->get_successors_iterator();
my $descendants_deleted = 0;
my $descendants_found = 0;
# loop over nodes returned by iterator
while(my $next_state = $iterator->()){
$next_descendant = AI::Pathfinding::SMAstar::Path->new(
lib/AI/Pathfinding/SMAstar/Path.pm view on Meta::CPAN
# no next successor found
$self->is_completed(1);
}
return $next_descendant;
}
}
sub get_data
{
my ($self) = @_;
my $get_data_func = $self->{_get_data_func};
my $data = $get_data_func->($self->{_state});
return $data;
}
sub DESTROY
{
my ($self) = @_;
# antecedent is no longer pointing at this object, or else
# DESTROY would not have been called.
if($self->{_antecedent}){
delete $self->{_antecedent};
}
}
lib/AI/Pathfinding/SMAstar/PriorityQueue.pm view on Meta::CPAN
use AI::Pathfinding::SMAstar::Path;
use AI::Pathfinding::SMAstar::TreeOfQueues;
use Carp;
use strict;
##################################################
# PriorityQueue constructor
##################################################
sub new {
my $invocant = shift;
my $class = ref($invocant) || $invocant;
my $self = {
_hash_of_trees_ref => {},
_cost_min_max_tree => Tree::AVL->new( fcompare => \&fp_compare, # floating-point compare
fget_key => sub { $_[0] },
fget_data => sub { $_[0] },),
f_depth => \&AI::Pathfinding::SMAstar::Path::depth,
f_fcost => \&AI::Pathfinding::SMAstar::Path::fcost,
f_avl_compare => \&AI::Pathfinding::SMAstar::Path::compare_by_depth,
f_avl_get_key => \&AI::Pathfinding::SMAstar::Path::depth,
f_avl_get_data => \&AI::Pathfinding::SMAstar::Path::get_data,
_size => 0,
@_, # attribute override
};
return bless $self, $class;
}
################################################
# accessors
################################################
sub hash_of_trees {
my $self = shift;
if (@_) { $self->{_hash_of_trees_ref} = shift }
return $self->{_hash_of_trees_ref};
}
sub size {
my $self = shift;
if (@_) { $self->{_size} = shift }
return $self->{_size};
}
################################################
##
## other methods
##
################################################
sub insert {
my ($self, $pobj) = @_;
my $cost_hash_ref = $self->{_hash_of_trees_ref};
my $cost_hash_key_func = $self->{f_fcost};
my $cost_min_max_tree = $self->{_cost_min_max_tree};
my $depth_func = $self->{f_depth};
my $avl_compare_func = $self->{f_avl_compare};
lib/AI/Pathfinding/SMAstar/PriorityQueue.pm view on Meta::CPAN
}
$self->{_size} = $self->{_size} + 1;
my $antecedent = $pobj->{_antecedent};
if($antecedent){
$antecedent->{_descendants_on_queue} = $antecedent->{_descendants_on_queue} + 1;
}
$pobj->is_on_queue(1);
}
sub print_trees_in_order
{
my ($self) = @_;
my $cost_hash_ref = $self->{_hash_of_trees_ref};
for my $cost_key (keys %$cost_hash_ref){
if(!$cost_hash_ref->{$cost_key}){
# no tree for this depth.
#print "no tree at key $depth_key\n";
}
lib/AI/Pathfinding/SMAstar/PriorityQueue.pm view on Meta::CPAN
}
}
#-----------------------------------
# get_list
#
# return a list of all objects in queue
#
#-----------------------------------
sub get_list
{
my ($self) = @_;
my $cost_hash_ref = $self->{_hash_of_trees_ref};
my @list;
for my $cost_key (keys %$cost_hash_ref){
if($cost_hash_ref->{$cost_key}){
my $avltree = $cost_hash_ref->{$cost_key};
push(@list, $$avltree->get_list());
}
}
return @list;
}
sub is_empty
{
my ($self) = @_;
my $cost_hash_ref = $self->{_hash_of_trees_ref};
my @cost_keys = (keys %$cost_hash_ref);
if(!@cost_keys){
return 1;
}
else{
return 0;
}
}
sub remove
{
my ($self, $obj, $cmp_func) = @_;
my $cost_hash_ref = $self->{_hash_of_trees_ref};
my @cost_keys = (keys %$cost_hash_ref);
my $cost_min_max_tree = $self->{_cost_min_max_tree};
lib/AI/Pathfinding/SMAstar/PriorityQueue.pm view on Meta::CPAN
}
my $antecedent = $obj->{_antecedent};
if($antecedent){
$antecedent->{_descendants_on_queue} = $antecedent->{_descendants_on_queue} - 1;
}
$obj->is_on_queue(0);
return;
}
sub deepest_lowest_cost_leaf
{
my ($self) = @_;
my $cost_hash_ref = $self->{_hash_of_trees_ref};
my @cost_keys = (keys %$cost_hash_ref);
my $cost_min_max_tree = $self->{_cost_min_max_tree};
if(!@cost_keys){
lib/AI/Pathfinding/SMAstar/PriorityQueue.pm view on Meta::CPAN
if($antecedent){
$antecedent->{_descendants_on_queue} = $antecedent->{_descendants_on_queue} - 1;
}
$obj->is_on_queue(0);
$self->{_size} = $self->{_size} - 1;
return $obj;
}
}
sub deepest_lowest_cost_leaf_dont_remove
{
my ($self) = @_;
my $avl_compare_func = $self->{f_avl_compare};
my $avl_get_key_func = $self->{f_avl_get_key};
my $avl_get_data_func = $self->{f_avl_get_data};
my $cost_hash_ref = $self->{_hash_of_trees_ref};
my @cost_keys = (keys %$cost_hash_ref);
lib/AI/Pathfinding/SMAstar/PriorityQueue.pm view on Meta::CPAN
my $obj = $$avltree->largest_oldest(); # get the deepest one
my $cost_key = $obj->$avl_get_key_func();
my $data = $obj->$avl_get_data_func();
return $obj;
}
}
# Return the shallowest, highest-cost leaf
sub shallowest_highest_cost_leaf
{
my ($self, $best, $succ, $str_function) = @_;
my $cost_hash_ref = $self->{_hash_of_trees_ref};
my @cost_keys = (keys %$cost_hash_ref);
my $cost_min_max_tree = $self->{_cost_min_max_tree};
my $obj;
lib/AI/Pathfinding/SMAstar/PriorityQueue.pm view on Meta::CPAN
my $compare_func = sub{
my ($obj1, $obj2) = @_;
my $obj1_str = $str_function->($obj1);
my $obj2_str = $str_function->($obj2);
if($obj1_str eq $obj2_str){
return 1;
}
return 0;
};
my $cmp_func = sub {
my ($phrase) = @_;
return sub{
my ($obj) = @_;
my $obj_phrase = $str_function->($obj);
if($obj_phrase eq $phrase){
return 1;
}
else{
return 0;
}
lib/AI/Pathfinding/SMAstar/PriorityQueue.pm view on Meta::CPAN
}
return $obj;
}
else{
return;
}
}
}
sub largest_element
{
my ($array) = @_;
if(!@$array){
return;
}
else{
my $i = 0;
my $largest = $$array[$i];
for($i = 1; $i < @$array; $i++)
{
if($largest < $$array[$i]){
$largest = $$array[$i];
}
}
return $largest;
}
}
sub next_largest_element
{
my ($array, $val) = @_;
if(!@$array){
return;
}
else{
my $i = 0;
my $largest = -1;
for($i = 0; $i < @$array; $i++)
lib/AI/Pathfinding/SMAstar/PriorityQueue.pm view on Meta::CPAN
return $largest;
}
else{
return;
}
}
}
sub next_smallest_non_zero_element
{
my ($array, $val) = @_;
my $max = 2^32-1;
if(!@$array){
return;
}
else{
my $i = 0;
lib/AI/Pathfinding/SMAstar/PriorityQueue.pm view on Meta::CPAN
if($smallest != $max){
return $smallest;
}
else{
return;
}
}
}
sub smallest_element
{
my ($array) = @_;
if(!@$array){
return;
}
else{
my $i = 0;
my $smallest = $$array[$i];
for($i = 1; $i < @$array; $i++){
if($smallest > $$array[$i]){
$smallest = $$array[$i];
}
}
return $smallest;
}
}
sub get_size{
my ($self) = @_;
my $cost_hash_ref = $self->{_hash_of_trees_ref};
my $size = 0;
foreach my $key (keys %$cost_hash_ref){
my $tree = $cost_hash_ref->{$key};
my $tree_size = $$tree->get_size();
$size += $tree_size;
}
return $size;
}
sub fp_compare
{
my ($obj1, $obj2) = @_;
if(fp_equal($obj1, $obj2, 10)){
return 0;
}
if($obj1 < $obj2){
return -1;
}
return 1;
}
sub fp_equal {
my ($A, $B, $dp) = @_;
return sprintf("%.${dp}g", $A) eq sprintf("%.${dp}g", $B);
}
lib/AI/Pathfinding/SMAstar/TreeOfQueues.pm view on Meta::CPAN
#
package AI::Pathfinding::SMAstar::TreeOfQueues;
use strict;
use Tree::AVL;
use AI::Pathfinding::SMAstar::AVLQueue;
##################################################
# TreeOfQueues constructor
##################################################
sub new {
my $invocant = shift;
my $class = ref($invocant) || $invocant;
my $self = {
f_avl_compare => undef,
f_obj_get_key => undef,
f_obj_get_data => undef,
_avl_tree => Tree::AVL->new(fcompare => \&AI::Pathfinding::SMAstar::AVLQueue::compare,
fget_key => \&AI::Pathfinding::SMAstar::AVLQueue::key,
fget_data => \&AI::Pathfinding::SMAstar::AVLQueue::key),
@_, # attribute override
};
return bless $self, $class;
}
sub insert{
my ($self, $obj) = @_;
# check to see if there is a Queue in the tree with the key of obj.
# if not, create one and insert
my $fget_key = $self->{f_obj_get_key};
my $avl_compare = $self->{f_avl_compare};
my $key = $obj->$fget_key();
my $queue = AI::Pathfinding::SMAstar::AVLQueue->new(_key => $key);
my $found_queue = $self->{_avl_tree}->lookup_obj($queue);
lib/AI/Pathfinding/SMAstar/TreeOfQueues.pm view on Meta::CPAN
if(!$found_queue){
$self->{_avl_tree}->insert($queue); # insert queue, with no duplicates
$queue->insert($obj); # insert object onto new queue
}
else { # found a queue here. insert obj
$found_queue->insert($obj);
}
}
sub remove{
my ($self, $obj, $cmp_func) = @_;
# check to see if there is a Queue in the tree with the key of obj.
# if not, create one and insert
my $fget_key = $self->{f_obj_get_key};
my $avl_compare = $self->{f_avl_compare};
my $key = $obj->$fget_key();
my $queue = AI::Pathfinding::SMAstar::AVLQueue->new(_key => $key);
my $avltree = \$self->{_avl_tree};
my $found_queue = $self->{_avl_tree}->lookup_obj($queue);
lib/AI/Pathfinding/SMAstar/TreeOfQueues.pm view on Meta::CPAN
else { # found a queue here. remove obj
#print "TreeOfQueues::remove: found queue, removing obj using $cmp_func\n";
$found_queue->remove($obj, $cmp_func);
if($found_queue->is_empty()){
#print "TreeOfQueues::remove: found queue is now empty, removing queue from tree\n";
$$avltree->remove($found_queue);
}
}
}
sub largest_oldest{
my ($self) = @_;
my $avltree = \$self->{_avl_tree};
# $$avltree->print("-");
# get the avl tree with the largest key
my $queue = $$avltree->largest();
if($queue){
my $key = $queue->key();
my $obj = $queue->top();
return $obj;
}
else{
return;
}
}
sub pop_largest_oldest{
my ($self) = @_;
my $avltree = \$self->{_avl_tree};
# $$avltree->print("*");
# get the avl tree with the largest key
my $queue = $$avltree->largest();
if($queue){
my $key = $queue->key();
my $obj = $queue->pop_top();
lib/AI/Pathfinding/SMAstar/TreeOfQueues.pm view on Meta::CPAN
if($queue->is_empty()){
$$avltree->remove($queue);
}
return $obj;
}
else{
return;
}
}
sub smallest_oldest{
my ($self) = @_;
my $avltree = \$self->{_avl_tree};
# $$avltree->print("-");
# get the avl tree with the largest key
my $queue = $$avltree->smallest();
if($queue){
my $key = $queue->key();
my $obj = $queue->top();
return $obj;
}
else{
return;
}
}
sub pop_smallest_oldest{
my ($self) = @_;
my $avltree = \$self->{_avl_tree};
# $$avltree->print("*");
# get the avl tree with the largest key
my $queue = $$avltree->smallest();
if($queue){
my $key = $queue->key();
my $obj = $queue->pop_top();
lib/AI/Pathfinding/SMAstar/TreeOfQueues.pm view on Meta::CPAN
$$avltree->remove($queue);
}
return $obj;
}
else{
return;
}
}
sub pop_oldest_at{
my ($self, $key) = @_;
my $avltree = \$self->{_avl_tree};
my $queue_to_find = AI::Pathfinding::SMAstar::AVLQueue->new(_key => $key);
my $queue = $$avltree->lookup_obj($queue_to_find);
if($queue){
# print "TreeOfQueues::pop_oldest_at: found queue with key: $key\n";
my $obj = $queue->pop_top();
lib/AI/Pathfinding/SMAstar/TreeOfQueues.pm view on Meta::CPAN
}
else{
# print "TreeOfQueues::pop_oldest_at: did not find queue with key: $key\n";
return;
}
}
sub oldest_at{
my ($self, $key) = @_;
my $avltree = \$self->{_avl_tree};
my $queue_to_find = AI::Pathfinding::SMAstar::AVLQueue->new(_key => $key);
my $queue = $$avltree->lookup_obj($queue_to_find);
if($queue){
# print "TreeOfQueues::oldest_at: found queue with key: $key\n";
my $obj = $queue->top();
return $obj;
}
else{
# print "TreeOfQueues::oldest_at: did not find queue with key: $key\n";
return;
}
}
sub largest{
my ($self) = @_;
my $avltree = \$self->{_avl_tree};
return $$avltree->largest();
}
sub get_queue{
my ($self, $key) = @_;
my $avltree = \$self->{_avl_tree};
my $queue_to_find = AI::Pathfinding::SMAstar::AVLQueue->new(_key => $key);
my $queue = $$avltree->lookup_obj($queue_to_find);
if($queue){
# print "TreeOfQueues::get_queue: found queue with key: $key\n";
return $queue;
lib/AI/Pathfinding/SMAstar/TreeOfQueues.pm view on Meta::CPAN
else{
# print "TreeOfQueues::get_queue: did not find queue with key: $key\n";
return;
}
}
sub get_keys_iterator
{
my ($self) = @_;
my $avltree = \$self->{_avl_tree};
return $$avltree->get_keys_iterator();
}
sub get_keys
{
my ($self) = @_;
my $avltree = \$self->{_avl_tree};
return $$avltree->get_keys();
}
sub print{
my ($self) = @_;
if($self->{_avl_tree}->is_empty()){
print "tree is empty\n";
}
my $get_key_func = $self->{f_obj_get_key};
my $get_data_func = $self->{f_obj_get_data};
my @queue_list = $self->{_avl_tree}->get_list();
lib/AI/Pathfinding/SMAstar/TreeOfQueues.pm view on Meta::CPAN
my $key = $obj->$get_key_func;
my $word = $obj->$get_data_func;
print " key: $key, data: $word\n";
}
}
}
sub is_empty{
my ($self) = @_;
if($self->{_avl_tree}->is_empty()){
return 1;
}
return 0;
}
sub get_size{
my ($self) = @_;
my $size = 0;
if($self->{_avl_tree}->is_empty()){
return $size;
}
my @queue_list = $self->{_avl_tree}->get_list();
foreach my $queue (@queue_list){
$size = $size + $queue->get_size();
}
return $size;
}
sub get_list{
my ($self) = @_;
my @objs;
if($self->{_avl_tree}->is_empty()){
return;
}
#$self->{_avl_tree}->print(">>>");
t/AI-Pathfinding-SMAstar.t view on Meta::CPAN
###########################################################################
#
# Auxiliary functions
#
###########################################################################
#----------------------------------------------------------------------------
sub log_function
{
my ($path_obj) = @_;
if(!$path_obj){
my ($pkg, $filename, $line) = caller();
print "$pkg, $filename, $line\n";
t/AI-Pathfinding-SMAstar.t view on Meta::CPAN
return $log_str;
}
#----------------------------------------------------------------------------
sub str_function
{
my ($path_obj) = @_;
my $sw = defined($path_obj->{_state}->{_start_word}) ? $path_obj->{_state}->{_start_word} : "";
my $phrase = defined($path_obj->{_state}->{_phrase}) ? $path_obj->{_state}->{_phrase} : "";
my $str = "$sw, $phrase";
return $str;
}
t/AI-Pathfinding-SMAstar.t view on Meta::CPAN
sub flush {
my $h = select($_[0]); my $a=$|; $|=1; $|=$a; select($h);
}
{my $spinny_thing = "-";
my $call_num = 0;
my $state;
sub show_progress {
$call_num++;
$state = $call_num % 4;
if($state == 0){
$spinny_thing = "-";
}
elsif($state == 1){
$spinny_thing = "\\";
}
elsif($state == 2){
$spinny_thing = "|";