summaryrefslogtreecommitdiff
path: root/wordle.pl
diff options
context:
space:
mode:
Diffstat (limited to 'wordle.pl')
-rw-r--r--wordle.pl264
1 files changed, 264 insertions, 0 deletions
diff --git a/wordle.pl b/wordle.pl
new file mode 100644
index 0000000..716c37c
--- /dev/null
+++ b/wordle.pl
@@ -0,0 +1,264 @@
+#!/bin/perl
+
+use warnings;
+use 5.010;
+use File::Slurp;
+use enum qw(CORRECT IN_WORD WRONG);
+use Term::ANSIColor qw(:constants);
+use Data::Dumper;
+use List::MoreUtils qw(uniq);
+use List::Util qw/shuffle/;
+
+
+say "welcome to my wordle pass filter program, where it tries to guess your wordle\n";
+
+$| = 1;
+# word len determines number of guesses and dict filter.
+my $answer = $ARGV[0];
+my $wlen = length($answer);
+
+# load dictionary
+my @words = read_file("dictionary.txt", chomp => 1);
+say "total dict size: " . scalar @words;
+my $total = scalar @words;
+
+# filter to word length
+@words = grep( {$_ =~ /^.{$wlen}$/} @words);
+say "total $wlen letter words: " . scalar @words . "\n";
+
+# In word letters
+my %iwl;
+# letters in correct position
+my @cl = split('', substr(("_" x $wlen), 0, $wlen));
+# incorrect letters
+my %wl;
+
+# incorrect position, avoid relooking for words that match that.
+my %iwp;
+
+for(0..$wlen-1) {
+ $iwp{$_} = ();
+}
+
+my $guess = "";
+my $tries = 0;
+
+say "picking first word!";
+while($guess ne $answer ) {
+
+ #this can happen if you input a word that doesnt exist in the dictionary..
+ if(scalar @words eq 0) {
+ say "I declare a scrabble challenge! Get out your dictionary!";
+ exit;
+ }
+
+ # pull our guess...
+ $guess = $words[0];
+
+ # optimization for first guess, avoid multiple of same letter, and rank
+ # letters by importance by weigning the frequency they occour in the english
+ # language and assigning a point value to score that from.
+ unless($tries) {
+ my @good_start_words = grep { uniqletters($_) } @words;
+
+ say "filtering letter duplicates for first guess:" . scalar @good_start_words;
+
+ @good_start_words = reverse sort wordvalue @good_start_words;
+ $guess = $good_start_words[0];
+
+ say "picking $guess (score:" . lvalue($guess) . ") because it scores highest in letter frequency.\n";
+ say " let's go!";
+ say " -------------------";
+ } else {
+ $guess = $words[0];
+ }
+
+ # keep track of our tries.
+ $tries++;
+
+ # keep track of how many right letters
+ my $corr = 0;
+
+ # get a check results (requivelant to the colors after sumbitting a word on
+ # the wordle site), and parse through them.
+ for my $res (check($guess)) {
+ # @res = [pos, char, status]
+
+ # if correct, add it to the correct position array
+ if($res->[2] == CORRECT) {
+ $cl[$res->[0]] = $res->[1];
+ $corr++;
+ print GREEN " " . $res->[1] . " " . RESET;
+ }
+ # if its in a word, add to the iwl hash and store its position.
+ elsif($res->[2] == IN_WORD) {
+ print YELLOW " " . $res->[1] . " " . RESET;
+ $iwl{$res->[1]} = 1;
+ push( @{ $iwp{ $res->[0] } }, $res->[1]);
+ }
+ # if its not in a word, add to the wl hash
+ elsif($res->[2] == WRONG ) {
+ print RED " " . $res->[1] . " " . RESET;
+ $wl{$res->[1]} = 1;
+ }
+ }
+ # if we're correct, we have found the right answer.
+ if($corr == $wlen) {
+ say "";
+ last;
+ }
+
+ # rip out any words that match with wrong positions exactly, clunky as heck
+ # but we basically go through iwp, which is an hash containing misses at that
+ # position like
+ # 0: a b c
+ # 1: a b c
+ #
+ # meaning at pos 0, a b c are in the word but not that position.
+ # and so forth
+ #
+ my $ignore = "";
+ foreach (sort keys %iwp) {
+ my $posstr = "";
+ foreach my $array ($iwp{$_}) {
+ $posstr = join('', uniq @$array);
+ }
+ if(length($posstr)) {
+ $ignore .= "[^" . $posstr . "]";
+ } else {
+ $ignore .= ".";
+ }
+ }
+
+ if($ignore =~ /[a-z]/) {
+ @words = grep { $_ =~ /$ignore/ } @words;
+ }
+ print "filtering: " . scalar @words;
+ sleep 1;
+ print "...";
+
+
+ # filter out any words we tried already.shouldnt happen unless our regexs are
+ # failing
+ #if($tries) {
+ # @words = grep { $_ ne $guess } @words;
+ # print scalar @words;
+ #}
+ #sleep 1;
+ #print "...";
+
+ # filter for letters in the answer only
+ if(scalar keys %iwl) {
+ my $pattern = '(' . join ('|', sort keys %iwl) . ')';
+ @words = grep { $_ =~ /$pattern/ } @words;
+ }
+ print scalar @words;
+ sleep 1;
+ print "...";
+
+ # if our guess has some correct letters, grep
+ # for those letters in exact position.
+ if($guess =~ /[^_]/ ) {
+ my $pattern = join('', @cl);
+ $pattern =~ s/_/./ig;
+ @words = grep { $_ =~ /$pattern/ } @words;
+ }
+ print scalar @words;
+ sleep 1;
+ print "...";
+
+ # filter out words with letters on the wrong
+ # letter list
+ if(scalar keys %wl) {
+ my $pattern = '(' . join ('|', sort keys %wl) . ')';
+ @words = grep { not $_ =~ /$pattern/ } @words;
+ }
+ print "" . scalar @words;
+ print "\n";
+
+}
+
+say "\nmy final answer is '$guess'\n";
+say "it took me $tries/$wlen tries\n";
+if($tries <= $wlen) {
+ say "I won!";
+}
+else{
+ say "Sorry, I lose!";
+}
+# say "remaining:" . scalar @words;
+# if($corr == $wlen-1) {
+# say "final guess:" . join('', @cl) . " answer: $answer";
+# exit
+# }
+#
+sub check {
+ my $word = shift;
+ my @res;
+
+
+ for $i (0..length($word)-1){
+ $char = substr($word, $i, 1);
+ $achar = substr($answer, $i, 1);
+ # check if in word at all
+ if($achar eq $char) {
+ push(@res, [$i, $char, CORRECT]);
+ }
+ elsif($answer =~ /$char/) {
+ push(@res, [$i, $char, IN_WORD]);
+ }
+ else {
+ push(@res, [$i, $char, WRONG]);
+ }
+ }
+ return @res;
+}
+
+sub uniqletters {
+ my $word = shift;
+
+ my @chars = split('', $word);
+
+ for my $char (@chars) {
+ my $ct = ()= $word =~ m/[$char]/g;
+
+ if($ct > 1) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+# okay so the premise here is to create a sort based on the frequency of a a
+# letter... higher frequnency = higher initial hit = higher sort order.
+
+sub lvalue {
+ my $w = shift;
+
+ # my $freq = "etaoinsrhdlucmfywgpbvkxqjz";
+ # reverse frequency, later numbers have higher value
+ my $f= "zjqxkvbpgwyfmculdhrsnioate";
+
+ my $v = 1;
+ for my $c (split('', $w)) {
+ # this is basically bullshit, but sum the value against the index
+ # position...
+ $v += (index($f, $c));
+ }
+ return $v;
+}
+
+# i stole this from stackoverflow.
+sub wordvalue {
+ $av = lvalue($a);
+ $bv = lvalue($b);
+
+ if($av < $bv){
+ return -1;
+ }elsif($av == $bv){
+ return 0;
+ }else{
+ return 1;
+ }
+}