File Coverage

lib/Catan/Game/Trade.pm
Criterion Covered Total %
statement 82 86 95.3
branch 19 26 73.0
condition 10 26 38.4
subroutine 13 14 92.8
pod 0 8 0.0
total 124 160 77.5


line stmt bran cond sub pod time code
1             package Catan::Game::Trade;
2             $Catan::Game::Trade::VERSION = '0.03';
3 3     3   554 use warnings;
  3     1   5  
  3         95  
  1         886  
  1         2  
  1         29  
4 3     3   16 use strict;
  3     1   13  
  3         85  
  1         5  
  1         2  
  1         23  
5 3     3   19 use List::Util 'sum';
  3     1   5  
  3         3174  
  1         5  
  1         2  
  1         1239  
6              
7              
8             sub new
9             {
10 144     144 0 8133 my ($class, $bank, $players, $details, $resource_production) = @_;
11              
12 144 50 33     2870 die __PACKAGE__ . ' new requires players and trade detail arguments'
      33        
      33        
      33        
      33        
      33        
      50        
13             unless $bank && $bank->isa('Catan::Game::Bank')
14             && $details && ref $details eq 'HASH' && keys %$details
15             && $players && ref $players eq 'ARRAY' && scalar @$players;
16              
17 144         290 my $self = bless { }, $class;
18              
19             # validate trade details
20 144         364 for my $player_number (keys %$details)
21             {
22 168         538 my @players = grep($player_number == $_->number, @$players);
23              
24 168 50       448 die __PACKAGE__ . ' is for invalid player number'
25             unless scalar @players == 1;
26              
27 168         588 my $resources = $bank->resource_from_notation($details->{$player_number});
28 168         613 $self->{$player_number} = { player => $players[0], resources => $resources };
29              
30 168         228 push @{$self->{players}}, $players[0];
  168         583  
31             }
32              
33 144         233 my @players = @{$self->players};
  144         340  
34              
35 144 100       317 if (@players == 1)
36             {
37 121         166 my $player = shift @players;
38 121         362 my $ratios = $player->ratios;
39 121         166 push @{$self->{players}}, $bank;
  121         228  
40              
41             # check trade ratios if its a bank trade
42 121         198 my ($allowed, $requested) = (0,0);
43              
44 121         163 for my $r (@{$self->resources($player->number)})
  121         307  
45             {
46 195 100       538 if ($r->amount < 0)
    50          
47             {
48 98         404 $allowed += (-$r->amount) / $ratios->{$r->code};
49             }
50             elsif ($r->amount > 0)
51             {
52 97         214 $requested += $r->amount;
53             }
54 195         318 push @{$self->{bank}{resources}}, $r->invert;
  195         816  
55             }
56 121 50 66     475 die "$player requested $requested resources, but provided too few resources trade with the bank"
57             unless $resource_production || $allowed == $requested;
58             }
59             # check that the trade contains only 2 players and the amounts balance
60             else
61             {
62 23 100       98 die 'a trade must be between 2 players only!' unless @players == 2;
63 22         77 my $resources1 = $self->resources($players[0]->number);
64 22         74 my $resources2 = $self->resources($players[1]->number);
65              
66 22         64 for my $r (@$resources1)
67             {
68 39 100       73 my $total2 = sum map { $_->isa(ref $r) ? $_->amount : 0 } @$resources2;
  85         564  
69 39 100       108 die 'a trade between 2 players must balance!'
70             unless $r->amount + $total2 == 0;
71             }
72             }
73              
74             # check both players can afford the trade
75 142 50       368 return $self if $self->can_afford;
76             }
77              
78 3     3 0 13 sub is_with_bank { exists $_[0]->{bank} }
79 571     571 0 1524 sub players { $_[0]->{players} }
80              
81             sub as_hashref
82             {
83 9     9 0 17 my $self = shift;
84 9         17 my $rv = {};
85 9         17 for my $player (@{$self->players})
  9         20  
86             {
87 18         22 for my $r (@{$self->resources($player->number)})
  18         52  
88             {
89 30         77 $rv->{$player->number}{$r->code} = $r->amount;
90             }
91             }
92 9         72 return $rv;
93             }
94              
95 0     0 0 0 sub resources_all { [ map { @{$_[0]->resources($_->number)} } @{$_[0]->players} ]}
  0         0  
  0         0  
  0         0  
96              
97             sub resources
98             {
99 472     472 0 860 my ($self, $player_number) = @_;
100              
101             die 'resources requires a valid player number argument'
102 472 50 33     1917 unless $player_number && exists $self->{$player_number};
103              
104 472         1315 return $self->{$player_number}{resources};
105             }
106              
107             sub execute
108             {
109 136     136 0 246 my $self = shift;
110 136 50       275 if ($self->can_afford)
111             {
112 136         201 my %results;
113              
114 136         155 for my $player (@{$self->players})
  136         274  
115             {
116 272         327 for my $r (@{$self->resources($player->number)})
  272         644  
117             {
118 440         1050 $player->resources->{$r->code} += $r->amount;
119 440         1132 $results{$player->number}{$r->code} = $r->amount;
120             }
121             }
122 136         609 return \%results;
123             }
124             }
125              
126             sub can_afford
127             {
128 278     278 0 400 my ($self) = @_;
129              
130 278         328 for my $player (@{$self->players})
  278         505  
131             {
132 555         631 for (@{$self->{$player->number}{resources}})
  555         1456  
133             {
134             return die "$player does not have enough resources for that"
135 904 100       2176 unless $player->resources->{$_->code} + $_->quantity >= 0;
136             }
137             }
138 276         1083 return 1;
139             }
140              
141             1;
142              
143             __END__