13
\$\begingroup\$

You've arrived on an alien planet. The writing system, the culture and the language there are completely foreign to any language you know. But somehow they are all playing wordle already.

So as a method to learn their language and culture you start playing their version of wordle. Their wordle works a little bit differently than ours. Just like ours it's a guesssing game. There's a secret hidden word which you must guess. The secret word is made of 5 glyphs, and at each step of the game you guess 5 glyphs. The game then highlights each of them either

  • \$\mathrm{Black}\$: if that glyph doesn't appear in the word at all.
  • \$\color{orange}{\overline{\mathrm{Yellow}}}\$: if that glyph appears in the target word but in a different place.
  • \$\color{green}{\underline{\mathrm{Green}}}\$: if that glyph appears in the target word at the same place.

Unlike our wordle there are no special rules for double letters.

Now because you don't know the language all you can do is guess random sequences of glyphs to eventually brute force some information out of the game.

If we look at this example game where I've replaced the alien glyphs with positive numbers for convenience:

$$ \begin{matrix} 1 & 2 & \color{green}{\underline 3} & 4 & 5 \\ 6 & \color{green}{\underline7} & \color{orange}{\overline 8} & \color{green}{\underline 9} & 4 \\ \color{orange}{\overline 0} & 10 &\color{orange}{\overline 7} & \color{green}{\underline9} & 4 \\ \end{matrix} $$

In the first guess we found out \$3\$ was the middle letter and that \$1, 2, 4, 5\$ don't appear in the solution. In the second guess we learned that \$7\$ and \$9\$ are the second and fourth positions respectively. We also learned that \$8\$ appears somewhere, and that \$6\$ doesn't appear anywhere. In the third guess we learned that \$0\$ appears but not in the first position.

At this point since we know that the second, third and fourth positions are occupied \$0\$ must be in the last position. This leaves one position, the first, open for \$8\$. So from this board we know the solution is \$8\,\,7\,\,3\,\,9\,\,0\$.

You don't know how many glyphs are present in the alien language, so you cannot do process of elimination to determine a glyph is in the solution without having a guess containing that glyph.

Task

Given a list of guesses and a list of color responses, determine if the solution can be uniquely determined from the board. Glyphs should be represented by integers, and you may assume they are strictly positive. Other than that you may take input in any reasonable format. For example the colors could be 0 1 2 or B Y G. You may take the list of guesses and color responses separately or zipped together as a single structure. You may assume that each guess is 5 glyphs long and each response as well.

You should output one of two fixed values if the solution is uniquely determined and the other of the two values if it is not.

This is so the goal is to minimize your source code as scored in bytes.

Test cases

Test cases are presented as alternating guesses and responses. The guesses are (mostly) single digit numbers for the convenience of formatting, but this should not be assumed. Responses are in the form of B Y G for black yellow green respectively.

Ones

Unique solution

5 4 3 2 1
G G G G G
1 2 3 4 5
Y Y G G G
1 2 3 4 5
Y B G B B
6 7 8 9 0
B B Y G G
1 2 3 4 5
B B G G G
6 7 8 9 0
G G B B B
9 3 3 8 4
G G Y Y G
1 2 3 5 6
B Y Y B B
2 2 3 2 1
Y Y G Y Y
4 1 3 1 5
Y Y G Y Y
6 5 3 4 10
B Y G Y B

No unique solution

1 1 1 1 1
1 2 2 2 2

The first value isn't 1, but it could be anything else. Example solutions: 0 1 1 1 1, 2 1 1 1 1

1 2 3 4 5
B G G G G
1 6 8 7 9
B B B B B

There is one value missing which could be anything. Example solutions: 0 2 3 4 5, 10 2 3 4 5

1 2 3 4 5
Y Y Y Y Y

You know the glyphs in the solution but cannot determine the order. Example solutions: 5 3 4 2 1, 5 4 2 3 1

1 2 2 3 4
G G Y Y G

Unlike earth wordle you don't know whether there are 1 or two 2s based on this. Example solutions: 1 2 3 2 4, 1 2 3 5 4


This puzzle was inspired by the kilordle wordle variant.

