File Coverage

blib/lib/Game/TextPacMonster/Map.pm
Criterion Covered Total %
statement 166 169 98.2
branch 44 46 95.6
condition 17 21 80.9
subroutine 25 26 96.1
pod 0 15 0.0
total 252 277 90.9


line stmt bran cond sub pod time code
1             package Game::TextPacMonster::Map;
2              
3 3     3   11482 use strict;
  3         6  
  3         79  
4 3     3   48 use warnings;
  3         5  
  3         78  
5 3     3   15 use utf8;
  3         6  
  3         23  
6              
7 3     3   1396 use Game::TextPacMonster::Point;
  3         7  
  3         79  
8 3     3   1567 use Game::TextPacMonster::Player;
  3         8  
  3         82  
9 3     3   1920 use Game::TextPacMonster::V;
  3         7  
  3         70  
10 3     3   1461 use Game::TextPacMonster::H;
  3         7  
  3         80  
11 3     3   1640 use Game::TextPacMonster::L;
  3         7  
  3         73  
12 3     3   1454 use Game::TextPacMonster::R;
  3         7  
  3         71  
13 3     3   1481 use Game::TextPacMonster::J;
  3         6  
  3         5725  
14              
15             my $_PLAYER_ID = 1;
16             my $_PLAYER_CHAR = '@';
17              
18             sub new {
19 18     18 0 6018 my ( $class, $map_info_ref ) = @_;
20              
21 18         51 chomp( $map_info_ref->{map_string} );
22              
23 18         141 my $self = {
24             _timelimit => $map_info_ref->{timelimit},
25             _current_time => 0,
26             _map_string => $map_info_ref->{map_string},
27             _map => undef,
28             _map_feeds => undef,
29             _map_width => undef,
30             _map_height => undef,
31             _log => q{},
32             _objects => {}
33             };
34              
35 18         51 bless $self, $class;
36 18         49 $self->_set_map();
37 18         46 return $self;
38             }
39              
40             sub command_player {
41 5     5 0 13 my ( $self, $command ) = @_;
42              
43 5 50 66     34 return 0 if ( $command && !( $command =~ /^(j|k|l|h|.)$/ ) );
44              
45 5         8 my $objects = $self->{_objects};
46              
47 5 100       20 return 0 if ( !$objects->{$_PLAYER_ID}->can_move($command) );
48              
49             # creature move first
50 2         14 for my $key ( keys %$objects ) {
51 12 100       25 next if ( $key eq $_PLAYER_ID );
52 10         55 $objects->{$key}->move();
53             }
54              
55             # player move second
56 2         10 $objects->{$_PLAYER_ID}->move($command);
57              
58 2         7 $self->increase_current_time;
59 2         4 $self->{_log} .= $command;
60              
61 2         10 return 1;
62             }
63              
64             sub get_log {
65 4     4 0 14 my $self = shift;
66 4         21 return $self->{_log};
67             }
68              
69             sub _set_map {
70 18     18   24 my $self = shift;
71 18         630 my @map_chars = split( //, $self->{_map_string} );
72              
73 18         84 my $map_feeds = [];
74 18         28 my $map = [];
75              
76 18         25 my $x = 0;
77 18         20 my $y = 0;
78              
79 18         26 my $object_id = $_PLAYER_ID + 1;
80              
81 18         35 for my $char (@map_chars) {
82              
83 2034 100       5650 if ( $char eq "\n" ) {
    100          
    100          
    100          
84 120         104 $x = 0;
85 120         120 $y += 1;
86 120         157 next;
87             }
88             elsif ( $char eq '.' ) {
89 595         891 $map_feeds->[$y][$x] = 1;
90 595         862 $map->[$y][$x] = q{ };
91             }
92             elsif ( $char eq '@' ) {
93 18         57 my $point = Game::TextPacMonster::Point->new( $x, $y );
94 18         68 my $player =
95             Game::TextPacMonster::Player->new( $_PLAYER_ID, $point, $self );
96 18         42 $self->{_objects}->{"$_PLAYER_ID"} = $player;
97 18         43 $map->[$y][$x] = q{ };
98             }
99             elsif ( $char =~ /^(R|L|V|H|J)$/ ) {
100 82         257 my $p = Game::TextPacMonster::Point->new( $x, $y );
101 82         435 my $enemy =
102             ("Game::TextPacMonster::$char")->new( $object_id, $p, $self );
103 82         214 $self->{_objects}->{"$object_id"} = $enemy;
104 82         158 $map->[$y][$x] = q{ };
105 82         95 ++$object_id;
106             }
107             else {
108 1219         2035 $map->[$y][$x] = $char;
109             }
110              
111 1914         2074 $x += 1;
112             }
113              
114 18         55 $self->{_map} = $map;
115 18         26 $self->{_map_feeds} = $map_feeds;
116 18         22 $self->{_map_width} = @$map;
117 18         20 $self->{_map_height} = @{ $map->[0] };
  18         31  
118 18         155 return $self;
119             }
120              
121             sub get_time {
122 10     10 0 13 my $self = shift;
123 10         32 return $self->{_current_time};
124             }
125              
126             sub can_move {
127 28     28 0 47 my ( $self, $point ) = @_;
128 28         74 my $x = $point->x_coord;
129 28         67 my $y = $point->y_coord;
130 28         76 my $char = $self->{_map}->[$y][$x];
131              
132 28 100 100     115 if ( $char && $char ne '#' ) {
133 17         79 return 1;
134             }
135 11         40 return 0;
136             }
137              
138             sub get_left_time {
139 0     0 0 0 my $self = shift;
140 0         0 return $self->{_timelimit} - $self->get_time;
141              
142             }
143              
144             sub get_current_time {
145 10     10 0 23 my $self = shift;
146 10         33 return $self->{_current_time};
147             }
148              
149             sub increase_current_time {
150 5     5 0 14 my $self = shift;
151 5         9 $self->{_current_time} += 1;
152 5         7 return $self;
153             }
154              
155             sub is_win {
156 5     5 0 12 my $self = shift;
157              
158 5 100       12 my $feeds_result = ( $self->count_feeds == 0 ) ? 1 : 0;
159 5 100       14 my $time_result =
160             ( $self->{_timelimit} >= $self->get_current_time ) ? 1 : 0;
161 5 100 100     24 my $result = ( $feeds_result && $time_result ) ? 1 : 0;
162              
163 5         27 return $result;
164             }
165              
166             sub count_feeds {
167 11     11 0 26 my $self = shift;
168              
169 11         14 my $feeds_num = 0;
170              
171 11         15 for ( @{ $self->{_map_feeds} } ) {
  11         35  
172 66 100       165 if ($_) {
173 35 100       430 $feeds_num += $_ ? 1 : 0 for (@$_);
174             }
175             }
176              
177 11         51 return $feeds_num;
178             }
179              
180             sub del_feed {
181 4     4 0 9 my ( $self, $point ) = @_;
182              
183 4 50       13 if ( ref($point) ne 'Game::TextPacMonster::Point' ) {
184 0         0 die 'Type error: delFeed require Point instance.';
185             }
186              
187 4 100       13 if ( $self->{_map_feeds}->[ $point->y_coord ][ $point->x_coord ] ) {
188 3         11 $self->{_map_feeds}->[ $point->y_coord ][ $point->x_coord ] = undef;
189             }
190 4         10 return $self;
191             }
192              
193             sub get_string {
194 4     4 0 15 my $self = shift;
195              
196             # make deep copy
197 308         393 my @map = map {
198 28         38 my @map = map { $_ } @$_;
  4         12  
199 28         44 \@map;
200 4         7 } @{ $self->{_map} };
201              
202 4         9 my $objects = $self->{_objects};
203              
204             # First, place player on the map.
205             # because player should be overlaied when player lose
206 4         8 my $player = $objects->{$_PLAYER_ID};
207 4         18 my $player_p = $player->point;
208 4         14 $map[ $player_p->y_coord ][ $player_p->x_coord ] = $_PLAYER_CHAR;
209              
210             # Second, enemies place on the map
211 4         14 for my $key ( keys %$objects ) {
212 22 100       73 next if $key eq $_PLAYER_ID;
213 18         27 my $obj = $objects->{$key};
214 18         66 my $p_x = $obj->point->x_coord;
215 18         47 my $p_y = $obj->point->y_coord;
216 18         59 my $object_char = ( split( /::/, ref($obj) ) )[-1];
217 18         41 $map[$p_y][$p_x] = $object_char;
218             }
219              
220 4         9 my $feeds = $self->{_map_feeds};
221 4         6 my $map_string = q{}; # empty string
222 4         4 my $y = 0;
223 4         7 for my $horizotal (@map) {
224 28         30 my $x = 0;
225 308 100 100     806 my @x_chars = map {
226 28         43 my $char = ( $_ eq q{ } && $feeds->[$y][$x] ) ? '.' : $_;
227 308         258 ++$x;
228 308         425 $char;
229 28         32 } @{$horizotal};
230              
231 28         45 push( @x_chars, "\n" );
232 28         126 $map_string .= $_ for (@x_chars);
233 28         70 ++$y;
234             }
235              
236 4         8 chomp($map_string);
237 4         42 return $map_string;
238             }
239              
240             sub get_player_point {
241 2     2 0 11 my $self = shift;
242 2         10 return $self->{_objects}->{$_PLAYER_ID}->point;
243             }
244              
245             sub is_lose {
246 8     8 0 28 my $self = shift;
247              
248 8 100       32 return 1 if ( $self->{_timelimit} <= $self->{_current_time} );
249              
250 6         26 my $player_p = $self->{_objects}->{$_PLAYER_ID}->point;
251 6         27 my $player_pre_p = $self->{_objects}->{$_PLAYER_ID}->pre_point;
252              
253 6         8 for my $key ( keys %{ $self->{_objects} } ) {
  6         21  
254              
255 15 100       31 next if ( $key eq $_PLAYER_ID );
256              
257 10         32 my $enemy_p = $self->{_objects}->{$key}->point;
258 10         51 my $enemy_pre_p = $self->{_objects}->{$key}->pre_point;
259              
260 10 100 66     42 if ( $player_pre_p && $enemy_pre_p ) { # not first time
261 9 100 66     23 if ( $player_p->equals($enemy_pre_p)
262             && $player_pre_p->equals($enemy_p) )
263             {
264 1         6 return 1;
265             }
266             }
267              
268 9 100       29 return 1 if $player_p->equals($enemy_p);
269             }
270 4         18 return 0;
271             }
272              
273              
274             sub count_movable_points {
275 17     17 0 49 my ( $self, $point ) = @_;
276              
277 17         27 my $map = $self->{_map};
278              
279 17         48 my @way_chars = (
280             $map->[ $point->y_coord ][ $point->x_coord - 1 ],
281             $map->[ $point->y_coord ][ $point->x_coord + 1 ],
282             $map->[ $point->y_coord - 1 ][ $point->x_coord ],
283             $map->[ $point->y_coord + 1 ][ $point->x_coord ],
284             );
285              
286 17         28 my $way_couter = 0;
287              
288 17         27 for my $char (@way_chars) {
289 68 100 66     252 ++$way_couter if ( $char && $char ne '#' );
290             }
291 17         70 return $way_couter;
292             }
293              
294             1;