| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
# -*- Perl -*- |
|
2
|
|
|
|
|
|
|
# |
|
3
|
|
|
|
|
|
|
# Dijkstra Map path finding. run perldoc(1) on this file for additional |
|
4
|
|
|
|
|
|
|
# documentation |
|
5
|
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Game::DijkstraMap; |
|
7
|
|
|
|
|
|
|
|
|
8
|
2
|
|
|
2
|
|
186450
|
use 5.24.0; |
|
|
2
|
|
|
|
|
16
|
|
|
9
|
2
|
|
|
2
|
|
11
|
use warnings; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
65
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
2
|
|
|
2
|
|
13
|
use Carp qw(croak); |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
118
|
|
|
12
|
2
|
|
|
2
|
|
13
|
use List::Util 1.26 qw(shuffle sum0); |
|
|
2
|
|
|
|
|
33
|
|
|
|
2
|
|
|
|
|
167
|
|
|
13
|
2
|
|
|
2
|
|
1039
|
use Moo; |
|
|
2
|
|
|
|
|
22387
|
|
|
|
2
|
|
|
|
|
9
|
|
|
14
|
2
|
|
|
2
|
|
3885
|
use namespace::clean; |
|
|
2
|
|
|
|
|
22604
|
|
|
|
2
|
|
|
|
|
13
|
|
|
15
|
2
|
|
|
2
|
|
599
|
use Scalar::Util qw(looks_like_number); |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
132
|
|
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
our $VERSION = '1.01'; |
|
18
|
|
|
|
|
|
|
|
|
19
|
2
|
|
|
2
|
|
15
|
use constant SQRT2 => sqrt(2); |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
7443
|
|
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
has bad_cost => ( is => 'rw', default => sub { -2147483648 } ); |
|
22
|
|
|
|
|
|
|
has min_cost => ( is => 'rw', default => sub { 0 } ); |
|
23
|
|
|
|
|
|
|
has max_cost => ( is => 'rw', default => sub { 2147483647 } ); |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
has costfn => ( |
|
26
|
|
|
|
|
|
|
is => 'rw', |
|
27
|
|
|
|
|
|
|
default => sub { |
|
28
|
|
|
|
|
|
|
return sub { |
|
29
|
|
|
|
|
|
|
my ( $self, $c ) = @_; |
|
30
|
|
|
|
|
|
|
if ( $c eq '#' ) { return $self->bad_cost } |
|
31
|
|
|
|
|
|
|
if ( $c eq 'x' ) { return $self->min_cost } |
|
32
|
|
|
|
|
|
|
return $self->max_cost; |
|
33
|
|
|
|
|
|
|
}; |
|
34
|
|
|
|
|
|
|
} |
|
35
|
|
|
|
|
|
|
); |
|
36
|
|
|
|
|
|
|
has dimap => ( is => 'rw' ); |
|
37
|
|
|
|
|
|
|
has iters => ( is => 'rwp', default => sub { 0 } ); |
|
38
|
|
|
|
|
|
|
has next_m => ( is => 'rw', default => sub { 'next' } ); |
|
39
|
|
|
|
|
|
|
has normfn => ( is => 'rw', default => sub { \&norm_4way } ); |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
sub BUILD { |
|
42
|
9
|
|
|
9
|
0
|
47
|
my ( $self, $param ) = @_; |
|
43
|
|
|
|
|
|
|
croak "cannot have both map and str2map arguments" |
|
44
|
9
|
50
|
66
|
|
|
46
|
if exists $param->{'map'} and exists $param->{'str2map'}; |
|
45
|
|
|
|
|
|
|
$self->map( $param->{'map'} ) |
|
46
|
8
|
50
|
|
|
|
16
|
if exists $param->{'map'}; |
|
47
|
|
|
|
|
|
|
$self->map( $self->str2map( $param->{'str2map'} ) ) |
|
48
|
8
|
100
|
|
|
|
34
|
if exists $param->{'str2map'}; |
|
49
|
|
|
|
|
|
|
} |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
sub adjacent_values { |
|
52
|
5
|
|
|
5
|
1
|
4765
|
my ( $dimap, $r, $c, $maxrow, $maxcol ) = @_; |
|
53
|
5
|
|
|
|
|
8
|
my @values; |
|
54
|
5
|
|
|
|
|
12
|
for my $i ( -1, 1 ) { |
|
55
|
10
|
|
|
|
|
16
|
my $x = $c + $i; |
|
56
|
10
|
100
|
100
|
|
|
43
|
push @values, $dimap->[$r][$x] if $x >= 0 and $x <= $maxcol; |
|
57
|
10
|
|
|
|
|
19
|
for my $j ( -1 .. 1 ) { |
|
58
|
30
|
|
|
|
|
34
|
$x = $r + $i; |
|
59
|
30
|
|
|
|
|
38
|
my $y = $c + $j; |
|
60
|
30
|
100
|
100
|
|
|
122
|
push @values, $dimap->[$x][$y] |
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
61
|
|
|
|
|
|
|
if $x >= 0 |
|
62
|
|
|
|
|
|
|
and $x <= $maxrow |
|
63
|
|
|
|
|
|
|
and $y >= 0 |
|
64
|
|
|
|
|
|
|
and $y <= $maxcol; |
|
65
|
|
|
|
|
|
|
} |
|
66
|
|
|
|
|
|
|
} |
|
67
|
5
|
|
|
|
|
20
|
return @values; |
|
68
|
|
|
|
|
|
|
} |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub adjacent_values_diag { |
|
71
|
50
|
|
|
50
|
1
|
2019
|
my ( $dimap, $r, $c, $maxrow, $maxcol ) = @_; |
|
72
|
50
|
|
|
|
|
61
|
my @values; |
|
73
|
50
|
100
|
100
|
|
|
149
|
push @values, $dimap->[ $r - 1 ][ $c - 1 ] if $r > 0 and $c > 0; |
|
74
|
50
|
100
|
100
|
|
|
135
|
push @values, $dimap->[ $r - 1 ][ $c + 1 ] if $r > 0 and $c < $maxcol; |
|
75
|
50
|
100
|
100
|
|
|
137
|
push @values, $dimap->[ $r + 1 ][ $c - 1 ] if $r < $maxrow and $c > 0; |
|
76
|
50
|
100
|
100
|
|
|
146
|
push @values, $dimap->[ $r + 1 ][ $c + 1 ] if $r < $maxrow and $c < $maxcol; |
|
77
|
50
|
|
|
|
|
190
|
return @values; |
|
78
|
|
|
|
|
|
|
} |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
sub adjacent_values_sq { |
|
81
|
230
|
|
|
230
|
1
|
2209
|
my ( $dimap, $r, $c, $maxrow, $maxcol ) = @_; |
|
82
|
230
|
|
|
|
|
275
|
my @values; |
|
83
|
230
|
100
|
|
|
|
529
|
push @values, $dimap->[$r][ $c - 1 ] if $c > 0; |
|
84
|
230
|
100
|
|
|
|
435
|
push @values, $dimap->[$r][ $c + 1 ] if $c < $maxcol; |
|
85
|
230
|
100
|
|
|
|
404
|
push @values, $dimap->[ $r - 1 ][$c] if $r > 0; |
|
86
|
230
|
100
|
|
|
|
396
|
push @values, $dimap->[ $r + 1 ][$c] if $r < $maxrow; |
|
87
|
230
|
|
|
|
|
517
|
return @values; |
|
88
|
|
|
|
|
|
|
} |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
sub dimap_with { |
|
91
|
2
|
|
|
2
|
1
|
50
|
my ( $self, $param ) = @_; |
|
92
|
2
|
|
|
|
|
7
|
my $dimap = $self->dimap; |
|
93
|
2
|
100
|
|
|
|
16
|
croak "cannot make new dimap from unset map" if !defined $dimap; |
|
94
|
1
|
|
|
|
|
1
|
my $new_dimap; |
|
95
|
1
|
|
|
|
|
4
|
my $badcost = $self->bad_cost; |
|
96
|
1
|
|
|
|
|
3
|
my $cols = $dimap->[0]->$#*; |
|
97
|
1
|
|
|
|
|
4
|
for my $r ( 0 .. $dimap->$#* ) { |
|
98
|
3
|
|
|
|
|
8
|
COL: for my $c ( 0 .. $cols ) { |
|
99
|
9
|
|
|
|
|
11
|
my $value = $dimap->[$r][$c]; |
|
100
|
9
|
50
|
|
|
|
19
|
if ( $value == $badcost ) { |
|
101
|
0
|
|
|
|
|
0
|
$new_dimap->[$r][$c] = $badcost; |
|
102
|
0
|
|
|
|
|
0
|
next COL; |
|
103
|
|
|
|
|
|
|
} |
|
104
|
9
|
|
50
|
|
|
15
|
$value *= $param->{my_weight} // 1; |
|
105
|
9
|
|
|
|
|
29
|
my @here = map $_->values( [ $r, $c ] )->[0], $param->{objs}->@*; |
|
106
|
9
|
|
|
|
|
19
|
for my $h ( 0 .. $#here ) { |
|
107
|
17
|
100
|
|
|
|
44
|
if ( $here[$h] == $badcost ) { |
|
108
|
1
|
|
|
|
|
2
|
$new_dimap->[$r][$c] = $badcost; |
|
109
|
1
|
|
|
|
|
4
|
next COL; |
|
110
|
|
|
|
|
|
|
} |
|
111
|
16
|
|
50
|
|
|
39
|
$value += $here[$h] * ( $param->{weights}->[$h] // 0 ); |
|
112
|
|
|
|
|
|
|
} |
|
113
|
8
|
|
|
|
|
21
|
$new_dimap->[$r][$c] = $value; |
|
114
|
|
|
|
|
|
|
} |
|
115
|
|
|
|
|
|
|
} |
|
116
|
1
|
|
|
|
|
17
|
return $new_dimap; |
|
117
|
|
|
|
|
|
|
} |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
sub map { |
|
120
|
8
|
|
|
8
|
1
|
8883
|
my ( $self, $map ) = @_; |
|
121
|
8
|
|
|
|
|
18
|
my $dimap = []; |
|
122
|
8
|
50
|
66
|
|
|
76
|
croak "no valid map supplied" |
|
|
|
|
66
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
123
|
|
|
|
|
|
|
if !defined $map |
|
124
|
|
|
|
|
|
|
or ref $map ne 'ARRAY' |
|
125
|
|
|
|
|
|
|
or !defined $map->[0] |
|
126
|
|
|
|
|
|
|
or ref $map->[0] ne 'ARRAY'; |
|
127
|
7
|
|
|
|
|
15
|
my $cols = $map->[0]->@*; |
|
128
|
7
|
|
|
|
|
23
|
for my $r ( 0 .. $map->$#* ) { |
|
129
|
21
|
50
|
|
|
|
45
|
croak "unexpected column count at row $r" if $map->[$r]->@* != $cols; |
|
130
|
21
|
|
|
|
|
36
|
for my $c ( 0 .. $cols - 1 ) { |
|
131
|
81
|
|
|
|
|
146
|
$dimap->[$r][$c] = $self->costfn->( $self, $map->[$r][$c] ); |
|
132
|
|
|
|
|
|
|
} |
|
133
|
|
|
|
|
|
|
} |
|
134
|
|
|
|
|
|
|
$self->_set_iters( |
|
135
|
7
|
|
|
|
|
22
|
$self->normfn->( $dimap, $self->min_cost, $self->max_cost ) ); |
|
136
|
7
|
|
|
|
|
27
|
$self->dimap($dimap); |
|
137
|
7
|
|
|
|
|
88
|
return $self; |
|
138
|
|
|
|
|
|
|
} |
|
139
|
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
sub next { |
|
141
|
16
|
|
|
16
|
1
|
7199
|
my ( $self, $r, $c, $value ) = @_; |
|
142
|
16
|
|
|
|
|
33
|
my $dimap = $self->dimap; |
|
143
|
16
|
100
|
|
|
|
62
|
croak "cannot pathfind on unset map" if !defined $dimap; |
|
144
|
13
|
|
|
|
|
21
|
my $maxrow = $dimap->$#*; |
|
145
|
13
|
|
|
|
|
23
|
my $maxcol = $dimap->[0]->$#*; |
|
146
|
13
|
50
|
33
|
|
|
56
|
croak "row $r out of bounds" if $r > $maxrow or $r < 0; |
|
147
|
13
|
50
|
33
|
|
|
44
|
croak "col $c out of bounds" if $c > $maxcol or $c < 0; |
|
148
|
13
|
|
|
|
|
24
|
my @adj; |
|
149
|
13
|
|
100
|
|
|
45
|
$value //= $dimap->[$r][$c]; |
|
150
|
13
|
100
|
|
|
|
33
|
return \@adj if $value <= $self->min_cost; |
|
151
|
|
|
|
|
|
|
|
|
152
|
12
|
|
|
|
|
23
|
for my $i ( -1, 1 ) { |
|
153
|
24
|
|
|
|
|
38
|
my $x = $c + $i; |
|
154
|
24
|
100
|
66
|
|
|
89
|
push @adj, [ [ $r, $x ], $dimap->[$r][$x] ] if $x >= 0 and $x <= $maxcol; |
|
155
|
24
|
|
|
|
|
45
|
for my $j ( -1 .. 1 ) { |
|
156
|
72
|
|
|
|
|
93
|
$x = $r + $i; |
|
157
|
72
|
|
|
|
|
92
|
my $y = $c + $j; |
|
158
|
72
|
100
|
66
|
|
|
334
|
push @adj, [ [ $x, $y ], $dimap->[$x][$y] ] |
|
|
|
|
100
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
159
|
|
|
|
|
|
|
if $x >= 0 |
|
160
|
|
|
|
|
|
|
and $x <= $maxrow |
|
161
|
|
|
|
|
|
|
and $y >= 0 |
|
162
|
|
|
|
|
|
|
and $y <= $maxcol; |
|
163
|
|
|
|
|
|
|
} |
|
164
|
|
|
|
|
|
|
} |
|
165
|
12
|
|
|
|
|
26
|
my $badcost = $self->bad_cost; |
|
166
|
12
|
100
|
|
|
|
21
|
return [ grep { $_->[1] < $value and $_->[1] != $badcost } @adj ]; |
|
|
76
|
|
|
|
|
329
|
|
|
167
|
|
|
|
|
|
|
} |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
sub next_best { |
|
170
|
15
|
|
|
15
|
1
|
704
|
my ( $self, $r, $c ) = @_; |
|
171
|
15
|
|
|
|
|
35
|
my $method = $self->next_m; |
|
172
|
|
|
|
|
|
|
my @ret = |
|
173
|
15
|
|
|
|
|
37
|
sort { $a->[1] <=> $b->[1] } shuffle $self->$method( $r, $c )->@*; |
|
|
2
|
|
|
|
|
7
|
|
|
174
|
13
|
|
|
|
|
128
|
return $ret[0]->[0]; |
|
175
|
|
|
|
|
|
|
} |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
# next() but only in square directions or "orthogonal" (but diagonals |
|
178
|
|
|
|
|
|
|
# are orthogonal to one another) or in the "cardinal directions" (NSEW) |
|
179
|
|
|
|
|
|
|
# but that term also seems unsatisfactory. "4-way" is also used for this |
|
180
|
|
|
|
|
|
|
# with the assumption of cardinal directions |
|
181
|
|
|
|
|
|
|
sub next_sq { |
|
182
|
7
|
|
|
7
|
1
|
762
|
my ( $self, $r, $c, $value ) = @_; |
|
183
|
7
|
|
|
|
|
15
|
my $dimap = $self->dimap; |
|
184
|
7
|
100
|
|
|
|
22
|
croak "cannot pathfind on unset map" if !defined $dimap; |
|
185
|
6
|
|
|
|
|
10
|
my $maxrow = $dimap->$#*; |
|
186
|
6
|
|
|
|
|
12
|
my $maxcol = $dimap->[0]->$#*; |
|
187
|
6
|
50
|
33
|
|
|
23
|
croak "row $r out of bounds" if $r > $maxrow or $r < 0; |
|
188
|
6
|
50
|
33
|
|
|
18
|
croak "col $c out of bounds" if $c > $maxcol or $c < 0; |
|
189
|
6
|
|
|
|
|
9
|
my @adj; |
|
190
|
6
|
|
66
|
|
|
22
|
$value //= $dimap->[$r][$c]; |
|
191
|
6
|
100
|
|
|
|
16
|
return \@adj if $value <= $self->min_cost; |
|
192
|
|
|
|
|
|
|
|
|
193
|
5
|
50
|
|
|
|
10
|
if ( $c > 0 ) { |
|
194
|
5
|
|
|
|
|
15
|
push @adj, [ [ $r, $c - 1 ], $dimap->[$r][ $c - 1 ] ]; |
|
195
|
|
|
|
|
|
|
} |
|
196
|
5
|
50
|
|
|
|
12
|
if ( $c < $maxcol ) { |
|
197
|
5
|
|
|
|
|
13
|
push @adj, [ [ $r, $c + 1 ], $dimap->[$r][ $c + 1 ] ]; |
|
198
|
|
|
|
|
|
|
} |
|
199
|
5
|
50
|
|
|
|
9
|
if ( $r > 0 ) { |
|
200
|
5
|
|
|
|
|
12
|
push @adj, [ [ $r - 1, $c ], $dimap->[ $r - 1 ][$c] ]; |
|
201
|
|
|
|
|
|
|
} |
|
202
|
5
|
50
|
|
|
|
10
|
if ( $r < $maxrow ) { |
|
203
|
5
|
|
|
|
|
11
|
push @adj, [ [ $r + 1, $c ], $dimap->[ $r + 1 ][$c] ]; |
|
204
|
|
|
|
|
|
|
} |
|
205
|
|
|
|
|
|
|
|
|
206
|
5
|
|
|
|
|
12
|
my $badcost = $self->bad_cost; |
|
207
|
5
|
100
|
|
|
|
8
|
return [ grep { $_->[1] < $value and $_->[1] != $badcost } @adj ]; |
|
|
20
|
|
|
|
|
75
|
|
|
208
|
|
|
|
|
|
|
} |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
sub next_with { |
|
211
|
1
|
|
|
1
|
1
|
5
|
my ( $self, $r, $c, $param ) = @_; |
|
212
|
1
|
|
|
|
|
3
|
my $dimap = $self->dimap; |
|
213
|
1
|
50
|
|
|
|
5
|
croak "cannot pathfind on unset map" if !defined $dimap; |
|
214
|
|
|
|
|
|
|
|
|
215
|
1
|
|
|
|
|
3
|
my $badcost = $self->bad_cost; |
|
216
|
|
|
|
|
|
|
|
|
217
|
1
|
|
|
|
|
3
|
my $curcost = $dimap->[$r][$c]; |
|
218
|
1
|
50
|
|
|
|
4
|
return undef if $curcost <= $self->min_cost; |
|
219
|
1
|
|
50
|
|
|
4
|
$curcost *= $param->{my_weight} // 1; |
|
220
|
1
|
|
|
|
|
5
|
my @here = map $_->values( [ $r, $c ] )->[0], $param->{objs}->@*; |
|
221
|
1
|
|
|
|
|
4
|
for my $h ( 0 .. $#here ) { |
|
222
|
|
|
|
|
|
|
# this may cause problems if something is standing on a cell |
|
223
|
|
|
|
|
|
|
# they can no longer move into but where it is still legal for |
|
224
|
|
|
|
|
|
|
# them to leave that cell |
|
225
|
2
|
50
|
|
|
|
16
|
return undef if $here[$h] == $badcost; |
|
226
|
2
|
|
50
|
|
|
8
|
$curcost += $here[$h] * ( $param->{weights}->[$h] // 0 ); |
|
227
|
|
|
|
|
|
|
} |
|
228
|
|
|
|
|
|
|
|
|
229
|
1
|
|
|
|
|
5
|
my $method = $self->next_m; |
|
230
|
1
|
|
|
|
|
6
|
my $coords = $self->$method( $r, $c, $self->max_cost ); |
|
231
|
1
|
50
|
|
|
|
4
|
return undef unless $coords->@*; |
|
232
|
1
|
|
|
|
|
5
|
my @costs = map $_->values( map $_->[0], $coords->@* ), $param->{objs}->@*; |
|
233
|
1
|
|
|
|
|
2
|
my @ret; |
|
234
|
1
|
|
|
|
|
11
|
COORD: for my $p ( 0 .. $coords->$#* ) { |
|
235
|
3
|
|
|
|
|
5
|
my @weights; |
|
236
|
3
|
|
|
|
|
5
|
for my $k ( 0 .. $#costs ) { |
|
237
|
5
|
100
|
|
|
|
14
|
next COORD if $costs[$k][$p] == $badcost; |
|
238
|
4
|
|
50
|
|
|
11
|
push @weights, $costs[$k][$p] * ( $param->{weights}->[$k] // 0 ); |
|
239
|
|
|
|
|
|
|
} |
|
240
|
2
|
|
50
|
|
|
11
|
my $newcost = sum0 $coords->[$p][1] * ( $param->{my_weight} // 1 ), @weights; |
|
241
|
2
|
100
|
|
|
|
8
|
push @ret, [ $coords->[$p][0], $newcost ] if $newcost < $curcost; |
|
242
|
|
|
|
|
|
|
} |
|
243
|
|
|
|
|
|
|
|
|
244
|
1
|
50
|
|
|
|
3
|
return undef unless @ret; |
|
245
|
1
|
|
|
|
|
4
|
@ret = sort { $a->[1] <=> $b->[1] } shuffle @ret; |
|
|
0
|
|
|
|
|
0
|
|
|
246
|
1
|
|
|
|
|
7
|
return $ret[0]->[0]; |
|
247
|
|
|
|
|
|
|
} |
|
248
|
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
# 4-way "square" normalization as seen in the Brogue article (was called |
|
250
|
|
|
|
|
|
|
# normalize_costs and used to be a method). one could possibly also |
|
251
|
|
|
|
|
|
|
# normalize only in the diagonal directions... |
|
252
|
|
|
|
|
|
|
sub norm_4way { |
|
253
|
7
|
|
|
7
|
1
|
16
|
my ( $dimap, $mincost, $maxcost, $avfn ) = @_; |
|
254
|
7
|
|
100
|
|
|
32
|
$avfn //= \&adjacent_values_sq; |
|
255
|
7
|
|
|
|
|
12
|
my $iters = 0; |
|
256
|
7
|
|
|
|
|
12
|
my $maxrow = $dimap->$#*; |
|
257
|
7
|
|
|
|
|
11
|
my $maxcol = $dimap->[0]->$#*; |
|
258
|
7
|
|
|
|
|
9
|
my $stable; |
|
259
|
7
|
|
|
|
|
11
|
while (1) { |
|
260
|
21
|
|
|
|
|
35
|
$stable = 1; |
|
261
|
21
|
|
|
|
|
30
|
$iters++; |
|
262
|
21
|
|
|
|
|
34
|
for my $r ( 0 .. $maxrow ) { |
|
263
|
77
|
|
|
|
|
125
|
for my $c ( 0 .. $maxcol ) { |
|
264
|
403
|
|
|
|
|
529
|
my $value = $dimap->[$r][$c]; |
|
265
|
403
|
100
|
|
|
|
640
|
next if $value <= $mincost; |
|
266
|
182
|
|
|
|
|
217
|
my $best = $maxcost; |
|
267
|
182
|
|
|
|
|
263
|
for my $nv ( $avfn->( $dimap, $r, $c, $maxrow, $maxcol ) ) { |
|
268
|
532
|
100
|
100
|
|
|
1284
|
$best = $nv if $nv < $best and $nv >= $mincost; |
|
269
|
532
|
100
|
|
|
|
946
|
last if $best == $mincost; |
|
270
|
|
|
|
|
|
|
} |
|
271
|
182
|
100
|
|
|
|
340
|
if ( $value > $best + 2 ) { |
|
272
|
34
|
|
|
|
|
53
|
$dimap->[$r][$c] = $best + 1; |
|
273
|
34
|
|
|
|
|
43
|
$stable = 0; |
|
274
|
|
|
|
|
|
|
} |
|
275
|
|
|
|
|
|
|
} |
|
276
|
|
|
|
|
|
|
} |
|
277
|
21
|
100
|
|
|
|
39
|
last if $stable; |
|
278
|
|
|
|
|
|
|
} |
|
279
|
7
|
|
|
|
|
48
|
return $iters; |
|
280
|
|
|
|
|
|
|
} |
|
281
|
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
# 8-way normalization could either be done with small integers where |
|
283
|
|
|
|
|
|
|
# diagonals cost the same as square motion (this is non-Euclidean though |
|
284
|
|
|
|
|
|
|
# traditional in roguelikes) ... |
|
285
|
|
|
|
|
|
|
sub norm_8way { |
|
286
|
1
|
|
|
1
|
1
|
4
|
push @_, \&adjacent_values; |
|
287
|
1
|
|
|
|
|
12
|
&norm_4way; # perldoc perlsub explains this calling form |
|
288
|
|
|
|
|
|
|
} |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
# ... or one could instead use floating point values to better |
|
291
|
|
|
|
|
|
|
# approximate diagonals costing sqrt(2) but this is more complicated, |
|
292
|
|
|
|
|
|
|
# which is perhaps why many roguelikes use 4-way or non-Euclidean 8-way |
|
293
|
|
|
|
|
|
|
sub norm_8way_euclid { |
|
294
|
2
|
|
|
2
|
1
|
5
|
my ( $dimap, $mincost, $maxcost ) = @_; |
|
295
|
2
|
|
|
|
|
5
|
my $iters = 0; |
|
296
|
2
|
|
|
|
|
3
|
my $maxrow = $dimap->$#*; |
|
297
|
2
|
|
|
|
|
4
|
my $maxcol = $dimap->[0]->$#*; |
|
298
|
2
|
|
|
|
|
4
|
my $stable; |
|
299
|
2
|
|
|
|
|
3
|
while (1) { |
|
300
|
5
|
|
|
|
|
8
|
$stable = 1; |
|
301
|
5
|
|
|
|
|
6
|
$iters++; |
|
302
|
5
|
|
|
|
|
11
|
for my $r ( 0 .. $maxrow ) { |
|
303
|
16
|
|
|
|
|
25
|
for my $c ( 0 .. $maxcol ) { |
|
304
|
56
|
|
|
|
|
80
|
my $value = $dimap->[$r][$c]; |
|
305
|
56
|
100
|
|
|
|
101
|
next if $value <= $mincost; |
|
306
|
47
|
|
|
|
|
76
|
my $best = [ $maxcost, 0 ]; |
|
307
|
47
|
|
|
|
|
78
|
for my $nr ( |
|
308
|
|
|
|
|
|
|
map( [ $_, 1 ], adjacent_values_sq( $dimap, $r, $c, $maxrow, $maxcol ) ), |
|
309
|
|
|
|
|
|
|
map( [ $_, SQRT2 ], adjacent_values_diag( $dimap, $r, $c, $maxrow, $maxcol ) ) |
|
310
|
|
|
|
|
|
|
) { |
|
311
|
186
|
100
|
100
|
|
|
443
|
$best = $nr if $nr->[0] < $best->[0] and $nr->[0] >= $mincost; |
|
312
|
186
|
100
|
|
|
|
333
|
last if $best->[0] == $mincost; |
|
313
|
|
|
|
|
|
|
} |
|
314
|
|
|
|
|
|
|
# TODO should this be + 2 like the others or is + SQRT2 |
|
315
|
|
|
|
|
|
|
# a better check? |
|
316
|
47
|
100
|
|
|
|
182
|
if ( $value > $best->[0] + SQRT2 ) { |
|
317
|
17
|
|
|
|
|
29
|
$dimap->[$r][$c] = $best->[0] + $best->[1]; |
|
318
|
17
|
|
|
|
|
33
|
$stable = 0; |
|
319
|
|
|
|
|
|
|
} |
|
320
|
|
|
|
|
|
|
} |
|
321
|
|
|
|
|
|
|
} |
|
322
|
5
|
100
|
|
|
|
9
|
last if $stable; |
|
323
|
|
|
|
|
|
|
} |
|
324
|
2
|
|
|
|
|
7
|
return $iters; |
|
325
|
|
|
|
|
|
|
} |
|
326
|
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
sub path_best { |
|
328
|
3
|
|
|
3
|
1
|
715
|
my ( $self, $r, $c, $method ) = @_; |
|
329
|
3
|
|
|
|
|
6
|
my @path; |
|
330
|
3
|
|
|
|
|
9
|
while ( my $next = $self->next_best( $r, $c, $method ) ) { |
|
331
|
7
|
|
|
|
|
13
|
push @path, $next; |
|
332
|
7
|
|
|
|
|
17
|
( $r, $c ) = @$next; |
|
333
|
|
|
|
|
|
|
} |
|
334
|
2
|
|
|
|
|
24
|
return \@path; |
|
335
|
|
|
|
|
|
|
} |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
sub recalc { |
|
338
|
3
|
|
|
3
|
1
|
2464
|
my ($self) = @_; |
|
339
|
3
|
|
|
|
|
9
|
my $dimap = $self->dimap; |
|
340
|
3
|
100
|
|
|
|
16
|
croak "cannot recalc unset map" if !defined $dimap; |
|
341
|
2
|
|
|
|
|
6
|
my $maxcost = $self->max_cost; |
|
342
|
2
|
|
|
|
|
4
|
my $mincost = $self->min_cost; |
|
343
|
2
|
|
|
|
|
5
|
my $maxcol = $dimap->[0]->$#*; |
|
344
|
2
|
|
|
|
|
6
|
for my $r ( 0 .. $dimap->$#* ) { |
|
345
|
7
|
|
|
|
|
14
|
for my $c ( 0 .. $maxcol ) { |
|
346
|
39
|
100
|
|
|
|
70
|
$dimap->[$r][$c] = $maxcost if $dimap->[$r][$c] > $mincost; |
|
347
|
|
|
|
|
|
|
} |
|
348
|
|
|
|
|
|
|
} |
|
349
|
2
|
|
|
|
|
6
|
$self->_set_iters( $self->normfn->( $dimap, $mincost, $maxcost ) ); |
|
350
|
2
|
|
|
|
|
16
|
$self->dimap($dimap); |
|
351
|
2
|
|
|
|
|
8
|
return $self; |
|
352
|
|
|
|
|
|
|
} |
|
353
|
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
sub str2map { |
|
355
|
6
|
|
|
6
|
1
|
862
|
my ( $self_or_class, $str, $lf ) = @_; |
|
356
|
6
|
50
|
|
|
|
16
|
croak "no string given" if !defined $str; |
|
357
|
6
|
|
33
|
|
|
32
|
$lf //= $/; |
|
358
|
6
|
|
|
|
|
9
|
my @map; |
|
359
|
6
|
|
|
|
|
85
|
for my $line ( split $lf, $str ) { |
|
360
|
18
|
|
|
|
|
78
|
push @map, [ split //, $line ]; |
|
361
|
|
|
|
|
|
|
} |
|
362
|
6
|
|
|
|
|
24
|
return \@map; |
|
363
|
|
|
|
|
|
|
} |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
sub to_tsv { |
|
366
|
3
|
|
|
3
|
1
|
1561
|
my ( $self, $ref ) = @_; |
|
367
|
3
|
100
|
|
|
|
10
|
if ( !defined $ref ) { |
|
368
|
2
|
|
|
|
|
7
|
$ref = $self->dimap; |
|
369
|
2
|
100
|
|
|
|
12
|
croak "cannot use an unset map" if !defined $ref; |
|
370
|
|
|
|
|
|
|
} |
|
371
|
2
|
|
|
|
|
5
|
my $s = ''; |
|
372
|
2
|
|
|
|
|
4
|
my $cols = $ref->[0]->$#*; |
|
373
|
2
|
|
|
|
|
7
|
for my $r ( 0 .. $ref->$#* ) { |
|
374
|
5
|
|
|
|
|
9
|
my $d = "\t"; |
|
375
|
5
|
|
|
|
|
9
|
for my $c ( 0 .. $cols ) { |
|
376
|
13
|
|
|
|
|
24
|
$s .= $ref->[$r][$c] . $d; |
|
377
|
13
|
100
|
|
|
|
29
|
$d = '' if $c == $cols - 1; |
|
378
|
|
|
|
|
|
|
} |
|
379
|
5
|
|
|
|
|
10
|
$s .= $/; |
|
380
|
|
|
|
|
|
|
} |
|
381
|
2
|
|
|
|
|
16
|
return $s; |
|
382
|
|
|
|
|
|
|
} |
|
383
|
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
sub unconnected { |
|
385
|
3
|
|
|
3
|
1
|
975
|
my ($self) = @_; |
|
386
|
3
|
|
|
|
|
9
|
my $dimap = $self->dimap; |
|
387
|
3
|
100
|
|
|
|
17
|
croak "nothing unconnected on unset map" if !defined $dimap; |
|
388
|
2
|
|
|
|
|
3
|
my @points; |
|
389
|
2
|
|
|
|
|
5
|
my $maxcost = $self->max_cost; |
|
390
|
2
|
|
|
|
|
5
|
my $maxcol = $dimap->[0]->$#*; |
|
391
|
2
|
|
|
|
|
7
|
for my $r ( 0 .. $dimap->$#* ) { |
|
392
|
7
|
|
|
|
|
12
|
for my $c ( 0 .. $maxcol ) { |
|
393
|
39
|
100
|
|
|
|
75
|
push @points, [ $r, $c ] if $dimap->[$r][$c] == $maxcost; |
|
394
|
|
|
|
|
|
|
} |
|
395
|
|
|
|
|
|
|
} |
|
396
|
2
|
|
|
|
|
17
|
return \@points; |
|
397
|
|
|
|
|
|
|
} |
|
398
|
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
sub update { |
|
400
|
2
|
|
|
2
|
1
|
710
|
my $self = shift; |
|
401
|
2
|
|
|
|
|
7
|
my $dimap = $self->dimap; |
|
402
|
2
|
100
|
|
|
|
15
|
croak "cannot update unset map" if !defined $dimap; |
|
403
|
1
|
|
|
|
|
2
|
my $maxrow = $dimap->$#*; |
|
404
|
1
|
|
|
|
|
3
|
my $maxcol = $dimap->[0]->$#*; |
|
405
|
1
|
|
|
|
|
3
|
for my $ref (@_) { |
|
406
|
1
|
|
|
|
|
3
|
my ( $r, $c ) = ( $ref->[0], $ref->[1] ); |
|
407
|
1
|
50
|
33
|
|
|
8
|
croak "row $r out of bounds" if $r > $maxrow or $r < 0; |
|
408
|
1
|
50
|
33
|
|
|
9
|
croak "col $c out of bounds" if $c > $maxcol or $c < 0; |
|
409
|
1
|
50
|
|
|
|
23
|
croak "value must be a number" unless looks_like_number $ref->[2]; |
|
410
|
1
|
|
|
|
|
5
|
$dimap->[$r][$c] = int $ref->[2]; |
|
411
|
|
|
|
|
|
|
} |
|
412
|
1
|
|
|
|
|
5
|
$self->dimap($dimap); |
|
413
|
1
|
|
|
|
|
3
|
return $self; |
|
414
|
|
|
|
|
|
|
} |
|
415
|
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
sub values { |
|
417
|
24
|
|
|
24
|
1
|
692
|
my $self = shift; |
|
418
|
24
|
|
|
|
|
46
|
my $dimap = $self->dimap; |
|
419
|
24
|
100
|
|
|
|
53
|
croak "cannot get values from unset map" if !defined $dimap; |
|
420
|
23
|
|
|
|
|
32
|
my @values; |
|
421
|
23
|
|
|
|
|
32
|
my $maxrow = $dimap->$#*; |
|
422
|
23
|
|
|
|
|
34
|
my $maxcol = $dimap->[0]->$#*; |
|
423
|
23
|
|
|
|
|
39
|
for my $point (@_) { |
|
424
|
29
|
|
|
|
|
51
|
my ( $r, $c ) = ( $point->[0], $point->[1] ); |
|
425
|
29
|
50
|
33
|
|
|
101
|
croak "row $r out of bounds" if $r > $maxrow or $r < 0; |
|
426
|
29
|
50
|
33
|
|
|
105
|
croak "col $c out of bounds" if $c > $maxcol or $c < 0; |
|
427
|
29
|
|
|
|
|
63
|
push @values, $dimap->[$r][$c]; |
|
428
|
|
|
|
|
|
|
} |
|
429
|
23
|
|
|
|
|
114
|
return \@values; |
|
430
|
|
|
|
|
|
|
} |
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
1; |
|
433
|
|
|
|
|
|
|
__END__ |