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   6926 use Game::TextMapper::Point;
  11         30  
  11         145  
45 11     11   503 use Modern::Perl '2018';
  11         24  
  11         94  
46 11     11   3651 use Mojo::Base -role;
  11         26  
  11         92  
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 82907     82907 0 110660 my ($x, $y) = @_;
55 82907         137246 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 108231     108231 1 122534 my $self = shift;
69 108231         122422 my $coordinates = shift;
70 108231         271895 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 82208     82208 1 93728 my $self = shift;
86 82208         105977 my ($x, $y) = @_;
87 82208 100       119395 ($x, $y) = $self->xy($x) if not defined $y;
88 82208 100 100     178930 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 65 my $self = shift;
104 40         143 my ($limit, @hexes) = @_;
105 40         68 my @filtered;
106             HEX:
107 40         82 for my $hex (@hexes) {
108 695         1087 my ($x1, $y1) = $self->xy($hex);
109             # check distances with all the hexes already in the list
110 695         921 for my $existing (@filtered) {
111 6268         8698 my ($x2, $y2) = $self->xy($existing);
112 6268         10475 my $distance = $self->distance($x1, $y1, $x2, $y2);
113             # warn "Distance between $x1$y1 and $x2$y2 is $distance\n";
114 6268 100       10411 next HEX if $distance < $limit;
115             }
116             # if this hex wasn't skipped, it goes on to the list
117 222         379 push(@filtered, $hex);
118             }
119 40         220 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 17 my $self = shift;
135 8         26 my ($altitude) = @_;
136 8         31 for my $y (1 .. $self->height) {
137 100         400 for my $x (1 .. $self->width) {
138 3600         5249 my $coordinates = coordinates($x, $y);
139 3600         7616 $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 219     219 1 337 my $self = shift;
155 219         451 my ($from, $to) = @_;
156 219         612 for my $i ($self->neighbors()) {
157 631 100       1469 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;