Wordle comparison: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{header|Raku}}: add another matching color/target)
Line 516: Line 516:
green green green green green
green green green green green
</pre>
</pre>

=={{header|Yabasic}}==
{{trans|Phix}}
<lang Yabasic>// Rosetta Code problem: http://rosettacode.org/wiki/Wordle_comparison
// by Galileo, 02/2022

sub wordle$(answer$, guess$)
local n, i, k, result$
n = len(guess$)
if n = len(answer$) then
result$ = left$("0000000000000000000", n)
for i = 1 to n
if mid$(guess$, i, 1) = mid$(answer$, i, 1) then
mid$(answer$, i, 1) = "0"
mid$(result$, i, 1) = "2"
end if
next
for i = 1 to n
k = instr(answer$, mid$(guess$, i, 1))
if k then
mid$(answer$, k, 1) = "0"
mid$(result$, i, 1) = "1"
end if
next
else
print "words must be same length"
end if
return result$
end sub

data "ALLOW", "LOLLY", "BULLY", "LOLLY", "ROBIN", "ALERT", "ROBIN", "SONIC", "ROBIN", "ROBIN"
dim colours$(3) : colours$(0) = "grey" : colours$(1) = "yellow" : colours$(2) = "green"
for i = 1 to 5
read answer$, guess$
res$ = wordle$(answer$, guess$)
print answer$, " v ", guess$, " => ";

for j = 1 to len(res$)
print colours$(val(mid$(res$, j, 1))), " ";
next
print
next
</lang>
{{out}}
<pre>ALLOW v LOLLY => yellow yellow green grey grey
BULLY v LOLLY => grey grey green green green
ROBIN v ALERT => grey grey grey yellow grey
ROBIN v SONIC => grey green yellow green grey
ROBIN v ROBIN => green green green green green
---Program done, press RETURN---</pre>

Revision as of 09:38, 27 February 2022

Wordle comparison is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
Rationale

While similar to both Bulls and cows and Mastermind, Wordle is a notable variation, having experienced a viral surge in popularity, and reverse engineering the game or creating variants has become a popular programming exercise. However, a sampling of the "code a Wordle clone" videos on YouTube shows that seven of the eight reviewed had a serious flaw in the way that they assigned colours to the letters of a guessed word. This aspect of the game is described here: en.wikipedia.org/wiki/Wordle#Gameplay

After every guess, each letter is marked as either green, yellow or gray: green indicates that letter is correct and in the correct position, yellow means it is in the answer but not in the right position, while gray indicates it is not in the answer at all. Multiple instances of the same letter in a guess, such as the "o"s in "robot", will be colored green or yellow only if the letter also appears multiple times in the answer; otherwise, excess repeating letters will be colored gray.

Task

Create a function or procedure that takes two strings; the answer string, and the guess string, and returns a string, list, dynamic array or other ordered sequence indicating how each letter should be marked as per the description above. (e.g. "green", "yellow", or "grey", or, equivalently, the integers 2, 1, or 0 or suchlike.)

You can assume that both the answer string and the guess string are the same length, and contain only printable characters/code points in the ASCII/UniCode Range ! to ~ (hex 20 to 7F) and that case is significant. (The original game only uses strings of 5 characters, all alphabetic letters, all in the same case, but this allows for most existing variants of the game.)

Provide test data and show the output here.

The test data should include the answer string ALLOW and the guess string LOLLY, and the result should be (yellow, yellow, green, grey, grey) or equivalent.


C

Translation of: Wren

<lang c>#include <stdio.h>

  1. include <stdlib.h>
  2. include <string.h>

