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   6522 use Modern::Perl '2018';
  11         25  
  11         77  
49 11     11   3013 use Mojo::Base -role;
  11         24  
  11         101  
50 11     11   5318 use POSIX qw(ceil);
  11         25  
  11         111  
51              
52             =head1 METHODS
53              
54             =head2 reverse
55              
56             Reverses a direction.
57              
58             =cut
59              
60             sub reverse {
61 2     2 1 6 my ($self, $i) = @_;
62 2         11 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 6121     6121 1 13822 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 223     223 1 504 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 8578     8578 1 16585 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 7362     7362 1 14758 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 34962     34962 1 43329 my $self = shift;
121             # $hex is [x,y] or "0x0y" and $i is a number 0 .. 5
122 34962         50008 my ($hex, $i) = @_;
123 34962 50       52554 die join(":", caller) . ": undefined direction for $hex\n" unless defined $i;
124 34962 50       71507 $hex = [$self->xy($hex)] unless ref $hex;
125 34962         115950 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 10038     10038 1 12264 my $self = shift;
154             # $hex is [x,y] or "0x0y" and $i is a number 0 .. 11
155 10038         15000 my ($hex, $i) = @_;
156 10038 50       15700 die join(":", caller) . ": undefined direction for $hex\n" unless defined $i;
157 10038 50       20722 $hex = [$self->xy($hex)] unless ref $hex;
158 10038         32387 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 12921     12921 1 21511 my $self = shift;
173 12921         20533 my ($x1, $y1, $x2, $y2) = @_;
174 12921 100       20259 if (@_ == 2) {
175 5179         12789 ($x1, $y1, $x2, $y2) = map { $self->xy($_) } @_;
  10358         15935  
176             }
177             # transform the coordinate system into a decent system with one axis tilted by
178             # 60°
179 12921         26153 $y1 = $y1 - POSIX::ceil($x1/2);
180 12921         21763 $y2 = $y2 - POSIX::ceil($x2/2);
181 12921 100       21168 if ($x1 > $x2) {
182             # only consider moves from left to right and transpose start and
183             # end point to make it so
184 6095         9182 my ($t1, $t2) = ($x1, $y1);
185 6095         8535 ($x1, $y1) = ($x2, $y2);
186 6095         8932 ($x2, $y2) = ($t1, $t2);
187             }
188 12921 100       18373 if ($y2>=$y1) {
189             # if it the move has a downwards component add Δx and Δy
190 4264         8724 return $x2-$x1 + $y2-$y1;
191             } else {
192             # else just take the larger of Δx and Δy
193 8657 100       18718 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;