File Coverage

blib/lib/Game/Deckar.pm
Criterion Covered Total %
statement 157 157 100.0
branch 56 58 96.5
condition 20 21 95.2
subroutine 27 27 100.0
pod 11 11 100.0
total 271 274 98.9


line stmt bran cond sub pod time code
1             # -*- Perl -*-
2             # a module for managing decks of cards
3             package Game::Deckar;
4             our $VERSION = '0.02';
5 3     3   407325 use 5.26.0;
  3         11  
6 3     3   20 use warnings;
  3         9  
  3         219  
7 3     3   2257 use Object::Pad 0.66;
  3         41131  
  3         281  
8              
9             sub fisher_yates_shuffle {
10 1     1 1 3 my ($deck) = @_;
11 1         3 my $i;
12 1         5 for ( $i = @$deck; --$i; ) {
13 6         41 my $j = int rand( $i + 1 );
14 6 100       16 next if $i == $j;
15 5         41 @$deck[ $i, $j ] = @$deck[ $j, $i ];
16             }
17             }
18              
19             class Game::Deckar::Card {
20 8     8   33 field $data :param :reader;
  8         42  
21             field %meta;
22              
23             BUILD {
24             my (%param) = @_;
25             %meta = $param{meta}->%* if exists $param{meta};
26             }
27              
28 7     7   16 method meta ($name) { $meta{$name} }
  7         32  
  7         12  
  7         8  
  7         26  
29              
30 3     3   5 method set_meta ( $name, $value ) {
  3         9  
  3         12  
  3         5  
  3         7  
31 3         7 my $old = $meta{$name};
32 3         5 $meta{$name} = $value;
33 3     1   14 return sub { $self->set_meta( $name, $old ); };
  1         3  
34             }
35             }
36              
37             class Game::Deckar {
38 3     3   4837 use Carp 'croak';
  3         7  
  3         15830  
39             field %decks;
40              
41             BUILD {
42             my (%param) = @_;
43             if ( exists $param{decks} ) {
44             for my $d ( $param{decks}->@* ) {
45             $decks{$d} = [];
46             }
47             }
48             if ( exists $param{initial} ) {
49             for my $d ( keys $param{initial}->%* ) {
50             croak "no such deck $d" unless exists $decks{$d};
51             push $decks{$d}->@*, $param{initial}->{$d}->@*;
52             }
53             }
54             if ( exists $param{initial_cards} ) {
55             for my $d ( keys $param{initial_cards}->%* ) {
56             push $decks{$d}->@*, map {
57             Game::Deckar::Card->new(
58             data => $_,
59             ( exists $param{meta} ? ( meta => $param{meta} ) : () )
60             )
61             } $param{initial_cards}->{$d}->@*;
62             }
63             }
64             }
65              
66 2     2 1 27 method add_deck ($name) {
  2         9  
  2         5  
  2         4  
67 2 100       192 croak 'deck already exists' if exists $decks{$name};
68 1         4 $decks{$name} = [];
69 1     1   8 return sub { $self->del_deck($name); };
  1         5  
70             }
71              
72 3     3 1 30 method del_deck ($name) {
  3         13  
  3         7  
  3         6  
73 3 100       232 croak 'no such deck' unless exists $decks{$name};
74 2 50       8 croak 'deck is not empty' if $decks{$name}->@*;
75 2         8 my $orig = $decks{$name};
76 2         4 delete $decks{$name};
77 2     1   14 return sub { $decks{$name} = $orig; };
  1         5  
78             }
79              
80 3     3 1 1199 method collect ( $name, @rest ) {
  3         13  
  3         6  
  3         8  
  3         5  
81 3 100       179 croak 'not enough decks' unless @rest;
82 2         5 for my $d ( $name, @rest ) {
83 5 100       180 croak "no such deck $d" unless exists $decks{$d};
84             }
85 1         30 my @rcards = [ $decks{$name}, [ $decks{$name}->@* ] ];
86 1         5 for my $d (@rest) {
87 2 50       7 next if $d eq $name; # so can collect on "get_decks"
88 2         8 push @rcards, [ $decks{$d}, [ $decks{$d}->@* ] ];
89             # cards are put onto the "top" of the target deck, which is
90             # how some humans might do it with real stacks of cards
91 2         9 unshift $decks{$name}->@*, splice $decks{$d}->@*;
92             }
93             return sub {
94 1     1   4 for my $r (@rcards) { $r->[0]->@* = $r->[1]->@* }
  3         11  
95 1         8 };
96             }
97              
98 10     10 1 2851 method deal ( $src, $dst, $index = 0, $top = 1 ) {
  10         46  
  10         22  
  10         20  
  10         29  
  10         14  
  10         17  
99             croak 'no such deck'
100 10 100 100     441 unless exists $decks{$src} and exists $decks{$dst};
101 8         28 my ( $sref, $dref ) = @decks{ $src, $dst };
102 8 100 100     388 croak 'index out of range' if $index < 0 or $index > $sref->$#*;
103 6         19 my $card = splice $sref->@*, $index, 1;
104 6 100       24 splice $dref->@*, ( $top ? 0 : $dref->@* ), 0, $card;
105             return $card, sub {
106 4 100   4   48 splice $sref->@*, $index, 0, splice $dref->@*, ( $top ? 0 : $dref->$#* ), 1;
107 6         43 };
108             }
109              
110 3     3 1 1232 method empty ($name) {
  3         14  
  3         37  
  3         6  
111 3 100       203 croak 'no such deck' unless exists $decks{$name};
112 2 100       174 croak 'deck is empty' unless $decks{$name}->@*;
113 1         7 my @orig = $decks{$name}->@*;
114 1         5 $decks{$name}->@* = ();
115 1     1   9 return sub { $decks{$name}->@* = @orig; };
  1         7  
116             }
117              
118 9     9 1 2933 method get_decks () {
  9         33  
  9         14  
119 9 100       184 croak 'no decks' unless %decks;
120 8         85 return sort keys %decks;
121             }
122              
123 47     47 1 769 method get ($name) {
  47         155  
  47         88  
  47         67  
124 47 100       304 croak 'no such deck' unless exists $decks{$name};
125 46         279 return $decks{$name};
126             }
127              
128 7     7 1 3912 method move ( $src, $dst, $count, $index = 0, $top = 1 ) {
  7         33  
  7         16  
  7         16  
  7         13  
  7         14  
  7         13  
  7         10  
129             croak 'no such deck'
130 7 100 100     427 unless exists $decks{$src} and exists $decks{$dst};
131 5         15 my ( $sref, $dref ) = @decks{ $src, $dst };
132 5 100 100     392 croak 'index out of range' if $index < 0 or $index > $sref->$#*;
133 3 100 66     228 croak 'count out of range' if $count < 1 or $index + $count > @$sref;
134 2         7 my @cards = splice $sref->@*, $index, $count;
135 2 100       10 splice $dref->@*, ( $top ? 0 : $dref->@* ), 0, @cards;
136             return \@cards, sub {
137 2 100   2   17 splice $decks{$src}->@*, $index, 0, splice $dref->@*,
138             ( $top ? 0 : $dref->@* - $count ), $count;
139 2         19 };
140             }
141              
142 10     10 1 12884 method pick ( $src, $dst, $indices, $top = 1 ) {
  10         45  
  10         23  
  10         19  
  10         16  
  10         19  
  10         13  
143             croak 'no such deck'
144 10 100 100     464 unless exists $decks{$src} and exists $decks{$dst};
145 8 100       201 croak 'no indices' unless $indices->@*;
146 7         26 my ( $sref, $dref ) = @decks{ $src, $dst };
147 7 100       193 croak 'too many indices' if $indices->@* > $sref->@*;
148 6         13 my ( @icard, %seen );
149 6         17 for my $index ( $indices->@* ) {
150 11 100 100     395 croak 'index out of range' if $index < 0 or $index > $sref->$#*;
151 9 100       217 croak 'duplicate index' if $seen{$index}++;
152 8         25 push @icard, [$index];
153             }
154 3         13 for my $r ( sort { $b->[0] <=> $a->[0] } @icard ) {
  6         17  
155 7         21 $r->[1] = splice $sref->@*, $r->[0], 1;
156             }
157 3         8 my @cards = map { $_->[1] } @icard;
  7         18  
158 3 100       13 splice $dref->@*, ( $top ? 0 : $dref->@* ), 0, @cards;
159             return \@cards, sub {
160 3     3   9 my $len = @icard;
161 3 100       11 splice $dref->@*, ( $top ? 0 : -$len ), $len;
162 3         10 for my $r ( sort { $a->[0] <=> $b->[0] } @icard ) {
  4         12  
163 7         19 splice $sref->@*, $r->[0], 1, $r->[1];
164             }
165 3         29 };
166             }
167              
168 3     3 1 2977 method shuffle ($name) {
  3         13  
  3         8  
  3         4  
169 3 100       145 croak 'no such deck' unless exists $decks{$name};
170 2         6 my $deck = $decks{$name};
171 2 100       175 croak 'deck is empty' unless $deck->@*;
172 1         6 my @orig = $deck->@*;
173 1         9 fisher_yates_shuffle( $decks{$name} );
174 1     1   9 return sub { $deck->@* = @orig; };
  1         6  
175             }
176             }
177              
178             1;
179             __END__