File Coverage

blib/lib/Games/Euchre/Trick.pm
Criterion Covered Total %
statement 9 92 9.7
branch 0 20 0.0
condition 0 12 0.0
subroutine 3 16 18.7
pod 13 13 100.0
total 25 153 16.3


line stmt bran cond sub pod time code
1             package Games::Euchre::Trick;
2              
3             =head1 NAME
4              
5             Games::Euchre::Trick - Trick class for Euchre card game
6              
7             =head1 DESCRIPTION
8              
9             Only one Trick instance is alive at one time per Euchre game. The
10             Trick keeps track of which cards have been played, and provides useful
11             functions to determine which cards are legal plays, as well as who
12             is the winner of the trick. The trick class makes the determination
13             of which card beats which card, given the current trump and lead. The
14             trick class knows how to handle an alone hand and it calls the
15             playCard() method for each player in turn in it's play() method,
16             usually called from the Games::Euchre->playHand() method.
17              
18             =cut
19              
20 1     1   5 use strict;
  1         1  
  1         32  
21 1     1   5 use warnings;
  1         1  
  1         30  
22 1     1   4 use Games::Cards;
  1         2  
  1         1101  
23              
24             =head1 CLASS METHODS
25              
26             =over 4
27              
28             =item new GAME LEAD NAME NUMBER
29              
30             Create and initialize a new Euchre trick. The lead is a
31             Games::Euchre::Player instance. The name is any string. The number
32             is a one-based index of which trick this is (from 1 to 5).
33              
34             =cut
35              
36             sub new {
37 0     0 1   my $pkg = shift;
38 0           my $game = shift;
39 0           my $lead = shift;
40 0           my $name = shift;
41 0           my $number = shift; # 1-based
42 0           my $self = bless({
43             game => $game,
44             name => $name,
45             number => $number,
46             players => [$game->getPlayers()],
47             hand => Games::Cards::Queue->new($game->{game}, $name),
48             play => 0,
49             leadIndex => undef,
50             }, $pkg);
51 0           for (my $i=$#{$self->{players}}; $i >= 0; $i--) {
  0            
52 0           my $player = $self->{players}->[$i];
53 0 0 0       if ((!$player->wentAlone()) && $player->getTeam()->wentAlone()) {
54             # Remove teammate of alone-goer
55 0           splice @{$self->{players}}, $i, 1;
  0            
56             }
57             }
58              
59 0           for (my $i=0; $i < @{$self->{players}}; $i++) {
  0            
60 0 0         last if ($lead->getName() eq $self->{players}->[0]->getName());
61 0           push @{$self->{players}}, shift(@{$self->{players}}); # rotate
  0            
  0            
62             }
63 0           return $self;
64             }
65              
66             =back
67              
68             =head1 CLASS METHODS
69              
70             =over 4
71              
72             =item getGame
73              
74             Return the Euchre game instance to which this trick belongs.
75              
76             =cut
77              
78             sub getGame {
79 0     0 1   my $self = shift;
80 0           return $self->{game};
81             }
82              
83             =item getName
84              
85             Return the name of this trick.
86              
87             =cut
88              
89             sub getName {
90 0     0 1   my $self = shift;
91 0           return $self->{name};
92             }
93              
94             =item getNumber
95              
96             Return the number of this trick (from 1 to 5).
97              
98             =cut
99              
100             sub getNumber {
101 0     0 1   my $self = shift;
102 0           return $self->{number};
103             }
104              
105             =item getHand
106              
107             Return the Games::Cards::Hand object representing this trick.
108              
109             =cut
110              
111             sub getHand {
112 0     0 1   my $self = shift;
113 0           return $self->{hand};
114             }
115              
116             =item getCards
117              
118             Return an array of the Games::Cards::Card objects played in this trick.
119              
120             =cut
121              
122             sub getCards {
123 0     0 1   my $self = shift;
124 0           return @{$self->getHand()->cards()};
  0            
125             }
126              
127             =item getPlayers
128              
129             Return an array of the players in the order they will play in this
130             trick. If someone went alone, this array will have three entries.
131             Otherwise it will always have four.
132              
133             =cut
134              
135             sub getPlayers {
136 0     0 1   my $self = shift;
137 0           return @{$self->{players}};
  0            
138             }
139              
140             =item getPlayerIndex PLAYER
141              
142             Returns the 0-based index of the specified player in the order that he
143             would play in the current trick. This is crucial for figuring out who
144             played which card. Returns undef in the case that the player did not
145             play (yet, or not at all if the partner went alone).
146              
147             =cut
148              
149             sub getPlayerIndex {
150 0     0 1   my $self = shift;
151 0           my $player = shift;
152 0           for (my $i=0; $i < @{$self->{players}}; $i++) {
  0            
153 0 0         if ($player->getName() eq $self->{players}->[$i]->getName()) {
154 0           return $i;
155             }
156             }
157 0           return undef;
158             }
159              
160             =item recordTrick
161              
162             Record the result of this trick by informing the winning team.
163              
164             =cut
165              
166             sub recordTrick {
167 0     0 1   my $self = shift;
168 0           my $winner = $self->getWinner();
169 0           $winner->getTeam()->addTrick();
170             }
171              
172             =item getWinner
173              
174             Returns the player who played the card that won the trick.
175              
176             =cut
177              
178             sub getWinner {
179 0     0 1   my $self = shift;
180            
181 0           my @cards = $self->getCards();
182 0           my $leader = 0;
183 0           for (my $i=1; $i<@cards; $i++) {
184 0 0         if ($self->cmpCards($cards[$leader], $cards[$i]) < 0) {
185 0           $leader = $i;
186             }
187             }
188             #print "winner: " . $self->{players}->[$leader]->getName() . "\n"
189             # if ($self->getGame()->{debug});
190 0           return $self->{players}->[$leader];
191             }
192              
193             =item cmpCards CARD1 CARD2
194              
195             Returns -1, 0, or 1 indicating the relative rank of the two cards.
196             Like the string 'cmp' operator -1 means that CARD2 beats CARD1, 1
197             means that CARD1 beats CARD2 and 0 means that they are equivalent
198             (i.e. both worthless!).
199              
200             =cut
201              
202             sub cmpCards {
203 0     0 1   my $self = shift;
204 0           my @cards = (shift, shift);
205              
206 0           my $leadcard = ($self->getCards())[0];
207 0           my $leadsuit = $self->getGame()->getCardSuit($leadcard);
208 0           my $trumpsuit = $self->getGame()->{trump};
209 0           my $othertrumpsuit = $self->getGame()->{othertrump};
210             # This is valid for NT too, since the "JN" for trump is never referenced
211 0           my %ranks = (
212             "A$leadsuit" => 6,
213             "K$leadsuit" => 5,
214             "Q$leadsuit" => 4,
215             "J$leadsuit" => 3,
216             "10$leadsuit" => 2,
217             "9$leadsuit" => 1,
218              
219             # Order matters:
220             # trump has to be after lead in case lead IS trump
221             # or if lead suit holds left jack
222             "J$trumpsuit" => 13,
223             "J$othertrumpsuit" => 12,
224             "A$trumpsuit" => 11,
225             "K$trumpsuit" => 10,
226             "Q$trumpsuit" => 9,
227             "10$trumpsuit" => 8,
228             "9$trumpsuit" => 7,
229             );
230              
231 0 0         my @cardranks = map {$ranks{$_->truename()} || 0} @cards;
  0            
232             #print "cmp " . join(" vs. ", map{$cards[$_]->truename()." $cardranks[$_]"} 0,1) . "\n"
233             # if ($self->getGame()->{debug});
234 0           return $cardranks[0] <=> $cardranks[1];
235             }
236              
237             =item play
238              
239             Calls the playCard() method for the player whose turn it is to play.
240              
241             =cut
242              
243             sub play {
244 0     0 1   my $self = shift;
245 0           my $player = $self->{players}->[$self->{play}++];
246 0           $player->playCard($self);
247 0           return $self;
248             }
249              
250             =item isLegalPlay PLAYER CHOICE
251              
252             Returns a boolean indicating whether the selected card to play is
253             legal, given the specified player's hand. CHOICE is a 0-based index
254             into the array of cards held by the player's hand.
255              
256             Checks if the choice is an actual card in the player's hand and
257             whether the card follows suit.
258              
259             =cut
260              
261             sub isLegalPlay {
262 0     0 1   my $self = shift;
263 0           my $player = shift;
264 0           my $choice = shift; # 0-based
265              
266 0           my @cards = $player->getCards();
267              
268             # Enforce valid choice values
269 0 0 0       return undef unless (defined $choice && $choice =~ /^\d$/ &&
      0        
      0        
270             $choice >= 0 && $choice < @cards);
271 0           my $card = $cards[$choice];
272 0 0         return undef if (!$card);
273              
274             # Is it the first card led?
275 0           my $leadcard = ($self->getCards())[0];
276 0 0         return $self if (!$leadcard); # lead card can be anything
277              
278             # Is it following suit?
279 0           my $cardsuit = $self->getGame()->getCardSuit($card);
280 0           my $leadsuit = $self->getGame()->getCardSuit($leadcard);
281 0 0         return $self if ($cardsuit eq $leadsuit);
282              
283             # Is the player out of the lead suit?
284 0           my $hasLeadSuit = undef;
285 0           foreach my $card (@cards) {
286 0           my $cardsuit = $self->getGame()->getCardSuit($card);
287 0 0         if ($cardsuit eq $leadsuit) {
288 0           $hasLeadSuit = $self;
289 0           last;
290             }
291             }
292 0           return !$hasLeadSuit;
293             }
294              
295             1;
296             __END__