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   874 use Modern::Perl '2018';
  1         2  
  1         9  
49 1     1   116 use Mojo::Base -role;
  1         2  
  1         6  
50 1     1   305 use POSIX qw(ceil);
  1         2  
  1         7  
51              
52             =head1 METHODS
53              
54             =head2 reverse
55              
56             Reverses a direction.
57              
58             =cut
59              
60             sub reverse {
61 1     1 1 3 my ($self, $i) = @_;
62 1         14 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 3123     3123 1 6527 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 140     140 1 330 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 4332     4332 1 8793 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 3797     3797 1 7700 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 17947     17947 1 22632 my $self = shift;
121             # $hex is [x,y] or "0x0y" and $i is a number 0 .. 5
122 17947         27576 my ($hex, $i) = @_;
123 17947 50       28699 die join(":", caller) . ": undefined direction for $hex\n" unless defined $i;
124 17947 50       37884 $hex = [$self->xy($hex)] unless ref $hex;
125 17947         65517 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 5477     5477 1 7204 my $self = shift;
154             # $hex is [x,y] or "0x0y" and $i is a number 0 .. 11
155 5477         8253 my ($hex, $i) = @_;
156 5477 50       8873 die join(":", caller) . ": undefined direction for $hex\n" unless defined $i;
157 5477 50       12361 $hex = [$self->xy($hex)] unless ref $hex;
158 5477         20134 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 7544     7544 1 11037 my $self = shift;
173 7544         10919 my ($x1, $y1, $x2, $y2) = @_;
174 7544 100       11502 if (@_ == 2) {
175 3375         4529 ($x1, $y1, $x2, $y2) = map { $self->xy($_) } @_;
  6750         10589  
176             }
177             # transform the coordinate system into a decent system with one axis tilted by
178             # 60°
179 7544         15482 $y1 = $y1 - POSIX::ceil($x1/2);
180 7544         11633 $y2 = $y2 - POSIX::ceil($x2/2);
181 7544 100       11695 if ($x1 > $x2) {
182             # only consider moves from left to right and transpose start and
183             # end point to make it so
184 3607         5196 my ($t1, $t2) = ($x1, $y1);
185 3607         4725 ($x1, $y1) = ($x2, $y2);
186 3607         4980 ($x2, $y2) = ($t1, $t2);
187             }
188 7544 100       10159 if ($y2>=$y1) {
189             # if it the move has a downwards component add Δx and Δy
190 2577         5356 return $x2-$x1 + $y2-$y1;
191             } else {
192             # else just take the larger of Δx and Δy
193 4967 100       10705 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;