File Coverage

blib/lib/App/Greple/wordle.pm
Criterion Covered Total %
statement 47 141 33.3
branch 0 58 0.0
condition 0 3 0.0
subroutine 16 32 50.0
pod 0 14 0.0
total 63 248 25.4


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