File Coverage

blib/lib/App/Greple/wordle.pm
Criterion Covered Total %
statement 47 153 30.7
branch 0 68 0.0
condition 0 3 0.0
subroutine 16 33 48.4
pod 0 15 0.0
total 63 272 23.1


line stmt bran cond sub pod time code
1             use v5.14;
2 1     1   806 use warnings;
  1         3  
3 1     1   5 use utf8;
  1         2  
  1         32  
4 1     1   603  
  1         14  
  1         5  
5             our $VERSION = "0.12";
6              
7             use Data::Dumper;
8 1     1   550 use List::Util qw(shuffle max);
  1         6493  
  1         93  
9 1     1   11 use Try::Tiny;
  1         2  
  1         104  
10 1     1   516 use Getopt::EX::Colormap qw(colorize ansi_code);
  1         1880  
  1         71  
11 1     1   559 use Text::VisualWidth::PP 0.05 'vwidth';
  1         21195  
  1         80  
12 1     1   599 use App::Greple::wordle::word_all qw(@word_all %word_all);
  1         22323  
  1         103  
13 1     1   838 use App::Greple::wordle::word_hidden qw(@word_hidden);
  1         5  
  1         495  
14 1     1   749 use App::Greple::wordle::game;
  1         3  
  1         151  
15 1     1   408 use App::Greple::wordle::util qw();
  1         4  
  1         41  
16 1     1   512  
  1         3  
  1         20  
17             use Getopt::EX::Hashed; {
18 1     1   474 Getopt::EX::Hashed->configure( DEFAULT => [ is => 'rw' ] );
  1         7893  
  1         13  
19             has answer => ' =s ' , default => $ENV{WORDLE_ANSWER} ;
20             has index => ' n =i ' , default => $ENV{WORDLE_INDEX} ;
21             has trial => ' x =i ' , default => 6 ;
22             has total => ' =i ' , default => 30 ;
23             has random => ' ! ' , default => 0 ;
24             has series => ' s =i ' , default => 1 ;
25             has compat => ' ' , action => sub { $_->series = 0 } ;
26             has keymap => ' ! ' , default => 1 ;
27             has result => ' ! ' , default => 1 ;
28             has correct => ' =s ' , default => "\N{U+1F389}" ; # PARTY POPPER
29             has wrong => ' =s ' , default => "\N{U+1F4A5}" ; # COLLISION SYMBOL
30             has debug => ' ! ' ;
31             }
32             no Getopt::EX::Hashed;
33 1     1   266  
  1         2  
  1         4  
34             my $app = shift;
35             my $argv = shift;
36 0     0 0   use Getopt::Long qw(GetOptionsFromArray Configure);
37 0           Configure qw(bundling no_getopt_compat pass_through);
38 1     1   936 $app->getopt($argv) || die "Option parse error.\n";
  1         9864  
  1         5  
39 0           $app;
40 0 0         }
41 0            
42             use Date::Calc qw(Delta_Days);
43             my($mday, $mon, $year, $yday) = (localtime(time))[3,4,5,7];
44             Delta_Days(2021, 6, 19, $year + 1900, $mon + 1, $mday);
45 1     1   861 }
  1         5302  
  1         1743  
46 0     0      
47 0           my $app = shift;
48             for ($app->index) {
49             $_ = int rand @word_hidden if $app->random;
50             $_ //= _days;
51 0     0 0   $_ += _days if /^[-+]/;
52 0           }
53 0 0         if (my $answer = $app->answer) {
54 0   0       $app->index = undef;
55 0 0         $word_all{$answer} or die "$answer: wrong word\n";
56             } else {
57 0 0         if ($app->series > 0) {
58 0           srand($app->series);
59 0 0         @word_hidden = shuffle @word_hidden;
60             }
61 0 0         $app->answer = $word_hidden[ $app->index ];
62 0           }
63 0           }
64              
65 0           my $app = shift;
66             my $answer = $app->answer;
67             my @re = map
68             { sprintf "(?<=^.{%d})%s", $_, substr($answer, $_, 1) }
69             0 .. length($answer) - 1;
70 0     0 0   my $green = join '|', @re;
71 0           my $yellow = "[$answer]";
72             my $black = "(?=[a-z])[^$answer]";
73 0           map { ( '--re' => $_ ) } $green, $yellow, $black;
  0            
74             }
75 0            
76 0           my $app = shift;
77 0           my $label = 'Greple::wordle';
78 0           return $label if not defined $app->index;
  0            
