Wordle comparison: Difference between revisions
Content added Content deleted
(→{{header|Haskell}}: Used bimap to eliminate a couple of redundant name bindings) |
(→{{header|Python}}: Added a version in Python) |
||
Line 436: | Line 436: | ||
ROBIN v ROBIN => {2,2,2,2,2} => {"green","green","green","green","green"} |
ROBIN v ROBIN => {2,2,2,2,2} => {"green","green","green","green","green"} |
||
</pre> |
</pre> |
||
=={{header|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 |
|||
''' |
|||
residue, matches = mapAccumL(green)([])( |
|||
zip(target, guess) |
|||
) |
|||
return mapAccumL(amber)( |
|||
charCounts(residue) |
|||
)( |
|||
zip(guess, matches) |
|||
)[1] |
|||
# green :: String -> (Char, Char) -> (String, Int) |
|||
def green(residue, tg): |
|||
'''The existing residue of unmatched characters, tupled |
|||
with a 2 if the target character and guess character |
|||
match. |
|||
Otherwise, a residue (extended by the unmatched |
|||
character) tupled with a 0. |
|||
''' |
|||
t, g = tg |
|||
return (residue, 2) if t == g else ( |
|||
[t] + residue, 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', 'Score'])) |
|||
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'} |
|||
return target + ': Expected 5 character target.' if ( |
|||
5 != len(target) |
|||
) else guess + ': Expected 5 character guess.' if ( |
|||
5 != len(guess) |
|||
) else ' -> '.join([ |
|||
target, guess, |
|||
' '.join([ |
|||
scoreName[n] for n in wordleScore(target, guess) |
|||
]) |
|||
]) |
|||
# ----------------------- 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), |
|||
{} |
|||
) |
|||
# 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: lambda 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> |
|||
{{Out}} |
|||
<pre>Target -> Guess -> Score |
|||
ALLOW -> LOLLY -> amber amber green gray gray |
|||
CHANT -> LATTE -> gray amber amber gray gray |
|||
ROBIN -> ALERT -> gray gray gray amber gray |
|||
ROBIN -> SONIC -> gray green amber green gray |
|||
ROBIN -> ROBIN -> green green green green green |
|||
BULLY -> LOLLY -> gray gray green green green |
|||
ADAPT -> SÅLÅD -> gray gray gray gray amber |
|||
Ukraine: Expected 5 character target. |
|||
BBBBBAA: Expected 5 character guess. |
|||
BBAABBB: Expected 5 character target.</pre> |
|||
=={{header|Quackery}}== |
=={{header|Quackery}}== |