Wordle comparison
- 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
<lang c>#include <stdio.h>
- include <stdlib.h>
- 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
<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]
Haskell
<lang haskell>import Data.Bifunctor (first) import Data.List (intercalate, mapAccumL) import qualified Data.Map.Strict as M import Data.Maybe (fromMaybe)
type Tally = M.Map Char Int
WORDLE COMPARISON -------------------
wordleScore :: String -> String -> [Int] wordleScore target guess =
snd $ uncurry (mapAccumL amber) $ first charCounts $ mapAccumL green [] (zip target guess)
green :: String -> (Char, Char) -> (String, (Char, Int)) green residue (t, g)
| t == g = (residue, (g, 2)) | otherwise = (t : residue, (g, 0))
amber :: Tally -> (Char, Int) -> (Tally, Int) amber tally (_, 2) = (tally, 2) amber tally (c, _)
| 0 < fromMaybe 0 (M.lookup c tally) = (M.adjust pred c tally, 1) | otherwise = (tally, 0)
charCounts :: String -> Tally charCounts =
foldr (flip (M.insertWith (+)) 1) M.empty
TEST -------------------------
main :: IO () main = do
putStrLn $ intercalate " -> " ["Target", "Guess", "Scores"] putStrLn [] mapM_ (either putStrLn putStrLn) $ uncurry wordleReport <$> [ ("ALLOW", "LOLLY"), ("CHANT", "LATTE"), ("ROBIN", "ALERT"), ("ROBIN", "SONIC"), ("ROBIN", "ROBIN"), ("BULLY", "LOLLY"), ("ADAPT", "SÅLÅD"), ("Ukraine", "Ukraíne"), ("BBAAB", "BBBBBAA"), ("BBAABBB", "AABBBAA") ]
wordleReport :: String -> String -> Either String String wordleReport target guess
| 5 /= length target = Left (target <> ": Expected 5 character target.") | 5 /= length guess = Left (guess <> ": Expected 5 character guess.") | otherwise = let scores = wordleScore target guess in Right ( intercalate " -> " [ target, guess, show scores, unwords (color <$> scores) ] )
color 2 = "green" color 1 = "amber" color _ = "gray"</lang>
- Output:
Target -> Guess -> Scores ALLOW -> LOLLY -> [1,1,2,0,0] -> amber amber green gray gray CHANT -> LATTE -> [0,1,1,0,0] -> gray amber amber gray gray ROBIN -> ALERT -> [0,0,0,1,0] -> gray gray gray amber gray ROBIN -> SONIC -> [0,2,1,2,0] -> gray green amber green gray ROBIN -> ROBIN -> [2,2,2,2,2] -> green green green green green BULLY -> LOLLY -> [0,0,2,2,2] -> gray gray green green green ADAPT -> SÅLÅD -> [0,0,0,0,1] -> gray gray gray gray amber Ukraine: Expected 5 character target. BBBBBAA: Expected 5 character guess. BBAABBB: Expected 5 character target.
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>
JavaScript
<lang javascript>(() => {
"use strict";
// ---------------- WORDLE COMPARISON ----------------
// wordleScore :: (String, String) -> [Int] const wordleScore = (target, guess) => { // A sequence of integers scoring characters // in the guess: // 2 for correct character and position // 1 for a character which is elsewhere in the target // 0 for for character not seen in the target. const [residue, scores] = mapAccumL(green)([])( zip([...target])([...guess]) );
return mapAccumL(amber)( charCounts(residue) )(scores)[1]; };
// green :: String -> // (Char, Char) -> (String, (Char, Int)) const green = residue => // The existing residue of unmatched characters, // tupled with a character score of 2 if the target // character and guess character match. // Otherwise, a residue (extended by the unmatched // character) tupled with a character score of 0. ([t, g]) => t === g ? ( Tuple(residue)(Tuple(g)(2)) ) : Tuple([t, ...residue])(Tuple(g)(0));
// amber :: Dict -> (Char, Int) -> (Dict, Int) const amber = tally => // An adjusted tally of the counts of unmatched // of remaining unmatched characters, tupled with // a 1 if the character was in the remaining tally // (now decremented) and otherwise a 0. ([c, n]) => 2 === n ? ( Tuple(tally)(2) ) : Boolean(tally[c]) ? ( Tuple( adjust(x => x - 1)(c)(tally) )(1) ) : Tuple(tally)(0);
// ---------------------- TEST ----------------------- // main :: IO () const main = () => [ ["ALLOW", "LOLLY"], ["CHANT", "LATTE"], ["ROBIN", "ALERT"], ["ROBIN", "SONIC"], ["ROBIN", "ROBIN"], ["BULLY", "LOLLY"], ["ADAPT", "SÅLÅD"], ["Ukraine", "Ukraíne"], ["BBAAB", "BBBBBAA"], ["BBAABBB", "AABBBAA"] ] .map(tg => wordleReport(...tg)) .join("\n");
// wordleReport :: (String, String) -> String const wordleReport = (target, guess) => { // Either a message, if target or guess are other than // five characters long, or a color-coded wordle score // for each character in the guess.
const scoreNames = ["gray", "amber", "green"];
return 5 !== target.length ? ( `${target}: Expected 5 character target.` ) : 5 !== guess.length ? ( `${guess}: Expected 5 character guess.` ) : (() => { const scores = wordleScore(target, guess);
return [ target, guess, JSON.stringify(scores), scores.map(n => scoreNames[n]) .join(" ") ].join(" -> "); })(); };
// --------------------- GENERIC ---------------------
// Tuple (,) :: a -> b -> (a, b) const Tuple = a => // A pair of values, possibly of // different types. b => ({ type: "Tuple", "0": a, "1": b, length: 2, *[Symbol.iterator]() { for (const k in this) { if (!isNaN(k)) { yield this[k]; } } } });
// add (+) :: Num a => a -> a -> a const add = a => // Curried addition. b => a + b;
// adjust :: (a -> a) -> Key -> // Dict Key a -> Dict Key a const adjust = f => k => dict => // The orginal dictionary, unmodified, if k is // not an existing key. // Otherwise, a new copy in which the existing // value of k is updated by application of f. k in dict ? ( Object.assign({}, dict, { [k]: f(dict[k]) }) ) : dict;
// charCounts :: String -> Dict Char Int const charCounts = cs => // Dictionary of chars, with the // frequency of each in cs. [...cs].reduce( (a, c) => insertWith(add)(c)( 1 )(a), {} );
// insertWith :: Ord k => (a -> a -> a) -> // k -> a -> Map k a -> Map k a const insertWith = f => // A new dictionary updated with a (k, f(v)(x)) pair. // Where there is no existing v for k, the supplied // x is used directly. k => x => dict => Object.assign({}, dict, { [k]: k in dict ? ( f(dict[k])(x) ) : x });
// mapAccumL :: (acc -> x -> (acc, y)) -> acc -> // [x] -> (acc, [y]) const mapAccumL = f => // A tuple of an accumulation and a list // obtained by a combined map and fold, // with accumulation from left to right. acc => xs => [...xs].reduce( ([a, bs], x) => second( v => bs.concat(v) )( f(a)(x) ), Tuple(acc)([]) );
// second :: (a -> b) -> ((c, a) -> (c, b)) const second = f => // A function over a simple value lifted // to a function over a tuple. // f (a, b) -> (a, f(b)) ([x, y]) => Tuple(x)(f(y));
// zip :: [a] -> [b] -> [(a, b)] const zip = xs => // The paired members of xs and ys, up to // the length of the shorter of the two lists. ys => Array.from({ length: Math.min(xs.length, ys.length) }, (_, i) => Tuple(xs[i])(ys[i]));
// MAIN --- return main();
})();</lang>
- Output:
ALLOW -> LOLLY -> [1,1,2,0,0] -> amber amber green gray gray CHANT -> LATTE -> [0,1,1,0,0] -> gray amber amber gray gray ROBIN -> ALERT -> [0,0,0,1,0] -> gray gray gray amber gray ROBIN -> SONIC -> [0,2,1,2,0] -> gray green amber green gray ROBIN -> ROBIN -> [2,2,2,2,2] -> green green green green green BULLY -> LOLLY -> [0,0,2,2,2] -> gray gray green green green ADAPT -> SÅLÅD -> [0,0,0,0,1] -> gray gray gray gray amber Ukraine: Expected 5 character target. BBBBBAA: Expected 5 character guess. BBAABBB: Expected 5 character target.
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
<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"}
Python
<lang python>Wordle comparison
from functools import reduce from operator import add
- wordleScore :: String -> String -> [Int]
def wordleScore(target, guess):
A sequence of integers scoring characters in the guess: 2 for correct character and position 1 for a character which is elsewhere in the target 0 for for character not seen in the target. return mapAccumL(amber)( *first(charCounts)( mapAccumL(green)( [], zip(target, guess) ) ) )[1]
- green :: String -> (Char, Char) -> (String, (Char, Int))
def green(residue, tg):
The existing residue of unmatched characters, tupled with a character score of 2 if the target character and guess character match. Otherwise, a residue (extended by the unmatched character) tupled with a character score of 0. t, g = tg return (residue, (g, 2)) if t == g else ( [t] + residue, (g, 0) )
- amber :: Dict -> (Char, Int) -> (Dict, Int)
def amber(tally, cn):
An adjusted tally of the counts of unmatched of remaining unmatched characters, tupled with a 1 if the character was in the remaining tally (now decremented) and otherwise a 0. c, n = cn return (tally, 2) if 2 == n else ( adjust( lambda x: x - 1, c, tally ), 1 ) if 0 < tally.get(c, 0) else (tally, 0)
- ------------------------- TEST -------------------------
- main :: IO ()
def main():
Scores for a set of (Target, Guess) pairs. print(' -> '.join(['Target', 'Guess', 'Scores'])) print() print( '\n'.join([ wordleReport(*tg) for tg in [ ("ALLOW", "LOLLY"), ("CHANT", "LATTE"), ("ROBIN", "ALERT"), ("ROBIN", "SONIC"), ("ROBIN", "ROBIN"), ("BULLY", "LOLLY"), ("ADAPT", "SÅLÅD"), ("Ukraine", "Ukraíne"), ("BBAAB", "BBBBBAA"), ("BBAABBB", "AABBBAA") ] ]) )
- wordleReport :: String -> String -> String
def wordleReport(target, guess):
Either a message, if target or guess are other than five characters long, or a color-coded wordle score for each character in the guess. scoreName = {2: 'green', 1: 'amber', 0: 'gray'}
if 5 != len(target): return f'{target}: Expected 5 character target.' elif 5 != len(guess): return f'{guess}: Expected 5 character guess.' else: scores = wordleScore(target, guess) return ' -> '.join([ target, guess, repr(scores), ' '.join([ scoreName[n] for n in scores ]) ])
- ----------------------- GENERIC ------------------------
- adjust :: (a -> a) -> Key -> Dict -> Dict
def adjust(f, k, dct):
A new copy of the Dict, in which any value for the given key has been updated by application of f to the existing value. return dict( dct, **{k: f(dct[k]) if k in dct else None} )
- charCounts :: String -> Dict Char Int
def charCounts(s):
A dictionary of the individual characters in s, with the frequency of their occurrence. return reduce( lambda a, c: insertWith(add)(c)(1)(a), list(s), {} )
- first :: (a -> b) -> ((a, c) -> (b, c))
def first(f):
A simple function lifted to a function over a tuple, with f applied only the first of two values. return lambda xy: (f(xy[0]), xy[1])
- insertWith :: Ord k => (a -> a -> a) ->
- k -> a -> Map k a -> Map k a
def insertWith(f):
A new dictionary updated with a (k, f(v)(x)) pair. Where there is no existing v for k, the supplied x is used directly. return lambda k: lambda x: lambda dct: dict( dct, **{k: f(dct[k], x) if k in dct else x} )
- mapAccumL :: (acc -> x -> (acc, y)) -> acc ->
- [x] -> (acc, [y])
def mapAccumL(f):
A tuple of an accumulation and a map with accumulation from left to right. def nxt(a, x): return second(lambda v: a[1] + [v])( f(a[0], x) ) return lambda acc, xs: reduce( nxt, xs, (acc, []) )
- second :: (a -> b) -> ((c, a) -> (c, b))
def second(f):
A simple function lifted to a function over a tuple, with f applied only to the second of two values. return lambda xy: (xy[0], f(xy[1]))
- MAIN ---
if __name__ == '__main__':
main()</lang>
- Output:
Target -> Guess -> Scores ALLOW -> LOLLY -> [1, 1, 2, 0, 0] -> amber amber green gray gray CHANT -> LATTE -> [0, 1, 1, 0, 0] -> gray amber amber gray gray ROBIN -> ALERT -> [0, 0, 0, 1, 0] -> gray gray gray amber gray ROBIN -> SONIC -> [0, 2, 1, 2, 0] -> gray green amber green gray ROBIN -> ROBIN -> [2, 2, 2, 2, 2] -> green green green green green BULLY -> LOLLY -> [0, 0, 2, 2, 2] -> gray gray green green green ADAPT -> SÅLÅD -> [0, 0, 0, 0, 1] -> gray gray gray gray amber Ukraine: Expected 5 character target. BBBBBAA: Expected 5 character guess. BBAABBB: Expected 5 character target.
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 [ne] @NFD 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
Tailspin
<lang tailspin> templates wordle
sink removeFirst @: $; @wordle: [$@wordle... -> #]; when <=$@> do ! @:; otherwise $! end removeFirst @: [$(1)...]; [$(2)...] -> \[i]( when <=$@wordle($i)> do (green:$)! @wordle($i): ; otherwise $! \) -> \[i]( when <'.' ?($@wordle <[<=$>]>)> do (yellow:$)! $ -> !removeFirst when <'.'> do (grey:$)! otherwise $! \) !
end wordle
test 'wordle'
assert ['ALLOW', 'LOLLY'] -> wordle <=[(yellow:'L'), (yellow:'O'), (green:'L'), (grey:'L'), (grey:'Y')]> 'guess LOLLY' assert ['ALLOW', 'STALL'] -> wordle <=[(grey:'S'), (grey:'T'), (yellow:'A'), (yellow:'L'), (yellow:'L')]> 'guess STALL' assert ['ALLOW', 'ALLEY'] -> wordle <=[(green:'A'), (green:'L'), (green:'L'), (grey:'E'), (grey:'Y')]> 'guess ALLEY' assert ['ALLOW', 'ALLOW'] -> wordle <=[(green:'A'), (green:'L'), (green:'L'), (green:'O'), (green:'W')]> 'guess correct'
end 'wordle'
['ALLOW', 'LOLLY'] -> wordle -> !OUT::write </lang>
- Output:
[yellow=L, yellow=O, green=L, grey=L, grey=Y]
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
<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---