File Coverage

blib/lib/Games/Cards.pm
Criterion Covered Total %
statement 113 263 42.9
branch 19 74 25.6
condition 8 32 25.0
subroutine 26 59 44.0
pod n/a
total 166 428 38.7


line stmt bran cond sub pod time code
1             package Games::Cards;
2              
3             =pod
4              
5             =head1 NAME
6              
7             Games::Cards -- Perl module for writing and playing card games
8              
9             =head1 SYNOPSIS
10              
11             use Games::Cards;
12             my $Rummy = new Games::Cards::Game;
13              
14             # Create the correct deck for a game of Rummy.
15             my $Deck = new Games::Cards::Deck ($Rummy, "Deck");
16              
17             # shuffle the deck and create the discard pile
18             $Deck->shuffle;
19             my $Discard = new Games::Cards::Queue "Discard Pile";
20              
21             # Deal out the hands
22             foreach my $i (1 .. 3) {
23             my $hand = new Games::Cards::Hand "Player $i" ;
24             $Deck->give_cards($hand, 7);
25             $hand->sort_by_value;
26             push @Hands, $hand;
27             }
28              
29             # print hands (e.g. "Player 1: AS 2C 3C 3H 10D QS KH")
30             foreach (@Hands) { print ($_->print("short"), "\n") }
31            
32             $Hands[1]->give_a_card ($Discard, "8D"); # discard 8 of diamonds
33              
34             =head1 DESCRIPTION
35              
36             This module creates objects and methods to allow easier programming of card
37             games in Perl. It allows you to do things like create decks of cards,
38             have piles of cards, hands, and other sets of cards, turn cards face-up
39             or face-down, and move cards from one set to another. Which is pretty much
40             all you need for most card games.
41              
42             Sub-packages include:
43              
44             =over 4
45              
46             =item Games::Cards::Undo
47              
48             This package handles undoing and redoing moves (important for solitaire).
49              
50             =item and Games::Cards::Tk
51              
52             This package allows you to write games that use a Tk graphical interface.
53             It's designed so that it will be relatively easy to write a game that uses
54             i a GUI or a simple text interface, depending on the player's
55             circumstances (availability of Tk, suspicious boss, etc.). See
56             L for more details on writing Tk games.
57              
58             =back
59              
60             =head2 Quick Overview of Classes
61              
62             A GC::Game stores information like what cards are in the starting deck,
63             plus pointers to the various Cards and CardSets.
64              
65             A GC::Card represents one playing card. Every Card must belong to one
66             (and only one) CardSet at every point during the game.
67              
68             A GC::CardSet is mostly just a set of GC::Cards. A CardSet has a unique
69             name. Many also have short nicknames, which make it easier to write games
70             that move cards between the sets. (See Klondike or FreeCell, for example.)
71              
72             There are many sorts of CardSet. The basic differentiation is Piles,
73             for which you only access the top or bottom card (or cards) and Hands,
74             where you might access any one of the cards in the Hand. Piles are
75             broken up into Stacks and Queues, and every game starts with a Deck of
76             cards (or more than one).
77              
78             =cut
79              
80             # TODO get rid of size, have cards return wantarray ? array of cards : size
81             #
82             # TODO Games::Cards::Undo::Exists. If not defined, don't bother calling
83             # GC::Undo::store etc. on every turn. Then each game can "use GCU" or not.
84              
85 2     2   1731 use strict;
  2         5  
  2         85  
86 2     2   16 use vars qw($VERSION);
  2         5  
  2         250  
