Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e62094a6f | ||
|
|
0f8640e9e8 | ||
|
|
6b07093d68 | ||
|
|
a3f9ae6a17 | ||
|
|
d34d98c15d | ||
|
|
8dfebaa9f8 | ||
|
|
f29d81e932 | ||
|
|
73f403693a | ||
|
|
1f6a5b4a44 | ||
|
|
437d46e975 | ||
|
|
7621938f04 | ||
|
|
1a9a440509 | ||
|
|
d9a7c94125 | ||
|
|
efa709e34e | ||
|
|
a7cdb712fd | ||
|
|
75074d8e84 |
@@ -33,7 +33,7 @@ function main() {
|
|||||||
declare -g ANSWER
|
declare -g ANSWER
|
||||||
declare -g POOL='QWERTYUIOPASDFGHJKLZXCVBNM'
|
declare -g POOL='QWERTYUIOPASDFGHJKLZXCVBNM'
|
||||||
declare -Ag YELLOWS
|
declare -Ag YELLOWS
|
||||||
declare -g REVEALING=0
|
declare -Ag KEYS
|
||||||
ANSWER=$(select_random_word)
|
ANSWER=$(select_random_word)
|
||||||
|
|
||||||
set_options "$@"
|
set_options "$@"
|
||||||
@@ -51,24 +51,31 @@ function main() {
|
|||||||
init_board
|
init_board
|
||||||
fi
|
fi
|
||||||
|
|
||||||
[[ "${ONLINE}" == 1 ]] && save_game "NO_EXIT"
|
[[ "${ONLINE}" == 1 ]] && save_game "${ROW}"
|
||||||
|
|
||||||
render_board
|
render_board
|
||||||
|
|
||||||
for (( ROW; ROW<HEIGHT; ROW++ )); do
|
for (( ROW; ROW<HEIGHT; ROW++ )); do
|
||||||
declare GUESS
|
declare GUESS
|
||||||
[[ "${KEYBOARD}" == 1 ]] && render_keyboard
|
[[ "${KEYBOARD}" == 1 ]] && render_keyboard
|
||||||
GUESS=$(retrive_guess)
|
GUESS=$(retrieve_guess) || { echo -en "${NEWLINE}"; exit 0; }
|
||||||
check_guess "${GUESS}" "${ROW}" ; WIN=$?
|
check_guess "${GUESS}" "${ROW}" ; WIN=$?
|
||||||
|
|
||||||
|
# Save before the reveal so quitting or disconnecting can't undo a guess
|
||||||
|
if [[ $WIN == 5 ]] || (( ROW+1 == HEIGHT )); then
|
||||||
|
save_result
|
||||||
|
elif [[ "${ONLINE}" == 1 ]]; then
|
||||||
|
save_game "$(( ROW+1 ))"
|
||||||
|
fi
|
||||||
|
|
||||||
reveal_row "${ROW}"
|
reveal_row "${ROW}"
|
||||||
REVEALING=0
|
|
||||||
|
|
||||||
if [[ $WIN == 5 ]]; then
|
if [[ $WIN == 5 ]]; then
|
||||||
save_result
|
|
||||||
echo -e "${CLEAR_DOWN}YOU WIN"
|
echo -e "${CLEAR_DOWN}YOU WIN"
|
||||||
if [[ "${ONLINE}" == 1 && "${DAILY_DONE}" != 1 ]]; then
|
if [[ "${ONLINE}" == 1 && "${DAILY_DONE}" != 1 ]]; then
|
||||||
echo "Share/view your game with -> curl \"${URL_BASE}/${RESULT_FILE}\""
|
echo "Share/view your game with -> curl \"${URL_BASE}/${RESULT_FILE}\""
|
||||||
|
echo "Share/view your stats with -> curl \"${URL_BASE}/stats/${USER}\""
|
||||||
|
echo "Check out the new homepage in your browser -> ${URL_BASE}"
|
||||||
while read -r board_line; do echo "${board_line//[A-Z]/ }"; done < "${RESULT_FILE}"
|
while read -r board_line; do echo "${board_line//[A-Z]/ }"; done < "${RESULT_FILE}"
|
||||||
fi
|
fi
|
||||||
echo "Guess - $(( ROW+1 ))/6"
|
echo "Guess - $(( ROW+1 ))/6"
|
||||||
@@ -76,16 +83,12 @@ function main() {
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
[[ "${BORDER}" == 1 ]] && echo -en "${CLEAR_DOWN}${INDENT}${INDENT}"
|
render_answer
|
||||||
echo -en "${CLEAR_LINE}${BOLD}"
|
|
||||||
for (( i=0; i<WIDTH; i++ )); do
|
|
||||||
echo -en " ${ANSWER:$i:1} "
|
|
||||||
done
|
|
||||||
echo -en "${NEWLINE}${RESET}"
|
|
||||||
|
|
||||||
save_result
|
if [[ "${ONLINE}" == 1 && "${DAILY_DONE}" != 1 ]]; then
|
||||||
if [[ "${ONLINE}" == 1 && "${DAILY_DONE}" != 1 ]]; then
|
|
||||||
echo "Share/view your game with -> curl \"${URL_BASE}/${RESULT_FILE}\""
|
echo "Share/view your game with -> curl \"${URL_BASE}/${RESULT_FILE}\""
|
||||||
|
echo "Share/view your stats with -> curl \"${URL_BASE}/stats/${USER}\""
|
||||||
|
echo "Check out the new homepage in your browser -> ${URL_BASE}"
|
||||||
while read -r board_line; do echo "${board_line//[A-Z]/ }"; done < "${RESULT_FILE}"
|
while read -r board_line; do echo "${board_line//[A-Z]/ }"; done < "${RESULT_FILE}"
|
||||||
fi
|
fi
|
||||||
exit 0
|
exit 0
|
||||||
@@ -137,9 +140,15 @@ function set_options() {
|
|||||||
echo -en "${CLEAR_SCREEN}"
|
echo -en "${CLEAR_SCREEN}"
|
||||||
|
|
||||||
declare -g USER
|
declare -g USER
|
||||||
read -r -p "Username: " USER
|
read -r -p "Username: " USER || exit 1
|
||||||
USER="${USER,,}"
|
USER="${USER,,}"
|
||||||
|
|
||||||
|
# Username is used in file names and URLs so only allow safe characters
|
||||||
|
while [[ -z "${USER}" || "${USER}" == *[!a-z0-9_]* || ${#USER} -gt 32 ]]; do
|
||||||
|
read -r -p "${CLEAR_UP1}Username (letters, numbers and _ only): " USER || exit 1
|
||||||
|
USER="${USER,,}"
|
||||||
|
done
|
||||||
|
|
||||||
declare -g DAY
|
declare -g DAY
|
||||||
DAY=$(( EPOCHSECONDS / 86400 ))
|
DAY=$(( EPOCHSECONDS / 86400 ))
|
||||||
|
|
||||||
@@ -173,14 +182,15 @@ function set_options() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
trap save_game SIGINT
|
# Progress is saved after every guess so Ctrl-C only needs to tidy the terminal
|
||||||
|
trap 'echo -en "${SHOW_CURSOR}${RESET}"; exit 0' SIGINT
|
||||||
;;
|
;;
|
||||||
d)
|
d)
|
||||||
DELAY=0
|
DELAY=0
|
||||||
;;
|
;;
|
||||||
h)
|
h)
|
||||||
echo -e \
|
echo -e \
|
||||||
"Usage: <program_name> [OPTIONS]
|
"Usage: wordle [OPTIONS]
|
||||||
Play a game for Wordle
|
Play a game for Wordle
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
@@ -189,6 +199,7 @@ Options:
|
|||||||
-H Enable hard mode
|
-H Enable hard mode
|
||||||
-S Enable strict hard mode (Cannot reuse grey letters)
|
-S Enable strict hard mode (Cannot reuse grey letters)
|
||||||
-k disable on screen keyboard
|
-k disable on screen keyboard
|
||||||
|
-o Enable online mode
|
||||||
-d Enable debug mode (no delay during letter reveal)
|
-d Enable debug mode (no delay during letter reveal)
|
||||||
-h Show this help message and exit"
|
-h Show this help message and exit"
|
||||||
|
|
||||||
@@ -227,29 +238,54 @@ function render_board() {
|
|||||||
[[ "${BORDER}" == 1 ]] && echo -e "└─────────────────┘"
|
[[ "${BORDER}" == 1 ]] && echo -e "└─────────────────┘"
|
||||||
}
|
}
|
||||||
|
|
||||||
function retrive_guess() {
|
function render_answer() {
|
||||||
|
[[ "${BORDER}" == 1 ]] && echo -en "${CLEAR_DOWN}${INDENT}${INDENT}"
|
||||||
|
echo -en "${CLEAR_LINE}${BOLD}"
|
||||||
|
for (( i=0; i<WIDTH; i++ )); do
|
||||||
|
echo -en " ${ANSWER:$i:1} "
|
||||||
|
done
|
||||||
|
echo -en "${NEWLINE}${RESET}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Exits non-zero when input has ended so the caller can quit
|
||||||
|
function retrieve_guess() {
|
||||||
if [[ -n $1 ]]; then
|
if [[ -n $1 ]]; then
|
||||||
read -r -p "${CLEAR_UP1}$1: " GUESS
|
read -r -p "${CLEAR_UP1}$1: " GUESS || end_of_input
|
||||||
else
|
else
|
||||||
read -r -p "${CLEAR_2END}Enter Guess: " GUESS
|
read -r -p "${CLEAR_2END}Enter Guess: " GUESS || end_of_input
|
||||||
fi
|
fi
|
||||||
|
|
||||||
while [[ ${#GUESS} -ne 5 ]] || [[ ! "${GUESS,,}" =~ ^[a-z]{5}$ ]]; do
|
while [[ ${#GUESS} -ne 5 ]] || [[ ! "${GUESS,,}" =~ ^[a-z]{5}$ ]]; do
|
||||||
read -r -p "${CLEAR_UP1}Must be 5 letters: " GUESS
|
read -r -p "${CLEAR_UP1}Must be 5 letters: " GUESS || end_of_input
|
||||||
done
|
done
|
||||||
declare valid_guess
|
declare valid_guess
|
||||||
validate_guess "${GUESS}" ; valid_guess=$?
|
validate_guess "${GUESS}" ; valid_guess=$?
|
||||||
|
|
||||||
[[ ${valid_guess} == 1 ]] && GUESS=$(retrive_guess "Not A Valid Word")
|
declare message
|
||||||
[[ ${valid_guess} == 2 ]] && GUESS=$(retrive_guess "Not A Valid Guess")
|
declare -r pos="${HARD_MODE_POS}"
|
||||||
[[ ${valid_guess} == 3 ]] && GUESS=$(retrive_guess "GREEN Issue")
|
declare -r letter="${GUESS:pos:1}"
|
||||||
[[ ${valid_guess} == 4 ]] && GUESS=$(retrive_guess "YELLOW Issue")
|
declare -ra ordinals=(1st 2nd 3rd 4th 5th)
|
||||||
[[ ${valid_guess} == 5 ]] && GUESS=$(retrive_guess "POOL Issue")
|
[[ ${valid_guess} == 1 ]] && message="Not A Valid Word"
|
||||||
[[ ${valid_guess} == 6 ]] && GUESS=$(retrive_guess "NEW Issue")
|
[[ ${valid_guess} == 3 ]] && message="${ordinals[pos]} letter must be ${greens[pos]}"
|
||||||
|
[[ ${valid_guess} == 4 ]] && message="Must contain ${yellows[pos]}"
|
||||||
|
[[ ${valid_guess} == 5 ]] && message="${letter^^} is not in the word"
|
||||||
|
[[ ${valid_guess} == 6 ]] && message="${ordinals[pos]} letter can't be ${letter^^}"
|
||||||
|
|
||||||
|
if [[ -n "${message}" ]]; then
|
||||||
|
GUESS=$(retrieve_guess "${message}") || exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo -n "${GUESS^^}"
|
echo -n "${GUESS^^}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Called when read gets Ctrl-D. On a terminal treat it like Enter, but if
|
||||||
|
# input has really ended (closed pipe) exit, or the retry loop spins forever
|
||||||
|
function end_of_input() {
|
||||||
|
[[ -t 0 ]] || exit 1
|
||||||
|
# Ctrl-D doesn't move to a new line like Enter, which the prompts expect
|
||||||
|
echo -en "${NEWLINE}" >&2
|
||||||
|
}
|
||||||
|
|
||||||
function render_keyboard() {
|
function render_keyboard() {
|
||||||
declare pos=$(( BORDER*2+7 ))
|
declare pos=$(( BORDER*2+7 ))
|
||||||
echo -en "${RESET}\e[$pos;0H"
|
echo -en "${RESET}\e[$pos;0H"
|
||||||
@@ -259,7 +295,12 @@ function render_keyboard() {
|
|||||||
elif [[ $i == 19 ]]; then
|
elif [[ $i == 19 ]]; then
|
||||||
echo -en "${NEWLINE} "
|
echo -en "${NEWLINE} "
|
||||||
fi
|
fi
|
||||||
echo -n "${POOL:$i:1} "
|
declare key="${POOL:$i:1}"
|
||||||
|
if [[ -n "${KEYS[$key]}" ]]; then
|
||||||
|
echo -n "${KEYS[$key]}${BOLD}${LETTER}${key}${RESET} "
|
||||||
|
else
|
||||||
|
echo -n "${key} "
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
echo -en "${NEWLINE}"
|
echo -en "${NEWLINE}"
|
||||||
}
|
}
|
||||||
@@ -285,6 +326,8 @@ function hard_mode_check() {
|
|||||||
declare GUESS=$1
|
declare GUESS=$1
|
||||||
|
|
||||||
for (( i=0; i<WIDTH; i++ )); do
|
for (( i=0; i<WIDTH; i++ )); do
|
||||||
|
HARD_MODE_POS=$i # letter position that failed, for the error message
|
||||||
|
|
||||||
if [[ "${STRICT_HARD_MODE}" == 1 ]]; then
|
if [[ "${STRICT_HARD_MODE}" == 1 ]]; then
|
||||||
[[ "${POOL}" == *${GUESS:i:1}* ]] || return 5
|
[[ "${POOL}" == *${GUESS:i:1}* ]] || return 5
|
||||||
fi
|
fi
|
||||||
@@ -300,7 +343,7 @@ function hard_mode_check() {
|
|||||||
for (( r=ROW-1; r>=0; r-- )); do
|
for (( r=ROW-1; r>=0; r-- )); do
|
||||||
CHAR="${GUESS:i:1}"
|
CHAR="${GUESS:i:1}"
|
||||||
PREV_CHAR="${YELLOWS[$r,$i]}"
|
PREV_CHAR="${YELLOWS[$r,$i]}"
|
||||||
[[ "${CHAR}" == "${PREV_CHAR}" ]] && return 4
|
[[ "${CHAR}" == "${PREV_CHAR}" ]] && return 6
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -330,6 +373,7 @@ function check_guess() {
|
|||||||
|
|
||||||
if [[ "${_char}" == "${_answer:$c:1}" ]]; then
|
if [[ "${_char}" == "${_answer:$c:1}" ]]; then
|
||||||
greens[c]="${_char}"
|
greens[c]="${_char}"
|
||||||
|
KEYS[$_char]="${GREEN}"
|
||||||
BOARD[$_row,$c]="${GREEN}${BOLD}${LETTER} ${_char} ${UNBOLD}"
|
BOARD[$_row,$c]="${GREEN}${BOLD}${LETTER} ${_char} ${UNBOLD}"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
@@ -338,6 +382,7 @@ function check_guess() {
|
|||||||
chars="${chars/${_char}/}"
|
chars="${chars/${_char}/}"
|
||||||
yellows[c]="${_char}" # volatile - stores previous row yellows
|
yellows[c]="${_char}" # volatile - stores previous row yellows
|
||||||
YELLOWS[$_row,$c]="${_char}" # stores all yellow from all rounds
|
YELLOWS[$_row,$c]="${_char}" # stores all yellow from all rounds
|
||||||
|
[[ "${KEYS[$_char]}" != "${GREEN}" ]] && KEYS[$_char]="${YELLOW}"
|
||||||
BOARD[$_row,$c]="${YELLOW}${BOLD}${LETTER} ${_char} ${UNBOLD}"
|
BOARD[$_row,$c]="${YELLOW}${BOLD}${LETTER} ${_char} ${UNBOLD}"
|
||||||
fi
|
fi
|
||||||
[[ "${ANSWER}" =~ ${_char} ]] && continue
|
[[ "${ANSWER}" =~ ${_char} ]] && continue
|
||||||
@@ -350,7 +395,6 @@ function check_guess() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function reveal_row() {
|
function reveal_row() {
|
||||||
REVEALING=1
|
|
||||||
echo -en "${UP1}${SAVE}"
|
echo -en "${UP1}${SAVE}"
|
||||||
echo -en "${HIDE_CURSOR}"
|
echo -en "${HIDE_CURSOR}"
|
||||||
# Must increment by 1 Row as 0 == 1 for escape code
|
# Must increment by 1 Row as 0 == 1 for escape code
|
||||||
@@ -370,8 +414,9 @@ function load_game() {
|
|||||||
source "${PROGRESS_FILE}"
|
source "${PROGRESS_FILE}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# shellcheck disable=SC2317 # Don't warn about unreachable commands in this function
|
# Takes the row the game should resume from
|
||||||
function save_game() {
|
function save_game() {
|
||||||
|
declare -r row=$1
|
||||||
: > "${PROGRESS_FILE}"
|
: > "${PROGRESS_FILE}"
|
||||||
|
|
||||||
for key in "${!BOARD[@]}"; do
|
for key in "${!BOARD[@]}"; do
|
||||||
@@ -382,36 +427,40 @@ function save_game() {
|
|||||||
echo "YELLOWS[$key]='${YELLOWS[$key]}'" >> "${PROGRESS_FILE}"
|
echo "YELLOWS[$key]='${YELLOWS[$key]}'" >> "${PROGRESS_FILE}"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
for key in "${!KEYS[@]}"; do
|
||||||
|
echo "KEYS[$key]='${KEYS[$key]}'" >> "${PROGRESS_FILE}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Previous row's hints, needed by hard_mode_check after a resume
|
||||||
|
for key in "${!greens[@]}"; do
|
||||||
|
echo "greens[$key]='${greens[$key]}'" >> "${PROGRESS_FILE}"
|
||||||
|
done
|
||||||
|
|
||||||
|
for key in "${!yellows[@]}"; do
|
||||||
|
echo "yellows[$key]='${yellows[$key]}'" >> "${PROGRESS_FILE}"
|
||||||
|
done
|
||||||
|
|
||||||
{
|
{
|
||||||
echo "ROW=$(( ROW + REVEALING ))"
|
echo "ROW=${row}"
|
||||||
echo "POOL=${POOL}"
|
echo "POOL=${POOL}"
|
||||||
echo "HARD_MODE=${HARD_MODE}"
|
echo "HARD_MODE=${HARD_MODE}"
|
||||||
echo "STRICT_HARD_MODE=${STRICT_HARD_MODE}"
|
echo "STRICT_HARD_MODE=${STRICT_HARD_MODE}"
|
||||||
echo "WIN=${WIN}"
|
echo "WIN=${WIN}"
|
||||||
echo "ANSWER=${ANSWER}"
|
echo "ANSWER=${ANSWER}"
|
||||||
echo "IN_PROGRESS=1"
|
echo "IN_PROGRESS=1"
|
||||||
[[ "${PLAYED}" == 1 ]] && echo 'PLAYED=1'
|
[[ "${PLAYED}" == 1 ]] && echo 'PLAYED=1'
|
||||||
} >> "${PROGRESS_FILE}"
|
} >> "${PROGRESS_FILE}"
|
||||||
|
|
||||||
echo -en "${RESET}"
|
|
||||||
[[ -z $1 ]] && exit 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function save_result() {
|
function save_result() {
|
||||||
[[ "${ONLINE}" == 0 ]] && return 0
|
[[ "${ONLINE}" == 0 ]] && return 0
|
||||||
[[ "${ONLINE}" == 1 ]] && update_stats
|
|
||||||
if [[ "${ONLINE}" == 1 ]] && [[ "${DAILY_DONE}" != 1 ]]; then
|
if [[ "${ONLINE}" == 1 ]] && [[ "${DAILY_DONE}" != 1 ]]; then
|
||||||
|
update_stats
|
||||||
render_board > "${RESULT_FILE}"
|
render_board > "${RESULT_FILE}"
|
||||||
render_board > "${CUR_RESULT_FILE}"
|
render_board > "${CUR_RESULT_FILE}"
|
||||||
if [[ $WIN != 5 ]]; then
|
if [[ $WIN != 5 ]]; then
|
||||||
[[ "${BORDER}" == 1 ]] && echo -en "${CLEAR_DOWN}${INDENT}${INDENT}" >> "${RESULT_FILE}"
|
render_answer >> "${RESULT_FILE}"
|
||||||
echo -en "${CLEAR_LINE}${BOLD}" >> "${RESULT_FILE}"
|
render_answer >> "${CUR_RESULT_FILE}"
|
||||||
for (( i=0; i<WIDTH; i++ )); do
|
|
||||||
echo -en " ${ANSWER:$i:1} " >> "${RESULT_FILE}"
|
|
||||||
echo -en " ${ANSWER:$i:1} " >> "${CUR_RESULT_FILE}"
|
|
||||||
done
|
|
||||||
echo -en "${NEWLINE}${RESET}" >> "${RESULT_FILE}"
|
|
||||||
echo -en "${NEWLINE}${RESET}" >> "${CUR_RESULT_FILE}"
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
echo 'PLAYED=1' > "${PROGRESS_FILE}"
|
echo 'PLAYED=1' > "${PROGRESS_FILE}"
|
||||||
@@ -451,6 +500,7 @@ function update_stats() {
|
|||||||
integer_part=$(( result / 100 ))
|
integer_part=$(( result / 100 ))
|
||||||
decimal_part=$(( result % 100 ))
|
decimal_part=$(( result % 100 ))
|
||||||
|
|
||||||
|
[[ ${decimal_part} -lt 10 ]] && decimal_part="0${decimal_part}"
|
||||||
AVERAGE="${integer_part}.${decimal_part}"
|
AVERAGE="${integer_part}.${decimal_part}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user