File Coverage

blib/lib/Game/TextMapper/Schroeder/Base.pm
Criterion Covered Total %
statement 39 39 100.0
branch 8 8 100.0
condition 9 9 100.0
subroutine 9 9 100.0
pod 5 6 83.3
total 70 71 98.5


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 11     11   9117 use Game::TextMapper::Point;
  11         40  
  11         176  
45 11     11   1335 use Modern::Perl '2018';
  11         29  
  11         120  
46 11     11   4762 use Mojo::Base -role;
  11         47  
  11         137  
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 84517     84517 0 146630 my ($x, $y) = @_;
55 84517         187889 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 111031     111031 1 160136 my $self = shift;
69 111031         172562 my $coordinates = shift;
70 111031         401428 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 82036     82036 1 122234 my $self = shift;
86 82036         141888 my ($x, $y) = @_;
87 82036 100       155741 ($x, $y) = $self->xy($x) if not defined $y;
88 82036 100 100     213922 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 40     40 1 92 my $self = shift;
104 40         205 my ($limit, @hexes) = @_;
105 40         73 my @filtered;
106             HEX:
107 40         110 for my $hex (@hexes) {
108 728         1509 my ($x1, $y1) = $self->xy($hex);
109             # check distances with all the hexes already in the list
110 728         1311 for my $existing (@filtered) {
111 6466         16797 my ($x2, $y2) = $self->xy($existing);
112 6466         17232 my $distance = $self->distance($x1, $y1, $x2, $y2);
113             # warn "Distance between $x1$y1 and $x2$y2 is $distance\n";
114 6466 100       27032 next HEX if $distance < $limit;
115             }
116             # if this hex wasn't skipped, it goes on to the list
117 221         588 push(@filtered, $hex);
118             }
119 40         286 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 8     8 1 21 my $self = shift;
135 8         23 my ($altitude) = @_;
136 8         55 for my $y (1 .. $self->height) {
137 100         434 for my $x (1 .. $self->width) {
138 3600         6647 my $coordinates = coordinates($x, $y);
139 3600         9187 $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 277     277 1 477 my $self = shift;
155 277         586 my ($from, $to) = @_;
156 277         759 for my $i ($self->neighbors()) {
157 918 100       2686 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;