Last active
February 7, 2022 07:59
-
-
Save shmerl/2d1860d2bdba921dccaf84abb98940cd to your computer and use it in GitHub Desktop.
A simple version of Wordle game
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# Uses ripgrep (rg) | |
# | |
length=${length:-5} | |
tries=${tries:-6} | |
# All words of given length from a dictionary | |
declare -a words | |
declare -A words_dict | |
words=( $(rg --color=never --no-line-number '^\w{'"${length}"'}$' /usr/share/dict/words) ) | |
# Generate random position in the dictionary by reading | |
# 8-byte integer from system random generator | |
random_pos=$(( $(od --output-duplicates --address-radix=n --read-bytes=8 --format=u8 < /dev/urandom) % ${#words[@]} )) | |
# Select the secret word and convert to upper case | |
secret=${words[$random_pos]^^} | |
# Prepare fast search dictionary in upper case and empty the raw array | |
for word in ${words[@]}; do | |
words_dict[${word^^}]="" | |
done | |
words=() | |
function validate_word() { | |
local word="$1" | |
if (( ${#word} != $length )); then | |
echo "You have to use ${length}-letter word!" | |
false | |
return | |
fi | |
if ! [ -v words_dict["${word^^}"] ]; then | |
echo "${word} is not recognized, try again!" | |
false | |
return | |
fi | |
true | |
} | |
clr_no=$(printf "\x1b[0m") | |
clr_green_bg=$(printf "\x1b[48;2;83;140;78m") | |
clr_yellow_bg=$(printf "\x1b[48;2;181;159;59m") | |
clr_gray_bg=$(printf "\x1b[48;2;58;58;60m") | |
clr_white_fg=$(printf "\x1b[1;38;2;215;218;220m") | |
function process_guess() { | |
local guess="${1^^}" | |
local secret="$2" | |
# Important values to calculate for a specific letter: | |
# ${secret_letter_total[$letter]} - number of appearances of the letter in the secret. | |
# ${guess_letter_correct[$letter]} - number of correct guessed positions for the letter (green). | |
# ${secret_letter_total[$letter]} - ${guess_letter_correct[$letter]} = number of maximum possible mismatched | |
# guessed letters to be shown (yellow). | |
# All guesses that exceed ${secret_letter_total[$letter]} - ${guess_letter_correct[$letter]} even if that letter | |
# was found somewhere will be shown as misses (gray). | |
local -A secret_letter_total=() | |
local -A guess_letter_correct=() | |
local -A guess_letter_mismatches=() | |
local letter | |
local guessed_letters=0 | |
# Secret letter frequencies | |
for ((i = 0; i < ${#secret}; i++)); do | |
letter=${secret:$i:1} | |
if ! [ -v "${secret_letter_total[$letter]}" ]; then | |
secret_letter_total[$letter]=1 | |
else | |
(( secret_letter_total[$letter]++ )) | |
fi | |
done | |
# First pass - exact matches. | |
for ((i = 0; i < ${#guess}; i++)); do | |
letter=${guess:$i:1} | |
if [[ "$letter" == "${secret:$i:1}" ]]; then | |
(( guessed_letters++ )) | |
if ! [ -v "${guess_letter_correct[$letter]}" ] || (( guess_letter_correct[$letter] == 0 )); then | |
guess_letter_correct[$letter]=1 | |
else | |
(( guess_letter_correct[$letter]++ )) | |
fi | |
elif ! [ -v "${guess_letter_correct[$letter]}" ]; then | |
guess_letter_correct[$letter]=0 | |
fi | |
done | |
# Second pass - completing the markup | |
printf "${clr_white_fg}" | |
for ((i = 0; i < ${#guess}; i++)); do | |
letter=${guess:$i:1} | |
# exact match | |
if [[ "$letter" == "${secret:$i:1}" ]]; then | |
printf "${clr_green_bg} ${letter} " | |
continue | |
fi | |
# miss | |
if ! [ -v "${secret_letter_total[$letter]}" ]; then | |
printf "${clr_gray_bg} ${letter} " | |
continue | |
fi | |
# mismatch | |
# update the frequency of that mismatch | |
if ! [ -v "${guess_letter_mismatches[$letter]}" ]; then | |
guess_letter_mismatches[$letter]=1 | |
else | |
(( guess_letter_mismatches[$letter]++ )) | |
fi | |
if (( ${guess_letter_mismatches[$letter]} <= ${secret_letter_total[$letter]} - ${guess_letter_correct[$letter]} )); then | |
printf "${clr_yellow_bg} ${letter} " | |
else | |
printf "${clr_gray_bg} ${letter} " | |
fi | |
done | |
printf "${clr_no}" | |
if ((guessed_letters == length)); then | |
return 1 | |
fi | |
return 0 | |
} | |
rc=0 | |
while (( (tries > 0 ) && (rc == 0) )); do | |
printf "Enter your guess (${tries} tries left): " | |
read guess | |
if ! validate_word "$guess"; then | |
continue | |
fi | |
hints=$(process_guess "$guess" "$secret") | |
rc=$? | |
printf "${hints}\n" | |
((--tries)) | |
done | |
if ((rc != 0)); then | |
echo "You win!" | |
else | |
echo "You lose!" | |
echo "The word was: ${secret}" | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment