Skip to content

Instantly share code, notes, and snippets.

@aaronNGi
Last active February 22, 2022 17:31
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save aaronNGi/04a209b1cd17f1fa03af7a87eb14cc42 to your computer and use it in GitHub Desktop.
Save aaronNGi/04a209b1cd17f1fa03af7a87eb14cc42 to your computer and use it in GitHub Desktop.
Wordle in 20 lines of bash
#!/usr/bin/env bash
mapfile -t words < <(grep -x '[a-z]\{5\}' "${WORDLIST:-/usr/share/dict/words}")
word=${words[RANDOM % ${#words[@]}]} pool=abcdefghijklmnopqrstuvwxyz
for ((round=1; round <= ${ROUNDS:=6}; round++)); do
while read -rp "$round/$ROUNDS: " guess || exit 1; do
[[ " ${words[@]} " == *" ${guess,,} "* ]] && guess=${guess,,} && break
done
for ((i=0, chars=0; i < ${#word}; i++)); do
[[ ${word:i:1} != ${guess:i:1} ]] && chars+=${word:i:1}
done
for ((i=0; color=7, i < ${#guess}; i++)); do char=${guess:i:1}
{ [[ ${word:i:1} == $char ]] && color=2; } ||
{ [[ ${chars#0} == *$char* ]] && color=3 chars=${chars/$char/}; }
[[ $word != *$char* ]] && pool=${pool/$char/}
printf '\033[30;4%dm %s \033[0m' "$color" "${char^^}"
done
printf ' %s\n' "$pool"
[[ $guess == $word ]] && exit
done
! printf 'The word was "%s"\n' "$word" && false
@jcromero
Copy link

jcromero commented Feb 7, 2022

Why the chars variable is initialized to 0 in line 8? I can not see any problem leaving the variable uninitialized

@matthewpersico
Copy link

matthewpersico commented Feb 7, 2022

if [[ $1 =~ -h ]]; then
    cat <<EOH
usage: [ROUNDS=n] [CHARS=n] wordle.bash
EOH
    exit 0
fi

declare letters=${CHARS:=5}
mapfile -t words < <(grep -x '[a-z]\{'"${letters}"'\}' "${WORDLIST:-/usr/share/dict/words}")
word=${words[RANDOM % ${#words[@]}]} pool=abcdefghijklmnopqrstuvwxyz
for ((round=1; round <= ${ROUNDS:=6}; round++)); do
    while read -rp "$letters letters, $round/$ROUNDS: " guess || exit 1; do
        # shellcheck disable=SC2199 #https://github.com/koalaman/shellcheck/wiki/SC2199
        [[ " ${words[@]} " == *" ${guess,,} "* ]] && guess=${guess,,} && break
    done
    for ((i=0, chars=0; i < ${#word}; i++)); do
        [[ ${word:i:1} != "${guess:i:1}" ]] && chars+=${word:i:1}
    done
    for ((i=0; color=7, i < ${#guess}; i++)); do
        char=${guess:i:1}
        { [[ ${word:i:1} == "$char" ]] && color=2; } ||
            { [[ ${chars#0} == *$char* ]] && color=3 chars=${chars/$char/}; }
        [[ $word != *$char* ]] && pool=${pool/$char/}
        printf '\033[30;4%dm %s \033[0m' "$color" "${char^^}"
    done
    printf ' %s\n' "$pool"
    [[ $guess == "$word" ]] && exit
done
! printf 'The word was "%s"\n' "$word" && false

@jcromero
Copy link

jcromero commented Feb 8, 2022

@matthewpersico Why not use " ${words[*]} " == ... in line 14 to avoid the shellcheck error?

@aaronNGi
Copy link
Author

aaronNGi commented Feb 8, 2022

@jcromero chars has to be set anew every round, so letting it uninitialized only works for the first time. It's set to 0 because it's not possible to set something to a string inside an arithmetic expression.

@aaronNGi
Copy link
Author

aaronNGi commented Feb 8, 2022

@matthewpersico nice but because the goal was to do it in as few lines as possible, I'd reduce the help logic to a single line. I'd also get rid of the declare and use "${letters:=${CHARS:=5}}". I thought about making the word length configurable but didn't do it because you can run abritary code using $CHARS.

[Edit] 3 extra lines.: CHARS and ROUNDS can only be integers. Help is printed to stderr when any argument is given. Amount of rounds and word length is printed once at the start.

#!/usr/bin/env bash
printf '%d%d' "${CHARS:=5}" "${ROUNDS:=6}" &>/dev/null || set 1
[[ $# != 0 ]] && { printf '[CHARS=n] [ROUNDS=n] [WORDLIST=file] %s\n' "${0##*/}" >&2; exit 1; }
printf 'Letters: %d Rounds: %d\n' "${CHARS#-}" "$ROUNDS"
mapfile -t words < <(grep -x '[a-z]\{'"${CHARS#-}"'\}' "${WORDLIST:-/usr/share/dict/words}")
word=${words[RANDOM % ${#words[@]}]} pool=abcdefghijklmnopqrstuvwxyz
for ((round=1; round <= $ROUNDS; round++)); do
	while read -rp "$round/$ROUNDS: " guess || exit 1; do
		[[ " ${words[*]} " == *" ${guess,,} "* ]] && guess=${guess,,} && break
	done
	for ((i=0, chars=0; i < ${#word}; i++)); do
		[[ ${word:i:1} != "${guess:i:1}" ]] && chars+=${word:i:1}
	done
	for ((i=0; color=7, i < ${#guess}; i++)); do char=${guess:i:1}
		{ [[ ${word:i:1} == "$char" ]] && color=2; } ||
		{ [[ ${chars#0} == *"$char"* ]] && color=3 chars=${chars/$char/}; }
		[[ $word != *"$char"* ]] && pool=${pool/$char/}
		printf '\033[30;4%dm %s \033[0m' "$color" "${char^^}"
	done
	printf ' %s\n' "$pool"
	[[ $guess == "$word" ]] && exit
done
! printf 'The word was "%s"\n' "$word" && false

@matthewpersico
Copy link

@jcromero - Because I didn't quite grok what was going on and wanted to disturb as little a possible.

@matthewpersico
Copy link

@aaronNGi - sweet. Thank you for considering my edits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment