File Coverage

blib/lib/Games/Euchre/Player.pm
Criterion Covered Total %
statement 12 130 9.2
branch 0 42 0.0
condition 0 12 0.0
subroutine 4 24 16.6
pod 20 20 100.0
total 36 228 15.7


line stmt bran cond sub pod time code
1             package Games::Euchre::Player;
2              
3             =head1 NAME
4              
5             Games::Euchre::Player - Player class for Euchre card game
6              
7             =head1 DESCRIPTION
8              
9             The four Player objects are used to interact with the humand and
10             computer players, as well as to keep the state of the players hand,
11             whether he bid and whether he went alone.
12              
13             =cut
14              
15             require 5.005_57;
16 1     1   5 use strict;
  1         2  
  1         27  
17 1     1   5 use warnings;
  1         2  
  1         21  
18 1     1   5 use Scalar::Util;
  1         1  
  1         58  
19 1     1   5 use Games::Cards;
  1         2  
  1         2207  
20              
21             =head1 CLASS METHODS
22              
23             =over 4
24              
25             =item new GAME NUMBER NAME
26              
27             Create and initialize a new Euchre player. The number is 1-4.
28              
29             =cut
30              
31             sub new {
32 0     0 1   my $pkg = shift;
33 0           my $game = shift;
34 0           my $number = shift; # 1-based
35 0           my $name = shift;
36 0           my $self = bless({
37             game => $game,
38             number => $number,
39             name => $name,
40             hand => undef,
41             team => undef,
42             ai => undef,
43             alone => undef,
44             bid => undef,
45             }, $pkg);
46 0           return $self;
47             }
48              
49             =back
50              
51             =head1 INSTANCE METHODS
52              
53             =over 4
54              
55             =item getGame
56              
57             Return the Euchre game instance to which this player belongs.
58              
59             =cut
60              
61             sub getGame {
62 0     0 1   my $self = shift;
63 0           return $self->{game};
64             }
65              
66             =item setTeam TEAM
67              
68             Record the Team instance that this player belongs to.
69              
70             =cut
71              
72             sub setTeam {
73 0     0 1   my $self = shift;
74 0           my $team = shift;
75 0           $self->{team} = $team;
76 0           weaken($self->{team}); # break anti-GC loop
77 0           return $self;
78             }
79              
80             =item getTeam
81              
82             Return the Team instance to which this player belongs.
83              
84             =cut
85              
86             sub getTeam {
87 0     0 1   my $self = shift;
88 0           return $self->{team};
89             }
90              
91             =item setAI AI
92              
93             Record the AI instance for this player.
94              
95             =cut
96              
97             sub setAI {
98 0     0 1   my $self = shift;
99 0           my $ai = shift;
100 0           $self->{ai} = $ai;
101 0           return $self;
102             }
103              
104             =item getAI
105              
106             Return the AI instance for this player.
107              
108             =cut
109              
110             sub getAI {
111 0     0 1   my $self = shift;
112 0           return $self->{ai};
113             }
114              
115             =item setAlone
116              
117             Indicate that this player has chosen to go alone in the current hand.
118              
119             =cut
120              
121             sub setAlone {
122 0     0 1   my $self = shift;
123 0           $self->{alone} = 1;
124 0           return $self;
125             }
126              
127             =item setBid
128              
129             Indicate that this player has chosen to choose trump in the current hand.
130              
131             =cut
132              
133             sub setBid {
134 0     0 1   my $self = shift;
135 0           $self->{bid} = 1;
136 0           return $self;
137             }
138              
139             =item wentAlone
140              
141             Returns a boolean indicating whether this player chose to go alone on
142             a bid.
143              
144             =cut
145              
146             sub wentAlone {
147 0     0 1   my $self = shift;
148 0           return $self->{alone};
149             }
150              
151             =item isBidder
152              
153             Returns a boolean indicating whether this player called the trump suit
154             during bidding.
155              
156             =cut
157              
158             sub isBidder {
159 0     0 1   my $self = shift;
160 0           return $self->{bid};
161             }
162              
163             =item getName
164              
165             Return this player's name
166              
167             =cut
168              
169             sub getName {
170 0     0 1   my $self = shift;
171 0           return $self->{name};
172             }
173              
174             =item getNumber
175              
176             Return this player's number, between 1 and 4
177              
178             =cut
179              
180             sub getNumber {
181 0     0 1   my $self = shift;
182 0           return $self->{number};
183             }
184              
185             =item getHand
186              
187             Return the Games::Cards::Hand object representing this player's
188             current hand.
189              
190             =cut
191              
192             sub getHand {
193 0     0 1   my $self = shift;
194 0           return $self->{hand};
195             }
196              
197             =item getCards
198              
199             Return an array of the Games::Cards::Card objects held in the player's hand.
200              
201             =cut
202              
203             sub getCards {
204 0     0 1   my $self = shift;
205 0           return @{$self->getHand()->cards()};
  0            
206             }
207              
208             =item resetGame
209              
210             Clear all of the state for the current game and get ready for the next one.
211              
212             =cut
213              
214             sub resetGame {
215 0     0 1   my $self = shift;
216 0           return $self->resetHand();
217             }
218              
219             =item resetHand
220              
221             Clear all of the state for the current hand and get ready for the next one.
222              
223             =cut
224              
225             sub resetHand {
226 0     0 1   my $self = shift;
227 0           $self->{alone} = undef;
228 0           $self->{bid} = undef;
229 0           $self->{hand} = Games::Cards::Hand->new($self->getGame()->{game}, $self->{name});
230 0           return $self;
231             }
232              
233             =item bid TURN
234              
235             Allow the player to choose trump or pass. Returns one of: H, C, D, S,
236             N, HA, CA, DA, SA, NA, or undef. If the player has an AI instance
237             set, that is invoked. Otherwise a pathetically simple AI decides the
238             bid.
239              
240             =cut
241              
242             sub bid {
243 0     0 1   my $self = shift;
244 0           my $turn = shift;
245              
246 0 0         if ($self->getAI()) {
247 0           my $state = {
248             name => $self->getName(),
249             names => {$self->getGame()->getPlayerNames()},
250             number => $self->getNumber(),
251             turnedUp => ($turn <= 4 ?
252             $self->getGame()->{blind}->top_card()->truename() :
253             undef),
254             passes => $turn-1,
255             ourScore => $self->getTeam()->getScore(),
256             theirScore => $self->getTeam()->getOtherTeam()->getScore(),
257             winScore => $self->getGame()->{winningScore},
258             hangdealer => $self->getGame()->{hangdealer},
259             notrump => $self->getGame()->{notrump},
260             trump => $self->getGame()->{trump},
261 0 0         hand => [map {$_->truename()} $self->getCards()],
262             debug => $self->getGame()->{debug},
263             };
264 0           my $suit = $self->getAI()->bid($state);
265 0 0         if ($self->isLegalBid($turn, $suit)) {
266 0           return $suit;
267             }
268             } else {
269             # DUMB computer player!!! pass unless last bid, then pick any one
270 0 0         if ($turn == 2*$self->getGame()->getPlayers()) {
271             # pick any card in hand
272 0           foreach my $card ($self->getCards()) {
273 0           my $suit = $self->getGame()->getCardSuit($card);
274 0 0         if ($self->isLegalBid($turn, $suit)) {
275 0           return $suit;
276             }
277             }
278             # pick any suit
279 0           foreach my $suit ("H", "S", "D", "C") {
280 0 0         if ($self->isLegalBid($turn, $suit)) {
281 0           return $suit;
282             }
283             }
284             } else {
285 0           return undef;
286             }
287             }
288 0           die "Failed to get a legal bid";
289             }
290              
291             =item pickItUp
292              
293             Allow the player, as dealer, to select which card to trade for the
294             turned up card. This method performs the actual trade. If the player
295             has an AI instance set, that is invoked. Otherwise a pathetically
296             simple AI chooses the card.
297              
298             =cut
299              
300             sub pickItUp {
301 0     0 1   my $self = shift;
302              
303 0           my @cards = $self->getCards();
304 0           my $index = undef;
305 0 0         if ($self->getAI()) {
306 0           my $state = {
307             name => $self->getName(),
308             names => {$self->getGame()->getPlayerNames()},
309             number => $self->getNumber(),
310             turnedUp => $self->getGame()->{blind}->top_card()->truename(),
311             trump => $self->getGame()->{trump},
312             bidder => $self->getGame()->{bidder}+1,
313             weBid => $self->getTeam()->isBidder(),
314             usAlone => $self->getTeam()->wentAlone(),
315             themAlone => $self->getTeam()->getOtherTeam()->wentAlone(),
316 0           hand => [map {$_->truename()} @cards],
317             debug => $self->getGame()->{debug},
318             };
319 0           $index = $self->getAI()->pickItUp($state);
320             } else {
321             # DUMB computer player!!! pick the first card
322 0           $index = 0;
323             }
324 0 0 0       if (defined $index && $index =~ /^\d+$/ &&
      0        
      0        
325             $index >= 0 && $index < @cards) {
326 0 0         print($self->getName() . " trades " . $cards[$index]->truename() . " for " .
327             $self->getGame()->{blind}->top_card()->truename() . "\n")
328             if ($self->getGame()->{debug});
329 0           $self->getGame()->{blind}->give_cards($self->getHand(), 1);
330 0           $self->getHand()->give_a_card($self->getGame()->{blind}, $index);
331             } else {
332 0           die "Failed to specify a legal card to discard";
333             }
334             }
335              
336             =item playCard TRICK
337              
338             Allow the player to select which card to play on the current trick.
339             This method performs the actual play. If the player has an AI
340             instance set, that is invoked. Otherwise a pathetically simple AI
341             chooses the card.
342              
343             =cut
344              
345             sub playCard {
346 0     0 1   my $self = shift;
347 0           my $trick = shift;
348              
349 0           my @cards = $self->getCards();
350 0           my $playedCard = undef;
351 0 0         if ($self->getAI()) {
352 0           my $state = {
353             name => $self->getName(),
354             names => {$self->getGame()->getPlayerNames()},
355             number => $self->getNumber(),
356             trump => $self->getGame()->{trump},
357             trick => $trick->getNumber(),
358             bidder => $self->getGame()->{bidder}+1,
359             weBid => $self->getTeam()->isBidder(),
360             usAlone => $self->getTeam()->wentAlone(),
361             themAlone => $self->getTeam()->getOtherTeam()->wentAlone(),
362             ourTricks => $self->getTeam()->getTricks(),
363             theirTricks => $self->getTeam()->getOtherTeam()->getTricks(),
364             ourScore => $self->getTeam()->getScore(),
365             theirScore => $self->getTeam()->getOtherTeam()->getScore(),
366             winScore => $self->getGame()->{winningScore},
367 0           played => [map {$_->truename()} $trick->getCards()],
368 0           playedBy => [map {$_->getNumber()} $trick->getPlayers()],
369 0           hand => [map {$_->truename()} @cards],
370             debug => $self->getGame()->{debug},
371             };
372 0           my $index = $self->getAI()->playCard($state);
373 0 0         if ($trick->isLegalPlay($self, $index)) {
374 0           $playedCard = $cards[$index];
375 0           $self->getHand()->give_a_card($trick->getHand(), $index);
376             }
377             } else {
378             # DUMB computer player!!! pick the first legal card
379 0           for (my $i=0; $i < @cards; $i++) {
380 0 0         if ($trick->isLegalPlay($self, $i)) {
381 0           $playedCard = $cards[$i];
382 0 0         print($self->getName() . " plays " . $playedCard->truename() .
383             " on " . $self->getHand()->print("short"))
384             if ($self->getGame()->{debug});
385 0           $self->getHand()->give_a_card($trick->getHand(), $i);
386 0           last;
387             }
388             }
389             }
390 0 0         if (!$playedCard) {
391 0           die "Failed to find a legal card to play";
392             }
393             }
394              
395             =item isLegalBid TURNNUMBER BID
396              
397             Given a bid, return a boolean indicating the validity of that bid.
398             The bid is tested for structure (one of H, C, D, S, N, HA, CA, DA, SA,
399             NA, or undef), tested against the bidding round (only the turned-up
400             card suit can be called in round 1 , and may not be called in round
401             2), against the game options (hang-the-dealer, no-trump).
402              
403             This is called from the bid() method.
404              
405             =cut
406              
407             sub isLegalBid {
408 0     0 1   my $self = shift;
409 0           my $turn = shift;
410 0           my $bid = shift;
411            
412 0           my $game = $self->getGame();
413              
414 0           my @players = $game->getPlayers();
415 0           my $lastturn = 2*@players;
416              
417             # Is it a pass?
418 0 0         if (!defined $bid) {
419             # Can't pass on the last bid if hang-the-dealer is in effect
420 0 0 0       if ($game->{hangdealer} && $turn == $lastturn) {
421 0           return undef;
422             } else {
423 0           return $self;
424             }
425             }
426              
427             # Is is a valid bid?
428 0 0         return undef if ($bid !~ /^([HSDCN])(|A)$/i);
429              
430 0           my $suit = uc($1);
431 0           my $alone = $2;
432              
433             # Is it no trump?
434 0 0         if ($suit eq "N") {
435             # NT must be enable to call it
436 0 0         return undef if (!$game->{notrump});
437             }
438              
439             # Must call THE suit in the first round
440 0 0         if ($turn <= @players) {
441 0           my $topsuit = $game->getCardSuit($game->{blind}->top_card());
442 0 0         return undef if ($suit ne $topsuit);
443             }
444              
445 0           return $self;
446             }
447              
448             1;
449             __END__