void wordle(const char *answer, const char *guess, int *result) {

   int i, ix, n = strlen(guess);
   char *ptr;
   if (n != strlen(answer)) {
       printf("The words must be of the same length.\n");
       exit(1);
   }
   char answer2[n+1];
   strcpy(answer2, answer);
   for (i = 0; i < n; ++i) {
       if (guess[i] == answer2[i]) {
           answer2[i] = '\v';
           result[i] = 2;
       }
   }
   for (i = 0; i < n; ++i) {
       if ((ptr = strchr(answer2, guess[i])) != NULL) {
           ix = ptr - answer2;
           answer2[ix] = '\v';
           result[i] = 1;
       }
   }

}

int main() {

   int i, j;
   const char *answer, *guess;
   int res[5];
   const char *res2[5];
   const char *colors[3] = {"grey", "yellow", "green"};
   const char *pairs[5][2] = {
       {"ALLOW", "LOLLY"},
       {"BULLY", "LOLLY"},
       {"ROBIN", "ALERT"},
       {"ROBIN", "SONIC"},
       {"ROBIN", "ROBIN"}
   };
   for (i = 0; i < 5; ++i) {
       answer = pairs[i][0];
       guess  = pairs[i][1];
       for (j = 0; j < 5; ++j) res[j] = 0;
       wordle(answer, guess, res);
       for (j = 0; j < 5; ++j) res2[j] = colors[res[j]];
       printf("%s v %s => { ", answer, guess);
       for (j = 0; j < 5; ++j) printf("%d ", res[j]);
       printf("} => { ");
       for (j = 0; j < 5; ++j) printf("%s ", res2[j]);
       printf("}\n");
   }
   return 0;

}</lang>

Output:
ALLOW v LOLLY => { 1 1 2 0 0 } => { yellow yellow green grey grey }
BULLY v LOLLY => { 0 0 2 2 2 } => { grey grey green green green }
ROBIN v ALERT => { 0 0 0 1 0 } => { grey grey grey yellow grey }
ROBIN v SONIC => { 0 2 1 2 0 } => { grey green yellow green grey }
ROBIN v ROBIN => { 2 2 2 2 2 } => { green green green green green }

Go

Translation of: Wren

<lang go>package main

import (

   "bytes"
   "fmt"
   "log"

)

func wordle(answer, guess string) []int {

   n := len(guess)
   if n != len(answer) {
       log.Fatal("The words must be of the same length.")
   }
   answerBytes := []byte(answer)
   result := make([]int, n) // all zero by default
   for i := 0; i < n; i++ {
       if guess[i] == answerBytes[i] {
           answerBytes[i] = '\000'
           result[i] = 2
       }
   }
   for i := 0; i < n; i++ {
       ix := bytes.IndexByte(answerBytes, guess[i])
       if ix >= 0 {
           answerBytes[ix] = '\000'
           result[i] = 1
       }
   }
   return result

}

func main() {

   colors := []string{"grey", "yellow", "green"}
   pairs := [][]string{
       {"ALLOW", "LOLLY"},
       {"BULLY", "LOLLY"},
       {"ROBIN", "ALERT"},
       {"ROBIN", "SONIC"},
       {"ROBIN", "ROBIN"},
   }
   for _, pair := range pairs {
       res := wordle(pair[0], pair[1])
       res2 := make([]string, len(res))
       for i := 0; i < len(res); i++ {
           res2[i] = colors[res[i]]
       }
       fmt.Printf("%s v %s => %v => %v\n", pair[0], pair[1], res, res2)
   }

}</lang>

Output:
ALLOW v LOLLY => [1 1 2 0 0] => [yellow yellow green grey grey]
BULLY v LOLLY => [0 0 2 2 2] => [grey grey green green green]
ROBIN v ALERT => [0 0 0 1 0] => [grey grey grey yellow grey]
ROBIN v SONIC => [0 2 1 2 0] => [grey green yellow green grey]
ROBIN v ROBIN => [2 2 2 2 2] => [green green green green green]

J

Implementation (brute force):