\$\endgroup\$
8
  • 1
    \$\begingroup\$ The challenge spec states that the glyphs are strictly positive integers, but the examples allow for 0. Can you make it consistent? \$\endgroup\$ Feb 27 at 13:19
  • \$\begingroup\$ @RobinRyder The test cases have to be in some format and I chose one that is particularly human readable. If your program cannot handle aspects of that format it's up to you to adjust them to the format when testing. \$\endgroup\$
    – Wheat Wizard
    Feb 27 at 13:32
  • 2
    \$\begingroup\$ My issue is with the sentence Glyphs should be represented by integers, and you may assume they are strictly positive. I suggest you change "strictly positive" to "non-negative" for consistency. \$\endgroup\$ Feb 27 at 13:55
  • \$\begingroup\$ @RobinRyder You are perfectly permitted to represent glyphs as positive numbers in your input. Whether you want to handle zero is perfectly up to you. I use 0 in the test cases because it is convenient for making the presentable, 0 is only 1 character so things line up. If you want to adjust it to your format simply add 1 to every glyph, just like if you want to represent BYG as 123 you will have to make that adjustment. \$\endgroup\$
    – Wheat Wizard
    Feb 27 at 14:01
  • \$\begingroup\$ I understand we can take input as [[glyphs 1], [glyphs 2], ...], [[colours 1], [colours 2], ...] or [[[glyphs 1], [colours 1]], [[glyphs 2], [colours 2]], ...], but are we allowed to take it as [[[glyph 1:1, colour 1:1], ..., [glyph 1:5, colour 1:5]], [[glyph 2:1, colour 2:1], ..., [glyph 2:5, colour 2:5]], ...] (Can we take input "double-zipped" together?) \$\endgroup\$
    – Lecdi
    Mar 9 at 18:26

3 Answers 3

5
\$\begingroup\$

BQN, 28 bytesSBCS

Takes glyphs as positive integers and the colors as 012 for BYG.

{1=+´𝕨≡⎉1⍉𝕩(∊+=)⌜⥊↕5⥊2+⌈´∾𝕩}

Run a few cases online! or All cases on ATO!.

Brute-force approach. Generates all words with glyphs in \$\{0,1, \cdots ,\operatorname{max}(\text{guesses}) + 1\}\$ and checks if there is exactly one word that produces the same colors for the given guesses.

Takes a list of guesses as the right argument 𝕩 and the response colors as 𝕨 on the left.

∾𝕩 Flatten the guesses to get a list of all glyphs used in the guesses.
⌈´ Get the maximum of that (call this \$m\$).
5⥊2+ a vector of 5 copies of \$m+2\$.
⥊↕ All indices of an array of these dimensions. This is a list of all 5-glyph word with glyphs up to \$m+2\$: \$\{0, 1, \cdots,m,m+1\}^5\$. Note that the two glyphs \$0\$ and \$m+1\$ did not appear in the guesses. This makes sure that there are at least two distinct possible secrets if a not-yet-guessed glyph can be in the solution.
𝕩(∊+=)⌜ For each of these words as a secret, and each guess in 𝕩, judge the guess.
The judge function ∊+= reads: (implicit: for each glyph in the guess) does it occur the secret? (∊) + does it appear at the same index? (=). (This is much simpler than highlighting a guess in the human variant)
Now we have a wide table table with the colors for one possible secret per column. This gets transposed (⍉) such that each secret gets its own row.
𝕨≡⎉1 For each row, do the colors equal the input colors?
+´ Sum the values, count how many times the previous was truthy.
1= Did this only happen once?

\$\endgroup\$
2
  • \$\begingroup\$ wow this is impressive! unfortunately it also just looks like unicode soup to me, any chance you could write out a longer step-by-step explanation of this? \$\endgroup\$
    – des54321
    Mar 7 at 17:49
  • \$\begingroup\$ @des54321 added some text. feel free to ask if something is unclear \$\endgroup\$
    – ovs
    Mar 7 at 22:10
3
\$\begingroup\$

Python 3, 432 428 401 348 335 330 319 295 bytes

Takes input as an array, [ [ [glyphs1],[colors1] ], [ [glyphs2],[colors2] ], ...], where the glyphs are integers > 0, yellow is -1, black is 0, and green is 1. Returns True and False for solvable/unsolvable.

def w(a):
 G,v,y=[0]*5,{0,1,2,3,4},{}
 for j in a:
  for i,g,c in zip(range(5),*j):
   if c>0and{i}&v:G[i]=g;v-={i}
   elif c<0:y[g]=y.get(g,{i})|{i}
 while 1:
  n=dict(y)
  for i in y:
   if x:=len(d:=v-y[i])<1:del n[i]
   elif x<2>1+G.count(i):j,*_=d;v-={j};G[j]=i
  if y==n:return not v
  y=n

TIO link with extra code to run all test cases

@ovs' brute force attempt is probably the optimal way to solve this problem, and Python is needless to say not going to compete with that Unicode soup, but I still felt like trying my hand at writing a more elegant solution than the brute-force approach, and this is what I came up with. If this inspires anyone to try a similar solution in another language, please go ahead, I'd love to see how short this approach can really get in the right language, and if it can compete much with the brute-force solution.

Edit -4 bytes: Just realized I can declare the empty dict at the start with {} instead of dict(), knew I missed something golfing this down last night.

