File Coverage

lib/Catan/Event/Robber.pm
Criterion Covered Total %
statement 67 78 85.9
branch 14 34 41.1
condition 3 9 33.3
subroutine 15 16 93.7
pod 0 10 0.0
total 99 147 67.3


line stmt bran cond sub pod time code
1             package Catan::Event::Robber;
2             $Catan::Event::Robber::VERSION = '0.03';
3 2     2   10 use strict;
  2     1   3  
  2         49  
  1         703  
  1         2  
  1         20  
4 2     2   10 use warnings;
  2     1   3  
  2         48  
  1         5  
  1         2  
  1         24  
5 2     2   1568 use POSIX 'ceil';
  2     1   16517  
  2         15  
  1         5  
  1         2  
  1         8  
6              
7             sub new
8             {
9 1     1 0 3 my ($class, $args) = @_;
10              
11             bless {
12             players_to_steal => [],
13             players_to_concede=> [],
14             active => 0,
15 1         8 location => set_initial_location($args->{map}),
16             moved => 0,
17             }, $class;
18             }
19              
20 151     151 0 596 sub active { $_[0]->{active} }
21 87     87 0 337 sub location { $_[0]->{location} }
22              
23             sub activate
24             {
25 8     8 0 16 my ($self, $players_to_concede) = @_;
26 8 50       20 die 'robber is already active' if $self->active;
27 8         16 $self->{active} = 1;
28              
29             # check which players have more than 7 cards and need to concede
30 8         23 for (@$players_to_concede)
31             {
32 20 50       54 next unless $_->resource_total > 7;
33 0         0 push @{$self->{players_to_concede}}, {player => $_, target_total => ceil($_->resource_total / 2)};
  0         0  
34             }
35 8         37 return $self->{players_to_concede};
36             }
37              
38             sub check_players_to_concede
39             {
40 14     14 0 22 my ($self) = @_;
41 14         23 my @remaining_players = ();
42 14         24 for my $pair (@{$self->{players_to_concede}})
  14         55  
43             {
44 0 0       0 next if $pair->{player}->resource_total <= $pair->{target_total};
45 0         0 push @remaining_players, $pair;
46             }
47 14         36 $self->{players_to_concede} = \@remaining_players;
48 14         34 return $self->{players_to_concede};
49             }
50              
51             sub deactivate
52             {
53 7     7 0 15 my $self = shift;
54 7 50       21 die 'robber is already inactive' unless $self->active;
55 7         21 $self->check_players_to_concede;
56 7 50       14 die 'Not all players have conceded their resources yet!' if @{$self->{players_to_concede}};
  7         28  
57 7 50       30 die 'robber hasn\'t moved yet!' unless $self->moved;
58 7         15 $self->{active} = 0;
59 7         19 $self->{moved} = 0;
60             }
61              
62             # deploy to a random desert tile
63             sub set_initial_location
64             {
65 1     1 0 7 my $tiles = shift->tiles_by_type_code('D');
66 1         7 my $tile = $tiles->[ int rand(@$tiles) ];
67 1 50       4 die 'There are no desert tiles!' unless $tile;
68 1         10 return $tile;
69             }
70              
71             sub move
72             {
73 7     7 0 16 my ($self, $tile, $players) = @_;
74 7         27 $self->check_players_to_concede;
75              
76 7 50 33     20 die 'move requires tile & players arguments'
      33        
      33        
77             unless $tile && $tile->isa('Catan::Map::Tile') && $players && ref $players eq 'ARRAY';
78              
79 7 50       14 die 'Not all players have conceded their resources yet!' if @{$self->{players_to_concede}};
  7         27  
80              
81 7 50       26 die 'the robber can only be moved when active' unless $self->{active};
82              
83 7 50       26 die 'the robber has already moved' if $self->{moved};
84              
85             die 'the robber cannot be moved to the same tile'
86 7 50       27 if $self->{location}->uuid eq $tile->uuid;
87              
88 7         16 $self->{location} = $tile;
89 7         15 $self->{moved} = 1;
90              
91 7         21 my @players_to_steal = ();
92              
93             PLAYER:
94 7         17 for my $player (@$players)
95             {
96 28         33 for my $property (@{$player->properties})
  28         87  
97             {
98 66 100       191 if ($property->location->is_adjacent($tile))
99             {
100 7         18 push @players_to_steal, $player;
101 7         27 next PLAYER;
102             }
103             }
104             }
105 7         19 $self->{players_to_steal} = \@players_to_steal;
106              
107             # if there are no eligible players to steal from deactivate the robber
108 7 50       26 $self->deactivate unless @players_to_steal;
109              
110 7         18 return $tile;
111             }
112              
113             # check player has an adjacent property to the robber
114             # return a random resource if they are
115             sub steal
116             {
117 0     0 0 0 my ($self, $player) = @_;
118 0 0       0 die 'the robber can only steal when active' unless $self->{active};
119 0 0       0 die 'the robber hasn\'t moved yet!' unless $self->{moved};
120              
121 0 0       0 if (grep($_->number == $player->number, @{$self->{players_to_steal}}))
  0         0  
122             {
123 0         0 return $player->resource_lose_random;
124             }
125             else
126             {
127 0         0 die "$player->{number} has no adjacent properties";
128             }
129             }
130 7     7 0 22 sub moved { $_[0]->{moved} }
131             1;
132              
133             __END__