File Coverage

blib/lib/Game/TextMapper/Schroeder/Hex.pm
Criterion Covered Total %
statement 39 43 90.7
branch 12 16 75.0
condition n/a
subroutine 11 12 91.6
pod 9 9 100.0
total 71 80 88.7


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::Hex - a role for hex map generators
21              
22             =head1 SYNOPSIS
23              
24             # create a map
25             package World;
26             use Modern::Perl;
27             use Mojo::Base -base;
28             use Role::Tiny::With;
29             with 'Game::TextMapper::Schroeder::Base';
30             with 'Game::TextMapper::Schroeder::Hex';
31             # use it
32             package main;
33             my $map = World->new(height => 10, width => 10);
34              
35             =head1 DESCRIPTION
36              
37             This role provides basic functionality for map generation with hex maps: the
38             number of neighbours within one or two regions distance, how to pick a random
39             neighbour direction, how to compute the coordinates of these neighbours, how to
40             draw arrows towards these neighbours.
41              
42             This inherits attributes and methods from L,
43             such as C and C.
44              
45             =cut
46              
47             package Game::TextMapper::Schroeder::Hex;
48 11     11   6871 use Modern::Perl '2018';
  11         26  
  11         79  
49 11     11   3364 use Mojo::Base -role;
  11         59  
  11         83  
50 11     11   6311 use POSIX qw(ceil);
  11         31  
  11         120  
51              
52             =head1 METHODS
53              
54             =head2 reverse
55              
56             Reverses a direction.
57              
58             =cut
59              
60             sub reverse {
61 2     2 1 8 my ($self, $i) = @_;
62 2         12 return ($i + 3) % 6;
63             }
64              
65             =head2 neighbors
66              
67             The list of directions for neighbours one step away (0 to 5).
68              
69             =cut
70              
71 5774     5774 1 16214 sub neighbors { 0 .. 5 }
72              
73             =head2 neighbors2
74              
75             The list of directions for neighbours two steps away (0 to 11).
76              
77             =cut
78              
79 266     266 1 864 sub neighbors2 { 0 .. 11 }
80              
81             =head2 random_neighbor
82              
83             A random direction for a neighbour one step away (a random integer from 0 to 5).
84              
85             =cut
86              
87 8572     8572 1 24468 sub random_neighbor { int(rand(6)) }
88              
89             =head2 random_neighbor2
90              
91             A random direction for a neighbour two steps away (a random integer from 0 to
92             11).
93              
94             =cut
95              
96 7424     7424 1 21693 sub random_neighbor2 { int(rand(12)) }
97              
98             my $delta_hex = [
99             # x is even
100             [[-1, 0], [ 0, -1], [+1, 0], [+1, +1], [ 0, +1], [-1, +1]],
101             # x is odd
102             [[-1, -1], [ 0, -1], [+1, -1], [+1, 0], [ 0, +1], [-1, 0]]];
103              
104             =head2 neighbor($hex, $i)
105              
106             say join(",", $map->neighbor("0203", 1));
107             # 2,2
108              
109             Returns the coordinates of a neighbor in a particular direction (0 to 5), one
110             step away.
111              
112             C<$hex> is an array reference of coordinates or a string that can be turned into
113             one using the C method from L.
114              
115             C<$i> is a direction (0 to 5).
116              
117             =cut
118              
119             sub neighbor {
120 35712     35712 1 54935 my $self = shift;
121             # $hex is [x,y] or "0x0y" and $i is a number 0 .. 5
122 35712         65636 my ($hex, $i) = @_;
123 35712 50       70915 die join(":", caller) . ": undefined direction for $hex\n" unless defined $i;
124 35712 50       96339 $hex = [$self->xy($hex)] unless ref $hex;
125 35712         156253 return ($hex->[0] + $delta_hex->[$hex->[0] % 2]->[$i]->[0],
126             $hex->[1] + $delta_hex->[$hex->[0] % 2]->[$i]->[1]);
127             }
128              
129             my $delta_hex2 = [
130             # x is even
131             [[-2, +1], [-2, 0], [-2, -1], [-1, -1], [ 0, -2], [+1, -1],
132             [+2, -1], [+2, 0], [+2, +1], [+1, +2], [ 0, +2], [-1, +2]],
133             # x is odd
134             [[-2, +1], [-2, 0], [-2, -1], [-1, -2], [ 0, -2], [+1, -2],
135             [+2, -1], [+2, 0], [+2, +1], [+1, +1], [ 0, +2], [-1, +1]]];
136              
137             =head2 neighbor2($hex, $i)
138              
139             say join(",", $map->neighbor2("0203", 1));
140             # 0, 3
141              
142             Returns the coordinates of a neighbor in a particular direction (0 to 11), two
143             steps away.
144              
145             C<$hex> is an array reference of coordinates or a string that can be turned into
146             one using the C method from L.
147              
148             C<$i> is a direction (0 to 5).
149              
150             =cut
151              
152             sub neighbor2 {
153 10616     10616 1 17798 my $self = shift;
154             # $hex is [x,y] or "0x0y" and $i is a number 0 .. 11
155 10616         21493 my ($hex, $i) = @_;
156 10616 50       22097 die join(":", caller) . ": undefined direction for $hex\n" unless defined $i;
157 10616 50       32749 $hex = [$self->xy($hex)] unless ref $hex;
158 10616         50640 return ($hex->[0] + $delta_hex2->[$hex->[0] % 2]->[$i]->[0],
159             $hex->[1] + $delta_hex2->[$hex->[0] % 2]->[$i]->[1]);
160             }
161              
162             =head2 distance($x1, $y1, $x2, $y2) or distance($coords1, $coords2)
163              
164             say $map->distance("0203", "0003");
165             # 2
166              
167             Returns the distance between two coordinates.
168              
169             =cut
170              
171             sub distance {
172 12311     12311 1 23546 my $self = shift;
173 12311         29479 my ($x1, $y1, $x2, $y2) = @_;
174 12311 100       25315 if (@_ == 2) {
175 5398         9975 ($x1, $y1, $x2, $y2) = map { $self->xy($_) } @_;
  10796         22996  
176             }
177             # transform the coordinate system into a decent system with one axis tilted by
178             # 60°
179 12311         35449 $y1 = $y1 - POSIX::ceil($x1/2);
180 12311         26388 $y2 = $y2 - POSIX::ceil($x2/2);
181 12311 100       25352 if ($x1 > $x2) {
182             # only consider moves from left to right and transpose start and
183             # end point to make it so
184 5472         10604 my ($t1, $t2) = ($x1, $y1);
185 5472         9882 ($x1, $y1) = ($x2, $y2);
186 5472         10567 ($x2, $y2) = ($t1, $t2);
187             }
188 12311 100       22901 if ($y2>=$y1) {
189             # if it the move has a downwards component add Δx and Δy
190 3991         11133 return $x2-$x1 + $y2-$y1;
191             } else {
192             # else just take the larger of Δx and Δy
193 8320 100       24911 return $x2-$x1 > $y1-$y2 ? $x2-$x1 : $y1-$y2;
194             }
195             }
196              
197             =head2 arrows
198              
199             A helper that returns the SVG fragments for arrows in six directions, to be used
200             in a C element.
201              
202             =cut
203              
204             sub arrows {
205 0     0 1   my $self = shift;
206             return
207             qq{},
208             qq{},
209             map {
210 0           my $angle = 60 * $_;
  0            
211 0           qq{},
212             qq{},
213             } ($self->neighbors());
214             }
215              
216             =head1 SEE ALSO
217              
218             L
219             L
220              
221             =cut
222              
223             1;