File Coverage

blib/lib/Games/Word.pm
Criterion Covered Total %
statement 89 89 100.0
branch 23 24 95.8
condition 16 18 88.8
subroutine 18 18 100.0
pod 10 10 100.0
total 156 159 98.1


line stmt bran cond sub pod time code
1             package Games::Word;
2             BEGIN {
3 15     15   323598 $Games::Word::AUTHORITY = 'cpan:DOY';
4             }
5             {
6             $Games::Word::VERSION = '0.06';
7             }
8 15     15   103 use strict;
  15         32  
  15         513  
9 15     15   76 use warnings;
  15         26  
  15         646  
10 15     15   82 use base 'Exporter';
  15         29  
  15         2660  
11             our @EXPORT_OK = qw/random_permutation is_permutation all_permutations
12             shared_letters shared_letters_by_position
13             random_string_from
14             is_substring all_substrings
15             is_subpermutation all_subpermutations/;
16              
17 15     15   23096 use Math::Combinatorics qw/factorial/;
  15         352495  
  15         1805  
18 15     15   17545 use Test::Deep::NoTest;
  15         228518  
  15         134  
19             # ABSTRACT: utility functions for writing word games
20              
21              
22             sub random_permutation {
23 281     281 1 60203 my $word = shift;
24              
25 281 100       1185 return '' if $word eq '';
26              
27 218         695 my $letter = substr $word, int(rand length $word), 1, '';
28              
29 218         330 return $letter . random_permutation($word);
30             }
31              
32              
33             sub is_permutation {
34 20     20 1 21181 my @word_letters = split //, shift;
35 20         68 my @perm_letters = split //, shift;
36              
37 20         71 return eq_deeply(\@word_letters, bag(@perm_letters));
38             }
39              
40             sub _permutation {
41 494     494   892 my $word = shift;
42 494         491 my $perm_index = shift;
43              
44 494 100       1454 return '' if $word eq '';
45              
46 365         399 my $len = length $word;
47 365 50 33     722 die "invalid permutation index" if $perm_index >= factorial($len) ||
48             $perm_index < 0;
49              
50 15     15   28448 use integer;
  15         175  
  15         141  
51              
52 365         3965 my $current_index = $perm_index / factorial($len - 1);
53 365         2607 my $rest = $perm_index % factorial($len - 1);
54              
55 365         2281 my $first_letter = substr($word, $current_index, 1);
56 365         440 substr($word, $current_index, 1) = '';
57              
58 365         594 return $first_letter . _permutation($word, $rest);
59             }
60              
61              
62             sub all_permutations {
63 40     40 1 52 my $word = shift;
64              
65 40         57 my @ret = ();
66             push @ret, _permutation($word, $_)
67 40         110 for 0..(factorial(length $word) - 1);
68              
69 40         172 return @ret;
70             }
71              
72              
73             sub shared_letters {
74 9     9 1 10242 my @a = sort split //, shift;
75 9         44 my @b = sort split //, shift;
76              
77 9         38 my @letters = ();
78 9         19 my ($a, $b) = (shift @a, shift @b);
79 9   100     62 while (defined $a && defined $b) {
80 30 100       60 if ($a eq $b) {
    100          
81 25         31 push @letters, $a;
82 25         106 ($a, $b) = (shift @a, shift @b);
83             }
84             elsif ($a lt $b) {
85 3         15 $a = shift @a;
86             }
87             else {
88 2         9 $b = shift @b;
89             }
90             }
91              
92 9         56 return @letters;
93             }
94              
95              
96             sub shared_letters_by_position {
97 18     18 1 73 my @a = split //, shift;
98 18         50 my @b = split //, shift;
99              
100 18         29 my @letters = ();
101 18         67 while (my ($a, $b) = (shift @a, shift @b)) {
102 94 100 100     261 last unless (defined $a || defined $b);
103 76 100 100     346 if (defined $a && defined $b && $a eq $b) {
      100        
104 20         66 push @letters, $a;
105             }
106             else {
107 56         168 push @letters, undef;
108             }
109             }
110              
111 18 100       92 return wantarray ? @letters : grep { defined } @letters;
  38         102  
112             }
113              
114              
115             sub random_string_from {
116 13     13 1 421039 my ($letters, $length) = @_;
117              
118 13 100 100     110 die "invalid letter list" if length $letters < 1 && $length > 0;
119 12         64 my @letters = split //, $letters;
120 12         30 my $ret = '';
121 12         153 $ret .= $letters[int rand @letters] for 1..$length;
122              
123 12         56 return $ret;
124             }
125              
126              
127             sub is_substring {
128 23     23 1 5584 my ($substring, $string) = @_;
129              
130 23 100       69 return 1 if $substring eq '';
131 21 100       47 return 0 if $string eq '';
132 20         50 my $re = join('?', map { quotemeta } split(//, $string)) . '?';
  79         145  
133 20         256 return $substring =~ /^$re$/;
134             }
135              
136              
137             sub all_substrings {
138 62     62 1 2430 my $string = shift;
139              
140 62 100       159 return ('') if $string eq '';
141              
142 30         50 my @substrings = ($string);
143 30         34 my $before = '';
144 30         53 my $current = substr $string, 0, 1, '';
145 30         85 while ($current) {
146 90         235 @substrings = (@substrings,
147 51         123 map { $before . $_ } all_substrings($string));
148 51         89 $before .= $current;
149 51         116 $current = substr $string, 0, 1, '';
150             }
151              
152 30         102 return @substrings;
153             }
154              
155              
156             sub is_subpermutation {
157 21     21 1 40047 my @subword = split //, shift;
158 21         80 my @word = split //, shift;
159              
160 21         91 return eq_deeply(\@subword, subbagof(@word));
161             }
162              
163              
164             sub all_subpermutations {
165 6     6 1 5189 return map { all_permutations $_ } all_substrings shift;
  39         71  
166             }
167              
168              
169             1;
170              
171             __END__