79             sprintf('%s %s%s',
80             $label,
81             $app->series == 0 ? '' : sprintf("%d-", $app->series),
82 0     0 0   $app->index);
83 0           }
84 0 0          
85 0 0         ######################################################################
86              
87             my $app = __PACKAGE__->new or die;
88             my $game;
89             my $interactive;
90              
91             sprintf '%d: ', $game->attempt + 1;
92             }
93              
94             my($mod, $argv) = @_;
95             $app->parseopt($argv)->setup;
96             $game = App::Greple::wordle::game->new(answer => $app->answer);
97             push @$argv, $app->patterns;
98 0     0 0   if ($interactive = -t STDIN) {
99             push @$argv, '--interactive', ('/dev/stdin') x $app->total;
100             select->autoflush;
101             say $app->title;
102 0     0 0   print prompt();
103 0           }
104 0           }
105 0            
106 0 0         local $_ = $_;
107 0           my $chomped = chomp;
108 0           print ansi_code("{CHA}{CUU}") if $chomped;
109 0           print ansi_code(sprintf("{CHA(%d)}",
110 0           max(11, vwidth($_) + length(prompt()) + 2)));
111             print s/(?<=.)\z/\n/r for @_;
112             }
113              
114             say colorize('#6aaa64', uc $game->answer);
115 0     0 0   }
116 0            
117 0 0         printf "\n%s %d/%d\n\n", $app->title, $game->attempt, $app->trial;
118 0           say $game->result;
119             }
120 0            
121             my $word = lc s/\n//r;
122             if (not $word_all{$word}) {
123             command($word) or respond $app->wrong;
124 0     0 0   $_ = '';
125             } else {
126             $game->try($word);
127             }
128 0     0 0   }
129 0            
130             my $word = shift;
131             my @cmd = split ' ', $word or return;
132             my @word = @word_all;
133 0     0 0   state @remember;
134 0 0         $cmd[0] =~ /^u(niq)?$/ and unshift @cmd, 'hint';
135 0 0          
136 0           while (@cmd) {
137             local $_ = shift @cmd;
138 0           try {
139             if ($_ eq '|') {}
140             elsif (/^d$/) {
141             $app->debug ^= 1;
142             printf 'Debug %s', $app->debug ? 'on' : 'off';
143 0     0 0   return;
144 0 0         }
145 0           elsif (/^\?$/) { help(); return }
146 0           elsif (/^!!$/) { @word = @remember }
147 0 0         elsif (/^h(int)?$/) { @word = choose($game->hint, @word) }
148             elsif (/^u(niq)?$/) { @word = grep { !/(.).*\1/i } @word }
149 0           elsif (/^=(.+)/) { @word = choose(includes($1), @word) }
150 0           elsif (/^!(.+)/) { @word = choose("^(?!.*[$1])", @word) }
151             elsif (/\W/) { @word = choose($_, @word); }
152 0 0   0     else { return }
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
153             1;
154 0           } or do {
155 0 0         warn "ERROR: $_" if $app->debug;
156 0           return /^[a-z]+$/i ? 0 : 1;
157             };
158 0           }
  0            
159 0           if (@word == 0) {
160 0           warn "No match\n";
161 0           return 1;
  0            
162 0           }
163 0           @remember = @word;
164 0           do {
165 0           local $, = ' ';
166 0           say $game->hint_color(@word);
167 0 0         };
168 0 0         1;
169 0 0         }
170              
171             my $message = << " END";
172 0 0         # d debug
173 0           ? help
174 0           h show hint
175             u uniq
176 0           !! repeat last result
177 0           =<str> include characters
178 0           !<str> exclude characters
179 0           END
180             print $message =~ s/^\s*(#.*)\n//gr;
181 0           }
182              
183             '^' . join '', map { "(?=.*$_)" } $_[0] =~ /./g;
184             }
185 0     0 0    
186             my $p = shift;
187             $p =~ s/([A-Z])/[^$1]/g;
188             warn "> $p\n" if $app->debug;
189             grep /$p/, @_;
190             }
191              
192             if ($game->solved) {
193             respond $app->correct x ($app->trial - $game->attempt + 1);
194 0           show_result if $app->result;
195             exit 0;
196             }
197             if (length) {
198 0     0 0   if ($game->attempt >= $app->trial) {
  0            
199             show_answer;
200             exit 1;
201             }
202 0     0 0   $app->keymap and respond $game->keymap;
203 0           }
204 0 0         print prompt();
205 0           }
206              
207             1;
208              
209 0 0   0 0    
210 0           mode function
211 0 0          
212 0           define GREEN #6aaa64
213             define YELLOW #c9b458
214 0 0         define BLACK #787c7e
215 0 0          
216 0           option default \
217 0           -i --need 1 --no-filename \
218             --cm 555/GREEN \
219 0 0         --cm 555/YELLOW \
220             --cm 555/BLACK
221 0            
222             # --interactive is set in initialize() when stdin is a tty
223              
224             option --interactive \
225             --if 'head -1' \
226             --begin __PACKAGE__::check \
227             --end __PACKAGE__::inspect \
228             --epilogue __PACKAGE__::show_answer