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   505 use Modern::Perl '2018';
  1         2  
  1         57  
49 1     1   120 use Mojo::Base -role;
  1         3  
  1         7  
50 1     1   371 use POSIX qw(ceil);
  1         3  
  1         10  
51              
52             =head1 METHODS
53              
54             =head2 reverse
55              
56             Reverses a direction.
57              
58             =cut
59              
60             sub reverse {
61 1     1 1 4 my ($self, $i) = @_;
62 1         5 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 2654     2654 1 5878 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 101     101 1 227 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 4333     4333 1 7669 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 3776     3776 1 6645 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 16991     16991 1 20359 my $self = shift;
121             # $hex is [x,y] or "0x0y" and $i is a number 0 .. 5
122 16991         25048 my ($hex, $i) = @_;
123 16991 50       26051 die join(":", caller) . ": undefined direction for $hex\n" unless defined $i;
124 16991 50       35748 $hex = [$self->xy($hex)] unless ref $hex;
125 16991         58882 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 4988     4988 1 5739 my $self = shift;
154             # $hex is [x,y] or "0x0y" and $i is a number 0 .. 11
155 4988         6868 my ($hex, $i) = @_;
156 4988 50       7356 die join(":", caller) . ": undefined direction for $hex\n" unless defined $i;
157 4988 50       10220 $hex = [$self->xy($hex)] unless ref $hex;
158 4988         16440 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 5060     5060 1 7545 my $self = shift;
173 5060         7153 my ($x1, $y1, $x2, $y2) = @_;
174 5060 100       7626 if (@_ == 2) {
175 2172         2834 ($x1, $y1, $x2, $y2) = map { $self->xy($_) } @_;
  4344         6760  
176             }
177             # transform the coordinate system into a decent system with one axis tilted by
178             # 60°
179 5060         10082 $y1 = $y1 - POSIX::ceil($x1/2);
180 5060         7762 $y2 = $y2 - POSIX::ceil($x2/2);
181 5060 100       7544 if ($x1 > $x2) {
182             # only consider moves from left to right and transpose start and
183             # end point to make it so
184 2280         3428 my ($t1, $t2) = ($x1, $y1);
185 2280         2918 ($x1, $y1) = ($x2, $y2);
186 2280         3181 ($x2, $y2) = ($t1, $t2);
187             }
188 5060 100       7348 if ($y2>=$y1) {
189             # if it the move has a downwards component add Δx and Δy
190 1532         3062 return $x2-$x1 + $y2-$y1;
191             } else {
192             # else just take the larger of Δx and Δy
193 3528 100       7332 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;