File Coverage

lib/Catan/Event/Robber.pm
Criterion Covered Total %
statement 21 78 26.9
branch 0 34 0.0
condition 0 9 0.0
subroutine 8 16 50.0
pod 0 10 0.0
total 29 147 19.7


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