| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
# Copyright (C) 2009-2021 Alex Schroeder |
|
2
|
|
|
|
|
|
|
# |
|
3
|
|
|
|
|
|
|
# This program is free software: you can redistribute it and/or modify it under |
|
4
|
|
|
|
|
|
|
# the terms of the GNU Affero General Public License as published by the Free |
|
5
|
|
|
|
|
|
|
# Software Foundation, either version 3 of the License, or (at your option) any |
|
6
|
|
|
|
|
|
|
# later version. |
|
7
|
|
|
|
|
|
|
# |
|
8
|
|
|
|
|
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT |
|
9
|
|
|
|
|
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
|
10
|
|
|
|
|
|
|
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more |
|
11
|
|
|
|
|
|
|
# details. |
|
12
|
|
|
|
|
|
|
# |
|
13
|
|
|
|
|
|
|
# You should have received a copy of the GNU Affero General Public License along |
|
14
|
|
|
|
|
|
|
# with this program. If not, see . |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=encoding utf8 |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head1 NAME |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
Game::TextMapper::Schroeder::Base - a base role for map generators |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# create a map |
|
25
|
|
|
|
|
|
|
package World; |
|
26
|
|
|
|
|
|
|
use Modern::Perl; |
|
27
|
|
|
|
|
|
|
use Game::TextMapper::Schroeder::Base; |
|
28
|
|
|
|
|
|
|
use Mojo::Base -base; |
|
29
|
|
|
|
|
|
|
use Role::Tiny::With; |
|
30
|
|
|
|
|
|
|
with 'Game::TextMapper::Schroeder::Base'; |
|
31
|
|
|
|
|
|
|
# use it |
|
32
|
|
|
|
|
|
|
package main; |
|
33
|
|
|
|
|
|
|
my $map = World->new(height => 10, width => 10); |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
Map generators that work for both hex maps and square maps use this role and |
|
38
|
|
|
|
|
|
|
either the Hex or Square role to provide basic functionality for their regions, |
|
39
|
|
|
|
|
|
|
such as the number of neighbours they have (six or four). |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=cut |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
package Game::TextMapper::Schroeder::Base; |
|
44
|
1
|
|
|
1
|
|
775
|
use Game::TextMapper::Point; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
11
|
|
|
45
|
1
|
|
|
1
|
|
39
|
use Modern::Perl '2018'; |
|
|
1
|
|
|
|
|
4
|
|
|
|
1
|
|
|
|
|
12
|
|
|
46
|
1
|
|
|
1
|
|
254
|
use Mojo::Base -role; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
11
|
|
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# We're assuming that $width and $height have two digits (10 <= n <= 99). |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
has width => 30; |
|
51
|
|
|
|
|
|
|
has height => 10; |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
sub coordinates { |
|
54
|
41951
|
|
|
41951
|
0
|
54583
|
my ($x, $y) = @_; |
|
55
|
41951
|
|
|
|
|
65160
|
return Game::TextMapper::Point::coord($x, $y); |
|
56
|
|
|
|
|
|
|
} |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=head1 METHODS |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
=head2 xy($coordinates) |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
C<$coordinates> is a string with four digites and interpreted as coordinates and |
|
63
|
|
|
|
|
|
|
returned, e.g. returns (2, 3) for "0203". |
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=cut |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub xy { |
|
68
|
56117
|
|
|
56117
|
1
|
66056
|
my $self = shift; |
|
69
|
56117
|
|
|
|
|
63508
|
my $coordinates = shift; |
|
70
|
56117
|
|
|
|
|
149934
|
return (substr($coordinates, 0, 2), substr($coordinates, 2)); |
|
71
|
|
|
|
|
|
|
} |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=head2 legal($x, $y) or $legal($coordinates) |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
say "legal" if $map->legal(10,10); |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
Turn $coordinates into ($x, $y), assuming each to be two digits, i.e. "0203" |
|
78
|
|
|
|
|
|
|
turns into (2, 3). |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
Return ($x, $y) if the coordinates are legal, i.e. on the map. |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=cut |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
sub legal { |
|
85
|
40631
|
|
|
40631
|
1
|
48866
|
my $self = shift; |
|
86
|
40631
|
|
|
|
|
51315
|
my ($x, $y) = @_; |
|
87
|
40631
|
100
|
|
|
|
60114
|
($x, $y) = $self->xy($x) if not defined $y; |
|
88
|
40631
|
100
|
100
|
|
|
85322
|
return @_ if $x > 0 and $x <= $self->width and $y > 0 and $y <= $self->height; |
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
89
|
|
|
|
|
|
|
} |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=head2 remove_closer_than($limit, @coordinates) |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
Each element of @coordinates is a string with four digites and interpreted as |
|
94
|
|
|
|
|
|
|
coordinates, e.g. "0203" is treated as (2, 3). Returns a list where each element |
|
95
|
|
|
|
|
|
|
is no closer than $limit to any existing element. |
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
This depends on L being used as a role by a |
|
98
|
|
|
|
|
|
|
class that implements C. |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=cut |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
sub remove_closer_than { |
|
103
|
20
|
|
|
20
|
1
|
46
|
my $self = shift; |
|
104
|
20
|
|
|
|
|
66
|
my ($limit, @hexes) = @_; |
|
105
|
20
|
|
|
|
|
27
|
my @filtered; |
|
106
|
|
|
|
|
|
|
HEX: |
|
107
|
20
|
|
|
|
|
56
|
for my $hex (@hexes) { |
|
108
|
381
|
|
|
|
|
566
|
my ($x1, $y1) = $self->xy($hex); |
|
109
|
|
|
|
|
|
|
# check distances with all the hexes already in the list |
|
110
|
381
|
|
|
|
|
556
|
for my $existing (@filtered) { |
|
111
|
4290
|
|
|
|
|
5836
|
my ($x2, $y2) = $self->xy($existing); |
|
112
|
4290
|
|
|
|
|
7507
|
my $distance = $self->distance($x1, $y1, $x2, $y2); |
|
113
|
|
|
|
|
|
|
# warn "Distance between $x1$y1 and $x2$y2 is $distance\n"; |
|
114
|
4290
|
100
|
|
|
|
7818
|
next HEX if $distance < $limit; |
|
115
|
|
|
|
|
|
|
} |
|
116
|
|
|
|
|
|
|
# if this hex wasn't skipped, it goes on to the list |
|
117
|
124
|
|
|
|
|
317
|
push(@filtered, $hex); |
|
118
|
|
|
|
|
|
|
} |
|
119
|
20
|
|
|
|
|
123
|
return @filtered; |
|
120
|
|
|
|
|
|
|
} |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=head2 flat($altitude) |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
my $altitude = {}; |
|
125
|
|
|
|
|
|
|
$map->flat($altitude); |
|
126
|
|
|
|
|
|
|
say $altitude->{"0203"}; |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
Initialize the altitude map; this is required so that we have a list of legal |
|
129
|
|
|
|
|
|
|
hex coordinates somewhere. |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=cut |
|
132
|
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
sub flat { |
|
134
|
4
|
|
|
4
|
1
|
11
|
my $self = shift; |
|
135
|
4
|
|
|
|
|
9
|
my ($altitude) = @_; |
|
136
|
4
|
|
|
|
|
17
|
for my $y (1 .. $self->height) { |
|
137
|
50
|
|
|
|
|
138
|
for my $x (1 .. $self->width) { |
|
138
|
1800
|
|
|
|
|
2478
|
my $coordinates = coordinates($x, $y); |
|
139
|
1800
|
|
|
|
|
4101
|
$altitude->{$coordinates} = 0; |
|
140
|
|
|
|
|
|
|
} |
|
141
|
|
|
|
|
|
|
} |
|
142
|
|
|
|
|
|
|
} |
|
143
|
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=head2 direction($from, $to) |
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
Return the direction (an integer) to step from C<$from> to reach C<$to>. |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
This depends on L being used as a role by a |
|
149
|
|
|
|
|
|
|
class that implements C and C. |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=cut |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
sub direction { |
|
154
|
136
|
|
|
136
|
1
|
170
|
my $self = shift; |
|
155
|
136
|
|
|
|
|
224
|
my ($from, $to) = @_; |
|
156
|
136
|
|
|
|
|
249
|
for my $i ($self->neighbors()) { |
|
157
|
347
|
100
|
|
|
|
634
|
return $i if $to eq coordinates($self->neighbor($from, $i)); |
|
158
|
|
|
|
|
|
|
} |
|
159
|
|
|
|
|
|
|
} |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
L and L |
|
164
|
|
|
|
|
|
|
both use this class to provide common functionality. |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=cut |
|
167
|
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
1; |