Wordle comparison: Difference between revisions
m (→{{header|J}}) |
(J: alternative implementation) |
||
Line 160: | Line 160: | ||
end. |
end. |
||
2 gr} 1 yw} (#y)#0 |
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 |
|||
}}</lang> |
|||
assert 1 1 2 0 0-: 'allow' wrdcmp 'lolly' |
assert 1 1 2 0 0-: 'allow' wrdcmp 'lolly' |
Revision as of 20:47, 16 February 2022
- 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]
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
}}</lang>
assert 1 1 2 0 0-: 'allow' wrdcmp 'lolly' 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'</lang>
Task example:
<lang J> ('allow' wrdcmp 'lolly')&{&.;: 'gray yellow green' yellow yellow green gray gray</lang>
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"}
Quackery
<lang Quackery> [ tuck witheach
[ over i^ peek = if [ 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
<lang perl6># 20220216 Raku programming solution
sub wordle ($answer,$guess where [==] ($answer,$guess)>>.chars ) {
my %answer = $answer.comb.pairs; my %guess = $guess.comb.pairs; my @return;
for %guess -> \trial { # pair if %answer{trial.key} eq trial.value { @return[trial.key] = 'green'; given trial.key { %answer{$_}:delete; %guess{$_}:delete } } }
for %guess.keys.sort -> \trial { # numeric pos @return[trial] = 'grey'; for %answer -> \actual { # pair if %guess{trial} eq actual.value { @return[trial] = 'yellow'; %answer{actual.key}:delete; last; } } } @return;
}
say .[0]~' vs '~.[1]~"\t"~ wordle .[0],.[1] for (<ALLOW LOLLY>,<ROBIN ALERT>, <ROBIN SONIC>,<ROBIN ROBIN>,<BULLY LOLLY>,<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 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]