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   503 use Game::TextMapper::Point;
  1         2  
  1         7  
45 1     1   33 use Modern::Perl '2018';
  1         2  
  1         5  
46 1     1   115 use Mojo::Base -role;
  1         2  
  1         5  
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 43042     43042 0 59974 my ($x, $y) = @_;
55 43042         70341 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 58607     58607 1 66033 my $self = shift;
69 58607         67071 my $coordinates = shift;
70 58607         153084 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 42463     42463 1 51125 my $self = shift;
86 42463         54100 my ($x, $y) = @_;
87 42463 100       65109 ($x, $y) = $self->xy($x) if not defined $y;
88 42463 100 100     90808 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 32 my $self = shift;
104 20         66 my ($limit, @hexes) = @_;
105 20         38 my @filtered;
106             HEX:
107 20         45 for my $hex (@hexes) {
108 428         603 my ($x1, $y1) = $self->xy($hex);
109             # check distances with all the hexes already in the list
110 428         612 for my $existing (@filtered) {
111 4098         5417 my ($x2, $y2) = $self->xy($existing);
112 4098         6987 my $distance = $self->distance($x1, $y1, $x2, $y2);
113             # warn "Distance between $x1$y1 and $x2$y2 is $distance\n";
114 4098 100       7095 next HEX if $distance < $limit;
115             }
116             # if this hex wasn't skipped, it goes on to the list
117 122         197 push(@filtered, $hex);
118             }
119 20         88 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 10 my $self = shift;
135 4         9 my ($altitude) = @_;
136 4         13 for my $y (1 .. $self->height) {
137 50         120 for my $x (1 .. $self->width) {
138 1800         2609 my $coordinates = coordinates($x, $y);
139 1800         3818 $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 75     75 1 112 my $self = shift;
155 75         117 my ($from, $to) = @_;
156 75         146 for my $i ($self->neighbors()) {
157 196 100       448 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;