Edit -27 bytes: golfed down conditionals, also function returns True/False instead of 1/0 now.

Edit -53 bytes: I can't believe I forgot you can still use ; to put multiple lines on one line and save whitespace, also the amount of bytes you can squeeze out of logical expressions in this language is insane, so remember that if your expressions are readable, you haven't golfed them enough.

Edit -13 bytes: Golfed the .remove()s down to -={}s, and golfed off another 2 by removing the l=len from the top, as it wasn't saving bytes anymore, and golfed another 1 byte by changing it from accepting glyphs starting with 0 to glyphs starting with 1, turning G=[-1]*5 into G=[0]*5. I'm pretty sure this is as golfed down as I can get this program, but I could well have missed something.

Edit -5 bytes: I think I've wrapped my head around that dang walrus operator now.

Edit -11 bytes all thanks to @MarcMush's idea to use a zip().

Edit -24 bytes: Wow I didn't think I'd get this thing under 300! Shaved off a few with a better return, and finally found a better solution to having to check if a glyph is already in the yellow dict before adding its index.

Ungolfed code and explanation

def wordle(guesses):
    green = [0,0,0,0,0] ## list tracking what we know of the solution
    vacant = {0,1,2,3,4} ## set of the slots we havent found yet
    yellow = dict() ## dict mapping each yellow glyph to the set of locations it was yellow at
    for guess in guesses: ## iterate through each guess made
        for i in range(5): ## for each glyph:
            glyph = guess[0][i] 
            color = guess[1][i]
            if color==1 and i in vacant: ## if its green, set green and mark as found
                green[i] = glyph
                vacant.remove(i)
            elif color==-1: ## if its yellow, add it to the dict
                if glyph not in yellow: 
                    yellow[glyph]={i} ## add the set if not already there
                else:
                    yellow[glyph].add(i) ## or add it to the set
    while True: ## loop until we get a solution
        newyellow=dict(yellow) ## copy yellow so we can modify it
        for i in yellow: ## loop over each yellow glyph
            difference=vacant-yellow[i] ## find the vacant slots it could fit in
            length=len(difference)
            if length==0: ## no vacant slots, this yellow has been placed somewhere already
                del newyellow[i]
            elif length==1 and green.count(i)==0: ## one slot and hasnt been placed in a slot yet
                j=difference.pop() 
                vacant.remove(j) ## no longer vacant
                green[j]=i ## add it to green
        if yellow==newyellow: ## we did nothing this loop, we have a result
            return len(vacant) == 0 ## if vacant is empty, we've solved it, otherwise its unsolvable
        yellow=newyellow
\$\endgroup\$
3
  • 1
    \$\begingroup\$ 319 bytes by using a zip Try it online! \$\endgroup\$
    – MarcMush
    Mar 9 at 19:40
  • \$\begingroup\$ oooooh good one @MarcMush!! I had just been trying something with a for j,k in a but thats even better, although I wonder if I can combine the two approaches \$\endgroup\$
    – des54321
    Mar 9 at 20:55
  • \$\begingroup\$ TIO link is broken because you need to use the Python 3.8 (pre-release) instead of Python 3 \$\endgroup\$
    – Steffan
    Apr 24 at 1:34
1
\$\begingroup\$

Python3, 362 bytes:

R=range
F=lambda t:{x for y in t for x in t[y]}
def f(b):
 d={},{},{}
 for x,y in b:
  for i in R(5):d[y[i]][x[i]]=d[y[i]].get(x[i],[])+[i]
 [d[1].pop(i,0)for i in d[0]]
 while 1:
  if not(Y:=d[1]):return F(G:=d[0])&F(B:=d[2])==F(B)and len(F(G))==5
  if not(k:=[(i,g)for i in Y if len(g:=({*R(5)}-F(G:=d[0])-{*Y[i]}))==1]):return 0
  for x,y in k:G[x]=y;del Y[x]

Try it online!

\$\endgroup\$
4
  • \$\begingroup\$ Looks like I have an aspiring competitor for best python answer to this question :P Best of luck getting this shorter than mine \$\endgroup\$
    – des54321
    Mar 9 at 18:00
  • \$\begingroup\$ @des54321 Thanks, it is only semi-golfed I'd say, so I imagine some improvements can be made \$\endgroup\$
    – Ajax1234
    Mar 9 at 18:05
  • \$\begingroup\$ Yeah I can definitely already see a few potential golfs, You can definitely use {*foo} instead of the various set(foo) calls you have at the bottom, which should save some bytes \$\endgroup\$
    – des54321
    Mar 9 at 18:13
  • \$\begingroup\$ Your last line can be for x,G[x]in k:del Y[x] \$\endgroup\$
    – ovs
    Mar 10 at 8:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.