Skip to content

Instantly share code, notes, and snippets.

@tb3088
Last active October 3, 2022 22:59
Show Gist options
  • Save tb3088/3d297d2327400c2915e55be2d6280a7a to your computer and use it in GitHub Desktop.
Save tb3088/3d297d2327400c2915e55be2d6280a7a to your computer and use it in GitHub Desktop.
#!/bin/bash -e
set -o pipefail; LC_COLATE=C
: "${LETTERS:=5}"
: "${ROUNDS:=7}"
: "${DICTIONARY:=/usr/share/dict/words}"
#https://www.shellhacks.com/bash-colors/
hit='\e[30;102m'; match='\e[30;103m'; miss='\e[30;107m'; reset='\e[0m'
pool=`printf '%s' {a..z}`
readlink -e "$DICTIONARY" >/dev/null
# pick first letter because uneven frequency distribution
first=${pool:(( RANDOM % ${#pool} )):1}
mapfile -t words < <(
( zcat "$DICTIONARY" 2>/dev/null || cat "$DICTIONARY" ) |
grep -xiE "^${first:?}[a-z]{$(( LETTERS - 1 ))}"
)
declare -i wc=${#words[@]}
declare -i line=$(( RANDOM % wc ))
declare -l guess='' secret=${words[$line]}
${DEBUG:+ echo $secret: $line/$wc}
function score {
local -a pad=() matched=(); color=$reset
# pre-mark hits with '=', then later '.' for match
for (( i=0; i < LETTERS; i++ )); do
c=${guess:$i:1}; k=${secret:$i:1}
[[ "$k" == "$c" ]] && pad[$i]='=' || pad[$i]=$k
done
for (( i=0; i < LETTERS; i++ )); do
c=${guess:$i:1}
if [[ ${pad[$i]} == '=' ]]; then
color=$hit; matched+=( "$c" )
elif [[ "$c" == " " ]]; then
# _hint
color=$reset; c='_'
elif [[ "${pad[@]}" =~ ${c:-\000} ]]; then
# mark with '.'
color=$match; pad=( "${pad[@]//$c/.}" ); matched+=( "$c" )
else
color=$miss
[[ "${matched[@]}" =~ $c ]] || pool=${pool/$c/ }
fi
pool=${pool/$c/${c^^}}
echo -en "$color ${c^^} "
done
echo -e "${reset}\t\t${pool}\n"
}
#--- MAIN ---
echo "Guess the secret word ($LETTERS letters) in $ROUNDS attempts. (Type '_hint' for an assist)"
echo
for (( j=1; j <= ROUNDS; j++ )); do
read -ep "$j/$ROUNDS: " guess
if [[ "$guess" =~ _hint ]]; then
offset=$(( RANDOM % LETTERS ))
guess=`printf '%*s%*s' $((offset + 1)) "${secret:$offset:1}" $((LETTERS - offset - 1)) ' '`
elif (( ${#guess} != LETTERS )) || ! [[ "$guess" =~ [a-z]{$LETTERS} ]]; then
echo -e " WARN\timproper guess"; continue
fi
score
[[ "$guess" == "$secret" ]] && { echo "Congratulations!"; exit; }
done
echo "The word was '$secret'. Better luck next time!"
# dictionaries
# [esl]='https://eslforums.com/5-letter-words/'
# [github]='https://gist.githubusercontent.com/prichey/95db6bdef37482ba3f6eb7b4dec99101/raw/'
# [popular]='https://raw.githubusercontent.com/dolph/dictionary/master/popular.txt'
# http://www.yougowords.com/5-letters
# http://www.newgeneralservicelist.org/
# [system]=/usr/share/dict/words
#[ -s "$DICTIONARY" ] || {
# command -v curl &>/dev/null || exit 1
# #pic a dict, fetch and process
#}
@tb3088
Copy link
Author

tb3088 commented Feb 6, 2022

Hey @CqN let's chat over here. 'declare -l' or '-u' forces lower or upper case when values are assigned.

@CqN
Copy link

CqN commented Feb 7, 2022 via email

@tb3088
Copy link
Author

tb3088 commented Feb 7, 2022

github doesn't publish emails and actively squashes them in comments/posts like this. There are excellent books out there on Bash scripting. Seems you could benefit from getting one.
https://www.oreilly.com/library/view/bash-cookbook-2nd/9781491975329/

@CqN
Copy link

CqN commented Feb 7, 2022 via email

@tb3088
Copy link
Author

tb3088 commented Feb 7, 2022

so you want to type in the guess as lowercase and for it to echo to the screen as uppercase? what on earth for? users have capslock key for a reason. but yes you can do this, you just need to turn off echo during a tight loop read() and then echo out. But if you want to upper-case immediately whatever the user typed in, just declare -u the variable. First rule of programming is don't fk with what the user is typing in.

@CqN
Copy link

CqN commented Feb 7, 2022 via email

@tb3088
Copy link
Author

tb3088 commented Feb 7, 2022

just because you 'score' the user's entry as all caps is distinct and separate from overriding the keystrokes they are entering during read(). NEVER lie to the user by changing his input as he's typing it. If you want to 'score' the guess as lowercase if he used lower, and score it as upper if he used upper, then don't use declare -u or declare -l for guess.

sorry man but you're wasting time on a broken UI paradigm. The correct answer is to not do that.

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