<lang J>wrdcmp=: {{

 yw=.gr=. I.x=y
 key=. '#' gr} x
 for_ch.y do.
   if.ch e. key do.
     key=. '#' (key i.ch)} key
     yw=. yw, ch_index
   end.
 end.
 2 gr} 1 yw} (#y)#0

}}</lang>

A bit more efficient (about 3 times faster on task example, which might matter if a few microseconds was important):

<lang J>wrdcmp=: {{

 yw=. ;(] , ({.~1<.#)@-.)&.>/(<@I.y=/~x#~y~:x),<I.x=y
 2 (I.x=y)} 1 yw} (#y)#0

}}

assert 1 1 2 0 0-: 'allow' wrdcmp 'lolly' assert 0 0 2 2 2-: 'bully' wrdcmp 'lolly' assert 0 0 0 1 0-: 'robin' wrdcmp 'alert' assert 0 2 1 2 0-: 'robin' wrdcmp 'sonic' assert 2 2 2 2 2-: 'robin' wrdcmp 'robin' assert 0 0 2 1 0-: 'mamma' wrdcmp 'nomad' assert 0 1 2 0 0-: 'nomad' wrdcmp 'mamma'</lang>

Explanation:

<I.x=y is a box containing the list of exact match indices, and (<@I.y=/~x#~y~:x) is a list of boxes (one box for each character in the guess) of the indices of not-necessarily-exact matches. Meanwhile ({.~1<.#) means "at most one" and operates on lists (so it's empty for an empty list and the first element for a non-empty list).

In other words, we build a list of candidate matches for each character and then, for each character, exclude any already picked index and if there's a remaining candidates, we pick the first of those.

(exact match indices will override inexact match indices, which makes the inexact match index calculation simpler -- we don't have to use a separate type for exact match indices.)

Task example:

<lang J> ('allow' wrdcmp 'lolly')&{&.;: 'gray yellow green' yellow yellow green gray gray</lang>

jq

Translation of: Wren
Works with: jq

Works with gojq, the Go implementation of jq <lang jq>def colors: ["grey", "yellow", "green"];

def wordle($answer; $guess):

 ($guess|length) as $n
 | if ($answer|length) != $n then "The words must be of the same length." | error
   else { answer: (answer | explode),
          guess:  (guess  | explode),

result: [range(0;$n)|0] }

   | reduce range(0; $n) as $i (.;
       if .guess[$i] == .answer[$i]
       then .answer[$i] = 0
       | .result[$i] = 2
       else .

end )

   | reduce range(0; $n) as $i (.;
       .guess[$i] as $g
       | (.answer | index($g) ) as $ix
       | if $ix
         then .answer[$ix] = 0
         | .result[$i] = 1
         else .

end )

   | .result
   end ;

def pairs:

   ["ALLOW", "LOLLY"],
   ["BULLY", "LOLLY"],
   ["ROBIN", "ALERT"],
   ["ROBIN", "SONIC"],
   ["ROBIN", "ROBIN"]

pairs | wordle(.[0]; .[1]) as $res | ($res | map(colors[.])) as $res2 | "\(.[0]) v \(.[1]) => \($res) => \($res2)"</lang>

Output:

As for #Wren.

Julia

Translation of: Wren

<lang julia>const colors = ["grey", "yellow", "green"]

function wordle(answer, guess)

   n = length(guess)
   length(answer) != n && error("The words must be of the same length.")
   answervector, result = collect(answer), zeros(Int, n)
   for i in 1:n
       if guess[i] == answervector[i]
           answervector[i] = '\0'
           result[i] = 2
       end
   end
   for i in 1:n
       c = guess[i]
       ix = findfirst(isequal(c), answervector)
       if ix != nothing
           answervector[ix] = '\0'
           result[i] = 1
       end
   end
   return result

end

const testpairs = [

   ["ALLOW", "LOLLY"],
   ["BULLY", "LOLLY"],
   ["ROBIN", "ALERT"],
   ["ROBIN", "SONIC"],
   ["ROBIN", "ROBIN"]

] for (pair0, pair1) in testpairs

   res  = wordle(pair0, pair1)
   res2 = [colors[i + 1] for i in res]
   println("$pair0 v $pair1 => $res => $res2")

end</lang>

Output:
ALLOW v LOLLY => [1, 1, 2, 0, 0] => ["yellow", "yellow", "green", "grey", "grey"]
BULLY v LOLLY => [0, 0, 2, 2, 2] => ["grey", "grey", "green", "green", "green"]
ROBIN v ALERT => [0, 0, 0, 1, 0] => ["grey", "grey", "grey", "yellow", "grey"]
ROBIN v SONIC => [0, 2, 1, 2, 0] => ["grey", "green", "yellow", "green", "grey"]
ROBIN v ROBIN => [2, 2, 2, 2, 2] => ["green", "green", "green", "green", "green"]

Perl

<lang perl>#!/usr/bin/perl

use strict; # https://rosettacode.org/wiki/Wordle_comparison use warnings;

for my $test ( ["ALLOW", "LOLLY"], ["BULLY", "LOLLY"], ["ROBIN", "ALERT"],

 ["ROBIN", "SONIC"], ["ROBIN", "ROBIN"])
 {
 local $_ = join "\n", @$test;
 1 while s/([ -~])(.*\n(??{$` =~ tr!!.!cr}))\1/\0$2\0/;
 1 while s/([ -~])(.*\n.*?)\1/\01$2\01/;
 print "@$test => @{[ qw( green yellow grey )
   [map ord, split //, s/.*\n//r =~ tr/\0\1/\2/cr] ]}\n";
 }</lang>
Output:
ALLOW LOLLY => yellow yellow green grey grey
BULLY LOLLY => grey grey green green green
ROBIN ALERT => grey grey grey yellow grey
ROBIN SONIC => grey green yellow green grey
ROBIN ROBIN => green green green green green

Phix

Aside: You may be mildly surprised to see the 2nd for loop limit being clobbered like this, but you cannot change the limit mid-loop in Phix (an explicit exit would be far clearer) and hence you can do this.
In practice the for loop takes a private copy of the value of the limit, be that n or more significantly say length(s), and ignores any changes you might subsequently make to it.

with javascript_semantics
function wordle(string answer, guess)
    integer n = length(guess)
    assert(n == length(answer),"words must be same length")
    sequence result = repeat(0,n)
    for i=1 to n do
        if guess[i]=answer[i] then
            answer[i] = '\0'
            result[i] = 2
        end if
    end for
    for i=1 to n do
        n = find(guess[i],answer)
        if n then
            answer[n] = '\0'
            result[i] = 1
        end if
    end for
    return result
end function
 
constant tests = {{"ALLOW", "LOLLY"},
                  {"BULLY", "LOLLY"},
                  {"ROBIN", "ALERT"},
                  {"ROBIN", "SONIC"},
                  {"ROBIN", "ROBIN"}},
         colours = {"grey", "yellow", "green"}
for i=1 to length(tests) do
    string {answer,guess} = tests[i]
    sequence res = wordle(answer,guess),
             rac = extract(colours,sq_add(res,1))
    printf(1,"%s v %s => %v => %v\n",{answer,guess,res,rac})
end for
Output:
ALLOW v LOLLY => {1,1,2,0,0} => {"yellow","yellow","green","grey","grey"}
BULLY v LOLLY => {0,0,2,2,2} => {"grey","grey","green","green","green"}
ROBIN v ALERT => {0,0,0,1,0} => {"grey","grey","grey","yellow","grey"}
ROBIN v SONIC => {0,2,1,2,0} => {"grey","green","yellow","green","grey"}
ROBIN v ROBIN => {2,2,2,2,2} => {"green","green","green","green","green"}

Quackery

<lang Quackery> [ tuck witheach

     [ over i^ peek = while
       2 rot i^ poke
       0 rot i^ poke ]
   witheach 
     [ over find 
       2dup swap found iff 
         [ 1 unrot poke ] 
       else drop ]
   [] swap witheach 
     [ dup 3 < * join ] ]   is wordle-compare ( $ $ --> [ ) 

 $ "ALLOW" $ "LOLLY" wordle-compare echo cr 
 $ "BULLY" $ "LOLLY" wordle-compare echo cr 
 $ "ROBIN" $ "ALERT" wordle-compare echo cr
 $ "ROBIN" $ "SONIC" wordle-compare echo cr
 $ "ROBIN" $ "ROBIN" wordle-compare echo cr

</lang>

Output:

2 is equivalent to green, 1 is equivalent to yellow, 1 is equivalent to grey

[ 1 1 2 0 0 ]
[ 0 0 2 2 2 ]
[ 0 0 0 1 0 ]
[ 0 2 1 2 0 ]
[ 2 2 2 2 2 ]

Raku

Updated to add a proof of concept matching for similarity where commonly found on spoofing domain names. Of course this is just the tip of the iceberg (only comparing results after decomposition) and there are way too many Unicode homoglyphs that can only be matched using a lookup table/database. <lang perl6># 20220216 Raku programming solution

sub wordle (\answer,\guess where [==] (answer,guess)».chars ) {

  my ($aSet, $gSet, @return) = (answer,guess)».&{ (set .comb.pairs).SetHash }
  (my \intersection = $aSet ∩ $gSet).keys».&{ @return[.key] = 'green' }
  ($aSet,$gSet)».&{ $_ ∖= intersection } # purge common subset
  for $gSet.keys.sort -> \trial { # pair 
     @return[trial.key] = 'grey';
     for $aSet.keys -> \actual { # pair
        if [eq] (trial,actual)».value {
           @return[trial.key] = 'yellow'; 
           $aSet{actual}:delete;
           last
        } 
        my @NFD = (trial,actual).map: { .value.NFD }
        if [≠] @NFD».elems and [==] @NFD».first {
           @return[trial.key] = 'azure';
           $aSet{actual}:delete;
           last
        }
     }
  }
  @return

}

say .[0]~' vs '~.[1]~"\t"~ wordle .[0],.[1] for ( <ALLOW LOLLY>, <ROBIN ALERT>, <ROBIN SONIC>, <ROBIN ROBIN>, <BULLY LOLLY>, <ADAPT SÅLÅD>, <Ukraine Ukraíne>, <BBAABBB BBBBBAA>, <BBAABBB AABBBAA> );</lang>

Output:
ALLOW vs LOLLY  yellow yellow green grey grey
ROBIN vs ALERT  grey grey grey yellow grey
ROBIN vs SONIC  grey green yellow green grey
ROBIN vs ROBIN  green green green green green
BULLY vs LOLLY  grey grey green green green
ADAPT vs SÅLÅD	grey azure grey azure yellow
Ukraine vs Ukraíne	green green green green azure green green
BBAABBB vs BBBBBAA      green green yellow yellow green yellow yellow
BBAABBB vs AABBBAA      yellow yellow yellow yellow green grey grey

Wren

<lang ecmascript>var colors = ["grey", "yellow", "green"]

var wordle = Fn.new { |answer, guess|

   var n = guess.count
   if (answer.count != n) Fiber.abort("The words must be of the same length.")
   answer = answer.toList
   var result = List.filled(n, 0)
   for (i in 0...n) {
       if (guess[i] == answer[i]) {
           answer[i] = "\0"
           result[i] = 2
       }
   }
   for (i in 0...n) {
       var ix = answer.indexOf(guess[i])
       if (ix >= 0) {
           answer[ix] = "\0"
           result[i] = 1
       }
   }
   return result

}

var pairs = [

   ["ALLOW", "LOLLY"],
   ["BULLY", "LOLLY"],
   ["ROBIN", "ALERT"],
   ["ROBIN", "SONIC"],
   ["ROBIN", "ROBIN"]

] for (pair in pairs) {

   var res  = wordle.call(pair[0], pair[1])
   var res2 = res.map { |i| colors[i] }.toList
   System.print("%(pair[0]) v %(pair[1]) => %(res) => %(res2)")

}</lang>

Output:
ALLOW v LOLLY => [1, 1, 2, 0, 0] => [yellow, yellow, green, grey, grey]
BULLY v LOLLY => [0, 0, 2, 2, 2] => [grey, grey, green, green, green]
ROBIN v ALERT => [0, 0, 0, 1, 0] => [grey, grey, grey, yellow, grey]
ROBIN v SONIC => [0, 2, 1, 2, 0] => [grey, green, yellow, green, grey]
ROBIN v ROBIN => [2, 2, 2, 2, 2] => [green, green, green, green, green]

XPL0

<lang XPL0>string 0;

proc ShowColors(Result); char Result; int Color, I; [Color:= ["gray ", "yellow ", "green "]; for I:= 0 to 4 do

   Text(0, Color(Result(I)));

CrLf(0); ];

func Wordle(Answer, Guess); char Answer, Guess, Result; int I, J; [Result:= " "; for I:= 0 to 4 do

   if Guess(I) = Answer(I) then
       [Result(I):= 2;  Answer(I):= 0]
   else Result(I):= 0;

for I:= 0 to 4 do

   for J:= 0 to 4 do
       if Guess(I) = Answer(J) then
           [Result(I):= 1;  Answer(J):= 0];

return Result; ];

[ShowColors(Wordle("ALLOW", "LOLLY"));

ShowColors(Wordle("BULLY", "LOLLY"));
ShowColors(Wordle("ROBIN", "ALERT"));
ShowColors(Wordle("ROBIN", "SONIC"));
ShowColors(Wordle("ROBIN", "ROBIN"));

]</lang>

Output:
yellow yellow green  gray   gray   
gray   gray   green  green  green  
gray   gray   gray   yellow gray   
gray   green  yellow green  gray   
green  green  green  green  green  

Yabasic

Translation of: Phix

<lang Yabasic>// Rosetta Code problem: http://rosettacode.org/wiki/Wordle_comparison // by Galileo, 02/2022

sub wordle$(answer$, guess$)

   local n, i, k, result$
   
   n = len(guess$)
   if n = len(answer$) then
       result$ = left$("0000000000000000000", n)
       for i = 1 to n
           if mid$(guess$, i, 1) = mid$(answer$, i, 1) then
               mid$(answer$, i, 1) = "0"
               mid$(result$, i, 1) = "2"
           end if
       next
       for i = 1 to n
           k = instr(answer$, mid$(guess$, i, 1))
           if k then
               mid$(answer$, k, 1) = "0"
               mid$(result$, i, 1) = "1"
           end if
       next
   else
       print "words must be same length"
   end if
   return result$

end sub

data "ALLOW", "LOLLY", "BULLY", "LOLLY", "ROBIN", "ALERT", "ROBIN", "SONIC", "ROBIN", "ROBIN" dim colours$(3) : colours$(0) = "grey" : colours$(1) = "yellow" : colours$(2) = "green"

for i = 1 to 5

   read answer$, guess$
   res$ = wordle$(answer$, guess$)
   print answer$, " v ", guess$, " => ";
   for j = 1 to len(res$)
       print colours$(val(mid$(res$, j, 1))), " ";
   next
   print

next </lang>

Output:
ALLOW v LOLLY => yellow yellow green grey grey
BULLY v LOLLY => grey grey green green green
ROBIN v ALERT => grey grey grey yellow grey
ROBIN v SONIC => grey green yellow green grey
ROBIN v ROBIN => green green green green green
---Program done, press RETURN---