File Coverage

blib/lib/AI/Pathfinding/AStar.pm
Criterion Covered Total %
statement 65 89 73.0
branch 9 16 56.2
condition 6 9 66.6
subroutine 9 11 81.8
pod 0 4 0.0
total 89 129 68.9


line stmt bran cond sub pod time code
1             package AI::Pathfinding::AStar;
2              
3 1     1   28870 use 5.006;
  1         5  
  1         51  
4 1     1   6 use strict;
  1         2  
  1         53  
5 1     1   6 use warnings;
  1         7  
  1         38  
6 1     1   6 use Carp;
  1         2  
  1         193  
7              
8             our $VERSION = '0.10';
9              
10 1     1   961 use Heap::Binomial;
  1         3034  
  1         52  
11              
12 1     1   1220 use AI::Pathfinding::AStar::AStarNode;
  1         3  
  1         1536  
13             my $nodes;
14              
15             sub _init {
16 0     0   0 my $self = shift;
17 0 0       0 croak "no getSurrounding() method defined" unless $self->can("getSurrounding");
18              
19 0         0 return $self->SUPER::_init(@_);
20             }
21              
22             sub doAStar
23             {
24 2     2 0 5 my ($map, $target, $open, $nodes, $max) = @_;
25              
26 2         3 my $n = 0;
27 2   66     8 FLOOP: while ( (defined $open->top()) && ($open->top()->{id} ne $target) ) {
28              
29             #allow incremental calculation
30 38 50 33     488 last FLOOP if (defined($max) and (++$n == $max));
31              
32 38         97 my $curr_node = $open->extract_top();
33 38         143 $curr_node->{inopen} = 0;
34 38         59 my $G = $curr_node->{g};
35              
36             #get surrounding squares
37 38         122 my $surr_nodes = $map->getSurrounding($curr_node->{id}, $target);
38 38         4007 foreach my $node (@$surr_nodes) {
39 207         828 my ($surr_id, $surr_cost, $surr_h) = @$node;
40              
41             #skip the node if it's in the CLOSED list
42 207 100 100     1046 next if ( (exists $nodes->{$surr_id}) && (! $nodes->{$surr_id}->{inopen}) );
43              
44             #add it if we haven't seen it before
45 118 100       239 if (! exists $nodes->{$surr_id}) {
46 45         205 my $surr_node = AI::Pathfinding::AStar::AStarNode->new($surr_id,$G+$surr_cost,$surr_h);
47 45         82 $surr_node->{parent} = $curr_node;
48 45         67 $surr_node->{cost} = $surr_cost;
49 45         64 $surr_node->{inopen} = 1;
50 45         85 $nodes->{$surr_id} = $surr_node;
51 45         123 $open->add($surr_node);
52             }
53             else {
54             #otherwise it's already in the OPEN list
55             #check to see if it's cheaper to go through the current
56             #square compared to the previous path
57 73         112 my $surr_node = $nodes->{$surr_id};
58 73         104 my $currG = $surr_node->{g};
59 73         90 my $possG = $G + $surr_cost;
60 73 100       247 if ($possG < $currG) {
61             #change the parent
62 26         38 $surr_node->{parent} = $curr_node;
63 26         35 $surr_node->{g} = $possG;
64 26         74 $open->decrease_key($surr_node);
65             }
66             }
67             }
68             }
69             }
70              
71             sub fillPath
72             {
73 2     2 0 5 my ($map,$open,$nodes,$target) = @_;
74 2         4 my $path = [];
75              
76 2 50       8 my $curr_node = (exists $nodes->{$target}) ? $nodes->{$target} : $open->top();
77 2         5 while (defined $curr_node) {
78 14         27 unshift @$path, $curr_node->{id};
79 14         30 $curr_node = $curr_node->{parent};
80             }
81 2         5 return $path;
82             }
83              
84              
85             sub findPath {
86 2     2 0 2010 my ($map, $start, $target) = @_;
87              
88 2         5 my $nodes = {};
89 2         5 my $curr_node = undef;
90              
91 2         13 my $open = Heap::Binomial->new;
92             #add starting square to the open list
93 2         25 $curr_node = AI::Pathfinding::AStar::AStarNode->new($start,0,0); # AStarNode(id,g,h)
94 2         10 $curr_node->{parent} = undef;
95 2         5 $curr_node->{cost} = 0;
96 2         3 $curr_node->{g} = 0;
97 2         3 $curr_node->{h} = 0;
98 2         4 $curr_node->{inopen} = 1;
99 2         5 $nodes->{$start} = $curr_node;
100 2         598 $open->add($curr_node);
101              
102 2         25 $map->doAStar($target,$open,$nodes,undef);
103              
104 2         39 my $path = $map->fillPath($open,$nodes,$target);
105              
106 2 50       10 return wantarray ? @{$path} : $path;
  0            
107             }
108              
109             sub findPathIncr {
110 0     0 0   my ($map, $start, $target, $state, $max) = @_;
111              
112 0           my $open = undef;
113 0           my $curr_node = undef;;
114 0           my $nodes = {};
115 0 0         if (defined($state)) {
116 0           $nodes = $state->{'visited'};
117 0           $open = $state->{'open'};
118             }
119             else {
120 0           $open = Heap::Binomial->new;
121             #add starting square to the open list
122 0           $curr_node = AI::Pathfinding::AStar::AStarNode->new($start,0,0); # AStarNode(id,g,h)
123 0           $curr_node->{parent} = undef;
124 0           $curr_node->{cost} = 0;
125 0           $curr_node->{g} = 0;
126 0           $curr_node->{h} = 0;
127 0           $curr_node->{inopen} = 1;
128 0           $nodes->{$start} = $curr_node;
129 0           $open->add($curr_node);
130             }
131              
132 0           $map->doAStar($target,$open,$nodes,$max);
133              
134 0           my $path = $map->fillPath($open,$nodes,$target);
135 0           $state = {
136             'path' => $path,
137             'open' => $open,
138             'visited' => $nodes,
139             'done' => defined($nodes->{$target}),
140             };
141              
142 0           return $state;
143             }
144              
145             1;
146              
147             __END__