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 1     1   886 use Modern::Perl '2018';
  1         3  
  1         66  
49 1     1   216 use Mojo::Base -role;
  1         3  
  1         11  
50 1     1   411 use POSIX qw(ceil);
  1         2  
  1         9  
51              
52             =head1 METHODS
53              
54             =head2 reverse
55              
56             Reverses a direction.
57              
58             =cut
59              
60             sub reverse {
61 1     1 1 6 my ($self, $i) = @_;
62 1         7 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 2840     2840 1 5914 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 117     117 1 321 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 4280     4280 1 7538 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 3750     3750 1 6889 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 16984     16984 1 20653 my $self = shift;
121             # $hex is [x,y] or "0x0y" and $i is a number 0 .. 5
122 16984         24134 my ($hex, $i) = @_;
123 16984 50       25852 die join(":", caller) . ": undefined direction for $hex\n" unless defined $i;
124 16984 50       35115 $hex = [$self->xy($hex)] unless ref $hex;
125 16984         58080 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 5154     5154 1 6390 my $self = shift;
154             # $hex is [x,y] or "0x0y" and $i is a number 0 .. 11
155 5154         7173 my ($hex, $i) = @_;
156 5154 50       7828 die join(":", caller) . ": undefined direction for $hex\n" unless defined $i;
157 5154 50       11032 $hex = [$self->xy($hex)] unless ref $hex;
158 5154         17858 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 6991     6991 1 10165 my $self = shift;
173 6991         10023 my ($x1, $y1, $x2, $y2) = @_;
174 6991 100       10662 if (@_ == 2) {
175 2681         3520 ($x1, $y1, $x2, $y2) = map { $self->xy($_) } @_;
  5362         8491  
176             }
177             # transform the coordinate system into a decent system with one axis tilted by
178             # 60°
179 6991         13961 $y1 = $y1 - POSIX::ceil($x1/2);
180 6991         10807 $y2 = $y2 - POSIX::ceil($x2/2);
181 6991 100       10836 if ($x1 > $x2) {
182             # only consider moves from left to right and transpose start and
183             # end point to make it so
184 3219         4570 my ($t1, $t2) = ($x1, $y1);
185 3219         4670 ($x1, $y1) = ($x2, $y2);
186 3219         4500 ($x2, $y2) = ($t1, $t2);
187             }
188 6991 100       9577 if ($y2>=$y1) {
189             # if it the move has a downwards component add Δx and Δy
190 2216         4441 return $x2-$x1 + $y2-$y1;
191             } else {
192             # else just take the larger of Δx and Δy
193 4775 100       10296 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;