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   7856 use Game::TextMapper::Point;
  11         34  
  11         132  
45 11     11   598 use Modern::Perl '2018';
  11         28  
  11         92  
46 11     11   3854 use Mojo::Base -role;
  11         27  
  11         89  
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 82868     82868 0 134166 my ($x, $y) = @_;
55 82868         174719 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 111738     111738 1 165246 my $self = shift;
69 111738         163000 my $coordinates = shift;
70 111738         375651 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 82664     82664 1 118090 my $self = shift;
86 82664         130070 my ($x, $y) = @_;
87 82664 100       155225 ($x, $y) = $self->xy($x) if not defined $y;
88 82664 100 100     226440 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 111 my $self = shift;
104 40         181 my ($limit, @hexes) = @_;
105 40         95 my @filtered;
106             HEX:
107 40         106 for my $hex (@hexes) {
108 726         1717 my ($x1, $y1) = $self->xy($hex);
109             # check distances with all the hexes already in the list
110 726         1551 for my $existing (@filtered) {
111 7326         14936 my ($x2, $y2) = $self->xy($existing);
112 7326         18784 my $distance = $self->distance($x1, $y1, $x2, $y2);
113             # warn "Distance between $x1$y1 and $x2$y2 is $distance\n";
114 7326 100       18405 next HEX if $distance < $limit;
115             }
116             # if this hex wasn't skipped, it goes on to the list
117 239         661 push(@filtered, $hex);
118             }
119 40         329 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         139 my ($altitude) = @_;
136 8         37 for my $y (1 .. $self->height) {
137 100         397 for my $x (1 .. $self->width) {
138 3600         5238 my $coordinates = coordinates($x, $y);
139 3600         7425 $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 215     215 1 309 my $self = shift;
155 215         370 my ($from, $to) = @_;
156 215         480 for my $i ($self->neighbors()) {
157 652 100       1436 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;