Compare commits

...
Author SHA1 Message Date
adamandClaude Opus 5.5 8dfebaa9f8 zero pad decimal part of average guesses
An average of 3.04 was shown as 3.4 because the hundredths weren't
padded. Pad with parameter expansion to keep the script bash-only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 17:24:24 +00:00
adamandClaude Opus 5.5 f29d81e932 keep hard mode hints when resuming a game
hard_mode_check uses the previous row's greens and yellows, but these
weren't written to the progress file, so after resuming a game the next
guess could drop known green and yellow letters. Save them alongside
the rest of the game state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 17:19:24 +00:00
adamandClaude Opus 5.5 73f403693a save progress after every guess
Progress was only saved at the start of a game and on Ctrl-C, so a
dropped connection lost every guess made so far. Ctrl-C during the
reveal also saved the scored row without advancing ROW, which gave the
player that guess back on resume.

Save straight after each guess is scored (before the reveal) with the
next row, record the result before the final reveal, and make the
SIGINT trap only tidy up the terminal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 17:17:20 +00:00
abrown38andClaude Opus 5.5 1f6a5b4a44 validate username in online mode
Usernames are used in file names and URLs, so a '/' could write files
outside the game directory and a space broke the share links. Only
allow a-z, 0-9 and _ (max 32 chars) and re-prompt otherwise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 17:12:44 +00:00
abrown38 437d46e975 add colour to keyboard 2026-09-29 17:07:46 +00:00
adam 7621938f04 set program name in help message
Deploy game to server / deploy (push) Successful in 8s
2026-02-11 21:43:00 +00:00
adam 1a9a440509 fix duplicate path in URL
Deploy game to server / deploy (push) Successful in 6s
2026-01-21 12:48:48 +00:00
adam d9a7c94125 add link to website
Deploy game to server / deploy (push) Successful in 7s
2025-11-29 03:50:26 +00:00
+46 -12
View File
@@ -33,6 +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 -Ag KEYS
ANSWER=$(select_random_word) ANSWER=$(select_random_word)
set_options "$@" set_options "$@"
@@ -50,7 +51,7 @@ function main() {
init_board init_board
fi fi
[[ "${ONLINE}" == 1 ]] && save_game "NO_EXIT" [[ "${ONLINE}" == 1 ]] && save_game "${ROW}"
render_board render_board
@@ -60,14 +61,21 @@ function main() {
GUESS=$(retrieve_guess) GUESS=$(retrieve_guess)
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}"
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 "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"
@@ -82,10 +90,10 @@ function main() {
done done
echo -en "${NEWLINE}${RESET}" 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 "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 +145,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 +187,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:
@@ -260,7 +275,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}"
} }
@@ -331,6 +351,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
@@ -339,6 +360,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
@@ -370,8 +392,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,8 +405,21 @@ 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}" 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}"
@@ -392,9 +428,6 @@ function save_game() {
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() {
@@ -451,6 +484,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