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 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;