87             require 5.004; # I use 'foreach my'
88              
89             # Stolen from `man perlmod`
90             $VERSION = do { my @r = (q$Revision: 1.45 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; # must be all one line, for MakeMaker
91              
92             # Handle undoing/redoing moves
93 2     2   4080 use Games::Cards::Undo;
  2         6  
  2         16558  
94              
95             # sub-packages
96             {
97             package Games::Cards::Game;
98              
99             package Games::Cards::Deck;
100             package Games::Cards::Queue;
101             package Games::Cards::Stack;
102             package Games::Cards::Pile;
103             package Games::Cards::Hand;
104             package Games::Cards::CardSet;
105              
106             package Games::Cards::Card;
107             }
108              
109              
110             =head2 Class Games::Cards::Game
111              
112             This class represents a certain game, like War, or Solitaire. This is
113             necessary to store the various rules for a given game, like the ranking
114             of the cards. (Or, for more exotic games, how many cards of what type are
115             in the deck.) Methods:
116              
117             =over 4
118              
119             =cut
120              
121             {
122             package Games::Cards::Game;
123             # suits is a reference to an array listing the suits in the deck
124             # cards_in_suit is a reference to a hash whose keys are the names of the
125             # cards in each suit, and values are the (default) values of those cards
126             # (Card names will be strings, although they might be "2". Values are
127             # integers, so that we can compare cards with other cards.)
128             #
129             # cardset_by_nickname is a hash whose keys are short (unique) nicknames and
130             # values are the CardSets (e.g., player's Hands, Piles, etc.) so nicknamed
131             # cardset_by_name is the same with the CardSet names
132             # card_by_truename stores Cards via their truenames. (See Card::truename)
133              
134             my $Default_Suits = [qw(Clubs Diamonds Hearts Spades)];
135             # (Parts of) this hash will need to be reset in lots of games.
136             my $Default_Cards_In_Suit = {
137             "Ace" => 1,
138             2 => 2,
139             3 => 3,
140             4 => 4,
141             5 => 5,
142             6 => 6,
143             7 => 7,
144             8 => 8,
145             9 => 9,
146             10 => 10,
147             "Jack" => 11,
148             "Queen" => 12,
149             "King" => 13,
150             };
151              
152             =item current_game
153              
154             Returns the current Game object. In almost every case, you'll only be
155             working with one at a time.
156              
157             =item set_current_game(GAME)
158              
159             In theory, these subs let you handle multiple Games at once, as long
160             as you set_current_game to the right one. Note that Game->new automatically
161             sets the current Game to be that game, so in 99% of cases, you won't have to
162             call set_current_game.
163              
164             =cut
165              
166             my $_Current_Game;
167 0     0   0 sub current_game { return $_Current_Game; }
168 1     1   2 sub set_current_game {$_Current_Game = shift;}
169              
170             =item new(HASHREF)
171              
172             creates a new game. HASHREF is a reference to a hash containing zero or more
173             of the keys "suits" and "cards_in_suit". "suits" is a list of the suits in a
174             deck, "cards_in_suit" is a reference to a hash whose keys are the names
175             of the cards in one suit and whose values are the values (or ranks) of those
176             cards. If "suits" is not given, the default suits (Clubs, Diamonds, Hearts,
177             Spades) are used. If "cards_in_suit" is not given, the default cards
178             (Ace, 2..10, Jack, Queen, King with values 1..13) are used.
179             For example, war would require "Ace"=>14.
180              
181             =cut
182              
183             sub new {
184 1     1   3 my $class = shift;
185 1         2 my $hashref = shift;
186 1   33     12 my $cardgame = {
      33        
187             "suits" => $hashref->{"suits"} || $Default_Suits,
188             "cards_in_suit" => $hashref->{"cards_in_suit"} ||
189             $Default_Cards_In_Suit,
190             "cardset_by_name" => {},
191             "cardset_by_nickname" => {},
192             };
193              
194 1         2 bless $cardgame, $class;
195             # For now, this game will be the current game
196 1         3 $cardgame->set_current_game;
197              
198 1         2 return $cardgame;
199             } # end sub Games::Cards::Game::new
200              
201             # Store a CardSet. Use separate hashes for cardset's name and nickname,
202             # for convenience.
203             sub store_cardset {
204 5     5   7 my ($self, $cardset) = @_;
205 5         25 $self->{"cardset_by_name"}->{$cardset->name} = $cardset;
206 5 50       19 if (defined (my $nick = $cardset->nickname)) {
207 0         0 $self->{"cardset_by_nickname"}->{$nick} = $cardset;
208             }
209             }
210              
211             =item get_cardset_by_name(NAME)
212              
213             Returns the CardSet with name NAME
214              
215             =cut
216              
217             sub get_cardset_by_name {
218 0     0   0 my ($self, $name) = @_;
219 0 0       0 if (exists ($self->{"cardset_by_name"}->{$name})) {
220 0         0 return $self->{"cardset_by_name"}->{$name};
221             } else {
222 0         0 return undef;
223             }
224             }
225              
226             =item get_cardset_by_nickname(NAME)
227              
228             Returns the CardSet with nickname NAME
229              
230             =cut
231              
232             sub get_cardset_by_nickname {
233 0     0   0 my ($self, $nickname) = @_;
234 0 0       0 if (exists ($self->{"cardset_by_nickname"}->{$nickname})) {
235 0         0 return $self->{"cardset_by_nickname"}->{$nickname};
236             } else {
237 0         0 return undef;
238             }
239             }
240              
241             # Store a Card
242             sub store_card {
243 52     52   65 my ($self, $card) = @_;
244 52         71 my $truename = $card->truename;
245 52         692 $self->{"card_by_truename"}->{$truename} = $card;
246             }
247              
248             =item get_card_by_truename(NAME)
249              
250             Returns the Card with truename NAME
251              
252             =cut
253              
254             sub get_card_by_truename {
255 0     0   0 my ($self, $truename) = @_;
256 0 0       0 if (exists ($self->{"card_by_truename"}->{$truename})) {
257 0         0 return $self->{"card_by_truename"}->{$truename};
258             } else {
259 0         0 return undef;
260             }
261             }
262              
263             } # end package Games::Cards::Game
264              
265             ######################################################################
266             # CardSet and its subclasses
267              
268             =head2 Games::Cards::Deck
269              
270             A deck is a deck of cards. The number of cards and identities of the cards in
271             the deck depend on the particular Game for which the deck is used.
272              
273             =over 4
274              
275             =cut
276              
277             {
278             package Games::Cards::Deck;
279             @Games::Cards::Deck::ISA = qw (Games::Cards::Queue);
280              
281             =item new (GAME, NAME)
282              
283             creates an I deck of cards. For each card in the deck it creates
284             a name, suit, value, and suit value. GAME is the GC::Game this deck
285             belongs to, and it stipulates the number of cards in the deck, etc. NAME is the
286             name to give the deck, e.g. "Deck".
287              
288             =back
289              
290             =cut
291              
292             sub new {
293 1     1   3 my ($class, $game, $deckname) = @_;
294 1 50       3 if (ref($class)) {$class = ref($class)}
  0         0  
295             # This allows us to get Tk or non-Tk automatically
296 1         5 (my $qclass = $class) =~ s/::Deck/::Queue/;
297 1         34 my $deck = $qclass->new($game, $deckname);
298 1         1 my %cards = %{$game->{"cards_in_suit"}};
  1         8  
299              
300             # make an unshuffled deck
301 1         4 (my $cclass = $class) =~ s/::Deck/::Card/;
302 1         2 foreach my $suit_value (1..@{$game->{"suits"}}) {
  1         3  
303 4         9 my $suit = $game->{"suits"}->[$suit_value-1];
304 4         12 foreach my $name (keys %cards) {
305 52         154 my $arg = {
306             "suit"=>$suit, "name"=> $name,
307             "suit_value" => $suit_value, "value" => $cards{$name}
308             };
309 52         132 my $new_card = $cclass->new($game, $arg);
310 52         54 push @{$deck->{"cards"}}, $new_card;
  52         116  
311 52         95 $new_card->set_owning_cardset($deck);
312             }
313             }
314              
315 1         7 bless $deck, $class;
316             } # end sub Games::Cards::Deck::new
317             } # end package Games::Cards::Deck
318              
319             =head2 Class Games::Cards::Queue
320              
321             A Queue (cf. computer science terminology, or the C++ stdlib) is a first-in
322             first-out pile of cards. Cards are removed from the top of the pile, but new
323             cards are added to the bottom of the pile. This might represent, say, a pile
324             of face-down cards, like the player's hand in War.
325              
326             =cut
327              
328             {
329             package Games::Cards::Queue;
330             # cards array has 0 as the top card, -1 as the bottom card (opposite of Queue,
331             # for convenience when moving cards from a Queue to a stack or vice versa).
332             # We push to add cards, but shift to remove cards.
333             @Games::Cards::Queue::ISA = qw(Games::Cards::Pile);
334              
335             # inherit SUPER::new
336              
337             sub remove_cards {
338             # remove (and return a ref to) top arg1 cards from the Queue
339 338     338   455 my ($thing, $number) = @_;
340 338         605 return $thing->splice (0, $number);
341             } # end sub Games::Cards::Queue::remove_cards
342              
343             sub add_cards {
344             # Add array of Cards arg1 to the Queue
345 308     308   403 my ($thing, $cards) = @_;
346 308         535 $thing->splice ($thing->size, 0, $cards);
347             } # end sub Games::Cards::Queue::add_cards
348              
349             sub top_card {
350 0     0   0 my $set = shift;
351 0 0       0 return $set->size ? $set->{"cards"}->[0] : 0;
352             } # end sub Games::Cards::Queue::top_card
353              
354             sub print_ordered_cards {
355             # returns the cards in the set in the correct order to be printed
356 0     0   0 return shift->{"cards"};
357             } # end sub Games::Cards::Queue::print_ordered_cards
358              
359             } #end package Games::Cards::Queue
360              
361             =head2 Class Games::Cards::Stack
362              
363             A stack (cf. computer science terminology, or the C++ stdlib) is a last-in
364             first-out pile of cards. Cards are removed from the top of the pile, and new
365             cards are also added to the top of the pile. This would usually represent a
366             pile of cards with its top card (and perhaps all cards) face up.
367              
368             =cut
369              
370             {
371             package Games::Cards::Stack;
372             # cards array has -1 as the top card, 0 as the bottom card (opposite of Queue,
373             # for convenience when moving cards from a Queue to a stack or vice versa).
374             # We only access the top of the stack, pushing to add and popping to remove.
375             @Games::Cards::Stack::ISA = qw(Games::Cards::Pile);
376              
377             # inherit SUPER::new
378              
379             sub remove_cards {
380             # remove (and return a ref to) top arg1 cards from the Stack
381 306     306   385 my ($thing, $number) = @_;
382 306         622 return $thing->splice (-$number);
383             } # end sub Games::Cards::Stack::remove_cards
384              
385             sub add_cards {
386             # Add array of Cards arg1 to the Stack
387 336     336   415 my ($thing, $cards) = @_;
388 336         656 $thing->splice($thing->size, 0, $cards);
389             } # end sub Games::Cards::Stack::add_cards
390              
391             sub top_card {
392 322     322   352 my $set = shift;
393 322 50       524 return $set->size ? $set->{"cards"}->[-1] : 0;
394             } # end sub Games::Cards::Stack::top_card
395              
396             # Use "reverse" to print the top card of the Set first
397             # (makes for easier reading when lists are long, since you usually
398             # care more about the next card to be played)
399             sub print_ordered_cards {
400             # returns the cards in the set in the correct order to be printed
401 0     0   0 return [reverse (@{shift->{"cards"}})];
  0         0  
402             } # end sub Games::Cards::Queue::print_ordered_cards
403              
404             } #end package Games::Cards::Stack
405              
406             #####################
407              
408             =head2 Class Games::Cards::Pile
409              
410             A Pile is a pile of cards. That is, it is a CardSet where we will only access
411             the beginning or end of the set. (This may include the first N cards in the
412             set, but we will never reference the 17'th card.) This is a super class of
413             Queue and Stack, and those classes should be used instead, so that we know
414             whether the cards in the pile are FIFO or LIFO. Methods:
415              
416             =over 4
417              
418             =cut
419              
420             {
421             package Games::Cards::Pile;
422             # The cards array is LIFO for the Stack subclass and FIFO for the Queue
423             # subclass. We always push things onto Queues or Stacks, but
424             # we use "pop", for Stacks, and "shift" for the Queues.
425              
426             @Games::Cards::Pile::ISA = qw(Games::Cards::CardSet);
427             # inherit SUPER::new
428              
429             =item give_cards(RECEIVER, NUMBER)
430              
431             Transfers NUMBER cards from the donor (the object on which this method was
432             called) to the CardSet RECEIVER. This method can used for dealing cards from
433             a deck, giving cards to another player (Go Fish), putting cards on the table
434             (War), or transferring a card or cards between piles in solitaire.
435              
436             If NUMBER is "all", then the donor gives all of its cards.
437              
438             Returns 1 usually. If the donor has too few cards, it returns 0 and does not
439             transfer any cards.
440              
441             =cut
442              
443             sub give_cards {
444             #TODO if called with a subref instead of a scalar, then sort the
445             #cards to the top of the Set using the sub, and then set $number!
446              
447             # If we're going from a Stack to a Queue, we would normally need to flip
448             # the stack of cards over. E.g. if you deal three cards from the stock to
449             # the waste pile in Solitaire, the top card of the stock becomes the
450             # *bottom* card of the waste. However, the cards arrays in Stacks and
451             # Queues are stored in opposite directions, so this works automatically!
452             # If we're giving to a Hand, which doesn't have a top card, it doesn't
453             # matter
454              
455 645     645   924 my ($donor, $receiver) = (shift, shift);
456 645         762 my $number = shift;
457 645 100       1740 $number = $donor->size if $number eq "all";
458              
459             # Remove the cards if we can
460 645 100       1203 if ($donor->size < $number) {
461             #print $donor->{"name"} . " is out of cards\n";
462 1         7 return 0;
463             }
464 644         1362 my $cards_ref = $donor->remove_cards($number);
465             #print $donor->{"name"}, " gives ";
466             #print map {$_->print("short")} @$cards_ref;
467             #print " to ", $receiver->{"name"}, "\n";
468              
469             # Add the cards
470 644         1451 $receiver->add_cards($cards_ref);
471              
472 644         2122 return 1;
473             } # end sub Games::Cards::Pile::give_cards
474              
475              
476             =item top_card
477              
478             Returns the top Card in the CardSet (or 0 if CardSet is empty)
479              
480             =cut
481              
482             # This sub is actually found in the subclasses, since their
483             # arrays are stored in different orders
484             } #end package Games::Cards::Pile
485              
486             #####################
487              
488             =head2 Class Games::Cards::Hand
489              
490             A Hand represents a player's hand. Most significantly, it's a CardSet which
491             is different from a Pile because the Cards in it are unordered. We may
492             reference any part of the CardSet, not just the top or bottom.
493             Methods:
494              
495             =over 4
496              
497             =cut
498              
499             {
500             package Games::Cards::Hand;
501              
502             @Games::Cards::Hand::ISA = qw(Games::Cards::CardSet);
503             # Use SUPER::new
504              
505             =item give_a_card(RECEIVER, DESCRIPTION)
506              
507             Transfers Card described by DESCRIPTION from the donor (the Hand on which
508             this method was called) to the CardSet RECEIVER. This method can used for
509             discarding a card from a hand, e.g.
510              
511             If DESCRIPTION matches /^-?\d+$/, then it is the index in the cards array of the
512             Card to give. Otherwise, DESCRIPTION is passed to Hand::index.
513              
514             Returns 1 usually. If the donor does not have the card, it returns 0 and does
515             not transfer anything.
516              
517             =cut
518              
519             sub give_a_card {
520 0     0   0 my ($donor, $receiver) = (shift, shift);
521 0         0 my $description = shift;
522              
523             # Which card to remove?
524 0 0       0 my $donor_index = $description =~ /^-?\d+$/ ?
525             $description :
526             $donor->index($description);
527              
528 0 0 0     0 unless (defined $donor_index && $donor_index < $donor->size) {
529             #print $donor->name . " does not have that card\n";
530 0         0 return;
531             }
532              
533             # Remove the card
534 0         0 my $card_ref = $donor->remove_a_card($donor_index);
535             #print $donor->name, " gives ";
536             #print map {$_->print("short") . " "} @$cards_ref;
537             #print " to ", $receiver->name, "\n";
538              
539             # Add the card
540 0         0 $receiver->add_cards([$card_ref]); # add_cards takes an array ref
541              
542 0         0 return 1;
543             } # end sub Games::Cards::Hand::give_card
544              
545             =item move_card(DESCRIPTION, INDEX)
546              
547             Rearrange a Hand by putting Card described by DESCRIPTION at index INDEX.
548              
549             If DESCRIPTION matches /^-?\d+$/, then it is the index in the cards array of the
550             Card to give. Otherwise, DESCRIPTION is passed to Hand::index.
551              
552             Returns 1 usually. If the donor does not have the card, it returns 0 and does
553             not transfer anything.
554              
555             =cut
556              
557             sub move_card {
558 0     0   0 my $hand = shift;
559 0         0 my ($description, $final) = @_;
560              
561             # Which card to remove?
562 0 0       0 my $initial = $description =~ /^-?\d+$/ ?
563             $description :
564             $hand->index($description);
565              
566             # don't have that card!
567 0 0       0 return unless defined $initial;
568              
569             # Remove the card
570 0         0 my $card_ref = $hand->remove_a_card($initial);
571              
572             # Add the card
573 0         0 $hand->add_a_card($card_ref, $final);
574              
575 0         0 return 1;
576             } # end sub Games::Cards::Hand::move_card
577              
578             sub remove_a_card {
579             # remove (and return a ref to an array with) card number arg1 of the Hand
580 0     0   0 my ($thing, $number) = @_;
581             # splice returns an array ref
582 0         0 my $listref = $thing->splice ($number,1);
583 0         0 return $listref->[0];
584             } # end sub Games::Cards::Stack::remove_cards
585              
586             sub add_a_card {
587             # add card arg1 at position arg2 number arg1 of the Hand arg0
588 0     0   0 my ($thing, $card, $number) = @_;
589 0         0 $thing->splice ($number,0,[$card]);
590             } # end sub Games::Cards::Stack::remove_cards
591              
592             sub add_cards {
593             # Add array of Cards arg1 to the Hand
594             # This sub is called by Pile::give_cards & doesn't care where in the
595             # Hand the cards end up. So just put 'em at the end
596 0     0   0 my ($thing, $cards) = @_;
597 0         0 $thing->splice($thing->size, 0, $cards);
598             } # end sub Games::Cards::Hand::add_cards
599              
600             =item index(DESCRIPTION)
601              
602             Given a card description DESCRIPTION return the index of that Card
603             in the Hand, or undef if it was not found. DESCRIPTION may be a Card or
604             a string (like "8H" or "KC").
605              
606             =cut
607              
608             sub index {
609             # Depending on the nature of the description arg1, we create a sub
610             # to match that description with a Card. Then we search among the
611             # cards in Hand arg0's cards array with that sub
612 0     0   0 my ($set, $description) = @_;
613 0         0 my $number;
614             my $find; # sub whose arg0 is a card to compare to
615              
616 0 0       0 if (ref $description eq "Games::Cards::Card") {
    0          
617 0     0   0 $find = sub {shift == $description};
  0         0  
618              
619             # but it matches 2-10 or AKQJ of CHDS
620             # TODO need to change this for multiple decks!
621             } elsif ($description =~ /^[\dakqj]+[chds]/i) {
622 0     0   0 $find = sub {shift->truename eq uc($description)};
  0         0  
623             } else {
624 0         0 my $caller = (caller(0))[3];
625 0         0 die "$caller called with unknown description $description\n";
626             }
627              
628 0         0 foreach my $i (0..$#{$set->{"cards"}}){
  0         0  
629 0         0 my $card = $set->{"cards"}->[$i];
630 0 0       0 $number = $i if &$find($card);
631             }
632              
633 0         0 return $number; # will return undef if card wasn't found
634             }
635              
636             sub print_ordered_cards {
637             # returns the cards in the set in the correct order to be printed
638 0     0   0 return shift->{"cards"};
639             } # end sub Games::Cards::Hand::print_ordered_cards
640              
641             } #end package Games::Cards::Hand
642              
643             ##################
644              
645             =head2 Class Games::Cards::CardSet
646              
647             A CardSet is just an array of cards (stored in the "cards" field). It could be
648             a player's hand, a deck, or a discard pile, for instance. This is a super class
649             of a number of other classes, and those subclasses should be used instead.
650              
651             =over 4
652              
653             =cut
654              
655             #####################
656              
657             {
658             package Games::Cards::CardSet;
659             # Fields:
660             # cards - array of Cards
661             # name - "Joe's Hand" for Joe's hand, "discard" for a
662             # discard pile, etc.
663              
664             =item new(GAME, NAME, NICKNAME)
665              
666             create a new (empty) CardSet. GAME is the Game object that this set belongs
667             to. NAME is a unique string that e.g. can be output when you print the CardSet.
668             Optionally, NICKNAME is a (unique!) short name that will be used to reference
669             the set.
670              
671             =cut
672              
673             sub new {
674 5     5   7 my $self = shift;
675             # so we can say $foo->new or new Bar
676 5   33     20 my $class = ref($self) || $self;
677 5         6 my $game = shift;
678             # TODO use carp!
679 5   50     11 my $name = shift || die "new $class must be called with a 'name' arg";
680 5         5 my $nickname = shift; # may be undef
681 5         19 my $set = {
682             "cards" => [],
683             "name" => $name,
684             "nickname" => $nickname,
685             };
686 5         11 bless $set, $class;
687              
688             # If this set is named "a" in this Game, then store
689             # "a"=>$set in the Game object. Same for nickname
690 5         12 $game->store_cardset($set);
691              
692 5         16 return $set;
693             } # end sub Games::Cards::CardSet::new
694              
695             # Splice cards into/out of a set
696             # Just like Perl's splice (with different argument types!)
697             # RESULT = splice(ARRAY, OFFSET, LENGTH, LIST);
698             # ARRAY is a CardSet,
699             # OFFSET is the index in the "cards" array
700             # LENGTH is the number of cards spliced out,
701             # LIST is a reference to an array of Cards to splice in
702             # RESULT is (empty or) a ref to an array of Cards that were spliced out
703             # (LENGTH and LIST are optional)
704             #
705             # This sub is private. People should use add_cards et al., which call
706             # this sub
707             sub splice {
708 1288     1288   1763 my ($set, $offset, $length, $in_cards) = @_;
709             # set in_cards to empty list if undef. Otherwise, we'd splice in (undef)
710 1288 100       2902 $in_cards = [] unless defined $in_cards;
711              
712             # Negative offsets will break if we try to undo them
713 1288 100       2913 $offset += $set->size if $offset < 0;
714              
715             # If we didn't get length, splice to end of array
716 1288 100       2641 $length = $set->size - $offset unless defined $length;
717             # print $set->name, ": ",$set->size,
718             # " cards - $length starting at $offset",
719             # " + ", scalar(@$in_cards)," = ";
720              
721             # Can't splice in past position #$cards+1==foo->size
722             # Can't splice out more cards than we have
723 1288 50 33     2239 warn "illegal splice!\n" if $offset > $set->size ||
724             $length + $offset > $set->size;
725              
726             # Do the splice
727 1288         1684 my $out_cards = [splice (@{$set->{"cards"}}, $offset,
  1288         3395  
728             $length, @$in_cards)];
729              
730             # Store the splice & its result for Undo
731 1288         7604 my $atom = new Games::Cards::Undo::Splice {
732             "set" => $set,
733             "offset" => $offset,
734             "length" => $length,
735             "in_cards" => $in_cards,
736             "out_cards" => $out_cards,
737             };
738 1288         3284 $atom->store; # store the atom in the Undo List
739              
740             # in_cards now belong to this set
741             # out_cards will be handled by another splice, presumably
742 1288         2365 foreach (@$in_cards) { $_->set_owning_cardset($set) }
  778         1533  
743              
744             # print $set->size,"\n";
745 1288         4631 return $out_cards;
746             } # end sub Games::Cards::CardSet::splice
747              
748             =item shuffle
749              
750             shuffles the cards in the CardSet. Shuffling is not undoable.
751              
752             =cut
753              
754             sub shuffle {
755             # shuffle the deck (or subset thereof)
756 1     1   2 my $deck = shift;
757              
758             # "Random Schwartz"
759             # Replace the cards in the deck with shuffled cards
760             # (Just pick N random numbers & sort them)
761 1         9 @{$deck->{"cards"}} =
  52         56  
762 236         247 map { $_->[0] }
763 52         75 sort { $a->[1] <=> $b->[1] }
764 1         9 map { [$_, rand] }
765 1         2 @{$deck->{"cards"}};
766              
767 1         10 return;
768             } # end sub CardSet::Shuffle
769              
770             =item sort_by_value
771              
772             Sorts the Set by value. This and other sort routines will probably be used
773             mostly on Hands, which are "ordered sets", but you might want to reorder a deck
774             or something. Sorting is not undoable.
775              
776             =item sort_by_suit
777              
778             Sorts the Set by suit, but not by value within the suit.
779              
780             =item sort_by_suit_and_value
781              
782             Sorts the Set by suit, then by value within the suit.
783              
784             =cut
785              
786             sub sort_by_value {
787 0     0   0 my $set = shift;
788 0         0 @{$set->{"cards"}} = sort {$a->value <=> $b->value} @{$set->{"cards"}}
  0         0  
  0         0  
  0         0  
789             } # end sub Games::Cards::CardSet::sort_by_value
790              
791             sub sort_by_suit {
792 0     0   0 my $set = shift;
793 0         0 @{$set->{"cards"}} = sort {$a->suit_value <=> $b->suit_value}
  0         0  
  0         0  
794 0         0 @{$set->{"cards"}}
795             } # end sub Games::Cards::CardSet::sort_by_suit
796              
797             sub sort_by_suit_and_value {
798 0     0   0 my $set = shift;
799 0 0       0 @{$set->{"cards"}} = sort {$a->suit_value <=> $b->suit_value ||
  0         0  
  0         0  
800             $a->value <=> $b->value}
801 0         0 @{$set->{"cards"}}
802             } # end sub Games::Cards::CardSet::sort_by_suit_and_value
803              
804             =item clone(GAME, NAME, NICKNAME)
805              
806             Create a copy of this CardSet. That is, create an object with the same class
807             as arg0. Then make a copy of each Card in the CardSet (true copy, not a
808             reference). arg1 is the Game that the set belongs to. arg2 is the name to give
809             the new CardSet. arg3 (optional) is the nickname.
810              
811             =cut
812              
813             sub clone {
814 0     0   0 my $this = shift;
815 0         0 my $clone = $this->new(@_);
816 0         0 my $game = shift; # shift *after* using @_!
817              
818 0         0 $clone->{"cards"} = [map {$_->clone($game)} @{$this->cards}];
  0         0  
  0         0  
819 0         0 foreach (@{$clone->cards}) {$_->set_owning_cardset($clone)};
  0         0  
  0         0  
820              
821 0         0 return $clone;
822             } # end sub Games::Cards::CardSet::clone
823              
824             =item face_down
825              
826             Makes a whole CardSet face down
827              
828             =cut
829              
830             sub face_down {
831 0     0   0 foreach (@{shift->{"cards"}}) {$_->face_down}
  0         0  
  0         0  
832             } # end sub Games::Cards::CardSet::face_down
833              
834             =item face_up
835              
836             Makes a whole CardSet face up
837              
838             =cut
839              
840             sub face_up {
841 0     0   0 foreach (@{shift->{"cards"}}) {$_->face_up}
  0         0  
  0         0  
842             } # end sub Games::Cards::CardSet::face_up
843              
844             =item print(LENGTH)
845              
846             Returns a string containing a printout of the Cards in the CardSet. Prints
847             a long printout if LENGTH is "long", short if "short" (or nothing).
848             The CardSet is printed out in reverse order, so that the top card of the set is
849             printed first.
850              
851             =cut
852              
853             sub print {
854 0     0   0 my $set = shift;
855 0         0 my $length = shift;
856 0   0     0 my $long = $length && $length eq "long";
857 0         0 my $max_per_line = 10;
858 0         0 my $i = 0;
859 0         0 my $to_print = "";
860             #print $set->{"name"}." has " . $set->size . " cards\n";
861              
862 0 0       0 $to_print .= $set->{"name"} . ":" . ($long ? "\n" : " ");
863              
864             # Print. Different types of Sets are printed in different order
865 0         0 foreach my $card (@{$set->print_ordered_cards}) {
  0         0  
866 0         0 $to_print .= $card->print($length);
867 0 0       0 if ($long) {
868 0         0 $to_print .= "\n";
869             } else { # short printout
870 0 0       0 if (++$i % $max_per_line) {
871 0         0 $to_print .= " ";
872             } else {
873 0         0 $to_print .= "\n";
874 0         0 $to_print .= " " x (length($set->{"name"}) + 1);
875             }
876             } # end if (short or long printout?)
877             }
878             # Or, if there are no cards...
879 0 0       0 $to_print .= "(none)" unless $set->size;
880              
881             # Always print \n at end, but don't print 2
882 0         0 chomp($to_print);
883 0         0 $to_print .= "\n";
884              
885 0         0 return $to_print;
886             } # end sub CardSet::Print
887              
888             =item name
889              
890             Returns the name of the Set
891              
892             =cut
893              
894 835     835   3210 sub name {return shift->{"name"}}
895              
896             =item nickname
897              
898             Returns the nickname of the Set (or undef if there is none)
899              
900             =cut
901              
902 5     5   24 sub nickname {return shift->{"nickname"}}
903              
904             =item cards
905              
906             Returns a reference to the array of Cards in the set
907              
908             =cut
909              
910 0     0   0 sub cards { return shift->{"cards"}; }
911              
912             =item size
913              
914             Tells how many cards are in the set
915              
916             =cut
917              
918 5413     5413   5526 sub size { return scalar(@{shift->{"cards"}}); }
  5413         18223  
919              
920             =back
921              
922             =cut
923              
924             } # end package Games::Cards::CardSet
925              
926             ######################################################################
927              
928             =head2 Class Games::Cards::Card
929              
930             A Card is a playing card. Methods:
931              
932             =over 4
933              
934             =cut
935              
936             {
937             package Games::Cards::Card;
938             # One playing card
939             # name is the name of the card (2-9, ace, king, queen, jack)
940             # value is the value of the card: e.g. ace may be 14 or 1. king may be 13 or 10.
941             # suit is the suit
942             # suit_value is the value of the suit: e.g. in bridge spades is 4, clubs 1
943             # (although that may change after bidding!)
944             # face_up tells whether the player can see the card
945             # owner is the name of the CardSet that this Card belongs to. A Card can
946             # only belong to one CardSet! (We store the name because storing a pointer
947             # might screw up garbage collection.)
948              
949             =item new(GAME, HASHREF)
950              
951             creates a new card. GAME is the Game this card is being created in. HASHREF
952             references a hash with keys "suit" and "name".
953              
954             =cut
955              
956             sub new {
957 52     52   60 my $a = shift;
958 52   33     160 my $class = ref($a) || $a;
959 52         52 my $game = shift;
960 52         50 my $hashref = shift;
961 52         210 my $card = {
962             "name" => $hashref->{"name"},
963             "suit" => $hashref->{"suit"},
964             "value" => $hashref->{"value"},
965             "suit_value" => $hashref->{"suit_value"},
966             "face_up" => 1, # by default, you can see a card
967             "owner" => undef,
968             };
969              
970             # turn it into a playing card
971 52         93 bless $card, $class;
972              
973             # store a pointer to the card in the Game object
974 52         78 $game->store_card($card);
975              
976 52         75 return $card;
977             } # end sub Games::Cards::Card::new
978              
979             =item clone(GAME)
980              
981             makes a copy of the Card (not just a reference to it).
982              
983             =cut
984              
985             sub clone {
986 0     0   0 my $old_card = shift;
987 0         0 my $game = shift;
988 0         0 my $class = ref($old_card);
989 0         0 my $suit = $old_card->suit("long");
990 0         0 my $name = $old_card->name("long");
991 0         0 my $value = $old_card->value;
992 0         0 my $suit_value = $old_card->suit_value;
993 0         0 my $new_card = $old_card->new ($game, {
994             "suit"=>$suit, "name"=> $name,
995             "suit_value" => $suit_value, "value" => $value
996             });
997              
998 0 0       0 $old_card->is_face_up ? $new_card->face_up : $new_card->face_down;
999             # Don't set owner, because it may be different
1000            
1001 0         0 return $new_card;
1002             } # end sub Games::Cards::Card::clone
1003              
1004             =item print(LENGTH)
1005              
1006             returns a string with the whole card name ("King of Hearts") if LENGTH is
1007             "long", otherwise a short version ("KH").
1008              
1009             =cut
1010              
1011             sub print {
1012 0     0   0 my $card = shift;
1013 0         0 my $length = shift;
1014 0   0     0 my $long = $length && $length eq "long";
1015 0         0 my ($name, $suit) = ($card->name($length), $card->suit($length));
1016 0         0 my $face_up = $card->{"face_up"};
1017              
1018 0 0       0 $long ? (
    0          
    0          
1019             $face_up ?
1020             $name . " of " . $suit :
1021             "(Face down card)"
1022             ) : ( # long
1023             $face_up ?
1024             sprintf("%3s ", $name . $suit) :
1025             "*** "
1026             )
1027             ;
1028              
1029             } # end sub Card::print
1030              
1031             =item truename
1032              
1033             Gives a unique description of this card, i.e., you're guaranteed that no
1034             other card in the Game will have the same description.
1035              
1036             =cut
1037              
1038             sub truename {
1039 52     52   53 my $self = shift;
1040 52         83 return join("", $self->name, $self->suit);
1041             } # end sub Games::Cards::Card::truename
1042            
1043             =item name(LENGTH)
1044              
1045             prints the name of the card. The full name ("King") if LENGTH is "long";
1046             otherwise a short version ("K");
1047              
1048             =cut
1049              
1050             sub name {
1051 52     52   63 my $name = shift->{"name"};
1052 52         46 my $length = shift;
1053 52   33     90 my $long = $length && $length eq "long";
1054            
1055 52 100       122 if ($name =~ /^\d+$/) {
1056 36         85 return $name;
1057             } else {
1058 16 50       57 return $long ? $name : uc(substr($name, 0, 1));
1059             }
1060             } # end sub Games::Cards::Card::name
1061              
1062             =item suit(LENGTH)
1063              
1064             Returns the suit of the card. Returns the long version ("Diamonds") if LENGTH
1065             is "long", else a short version ("D").
1066              
1067             =cut
1068              
1069             sub suit {
1070 52     52   58 my $suit = shift->{"suit"};
1071 52         49 my $length = shift;
1072 52   33     82 my $long = $length && $length eq "long";
1073 52 50       181 return $long ? $suit : uc(substr($suit,0,1));
1074             } # end sub Games::Cards::Card::suit
1075              
1076             =item color
1077              
1078             Is the card "red" or "black"? Returns the color or undef for unknown color.
1079              
1080             =cut
1081              
1082             sub color {
1083 0     0   0 my $suit = shift->suit("long");
1084 0         0 my %color_map = (
1085             "Diamonds" => "red",
1086             "Hearts" => "red",
1087             "Spades" => "black",
1088             "Clubs" => "black",
1089             );
1090              
1091 0 0       0 if (exists ($color_map{$suit})) {
1092 0         0 return $color_map{$suit};
1093             } else {
1094 0         0 warn "unknown suit '$suit'";
1095 0         0 return;
1096             }
1097             } # end sub Games::Cards::Card::color
1098              
1099             =item value
1100              
1101             Calculates the value of a card
1102              
1103             =cut
1104              
1105 322     322   832 sub value { return shift->{"value"}}
1106              
1107             =item suit_value
1108              
1109             Returns the suit_value of a card (1..number of suits)
1110              
1111             =cut
1112              
1113 0     0   0 sub suit_value { return shift->{"suit_value"}}
1114              
1115             =item is_face_up
1116              
1117             Returns true if a card is face up
1118              
1119             =cut
1120              
1121 0     0   0 sub is_face_up { return shift->{"face_up"} }
1122              
1123             =item is_face_down
1124              
1125             Returns true if a card is face down
1126              
1127             =cut
1128              
1129 0     0   0 sub is_face_down { return !shift->{"face_up"} }
1130              
1131             =item face_up
1132              
1133             Makes a card face up
1134              
1135             =cut
1136              
1137             sub face_up {
1138 0     0   0 my $card = shift;
1139 0 0       0 unless ($card->{"face_up"}) {
1140 0         0 $card->{"face_up"} = 1;
1141 0         0 my $atom = new Games::Cards::Undo::Face {
1142             "card" => $card,
1143             "direction" => "up",
1144             };
1145 0         0 $atom->store; # store the atom in the Undo List
1146             }
1147             } # end sub Games::Cards::Card::face_up
1148              
1149             =item face_down
1150              
1151             Makes a card face down
1152              
1153             =cut
1154              
1155             sub face_down {
1156 0     0   0 my $card = shift;
1157 0 0       0 if ($card->{"face_up"}) {
1158 0         0 $card->{"face_up"} = 0;
1159 0         0 my $atom = new Games::Cards::Undo::Face {
1160             "card" => $card,
1161             "direction" => "down",
1162             };
1163 0         0 $atom->store; # store the atom in the Undo List
1164             }
1165             } # end sub Games::Cards::Card::face_down
1166              
1167             =item owning_cardset
1168              
1169             Returns the CardSet which this Card is a part of
1170              
1171             =item set_owning_cardset(SET_OR_NAME)
1172              
1173             Makes the Card a part of a CardSet. Arg0 is either an actual CardSet ref, or
1174             the name of a CardSet.
1175              
1176             =cut
1177              
1178             sub owning_cardset {
1179 0     0   0 my $self = shift;
1180 0         0 my $set_name = $self->{"owner"};
1181 0         0 my $game = &Games::Cards::Game::current_game;
1182 0         0 my $set = $game->get_cardset_by_name($set_name);
1183             # TODO use carp!
1184 0 0       0 warn $self->print("long"), " doesn't belong to any CardSets!\n"
1185             unless defined $set;
1186 0         0 return $set;
1187             }
1188             sub set_owning_cardset {
1189 830     830   1153 my ($self, $cardset) = @_;
1190 830 50       2988 $self->{"owner"} =
1191             $cardset->isa("Games::Cards::CardSet") ? $cardset->name : $cardset;
1192             } # end sub Games::Cards::Card::set_owning_cardset
1193              
1194             =back
1195              
1196             =cut
1197              
1198             } # end package Card
1199              
1200              
1201             1; # end package Games::Cards
1202              
1203             __END__