File Coverage

blib/lib/Games/Set.pm
Criterion Covered Total %
statement 47 53 88.6
branch 6 6 100.0
condition n/a
subroutine 12 13 92.3
pod 5 5 100.0
total 70 77 90.9


line stmt bran cond sub pod time code
1 1     1   760 use strict;
  1         3  
  1         36  
2             package Games::Set;
3 1     1   491 use Games::Set::Card;
  1         1  
  1         9  
4 1     1   810 use Algorithm::ChooseSubsets;
  1         580  
  1         28  
5 1     1   630 use Algorithm::GenerateSequence;
  1         377  
  1         25  
6 1     1   6 use List::Util qw( max );
  1         1  
  1         89  
7 1     1   4 use Class::Accessor::Fast;
  1         2  
  1         8  
8 1     1   20 use base 'Class::Accessor::Fast';
  1         1  
  1         67  
9             __PACKAGE__->mk_accessors(qw( deck ));
10              
11 1     1   4 use vars qw( $VERSION );
  1         1  
  1         491  
12             $VERSION = '0.01';
13              
14             =head1 NAME
15              
16             Games::Set - The rules for the game of Set
17              
18             =head1 SYNOPSIS
19              
20             my $game = Games::Set->new({ deck => [ Games::Set->standard_deck ] });
21             $game->shuffle;
22             my @cards = map { $game->deal } 1..3; # give me 3 cards
23             print $game->set( @cards ) ? "set\n" : "no set\n";
24              
25             =head1 DESCRIPTION
26              
27             Games::Set understands some of the rules of the card game Set. It
28             also knows how to generate a standard deck.
29              
30             =head1 METHODS
31              
32             =head2 new
33              
34             Class::Accessor inherited constructor, returns a new gamestate
35              
36             =head2 deck
37              
38             The current deck. A reference to an array containing many
39             Games::Set::Card objects.
40              
41             =head2 shuffle
42              
43             Shuffle the current deck.
44              
45             =cut
46              
47             # the fisher-yates shuffle from perlfaq4
48              
49             sub shuffle {
50 0     0 1 0 my $self = shift;
51 0         0 my $deck = $self->deck;
52              
53 0         0 my $i = @$deck;
54 0         0 while ($i--) {
55 0         0 my $j = int rand ($i+1);
56 0         0 @$deck[$i,$j] = @$deck[$j,$i];
57             }
58             }
59              
60             =head2 deal
61              
62             Take a card from the top of the deck.
63              
64             =cut
65              
66             sub deal {
67 2     2 1 2425 my $self = shift;
68 2         3 shift @{ $self->deck };
  2         6  
69             }
70              
71             =head2 standard_deck
72              
73             Calculates the standard deck as a list of Games::Set::Card objects.
74              
75             =cut
76              
77             sub standard_deck {
78 1     1 1 3 my $self = shift;
79 1         8 my $iter = Algorithm::GenerateSequence->new(
80             values %Games::Set::Card::properties
81             );
82 81         1910 map {
83 1         16 my %h; @h{ keys %Games::Set::Card::properties } = @$_;
  81         240  
84 81         199 Games::Set::Card->new(\%h)
85             } $iter->as_list;
86             }
87              
88              
89             =head2 set( @cards )
90              
91             Returns true if the cards make a set.
92              
93             =cut
94              
95             sub set {
96 85323     85323 1 100512 my $self = shift;
97 85323         178501 for my $property ( keys %Games::Set::Card::properties ) {
98 126477         119209 my %seen;
99 126477         368513 $seen{ $_->$property() }++ for @_;
100 126477 100       1674030 next if (keys %seen) == 1; # all same
101 113402 100       324592 next if (max values %seen) == 1; # all different
102 84241         416549 return;
103             }
104 1082         5674 return 1;
105             }
106              
107             =head2 find_sets( @cards )
108              
109             returns all the possible sets within @cards as array references
110              
111             =cut
112              
113             sub find_sets {
114 1     1 1 497 my $self = shift;
115              
116 1         1 my @found;
117 1         7 my $iter = Algorithm::ChooseSubsets->new( \@_, 3 );
118 1         21 while (my $set = $iter->next) {
119 85320 100       1396554 push @found, $set if $self->set(@$set);
120             }
121 1         253 return @found;
122             }
123              
124              
125             1;
126             __END__