From: voloved <36523934+voloved@users.noreply.github.com> Date: Sun, 3 Aug 2025 16:56:23 +0000 (-0400) Subject: Wordle port (#45) X-Git-Url: https://git.earman.xyz/?a=commitdiff_plain;h=4b4735065fb33531b7262e6fb9d76cca1799ff2b;p=sensor-watch.git Wordle port (#45) * Moved Wordle from legacy to main folder * Add delays before registering btns, don't repeat words, cleaned up enum names * Updated print logic for second movement * Removed from watch_faces --- diff --git a/legacy/watch_faces/complication/wordle_face.c b/legacy/watch_faces/complication/wordle_face.c deleted file mode 100644 index a93a0e0..0000000 --- a/legacy/watch_faces/complication/wordle_face.c +++ /dev/null @@ -1,599 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2024 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include "wordle_face.h" -#include "watch_utility.h" - -static uint32_t get_random(uint32_t max) { - #if __EMSCRIPTEN__ - return rand() % max; - #else - return arc4random_uniform(max); - #endif -} - -static uint8_t get_first_pos(WordleLetterResult *word_elements_result) { - for (size_t i = 0; i < WORDLE_LENGTH; i++) { - if (word_elements_result[i] != WORDLE_LETTER_CORRECT) - return i; - } - return 0; -} - -static uint8_t get_next_pos(uint8_t curr_pos, WordleLetterResult *word_elements_result) { - for (size_t pos = curr_pos; pos < WORDLE_LENGTH;) { - if (word_elements_result[++pos] != WORDLE_LETTER_CORRECT) - return pos; - } - return WORDLE_LENGTH; -} - -static uint8_t get_prev_pos(uint8_t curr_pos, WordleLetterResult *word_elements_result) { - if (curr_pos == 0) return 0; - for (int8_t pos = curr_pos; pos >= 0;) { - if (word_elements_result[--pos] != WORDLE_LETTER_CORRECT) - return pos; - } - return curr_pos; -} - -static void get_next_letter(const uint8_t curr_pos, uint8_t *word_elements, const bool *known_wrong_letters, const bool skip_wrong_letter) { - do { - if (word_elements[curr_pos] >= WORDLE_NUM_VALID_LETTERS) word_elements[curr_pos] = 0; - else word_elements[curr_pos] = (word_elements[curr_pos] + 1) % WORDLE_NUM_VALID_LETTERS; - } while (skip_wrong_letter && known_wrong_letters[word_elements[curr_pos]]); -} - -static void get_prev_letter(const uint8_t curr_pos, uint8_t *word_elements, const bool *known_wrong_letters, const bool skip_wrong_letter) { - do { - if (word_elements[curr_pos] >= WORDLE_NUM_VALID_LETTERS) word_elements[curr_pos] = WORDLE_NUM_VALID_LETTERS - 1; - else word_elements[curr_pos] = (word_elements[curr_pos] + WORDLE_NUM_VALID_LETTERS - 1) % WORDLE_NUM_VALID_LETTERS; - } while (skip_wrong_letter && known_wrong_letters[word_elements[curr_pos]]); -} - -static void display_letter(wordle_state_t *state, bool display_dash) { - char buf[1 + 1]; - if (state->word_elements[state->position] >= WORDLE_NUM_VALID_LETTERS) { - if (display_dash) - watch_display_string("-", state->position + 5); - else - watch_display_string(" ", state->position + 5); - return; - } - sprintf(buf, "%c", _valid_letters[state->word_elements[state->position]]); - watch_display_string(buf, state->position + 5); -} - -static void display_all_letters(wordle_state_t *state) { - uint8_t prev_pos = state->position; - watch_display_string(" ", 4); - for (size_t i = 0; i < WORDLE_LENGTH; i++) { - state->position = i; - display_letter(state, false); - } - state->position = prev_pos; -} - -#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES -static void display_not_in_dict(wordle_state_t *state) { - state->curr_screen = SCREEN_NO_DICT; - watch_display_string("nodict", 4); -} - -static void display_already_guessed(wordle_state_t *state) { - state->curr_screen = SCREEN_ALREADY_GUESSED; - watch_display_string("GUESSD", 4); -} - -static uint32_t check_word_in_dict(uint8_t *word_elements) { - bool is_exact_match; - for (uint16_t i = 0; i < WORDLE_NUM_WORDS; i++) { - is_exact_match = true; - for (size_t j = 0; j < WORDLE_LENGTH; j++) { - if (_valid_letters[word_elements[j]] != _valid_words[i][j]) { - is_exact_match = false; - break; - } - } - if (is_exact_match) return i; - } - for (uint16_t i = 0; i < WORDLE_NUM_POSSIBLE_WORDS; i++) { - is_exact_match = true; - for (size_t j = 0; j < WORDLE_LENGTH; j++) { - if (_valid_letters[word_elements[j]] != _possible_words[i][j]) { - is_exact_match = false; - break; - } - } - if (is_exact_match) return WORDLE_NUM_WORDS + i; - } - return WORDLE_NUM_WORDS + WORDLE_NUM_POSSIBLE_WORDS; -} -#endif - -static bool check_word(wordle_state_t *state) { - // Exact - bool is_exact_match = true; - bool answer_already_accounted[WORDLE_LENGTH] = { false }; - for (size_t i = 0; i < WORDLE_LENGTH; i++) { - if (_valid_letters[state->word_elements[i]] == _valid_words[state->curr_answer][i]) { - state->word_elements_result[i] = WORDLE_LETTER_CORRECT; - answer_already_accounted[i] = true; - } - else { - state->word_elements_result[i] = WORDLE_LETTER_WRONG; - is_exact_match = false; - } - } - if (is_exact_match) return true; - // Wrong Location - for (size_t i = 0; i < WORDLE_LENGTH; i++) { - if (state->word_elements_result[i] != WORDLE_LETTER_WRONG) continue; - for (size_t j = 0; j < WORDLE_LENGTH; j++) { - if (answer_already_accounted[j]) continue; - if (_valid_letters[state->word_elements[i]] == _valid_words[state->curr_answer][j]) { - state->word_elements_result[i] = WORDLE_LETTER_WRONG_LOC; - answer_already_accounted[j] = true; - break; - } - } - } - return false; -} - -static void show_skip_wrong_letter_indicator(bool skipping, WordleScreen curr_screen) { - if (curr_screen >= SCREEN_PLAYING) return; - if (skipping) - watch_display_string("H", 3); - else - watch_display_string(" ", 3); -} - -static void update_known_wrong_letters(wordle_state_t *state) { - bool wrong_loc[WORDLE_NUM_VALID_LETTERS] = {false}; - // To ignore letters that appear, but are in the wrong location, as letters that are guessed - // more often than they appear in the word will display as WORDLE_LETTER_WRONG - for (size_t i = 0; i < WORDLE_LENGTH; i++) { - if (state->word_elements_result[i] == WORDLE_LETTER_WRONG_LOC) { - for (size_t j = 0; j < WORDLE_NUM_VALID_LETTERS; j++) { - if (state->word_elements[i] == j) - wrong_loc[j] = true; - } - } - } - for (size_t i = 0; i < WORDLE_LENGTH; i++) { - if (state->word_elements_result[i] == WORDLE_LETTER_WRONG) { - for (size_t j = 0; j < WORDLE_NUM_VALID_LETTERS; j++) { - if (state->word_elements[i] == j && !wrong_loc[j]) - state->known_wrong_letters[j] = true; - } - } - } -} - -static void display_attempt(uint8_t attempt) { - char buf[3]; - sprintf(buf, "%d", attempt+1); - watch_display_string(buf, 3); -} - -static void display_playing(wordle_state_t *state) { - state->curr_screen = SCREEN_PLAYING; - display_attempt(state->attempt); - display_all_letters(state); -} - -static void reset_all_elements(wordle_state_t *state) { - for (size_t i = 0; i < WORDLE_LENGTH; i++) { - state->word_elements[i] = WORDLE_NUM_VALID_LETTERS; - state->word_elements_result[i] = WORDLE_LETTER_WRONG; - } - for (size_t i = 0; i < WORDLE_NUM_VALID_LETTERS; i++){ - state->known_wrong_letters[i] = false; - } -#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES - for (size_t i = 0; i < WORDLE_MAX_ATTEMPTS; i++) { - state->guessed_words[i] = WORDLE_NUM_WORDS + WORDLE_NUM_POSSIBLE_WORDS; - } -#endif - state->using_random_guess = false; - state->attempt = 0; -} - -static void reset_incorrect_elements(wordle_state_t *state) { - for (size_t i = 0; i < WORDLE_LENGTH; i++) { - if (state->word_elements_result[i] != WORDLE_LETTER_CORRECT) - state->word_elements[i] = WORDLE_NUM_VALID_LETTERS; - } -} - -static void reset_board(wordle_state_t *state) { - reset_all_elements(state); - state->curr_answer = get_random(WORDLE_NUM_WORDS); - watch_clear_colon(); - state->position = get_first_pos(state->word_elements_result); - display_playing(state); - watch_display_string(" -", 4); -#if __EMSCRIPTEN__ - printf("ANSWER: %s\r\n", _valid_words[state->curr_answer]); -#endif -} - -static void display_title(wordle_state_t *state) { - state->curr_screen = SCREEN_TITLE; - watch_display_string("WO WordLE", 0); - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); -} - -#if WORDLE_USE_DAILY_STREAK != 2 -static void display_continue_result(bool continuing) { - watch_display_string(continuing ? "y" : "n", 9); -} - -static void display_continue(wordle_state_t *state) { - state->curr_screen = SCREEN_CONTINUE; - watch_display_string("Cont ", 4); - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); - display_continue_result(state->continuing); -} -#endif - -static void display_streak(wordle_state_t *state) { - char buf[12]; - state->curr_screen = SCREEN_STREAK; -#if WORDLE_USE_DAILY_STREAK == 2 - if (state->streak > 99) - sprintf(buf, "WO St--dy"); - else - sprintf(buf, "WO St%2ddy", state->streak); -#else - sprintf(buf, "WO St%4d", state->streak); -#endif - watch_display_string(buf, 0); - watch_set_colon(); - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); -} - -#if WORDLE_USE_DAILY_STREAK == 2 -static void display_wait(wordle_state_t *state) { - state->curr_screen = SCREEN_WAIT; - if (state->streak < 40) { - char buf[13]; - sprintf(buf,"WO%2d WaIt ", state->streak); - watch_display_string(buf, 0); - } - else { // Streak too long to display in top-right - watch_display_string("WO WaIt ", 0); - } - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); -} -#endif - -static uint32_t get_day_unix_time(void) { - watch_date_time_t now = watch_rtc_get_date_time(); -#if WORDLE_USE_DAILY_STREAK == 2 - now.unit.hour = now.unit.minute = now.unit.second = 0; -#endif - return watch_utility_date_time_to_unix_time(now, 0); -} - -static void display_lose(wordle_state_t *state, uint8_t subsecond) { - char buf[WORDLE_LENGTH + 6]; - sprintf(buf," L %s", subsecond % 2 ? _valid_words[state->curr_answer] : " "); - watch_display_string(buf, 0); -} - -static void display_win(wordle_state_t *state, uint8_t subsecond) { - (void) state; - char buf[13]; - sprintf(buf," W %s ", subsecond % 2 ? "NICE" : "JOb "); - watch_display_string(buf, 0); -} - -static bool is_playing(const wordle_state_t *state) { - if (state->attempt > 0) return true; - for (size_t i = 0; i < WORDLE_LENGTH; i++) { - if (state->word_elements[i] != WORDLE_NUM_VALID_LETTERS) - return true; - } - return false; -} - -static void display_result(wordle_state_t *state, uint8_t subsecond) { - char buf[WORDLE_LENGTH + 1]; - for (size_t i = 0; i < WORDLE_LENGTH; i++) - { - switch (state->word_elements_result[i]) - { - case WORDLE_LETTER_WRONG: - buf[i] = '-'; - break; - case WORDLE_LETTER_CORRECT: - buf[i] = _valid_letters[state->word_elements[i]]; - break; - case WORDLE_LETTER_WRONG_LOC: - if (subsecond % 2) - buf[i] = ' '; - else - buf[i] = _valid_letters[state->word_elements[i]]; - default: - break; - } - } - watch_display_string(buf, 5); -} - -static bool act_on_btn(wordle_state_t *state, const uint8_t pin) { - switch (state->curr_screen) - { - case SCREEN_RESULT: - reset_incorrect_elements(state); - state->position = get_first_pos(state->word_elements_result); - display_playing(state); - return true; - case SCREEN_TITLE: -#if WORDLE_USE_DAILY_STREAK == 2 - if (state->day_last_game_started == get_day_unix_time()) { - display_wait(state); - } - else if (is_playing(state)) - display_playing(state); - else - display_streak(state); -#else - if (is_playing(state)) { - state->continuing = true; - display_continue(state); - } - else - display_streak(state); -#endif - return true; - case SCREEN_STREAK: - state->day_last_game_started = get_day_unix_time(); - reset_board(state); - return true; - case SCREEN_WIN: - case SCREEN_LOSE: - display_title(state); - return true; - case SCREEN_NO_DICT: - case SCREEN_ALREADY_GUESSED: - state->position = get_first_pos(state->word_elements_result); - display_playing(state); - return true; -#if WORDLE_USE_DAILY_STREAK == 2 - case SCREEN_WAIT: - (void) pin; - display_title(state); - return true; -#else - case SCREEN_CONTINUE: - switch (pin) - { - case BTN_ALARM: - if (state->continuing) - display_playing(state); - else { - reset_board(state); - state->streak = 0; - display_streak(state); - } - break; - case BTN_LIGHT: - state->continuing = !state->continuing; - display_continue_result(state->continuing); - break; - } - return true; -#endif - default: - return false; - } - return false; -} - -static void get_result(wordle_state_t *state) { -#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES - // Check if it's in the dict - uint16_t in_dict = check_word_in_dict(state->word_elements); - if (in_dict == WORDLE_NUM_WORDS + WORDLE_NUM_POSSIBLE_WORDS) { - display_not_in_dict(state); - return; - } - - // Check if already guessed - for (size_t i = 0; i < WORDLE_MAX_ATTEMPTS; i++) { - if(in_dict == state->guessed_words[i]) { - display_already_guessed(state); - return; - } - } - - state->guessed_words[state->attempt] = in_dict; -#endif - bool exact_match = check_word(state); - if (exact_match) { - reset_all_elements(state); - state->curr_screen = SCREEN_WIN; - if (state->streak < 0x7F) - state->streak++; -#if WORDLE_USE_DAILY_STREAK == 2 - state->day_last_game_started = get_day_unix_time(); // On the edge-case where we solve the puzzle at midnight -#endif - return; - } - if (++state->attempt >= WORDLE_MAX_ATTEMPTS) { - reset_all_elements(state); - state->curr_screen = SCREEN_LOSE; - state->streak = 0; - return; - } - update_known_wrong_letters(state); - state->curr_screen = SCREEN_RESULT; - return; -} - -#if (WORDLE_USE_RANDOM_GUESS != 0) -static void insert_random_guess(wordle_state_t *state) { - uint16_t random_guess; - do { // Don't allow the guess to be the same as the answer - random_guess = get_random(_num_random_guess_words); - } while (random_guess == state->curr_answer); - for (size_t i = 0; i < WORDLE_LENGTH; i++) { - for (size_t j = 0; j < WORDLE_NUM_VALID_LETTERS; j++) - { - if (_valid_words[random_guess][i] == _valid_letters[j]) - state->word_elements[i] = j; - } - } - state->position = WORDLE_LENGTH - 1; - display_all_letters(state); - state->using_random_guess = true; -} -#endif - -void wordle_face_setup(uint8_t watch_face_index, void ** context_ptr) { - (void) watch_face_index; - if (*context_ptr == NULL) { - *context_ptr = malloc(sizeof(wordle_state_t)); - memset(*context_ptr, 0, sizeof(wordle_state_t)); - wordle_state_t *state = (wordle_state_t *)*context_ptr; - state->curr_screen = SCREEN_TITLE; - state->skip_wrong_letter = false; - reset_all_elements(state); - } - // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep. -} - -void wordle_face_activate(void *context) { - wordle_state_t *state = (wordle_state_t *)context; -#if WORDLE_USE_DAILY_STREAK != 0 - uint32_t now = get_day_unix_time(); - uint32_t one_day = 60 *60 * 24; - if ((WORDLE_USE_DAILY_STREAK == 2 && now >= (state->day_last_game_started + (2*one_day))) - || (now >= (state->day_last_game_started + one_day) && is_playing(state))) { - state->streak = 0; - reset_board(state); - } -#endif - state->using_random_guess = false; - if (is_playing(state) && state->curr_screen >= SCREEN_RESULT) { - reset_incorrect_elements(state); - state->position = get_first_pos(state->word_elements_result); - } - movement_request_tick_frequency(2); - display_title(state); -} - -bool wordle_face_loop(movement_event_t event, void *context) { - wordle_state_t *state = (wordle_state_t *)context; - - switch (event.event_type) { - case EVENT_TICK: - switch (state->curr_screen) - { - case SCREEN_PLAYING: - if (event.subsecond % 2) { - display_letter(state, true); - } else { - watch_display_string(" ", state->position + 5); - } - break; - case SCREEN_RESULT: - display_result(state, event.subsecond); - break; - case SCREEN_LOSE: - display_lose(state, event.subsecond); - break; - case SCREEN_WIN: - display_win(state, event.subsecond); - break; - default: - break; - } - break; - case EVENT_LIGHT_BUTTON_UP: - if (act_on_btn(state, BTN_LIGHT)) break; - get_next_letter(state->position, state->word_elements, state->known_wrong_letters, state->skip_wrong_letter); - display_letter(state, true); - break; - case EVENT_LIGHT_LONG_PRESS: - if (state->curr_screen < SCREEN_PLAYING) { - state->skip_wrong_letter = !state->skip_wrong_letter; - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); - break; - } - if (state->curr_screen != SCREEN_PLAYING) break; - get_prev_letter(state->position, state->word_elements, state->known_wrong_letters, state->skip_wrong_letter); - display_letter(state, true); - break; - case EVENT_ALARM_BUTTON_UP: - if (act_on_btn(state, BTN_ALARM)) break; - display_letter(state, true); - if (state->word_elements[state->position] == WORDLE_NUM_VALID_LETTERS) break; -#if (WORDLE_USE_RANDOM_GUESS != 0) - if (HAL_GPIO_BTN_LIGHT_read() && - (state->using_random_guess || (state->attempt == 0 && state->position == 0))) { - insert_random_guess(state); - break; - } -#endif - state->position = get_next_pos(state->position, state->word_elements_result); - if (state->position >= WORDLE_LENGTH) { - get_result(state); - state->using_random_guess = false; - } - break; - case EVENT_ALARM_LONG_PRESS: - if (state->curr_screen != SCREEN_PLAYING) break; - display_letter(state, true); - state->position = get_prev_pos(state->position, state->word_elements_result); - break; - case EVENT_LIGHT_BUTTON_DOWN: - case EVENT_ACTIVATE: - break; - case EVENT_TIMEOUT: - if (state->curr_screen >= SCREEN_RESULT) { - reset_incorrect_elements(state); - state->position = get_first_pos(state->word_elements_result); - display_title(state); - } - break; - case EVENT_LOW_ENERGY_UPDATE: - if (state->curr_screen != SCREEN_TITLE) - display_title(state); - break; - default: - return movement_default_loop_handler(event); - } - return true; -} - -void wordle_face_resign(void *context) { - (void) context; -} - diff --git a/legacy/watch_faces/complication/wordle_face.h b/legacy/watch_faces/complication/wordle_face.h deleted file mode 100644 index 5cc2f09..0000000 --- a/legacy/watch_faces/complication/wordle_face.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2024 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef WORDLE_FACE_H_ -#define WORDLE_FACE_H_ - -#include "movement.h" - -/* - * Wordle Face - * A port of NY Times' Wordle game (https://www.nytimes.com/games/wordle/index.html) - * A random 5 letter word is chosen and you have WORDLE_MAX_ATTEMPTS attempts to guess it. - * Each guess must be a valid 5-letter word found in _legal_words in the C file. - * The only letters used are _valid_letters, also found in the C file. - * After a guess, the letters in the correct spot will remain, - * and the letters found in the word, but in the incorrect spot will blink. - * The screen after the title screen if a new game is started shows the streak of games won in a row. - * - * If WORDLE_USE_DAILY_STREAK is set to True, then the game can only be played once per day, - * and the streak resets to 0 if a day goes by without playing the game. - * - * Controls: - * Light Press - * If Playing: Next letter - * Else: Next screen - * Light Hold - * If Playing: Previous letter - * Else: Toggle Hard-Mode. This is skipping over letters that have been confirmed - * to not be in the word (indicated via 'H' in the top-right) - * - * Alarm Press - * If Playing: If WORDLE_USE_RANDOM_GUESS is set and Light btn held and - * (on first letter or already used a random guess) - * and first attempt: Use a random 5 letter word with all letters that are different. - * Else: Next position - * Else: Next screen - * Alarm Hold - * If Playing: Previous position - * Else: None - * - * Note: Actual Hard Mode in Wordle game is "Any revealed hints must be used in subsequent guesses" - * But that came off as clunky UX on the Casio. So instead it only removes unused letters from the keyboard - * as that also simplifies the keyboard. - */ - -#define WORDLE_LENGTH 5 -#define WORDLE_MAX_ATTEMPTS 6 -/* WORDLE_USE_DAILY_STREAK - * 0 = Don't ever reset the streak or the puzzle. - * 1 = Reset the streak and puzzle 24hrs after starting a puzzle and not finishing it. - * If the last puzzle was started at 8AM, it'll be considered failed at 8AM the next day. - * 2 = Reset the streak and puzzle if a puzzle goes unsolved or not started a day after the previous one. - * If the last puzzle was started at 8AM, it'll be considered failed at midnight the next day. - * This will not be the case if the puzzle is started at 8AM, continued at 11:59PM and solved at 12:01AM, the game will let that slide. - * Starting a new game instead of continuing is not allowed in this state. -*/ -#define WORDLE_USE_DAILY_STREAK 1 -#define WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES false // This allows non-words to be entered and repeat guesses to be made. It saves ~11.5KB of ROM. -/* WORDLE_USE_RANDOM_GUESS - * 0 = Don't allow quickly choosing a random quess - * 1 = Allow using a random guess of any value that can be an answer - * 2 = Allow using a random guess of any value that can be an answer where all of its letters are unique - * 3 = Allow using a random guess of any value that can be an answer, and it's considered one of the best initial choices. -*/ -#define WORDLE_USE_RANDOM_GUESS 2 -#include "wordle_face_dict.h" - -#define WORDLE_NUM_WORDS (sizeof(_valid_words) / sizeof(_valid_words[0])) -#define WORDLE_NUM_POSSIBLE_WORDS (sizeof(_possible_words) / sizeof(_possible_words[0])) -#define WORDLE_NUM_VALID_LETTERS (sizeof(_valid_letters) / sizeof(_valid_letters[0])) - -typedef enum { - WORDLE_LETTER_WRONG = 0, - WORDLE_LETTER_WRONG_LOC, - WORDLE_LETTER_CORRECT, - WORDLE_LETTER_COUNT -} WordleLetterResult; - -typedef enum { - SCREEN_TITLE = 0, - SCREEN_STREAK, - SCREEN_CONTINUE, -#if WORDLE_USE_DAILY_STREAK - SCREEN_WAIT, -#endif - SCREEN_PLAYING, - SCREEN_RESULT, - SCREEN_WIN, - SCREEN_LOSE, - SCREEN_NO_DICT, - SCREEN_ALREADY_GUESSED, - SCREEN_COUNT -} WordleScreen; - -typedef struct { - // Anything you need to keep track of, put it here! - uint8_t word_elements[WORDLE_LENGTH]; - WordleLetterResult word_elements_result[WORDLE_LENGTH]; -#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES - uint16_t guessed_words[WORDLE_MAX_ATTEMPTS]; -#endif - uint8_t attempt : 4; - uint8_t position : 3; - bool using_random_guess : 1; - uint16_t curr_answer : 14; - bool continuing : 1; - bool skip_wrong_letter : 1; - uint8_t streak; - WordleScreen curr_screen; - bool known_wrong_letters[WORDLE_NUM_VALID_LETTERS]; - uint32_t day_last_game_started; -} wordle_state_t; - -void wordle_face_setup(uint8_t watch_face_index, void ** context_ptr); -void wordle_face_activate(void *context); -bool wordle_face_loop(movement_event_t event, void *context); -void wordle_face_resign(void *context); - -#define wordle_face ((const watch_face_t){ \ - wordle_face_setup, \ - wordle_face_activate, \ - wordle_face_loop, \ - wordle_face_resign, \ - NULL, \ -}) - -#endif // WORDLE_FACE_H_ - diff --git a/legacy/watch_faces/complication/wordle_face_dict.h b/legacy/watch_faces/complication/wordle_face_dict.h deleted file mode 100644 index 6b82976..0000000 --- a/legacy/watch_faces/complication/wordle_face_dict.h +++ /dev/null @@ -1,293 +0,0 @@ -#ifndef WORDLE_FACE_DICT_H_ -#define WORDLE_FACE_DICT_H_ - -#ifndef WORDLE_LENGTH -#define WORDLE_LENGTH 5 -#endif - -#ifndef WORDLE_USE_RANDOM_GUESS -#define WORDLE_USE_RANDOM_GUESS 2 -#endif - -static const char _valid_letters[] = {'A', 'C', 'E', 'H', 'I', 'L', 'N', 'O', 'P', 'R', 'S', 'T'}; - -// From: https://matthewminer.name/projects/calculators/wordle-words-left/ -// Number of words found: 432 -static const char _valid_words[][WORDLE_LENGTH + 1] = { - "SLATE", "STARE", "SNARE", "SANER", "CRANE", "STALE", "CRATE", "RAISE", "TRACE", - "SHARE", "ARISE", "SCARE", "SPARE", "CHAOS", "TAPIR", "CAIRN", "TENOR", "CLEAN", - "HEART", "SCOPE", "SNARL", "SLEPT", "SINCE", "EPOCH", "SPACE", "RELIC", "SPOIL", - "LITER", "LEAPT", "LANCE", "RANCH", "HORSE", "LEACH", "LATER", "STEAL", "CHEAP", - "SHORT", "ETHIC", "CHANT", "ACTOR", "REACH", "SEPIA", "ONSET", "SPLAT", "LEANT", - "REACT", "OCTAL", "SPORE", "IRATE", "CORAL", "NICER", "SPILT", "SCENT", "PANIC", - "SHIRT", "PECAN", "SLAIN", "SPLIT", "ROACH", "ASCOT", "PHONE", "LITHE", "STOIC", - "STRIP", "RENAL", "POISE", "ENACT", "CHEAT", "PITCH", "NOISE", "INLET", "PEARL", - "POLAR", "PEACH", "STOLE", "CASTE", "CREST", "CRONE", "ETHOS", "THEIR", "STONE", - "SHIRE", "LATCH", "HASTE", "CLOSE", "SPINE", "SLANT", "SPEAR", "SCALE", "CAPER", - "RETCH", "PESTO", "CHIRP", "SPORT", "OPTIC", "SNAIL", "PRICE", "PLANE", "TORCH", - "PASTE", "RECAP", "SOLAR", "CRASH", "LINER", "OPINE", "ASHEN", "PALER", "ECLAT", - "SPELT", "TRIAL", "PERIL", "SLICE", "SCANT", "SAINT", "POSIT", "ATONE", "SPIRE", - "COAST", "INEPT", "SHOAL", "CLASH", "THORN", "PHASE", "SCORE", "TRICE", "PERCH", - "PORCH", "SHEAR", "CHOIR", "RHINO", "PLANT", "SHONE", "CHORE", "LEARN", "ALTER", - "CHAIN", "PANEL", "PLIER", "STEIN", "COPSE", "SONIC", "ALIEN", "CHOSE", "ACORN", - "ANTIC", "CHEST", "OTHER", "CHINA", "TALON", "SCORN", "PLAIN", "PILOT", "RIPEN", - "PATCH", "SPICE", "CLONE", "SCION", "SCONE", "STRAP", "PARSE", "SHALE", "RISEN", - "CANOE", "INTER", "LEASH", "ISLET", "PRINT", "SHINE", "NORTH", "CLEAT", "PLAIT", - "SCRAP", "CLEAR", "SLOTH", "LAPSE", "CHAIR", "SNORT", "SHARP", "OPERA", "STAIN", - "TEACH", "TRAIL", "TRAIN", "LATHE", "PIANO", "PINCH", "PETAL", "STERN", "PRONE", - "PROSE", "PLEAT", "TROPE", "PLACE", "POSER", "INERT", "CHASE", "CAROL", "STAIR", - "SATIN", "SPITE", "LOATH", "ROAST", "ARSON", "SHAPE", "CLASP", "LOSER", "SALON", - "CATER", "SHALT", "INTRO", "ALERT", "PENAL", "SHORE", "RINSE", "CREPT", "APRON", - "SONAR", "AISLE", "AROSE", "HATER", "NICHE", "POINT", "EARTH", "PINTO", "THOSE", - "CLOTH", "NOTCH", "TOPIC", "RESIN", "SCALP", "HEIST", "HERON", "TRIPE", "TONAL", - "TAPER", "SHORN", "TONIC", "HOIST", "SNORE", "STORE", "SLOPE", "OCEAN", "CHART", - "PAINT", "SPENT", "SNIPE", "CRISP", "TRASH", "PATIO", "PLATE", "HOTEL", "LEAST", - "ALONE", "RALPH", "SPIEL", "SIREN", "RATIO", "STOOP", "TROLL", "ATOLL", "SLASH", - "RETRO", "CREEP", "STILT", "SPREE", "TASTE", "CACHE", "CANON", "EATEN", "TEPEE", - "SHEET", "SNEER", "ERROR", "NATAL", "SLEEP", "STINT", "TROOP", "SHALL", "STALL", - "PIPER", "TOAST", "NASAL", "CORER", "THERE", "POOCH", "SCREE", "ELITE", "ALTAR", - "PENCE", "EATER", "ALPHA", "TENTH", "LINEN", "SHEER", "TAINT", "HEATH", "CRIER", - "TENSE", "CARAT", "CANAL", "APNEA", "THESE", "HATCH", "SHELL", "CIRCA", "APART", - "SPILL", "STEEL", "LOCAL", "STOOL", "SHEEN", "RESET", "STEEP", "ELATE", "PRESS", - "SLEET", "CROSS", "TOTAL", "TREAT", "ONION", "STATE", "CINCH", "ASSET", "THREE", - "TORSO", "SNOOP", "PENNE", "SPOON", "SHEEP", "PAPAL", "STILL", "CHILL", "THETA", - "LEECH", "INNER", "HONOR", "LOOSE", "CONIC", "SCENE", "COACH", "CONCH", "LATTE", - "ERASE", "ESTER", "PEACE", "PASTA", "INANE", "SPOOL", "TEASE", "HARSH", "PIECE", - "STEER", "SCOOP", "NINTH", "OTTER", "OCTET", "EERIE", "RISER", "LAPEL", "HIPPO", - "PREEN", "ETHER", "AORTA", "SENSE", "TRACT", "SHOOT", "SLOOP", "REPEL", "TITHE", - "IONIC", "CELLO", "CHESS", "SOOTH", "COCOA", "TITAN", "TOOTH", "TIARA", "CRESS", - "SLOSH", "RARER", "TERSE", "ERECT", "HELLO", "PARER", "RIPER", "NOOSE", "CREPE", - "CACAO", "ILIAC", "POSSE", "CACTI", "EASEL", "LASSO", "ROOST", "ALLOT", "COLON", - "LEPER", "TEETH", "TITLE", "HENCE", "NIECE", "PAPER", "TRITE", "SPELL", "RACER", - "ATTIC", "CRASS", "HITCH", "LEASE", "CEASE", "ROTOR", "ELOPE", "APPLE", "CHILI", - "START", "PHOTO", "SALSA", "STASH", "PRIOR", "TAROT", "COLOR", "CHEER", "CLASS", - "ARENA", "ELECT", "ENTER", "CATCH", "TENET", "TACIT", "TRAIT", "TERRA", "LILAC", -}; - -// These are words that'll never be used, but still need to be in the dictionary for guesses. -// Number of words found: 1898 -static const char _possible_words[][WORDLE_LENGTH + 1] = { -#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES - "AALII", "AARTI", "ACAIS", "ACARI", "ACCAS", "ACERS", "ACETA", "ACHAR", "ACHES", - "ACHOO", "ACINI", "ACNES", "ACRES", "ACROS", "ACTIN", "ACTON", "AECIA", "AEONS", - "AERIE", "AEROS", "AESIR", "AHEAP", "AHENT", "AHINT", "AINEE", "AIOLI", "AIRER", - "AIRNS", "AIRTH", "AIRTS", "AITCH", "ALAAP", "ALANE", "ALANS", "ALANT", "ALAPA", - "ALAPS", "ALATE", "ALCOS", "ALECS", "ALEPH", "ALIAS", "ALINE", "ALIST", "ALLEE", - "ALLEL", "ALLIS", "ALOES", "ALOHA", "ALOIN", "ALOOS", "ALTHO", "ALTOS", "ANANA", - "ANATA", "ANCHO", "ANCLE", "ANCON", "ANEAR", "ANELE", "ANENT", "ANILE", "ANILS", - "ANION", "ANISE", "ANLAS", "ANNAL", "ANNAS", "ANNAT", "ANOAS", "ANOLE", "ANSAE", - "ANTAE", "ANTAR", "ANTAS", "ANTES", "ANTIS", "ANTRA", "ANTRE", "APACE", "APERS", - "APERT", "APHIS", "APIAN", "APIOL", "APISH", "APOOP", "APORT", "APPAL", "APPEL", - "APPRO", "APRES", "APSES", "APSIS", "APSOS", "APTER", "ARARS", "ARCHI", "ARCOS", - "AREAE", "AREAL", "AREAR", "AREAS", "ARECA", "AREIC", "ARENE", "AREPA", "ARERE", - "ARETE", "ARETS", "ARETT", "ARHAT", "ARIAS", "ARIEL", "ARILS", "ARIOT", "ARISH", - "ARLES", "ARNAS", "AROHA", "ARPAS", "ARPEN", "ARRAH", "ARRAS", "ARRET", "ARRIS", - "ARSES", "ARSIS", "ARTAL", "ARTEL", "ARTIC", "ARTIS", "ASANA", "ASCON", "ASHES", - "ASHET", "ASPEN", "ASPER", "ASPIC", "ASPIE", "ASPIS", "ASPRO", "ASSAI", "ASSES", - "ASSOT", "ASTER", "ASTIR", "ATAPS", "ATILT", "ATLAS", "ATOCS", "ATRIA", "ATRIP", - "ATTAP", "ATTAR", "CACAS", "CAECA", "CAESE", "CAINS", "CALLA", "CALLS", "CALOS", - "CALPA", "CALPS", "CANEH", "CANER", "CANES", "CANNA", "CANNS", "CANSO", "CANST", - "CANTO", "CANTS", "CAPAS", "CAPES", "CAPHS", "CAPLE", "CAPON", "CAPOS", "CAPOT", - "CAPRI", "CARAP", "CARER", "CARES", "CARET", "CARLE", "CARLS", "CARNS", "CARON", - "CARPI", "CARPS", "CARRS", "CARSE", "CARTA", "CARTE", "CARTS", "CASAS", "CASCO", - "CASES", "CASTS", "CATES", "CECAL", "CEILI", "CEILS", "CELLA", "CELLI", "CELLS", - "CELTS", "CENSE", "CENTO", "CENTS", "CEORL", "CEPES", "CERCI", "CERES", "CERIA", - "CERIC", "CERNE", "CEROC", "CEROS", "CERTS", "CESSE", "CESTA", "CESTI", "CETES", - "CHACE", "CHACO", "CHAIS", "CHALS", "CHANA", "CHAPE", "CHAPS", "CHAPT", "CHARA", - "CHARE", "CHARR", "CHARS", "CHATS", "CHEEP", "CHELA", "CHELP", "CHERE", "CHERT", - "CHETH", "CHIAO", "CHIAS", "CHICA", "CHICH", "CHICO", "CHICS", "CHIEL", "CHILE", - "CHINE", "CHINO", "CHINS", "CHIPS", "CHIRL", "CHIRO", "CHIRR", "CHIRT", "CHITS", - "CHOCO", "CHOCS", "CHOIL", "CHOLA", "CHOLI", "CHOLO", "CHONS", "CHOON", "CHOPS", - "CHOTA", "CHOTT", "CIELS", "CILIA", "CILLS", "CINCT", "CINES", "CIONS", "CIPPI", - "CIRCS", "CIRES", "CIRLS", "CIRRI", "CISCO", "CISTS", "CITAL", "CITER", "CITES", - "CLACH", "CLAES", "CLANS", "CLAPS", "CLAPT", "CLARO", "CLART", "CLAST", "CLATS", - "CLEEP", "CLEPE", "CLEPT", "CLIES", "CLINE", "CLINT", "CLIPE", "CLIPS", "CLIPT", - "CLITS", "CLONS", "CLOOP", "CLOOT", "CLOPS", "CLOTE", "CLOTS", "COACT", "COALA", - "COALS", "COAPT", "COATE", "COATI", "COATS", "COCAS", "COCCI", "COCCO", "COCOS", - "COHEN", "COHOE", "COHOS", "COILS", "COINS", "COIRS", "COITS", "COLAS", "COLES", - "COLIC", "COLIN", "COLLS", "COLTS", "CONES", "CONIA", "CONIN", "CONNE", "CONNS", - "CONTE", "CONTO", "COOCH", "COOEE", "COOER", "COOLS", "COONS", "COOPS", "COOPT", - "COOST", "COOTS", "COPAL", "COPEN", "COPER", "COPES", "COPRA", "CORES", "CORIA", - "CORNI", "CORNO", "CORNS", "CORPS", "CORSE", "CORSO", "COSEC", "COSES", "COSET", - "COSIE", "COSTA", "COSTE", "COSTS", "COTAN", "COTES", "COTHS", "COTTA", "COTTS", - "CRAAL", "CRAIC", "CRANS", "CRAPE", "CRAPS", "CRARE", "CREEL", "CREES", "CRENA", - "CREPS", "CRIAS", "CRIES", "CRINE", "CRIOS", "CRIPE", "CRIPS", "CRISE", "CRITH", - "CRITS", "CROCI", "CROCS", "CRONS", "CROOL", "CROON", "CROPS", "CRORE", "CROST", - "CTENE", "EALES", "EARLS", "EARNS", "EARNT", "EARST", "EASER", "EASES", "EASLE", - "EASTS", "EATHE", "ECHES", "ECHOS", "EISEL", "ELAIN", "ELANS", "ELCHI", "ELINT", - "ELOIN", "ELOPS", "ELPEE", "ELSIN", "ENATE", "ENIAC", "ENLIT", "ENOLS", "ENROL", - "ENTIA", "EORLS", "EOSIN", "EPACT", "EPEES", "EPHAH", "EPHAS", "EPHOR", "EPICS", - "EPOPT", "EPRIS", "ERICA", "ERICS", "ERNES", "EROSE", "ERSES", "ESCAR", "ESCOT", - "ESILE", "ESNES", "ESSES", "ESTOC", "ESTOP", "ESTRO", "ETAPE", "ETATS", "ETENS", - "ETHAL", "ETHNE", "ETICS", "ETNAS", "ETTIN", "ETTLE", "HAARS", "HAETS", "HAHAS", - "HAILS", "HAINS", "HAINT", "HAIRS", "HAITH", "HALAL", "HALER", "HALES", "HALLO", - "HALLS", "HALON", "HALOS", "HALSE", "HALTS", "HANAP", "HANCE", "HANCH", "HANSA", - "HANSE", "HANTS", "HAOLE", "HAPPI", "HARES", "HARLS", "HARNS", "HAROS", "HARPS", - "HARTS", "HASPS", "HASTA", "HATES", "HATHA", "HEALS", "HEAPS", "HEARE", "HEARS", - "HEAST", "HEATS", "HECHT", "HEELS", "HEILS", "HEIRS", "HELES", "HELIO", "HELLS", - "HELOS", "HELOT", "HELPS", "HENCH", "HENNA", "HENTS", "HEPAR", "HERES", "HERLS", - "HERNS", "HEROS", "HERSE", "HESPS", "HESTS", "HETES", "HETHS", "HIANT", "HILAR", - "HILCH", "HILLO", "HILLS", "HILTS", "HINTS", "HIOIS", "HIREE", "HIRER", "HIRES", - "HISTS", "HITHE", "HOARS", "HOAST", "HOERS", "HOISE", "HOLES", "HOLLA", "HOLLO", - "HOLON", "HOLOS", "HOLTS", "HONAN", "HONER", "HONES", "HOOCH", "HOONS", "HOOPS", - "HOORS", "HOOSH", "HOOTS", "HOPER", "HOPES", "HORAH", "HORAL", "HORAS", "HORIS", - "HORNS", "HORST", "HOSEL", "HOSEN", "HOSER", "HOSES", "HOSTA", "HOSTS", "HOTCH", - "HOTEN", "ICERS", "ICHES", "ICHOR", "ICIER", "ICONS", "ICTAL", "ICTIC", "ILEAC", - "ILEAL", "ILIAL", "ILLER", "ILLTH", "INAPT", "INCEL", "INCLE", "INION", "INNIT", - "INSET", "INSPO", "INTEL", "INTIL", "INTIS", "INTRA", "IOTAS", "IPPON", "IRONE", - "IRONS", "ISHES", "ISLES", "ISNAE", "ISSEI", "ISTLE", "ITHER", "LAARI", "LACER", - "LACES", "LACET", "LAERS", "LAHAL", "LAHAR", "LAICH", "LAICS", "LAIRS", "LAITH", - "LALLS", "LANAI", "LANAS", "LANCH", "LANES", "LANTS", "LAPIN", "LAPIS", "LARCH", - "LAREE", "LARES", "LARIS", "LARNS", "LARNT", "LASER", "LASES", "LASSI", "LASTS", - "LATAH", "LATEN", "LATHI", "LATHS", "LEANS", "LEAPS", "LEARE", "LEARS", "LEATS", - "LEEAR", "LEEPS", "LEERS", "LEESE", "LEETS", "LEHRS", "LEIRS", "LEISH", "LENES", - "LENIS", "LENOS", "LENSE", "LENTI", "LENTO", "LEONE", "LEPRA", "LEPTA", "LERES", - "LERPS", "LESES", "LESTS", "LETCH", "LETHE", "LIANA", "LIANE", "LIARS", "LIART", - "LICHI", "LICHT", "LICIT", "LIENS", "LIERS", "LILLS", "LILOS", "LILTS", "LINAC", - "LINCH", "LINES", "LININ", "LINNS", "LINOS", "LINTS", "LIONS", "LIPAS", "LIPES", - "LIPIN", "LIPOS", "LIRAS", "LIROT", "LISLE", "LISPS", "LISTS", "LITAI", "LITAS", - "LITES", "LITHO", "LITHS", "LITRE", "LLANO", "LOACH", "LOANS", "LOAST", "LOCHE", - "LOCHS", "LOCIE", "LOCIS", "LOCOS", "LOESS", "LOHAN", "LOINS", "LOIPE", "LOIRS", - "LOLLS", "LONER", "LOOIE", "LOONS", "LOOPS", "LOOTS", "LOPER", "LOPES", "LORAL", - "LORAN", "LOREL", "LORES", "LORIC", "LORIS", "LOSEL", "LOSEN", "LOSES", "LOTAH", - "LOTAS", "LOTES", "LOTIC", "LOTOS", "LOTSA", "LOTTA", "LOTTE", "LOTTO", "NAANS", - "NACHE", "NACHO", "NACRE", "NAHAL", "NAILS", "NAIRA", "NALAS", "NALLA", "NANAS", - "NANCE", "NANNA", "NANOS", "NAPAS", "NAPES", "NAPOO", "NAPPA", "NAPPE", "NARAS", - "NARCO", "NARCS", "NARES", "NARIC", "NARIS", "NARRE", "NASHI", "NATCH", "NATES", - "NATIS", "NEALS", "NEAPS", "NEARS", "NEATH", "NEATS", "NEELE", "NEEPS", "NEESE", - "NEIST", "NELIS", "NENES", "NEONS", "NEPER", "NEPIT", "NERAL", "NEROL", "NERTS", - "NESTS", "NETES", "NETOP", "NETTS", "NICHT", "NICOL", "NIHIL", "NILLS", "NINER", - "NINES", "NINON", "NIPAS", "NIRLS", "NISEI", "NISSE", "NITER", "NITES", "NITON", - "NITRE", "NITRO", "NOAHS", "NOELS", "NOILS", "NOINT", "NOIRS", "NOLES", "NOLLS", - "NOLOS", "NONAS", "NONCE", "NONES", "NONET", "NONIS", "NOOIT", "NOONS", "NOOPS", - "NOPAL", "NORIA", "NORIS", "NOSER", "NOSES", "NOTAL", "NOTER", "NOTES", "OASES", - "OASIS", "OASTS", "OATEN", "OATER", "OATHS", "OCHER", "OCHES", "OCHRE", "OCREA", - "OCTAN", "OCTAS", "OHIAS", "OHONE", "OILER", "OINTS", "OLEIC", "OLEIN", "OLENT", - "OLEOS", "OLIOS", "OLLAS", "OLLER", "OLLIE", "OLPAE", "OLPES", "ONCER", "ONCES", - "ONCET", "ONERS", "ONTIC", "OONTS", "OORIE", "OOSES", "OPAHS", "OPALS", "OPENS", - "OPEPE", "OPPOS", "OPSIN", "OPTER", "ORACH", "ORALS", "ORANT", "ORATE", "ORCAS", - "ORCIN", "ORIEL", "ORLES", "ORLON", "ORLOP", "ORNIS", "ORPIN", "ORRIS", "ORTHO", - "OSCAR", "OSHAC", "OSIER", "OSSIA", "OSTIA", "OTTAR", "OTTOS", "PAALS", "PAANS", - "PACAS", "PACER", "PACES", "PACHA", "PACOS", "PACTA", "PACTS", "PAEAN", "PAEON", - "PAILS", "PAINS", "PAIRE", "PAIRS", "PAISA", "PAISE", "PALAS", "PALEA", "PALES", - "PALET", "PALIS", "PALLA", "PALLS", "PALPI", "PALPS", "PALSA", "PANCE", "PANES", - "PANNE", "PANNI", "PANTO", "PANTS", "PAOLI", "PAOLO", "PAPAS", "PAPES", "PAPPI", - "PARAE", "PARAS", "PARCH", "PAREN", "PAREO", "PARES", "PARIS", "PARLE", "PAROL", - "PARPS", "PARRA", "PARRS", "PARTI", "PARTS", "PASEO", "PASES", "PASHA", "PASSE", - "PASTS", "PATEN", "PATER", "PATES", "PATHS", "PATIN", "PATTE", "PEALS", "PEANS", - "PEARE", "PEARS", "PEART", "PEASE", "PEATS", "PECHS", "PEECE", "PEELS", "PEENS", - "PEEPE", "PEEPS", "PEERS", "PEINS", "PEISE", "PELAS", "PELES", "PELLS", "PELON", - "PELTA", "PELTS", "PENES", "PENIE", "PENIS", "PENNA", "PENNI", "PENTS", "PEONS", - "PEPLA", "PEPOS", "PEPSI", "PERAI", "PERCE", "PERCS", "PEREA", "PERES", "PERIS", - "PERNS", "PERPS", "PERSE", "PERST", "PERTS", "PESOS", "PESTS", "PETAR", "PETER", - "PETIT", "PETRE", "PETRI", "PETTI", "PETTO", "PHARE", "PHEER", "PHENE", "PHEON", - "PHESE", "PHIAL", "PHISH", "PHOCA", "PHONO", "PHONS", "PHOTS", "PHPHT", "PIANI", - "PIANS", "PICAL", "PICAS", "PICOT", "PICRA", "PIERS", "PIERT", "PIETA", "PIETS", - "PILAE", "PILAO", "PILAR", "PILCH", "PILEA", "PILEI", "PILER", "PILES", "PILIS", - "PILLS", "PINAS", "PINES", "PINNA", "PINON", "PINOT", "PINTA", "PINTS", "PIONS", - "PIPAL", "PIPAS", "PIPES", "PIPET", "PIPIS", "PIPIT", "PIRAI", "PIRLS", "PIRNS", - "PISCO", "PISES", "PISOS", "PISTE", "PITAS", "PITHS", "PITON", "PITOT", "PITTA", - "PLAAS", "PLANS", "PLAPS", "PLASH", "PLAST", "PLATS", "PLATT", "PLEAS", "PLENA", - "PLEON", "PLESH", "PLICA", "PLIES", "PLOAT", "PLOPS", "PLOTS", "POACH", "POEPS", - "POETS", "POLER", "POLES", "POLIO", "POLIS", "POLLS", "POLOS", "POLTS", "PONCE", - "PONES", "PONTS", "POOHS", "POOLS", "POONS", "POOPS", "POORI", "POORT", "POOTS", - "POPES", "POPPA", "PORAE", "PORAL", "PORER", "PORES", "PORIN", "PORNO", "PORNS", - "PORTA", "PORTS", "POSES", "POSHO", "POSTS", "POTAE", "POTCH", "POTES", "POTIN", - "POTOO", "POTTO", "POTTS", "PRANA", "PRAOS", "PRASE", "PRATE", "PRATS", "PRATT", - "PREES", "PRENT", "PREON", "PREOP", "PREPS", "PRESA", "PRESE", "PREST", "PRIAL", - "PRIER", "PRIES", "PRILL", "PRION", "PRISE", "PRISS", "PROAS", "PROIN", "PROLE", - "PROLL", "PROPS", "PRORE", "PROSO", "PROSS", "PROST", "PROTO", "PSION", "PSOAE", - "PSOAI", "PSOAS", "PSORA", "RACES", "RACHE", "RACON", "RAIAS", "RAILE", "RAILS", - "RAINE", "RAINS", "RAITA", "RAITS", "RALES", "RANAS", "RANCE", "RANEE", "RANIS", - "RANTS", "RAPER", "RAPES", "RAPHE", "RAPPE", "RAREE", "RARES", "RASER", "RASES", - "RASPS", "RASSE", "RASTA", "RATAL", "RATAN", "RATAS", "RATCH", "RATEL", "RATER", - "RATES", "RATHA", "RATHE", "RATHS", "RATOO", "RATOS", "REAIS", "REALO", "REALS", - "REANS", "REAPS", "REARS", "REAST", "REATA", "REATE", "RECAL", "RECCE", "RECCO", - "RECIT", "RECON", "RECTA", "RECTI", "RECTO", "REECH", "REELS", "REENS", "REEST", - "REINS", "REIST", "RELET", "RELIE", "RELIT", "RELLO", "RENIN", "RENNE", "RENOS", - "RENTE", "RENTS", "REOIL", "REPIN", "REPLA", "REPOS", "REPOT", "REPPS", "REPRO", - "RERAN", "RESAT", "RESEE", "RESES", "RESIT", "RESTO", "RESTS", "RETIA", "RETIE", - "RHEAS", "RHIES", "RHINE", "RHONE", "RIALS", "RIANT", "RIATA", "RICER", "RICES", - "RICHT", "RICIN", "RIELS", "RILES", "RILLE", "RILLS", "RINES", "RIOTS", "RIPES", - "RIPPS", "RISES", "RISHI", "RISPS", "RITES", "RITTS", "ROANS", "ROARS", "ROATE", - "ROHES", "ROILS", "ROINS", "ROIST", "ROLES", "ROLLS", "RONEO", "RONES", "RONIN", - "RONNE", "RONTE", "RONTS", "ROONS", "ROOPS", "ROOSA", "ROOSE", "ROOTS", "ROPER", - "ROPES", "RORAL", "RORES", "RORIC", "RORIE", "RORTS", "ROSES", "ROSET", "ROSHI", - "ROSIN", "ROSIT", "ROSTI", "ROSTS", "ROTAL", "ROTAN", "ROTAS", "ROTCH", "ROTES", - "ROTIS", "ROTLS", "ROTON", "ROTOS", "ROTTE", "SACRA", "SAICE", "SAICS", "SAILS", - "SAINE", "SAINS", "SAIRS", "SAIST", "SAITH", "SALAL", "SALAT", "SALEP", "SALES", - "SALET", "SALIC", "SALLE", "SALOL", "SALOP", "SALPA", "SALPS", "SALSE", "SALTO", - "SALTS", "SANES", "SANSA", "SANTO", "SANTS", "SAOLA", "SAPAN", "SAPOR", "SARAN", - "SAREE", "SARIN", "SARIS", "SAROS", "SASER", "SASIN", "SASSE", "SATAI", "SATES", - "SATIS", "SCAIL", "SCALA", "SCALL", "SCANS", "SCAPA", "SCAPE", "SCAPI", "SCARP", - "SCARS", "SCART", "SCATH", "SCATS", "SCATT", "SCEAT", "SCENA", "SCOOT", "SCOPA", - "SCOPS", "SCOTS", "SCRAE", "SCRAN", "SCRAT", "SCRIP", "SEALS", "SEANS", "SEARE", - "SEARS", "SEASE", "SEATS", "SECCO", "SECHS", "SECTS", "SEELS", "SEEPS", "SEERS", - "SEHRI", "SEILS", "SEINE", "SEIRS", "SEISE", "SELAH", "SELES", "SELLA", "SELLE", - "SELLS", "SENAS", "SENES", "SENNA", "SENOR", "SENSA", "SENSI", "SENTE", "SENTI", - "SENTS", "SEPAL", "SEPIC", "SEPTA", "SEPTS", "SERAC", "SERAI", "SERAL", "SERER", - "SERES", "SERIC", "SERIN", "SERON", "SERRA", "SERRE", "SERRS", "SESSA", "SETAE", - "SETAL", "SETON", "SETTS", "SHAHS", "SHANS", "SHAPS", "SHARN", "SHASH", "SHCHI", - "SHEAL", "SHEAS", "SHEEL", "SHENT", "SHEOL", "SHERE", "SHERO", "SHETS", "SHIAI", - "SHIEL", "SHIER", "SHIES", "SHILL", "SHINS", "SHIPS", "SHIRR", "SHIRS", "SHISH", - "SHISO", "SHIST", "SHITE", "SHITS", "SHLEP", "SHOAT", "SHOER", "SHOES", "SHOLA", - "SHOOL", "SHOON", "SHOOS", "SHOPE", "SHOPS", "SHORL", "SHOTE", "SHOTS", "SHOTT", - "SHRIS", "SIALS", "SICES", "SICHT", "SIENS", "SIENT", "SIETH", "SILEN", "SILER", - "SILES", "SILLS", "SILOS", "SILTS", "SINES", "SINHS", "SIPES", "SIREE", "SIRES", - "SIRIH", "SIRIS", "SIROC", "SIRRA", "SISAL", "SISES", "SISTA", "SISTS", "SITAR", - "SITES", "SITHE", "SLAES", "SLANE", "SLAPS", "SLART", "SLATS", "SLEER", "SLIER", - "SLIPE", "SLIPS", "SLIPT", "SLISH", "SLITS", "SLOAN", "SLOES", "SLOOT", "SLOPS", - "SLOTS", "SNAPS", "SNARS", "SNASH", "SNATH", "SNEAP", "SNEES", "SNELL", "SNIES", - "SNIPS", "SNIRT", "SNITS", "SNOEP", "SNOOL", "SNOOT", "SNOTS", "SOAPS", "SOARE", - "SOARS", "SOCAS", "SOCES", "SOCLE", "SOILS", "SOLAH", "SOLAN", "SOLAS", "SOLEI", - "SOLER", "SOLES", "SOLON", "SOLOS", "SONCE", "SONES", "SONNE", "SONSE", "SOOLE", - "SOOLS", "SOOPS", "SOOTE", "SOOTS", "SOPHS", "SOPOR", "SOPRA", "SORAL", "SORAS", - "SOREE", "SOREL", "SORER", "SORES", "SORNS", "SORRA", "SORTA", "SORTS", "SOTHS", - "SOTOL", "SPAER", "SPAES", "SPAHI", "SPAIL", "SPAIN", "SPAIT", "SPALE", "SPALL", - "SPALT", "SPANE", "SPANS", "SPARS", "SPART", "SPATE", "SPATS", "SPEAL", "SPEAN", - "SPEAT", "SPECS", "SPECT", "SPEEL", "SPEER", "SPEIL", "SPEIR", "SPEOS", "SPETS", - "SPIAL", "SPICA", "SPICS", "SPIER", "SPIES", "SPILE", "SPINA", "SPINS", "SPIRT", - "SPITS", "SPOOR", "SPOOT", "SPOSH", "SPOTS", "SPRAT", "SPRIT", "STANE", "STAPH", - "STAPS", "STARN", "STARR", "STARS", "STATS", "STEAN", "STEAR", "STEEN", "STEIL", - "STELA", "STELE", "STELL", "STENO", "STENS", "STENT", "STEPS", "STEPT", "STERE", - "STETS", "STICH", "STIES", "STILE", "STIPA", "STIPE", "STIRE", "STIRP", "STIRS", - "STOAE", "STOAI", "STOAS", "STOAT", "STOEP", "STOIT", "STOLN", "STONN", "STOOR", - "STOPE", "STOPS", "STOPT", "STOSS", "STOTS", "STOTT", "STRAE", "STREP", "STRIA", - "STROP", "TAALS", "TAATA", "TACAN", "TACES", "TACET", "TACHE", "TACHO", "TACHS", - "TACOS", "TACTS", "TAELS", "TAHAS", "TAHRS", "TAILS", "TAINS", "TAIRA", "TAISH", - "TAITS", "TALAR", "TALAS", "TALCS", "TALEA", "TALER", "TALES", "TALLS", "TALPA", - "TANAS", "TANHS", "TANNA", "TANTI", "TANTO", "TAPAS", "TAPEN", "TAPES", "TAPET", - "TAPIS", "TAPPA", "TARAS", "TARES", "TARNS", "TAROC", "TAROS", "TARPS", "TARRE", - "TARSI", "TARTS", "TASAR", "TASER", "TASES", "TASSA", "TASSE", "TASSO", "TATAR", - "TATER", "TATES", "TATHS", "TATIE", "TATTS", "TEALS", "TEARS", "TEATS", "TECHS", - "TECTA", "TEELS", "TEENE", "TEENS", "TEERS", "TEHRS", "TEILS", "TEINS", "TELAE", - "TELCO", "TELES", "TELIA", "TELIC", "TELLS", "TELOI", "TELOS", "TENCH", "TENES", - "TENIA", "TENNE", "TENNO", "TENON", "TENTS", "TEPAL", "TEPAS", "TERAI", "TERAS", - "TERCE", "TERES", "TERNE", "TERNS", "TERTS", "TESLA", "TESTA", "TESTE", "TESTS", - "TETES", "TETHS", "TETRA", "TETRI", "THALE", "THALI", "THANA", "THANE", "THANS", - "THARS", "THECA", "THEES", "THEIC", "THEIN", "THENS", "THESP", "THETE", "THILL", - "THINE", "THINS", "THIOL", "THIRL", "THOLE", "THOLI", "THORO", "THORP", "THRAE", - "THRIP", "THROE", "TIANS", "TIARS", "TICAL", "TICCA", "TICES", "TIERS", "TILER", - "TILES", "TILLS", "TILTH", "TILTS", "TINAS", "TINCT", "TINEA", "TINES", "TINTS", - "TIPIS", "TIRES", "TIRLS", "TIROS", "TIRRS", "TITCH", "TITER", "TITIS", "TITRE", - "TOCOS", "TOEAS", "TOHOS", "TOILE", "TOILS", "TOISE", "TOITS", "TOLAN", "TOLAR", - "TOLAS", "TOLES", "TOLLS", "TOLTS", "TONER", "TONES", "TONNE", "TOOLS", "TOONS", - "TOOTS", "TOPEE", "TOPER", "TOPES", "TOPHE", "TOPHI", "TOPHS", "TOPIS", "TOPOI", - "TOPOS", "TORAH", "TORAN", "TORAS", "TORCS", "TORES", "TORIC", "TORII", "TOROS", - "TOROT", "TORRS", "TORSE", "TORSI", "TORTA", "TORTE", "TORTS", "TOSAS", "TOSES", - "TOTER", "TOTES", "TRANS", "TRANT", "TRAPE", "TRAPS", "TRAPT", "TRASS", "TRATS", - "TRATT", "TREEN", "TREES", "TRESS", "TREST", "TRETS", "TRIAC", "TRIER", "TRIES", - "TRILL", "TRINE", "TRINS", "TRIOL", "TRIOR", "TRIOS", "TRIPS", "TRIST", "TROAT", - "TROIS", "TRONA", "TRONC", "TRONE", "TRONS", "TROTH", "TROTS", "TSARS", -#endif -}; - -#if (WORDLE_USE_RANDOM_GUESS == 3) -static const uint16_t _num_random_guess_words = 13; // The valid_words array begins with this many words that are considered the top 3% best options. -#elif (WORDLE_USE_RANDOM_GUESS == 2) -static const uint16_t _num_random_guess_words = 257; // The valid_words array begins with this many words where each letter is different. -#elif (WORDLE_USE_RANDOM_GUESS == 1) -static const uint16_t _num_random_guess_words = _num_words; -#endif - -#endif // WORDLE_FACE_DICT_H_ \ No newline at end of file diff --git a/movement_faces.h b/movement_faces.h index 3583e9a..f895abd 100644 --- a/movement_faces.h +++ b/movement_faces.h @@ -62,4 +62,5 @@ #include "tally_face.h" #include "probability_face.h" #include "ke_decimal_time_face.h" +#include "wordle_face.h" // New includes go above this line. diff --git a/watch-faces.mk b/watch-faces.mk index eff50d2..acdabd3 100644 --- a/watch-faces.mk +++ b/watch-faces.mk @@ -16,6 +16,7 @@ SRCS += \ ./watch-faces/complication/squash_face.c \ ./watch-faces/complication/totp_face.c \ ./watch-faces/complication/tally_face.c \ + ./watch-faces/complication/wordle_face.c \ ./watch-faces/demo/all_segments_face.c \ ./watch-faces/demo/character_set_face.c \ ./watch-faces/demo/light_sensor_face.c \ diff --git a/watch-faces/complication/wordle_face.c b/watch-faces/complication/wordle_face.c new file mode 100644 index 0000000..c3a2e69 --- /dev/null +++ b/watch-faces/complication/wordle_face.c @@ -0,0 +1,647 @@ +/* + * MIT License + * + * Copyright (c) 2024 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "wordle_face.h" +#include "watch_utility.h" +#include "watch_common_display.h" + +static uint32_t get_random(uint32_t max) { + #if __EMSCRIPTEN__ + return rand() % max; + #else + return arc4random_uniform(max); + #endif +} + +static uint8_t get_first_pos(wordle_letter_result *word_elements_result) { + for (size_t i = 0; i < WORDLE_LENGTH; i++) { + if (word_elements_result[i] != WORDLE_LETTER_CORRECT) + return i; + } + return 0; +} + +static uint8_t get_next_pos(uint8_t curr_pos, wordle_letter_result *word_elements_result) { + for (size_t pos = curr_pos; pos < WORDLE_LENGTH;) { + if (word_elements_result[++pos] != WORDLE_LETTER_CORRECT) + return pos; + } + return WORDLE_LENGTH; +} + +static uint8_t get_prev_pos(uint8_t curr_pos, wordle_letter_result *word_elements_result) { + if (curr_pos == 0) return 0; + for (int8_t pos = curr_pos; pos >= 0;) { + if (word_elements_result[--pos] != WORDLE_LETTER_CORRECT) + return pos; + } + return curr_pos; +} + +static void get_next_letter(const uint8_t curr_pos, uint8_t *word_elements, const bool *known_wrong_letters, const bool skip_wrong_letter) { + do { + if (word_elements[curr_pos] >= WORDLE_NUM_VALID_LETTERS) word_elements[curr_pos] = 0; + else word_elements[curr_pos] = (word_elements[curr_pos] + 1) % WORDLE_NUM_VALID_LETTERS; + } while (skip_wrong_letter && known_wrong_letters[word_elements[curr_pos]]); +} + +static void get_prev_letter(const uint8_t curr_pos, uint8_t *word_elements, const bool *known_wrong_letters, const bool skip_wrong_letter) { + do { + if (word_elements[curr_pos] >= WORDLE_NUM_VALID_LETTERS) word_elements[curr_pos] = WORDLE_NUM_VALID_LETTERS - 1; + else word_elements[curr_pos] = (word_elements[curr_pos] + WORDLE_NUM_VALID_LETTERS - 1) % WORDLE_NUM_VALID_LETTERS; + } while (skip_wrong_letter && known_wrong_letters[word_elements[curr_pos]]); +} + +static void display_letter(wordle_state_t *state, bool display_dash) { + char buf[3]; + if (state->word_elements[state->position] >= WORDLE_NUM_VALID_LETTERS) { + if (display_dash) + watch_display_character('-', state->position + 5); + else + watch_display_character(' ', state->position + 5); + return; + } + sprintf(buf, "%c", _valid_letters[state->word_elements[state->position]]); + watch_display_character(buf[0], state->position + 5); +} + +static void display_all_letters(wordle_state_t *state) { + uint8_t prev_pos = state->position; + watch_display_character(' ', 4); + for (size_t i = 0; i < WORDLE_LENGTH; i++) { + state->position = i; + display_letter(state, false); + } + state->position = prev_pos; +} + +#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES +static void display_not_in_dict(wordle_state_t *state) { + state->curr_screen = WORDLE_SCREEN_NO_DICT; + watch_display_text(WATCH_POSITION_BOTTOM, "nodict"); + state->ignore_btn_ticks = WORDLE_TICK_BAD_GUESS; +} + +static void display_already_guessed(wordle_state_t *state) { + state->curr_screen = WORDLE_SCREEN_ALREADY_GUESSED; + watch_display_text(WATCH_POSITION_BOTTOM, "GUESSD"); + state->ignore_btn_ticks = WORDLE_TICK_BAD_GUESS; +} + +static uint32_t check_word_in_dict(uint8_t *word_elements) { + bool is_exact_match; + for (uint16_t i = 0; i < WORDLE_NUM_WORDS; i++) { + is_exact_match = true; + for (size_t j = 0; j < WORDLE_LENGTH; j++) { + if (_valid_letters[word_elements[j]] != _valid_words[i][j]) { + is_exact_match = false; + break; + } + } + if (is_exact_match) return i; + } + for (uint16_t i = 0; i < WORDLE_NUM_POSSIBLE_WORDS; i++) { + is_exact_match = true; + for (size_t j = 0; j < WORDLE_LENGTH; j++) { + if (_valid_letters[word_elements[j]] != _possible_words[i][j]) { + is_exact_match = false; + break; + } + } + if (is_exact_match) return WORDLE_NUM_WORDS + i; + } + return WORDLE_NUM_WORDS + WORDLE_NUM_POSSIBLE_WORDS; +} +#endif + +static bool check_word(wordle_state_t *state) { + // Exact + bool is_exact_match = true; + bool answer_already_accounted[WORDLE_LENGTH] = { false }; + for (size_t i = 0; i < WORDLE_LENGTH; i++) { + if (_valid_letters[state->word_elements[i]] == _valid_words[state->curr_answer][i]) { + state->word_elements_result[i] = WORDLE_LETTER_CORRECT; + answer_already_accounted[i] = true; + } + else { + state->word_elements_result[i] = WORDLE_LETTER_WRONG; + is_exact_match = false; + } + } + if (is_exact_match) return true; + // Wrong Location + for (size_t i = 0; i < WORDLE_LENGTH; i++) { + if (state->word_elements_result[i] != WORDLE_LETTER_WRONG) continue; + for (size_t j = 0; j < WORDLE_LENGTH; j++) { + if (answer_already_accounted[j]) continue; + if (_valid_letters[state->word_elements[i]] == _valid_words[state->curr_answer][j]) { + state->word_elements_result[i] = WORDLE_LETTER_WRONG_LOC; + answer_already_accounted[j] = true; + break; + } + } + } + return false; +} + +static void show_skip_wrong_letter_indicator(bool skipping, wordle_screen curr_screen) { + if (curr_screen >= WORDLE_SCREEN_PLAYING) return; + if (skipping) + watch_display_character('H', 3); + else + watch_display_character(' ', 3); +} + +static void update_known_wrong_letters(wordle_state_t *state) { + bool wrong_loc[WORDLE_NUM_VALID_LETTERS] = {false}; + // To ignore letters that appear, but are in the wrong location, as letters that are guessed + // more often than they appear in the word will display as WORDLE_LETTER_WRONG + for (size_t i = 0; i < WORDLE_LENGTH; i++) { + if (state->word_elements_result[i] == WORDLE_LETTER_WRONG_LOC) { + for (size_t j = 0; j < WORDLE_NUM_VALID_LETTERS; j++) { + if (state->word_elements[i] == j) + wrong_loc[j] = true; + } + } + } + for (size_t i = 0; i < WORDLE_LENGTH; i++) { + if (state->word_elements_result[i] == WORDLE_LETTER_WRONG) { + for (size_t j = 0; j < WORDLE_NUM_VALID_LETTERS; j++) { + if (state->word_elements[i] == j && !wrong_loc[j]) + state->known_wrong_letters[j] = true; + } + } + } +} + +static void display_attempt(uint8_t attempt) { + char buf[3]; + sprintf(buf, "%d", attempt+1); + watch_display_character(buf[0], 3); +} + +static void display_playing(wordle_state_t *state) { + state->curr_screen = WORDLE_SCREEN_PLAYING; + display_attempt(state->attempt); + display_all_letters(state); +} + +static void reset_all_elements(wordle_state_t *state) { + for (size_t i = 0; i < WORDLE_LENGTH; i++) { + state->word_elements[i] = WORDLE_NUM_VALID_LETTERS; + state->word_elements_result[i] = WORDLE_LETTER_WRONG; + } + for (size_t i = 0; i < WORDLE_NUM_VALID_LETTERS; i++){ + state->known_wrong_letters[i] = false; + } +#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES + for (size_t i = 0; i < WORDLE_MAX_ATTEMPTS; i++) { + state->guessed_words[i] = WORDLE_NUM_WORDS + WORDLE_NUM_POSSIBLE_WORDS; + } +#endif + state->using_random_guess = false; + state->attempt = 0; +} + +static void reset_incorrect_elements(wordle_state_t *state) { + for (size_t i = 0; i < WORDLE_LENGTH; i++) { + if (state->word_elements_result[i] != WORDLE_LETTER_CORRECT) + state->word_elements[i] = WORDLE_NUM_VALID_LETTERS; + } +} + +static bool is_in_do_not_use_list(uint16_t guess, const uint16_t *not_to_use, uint8_t max_repeats) { + for (size_t i = 0; i < max_repeats; i++) { + if (guess == not_to_use[i]) return true; + } + return false; +} + +static void reset_board(wordle_state_t *state) { + reset_all_elements(state); + do { + state->curr_answer = get_random(WORDLE_NUM_WORDS); + } while (is_in_do_not_use_list(state->curr_answer, state->not_to_use, WORDLE_MAX_BETWEEN_REPEATS)); + watch_clear_colon(); + state->position = get_first_pos(state->word_elements_result); + display_playing(state); + watch_display_character('-', 5); +#if __EMSCRIPTEN__ + printf("ANSWER: %s\r\n", _valid_words[state->curr_answer]); +#endif +} + +static void display_title(wordle_state_t *state) { + state->curr_screen = WORDLE_SCREEN_TITLE; + watch_display_text(WATCH_POSITION_TOP_LEFT, "WO"); + watch_display_text(WATCH_POSITION_TOP_RIGHT, " "); + watch_display_text(WATCH_POSITION_BOTTOM, "WordLE"); + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); +} + +#if WORDLE_USE_DAILY_STREAK != 2 +static void display_continue_result(bool continuing) { + watch_display_character(continuing ? 'y' : 'n', 9); +} + +static void display_continue(wordle_state_t *state) { + state->curr_screen = WORDLE_SCREEN_CONTINUE; + watch_display_text(WATCH_POSITION_BOTTOM, "Cont "); + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); + display_continue_result(state->continuing); +} +#endif + +static void display_streak(wordle_state_t *state) { + char buf[10]; + state->curr_screen = WORDLE_SCREEN_STREAK; +#if WORDLE_USE_DAILY_STREAK == 2 + if (state->streak > 99) + sprintf(buf, "St--dy"); + else + sprintf(buf, "St%2ddy", state->streak); +#else + sprintf(buf, "St%4d", state->streak); +#endif + watch_display_text(WATCH_POSITION_TOP_LEFT, "WO"); + watch_display_text(WATCH_POSITION_TOP_RIGHT, " "); + watch_display_text(WATCH_POSITION_BOTTOM, buf); + watch_set_colon(); + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); +} + +#if WORDLE_USE_DAILY_STREAK == 2 +static void display_wait(wordle_state_t *state) { + state->curr_screen = WORDLE_SCREEN_WAIT; + if (state->streak < 40) { + char buf[5]; + sprintf(buf,"%2d", state->streak); + watch_display_text(WATCH_POSITION_TOP_RIGHT, buf); + } + else { // Streak too long to display in top-right + watch_display_text(WATCH_POSITION_TOP_RIGHT, " "); + } + watch_display_text(WATCH_POSITION_TOP_LEFT, "WO"); + watch_display_text(WATCH_POSITION_BOTTOM, " WaIt "); + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); +} +#endif + +static uint32_t get_day_unix_time(void) { + watch_date_time_t now = watch_rtc_get_date_time(); +#if WORDLE_USE_DAILY_STREAK == 2 + now.unit.hour = now.unit.minute = now.unit.second = 0; +#endif + return watch_utility_date_time_to_unix_time(now, 0); +} + +static void display_lose(wordle_state_t *state, uint8_t subsecond) { + char buf[10]; + sprintf(buf," %s", subsecond % 2 ? _valid_words[state->curr_answer] : " "); + watch_display_text(WATCH_POSITION_TOP, "L "); + watch_display_text(WATCH_POSITION_BOTTOM, buf); +} + +static void display_win(wordle_state_t *state, uint8_t subsecond) { + (void) state; + char buf[10]; + sprintf(buf," %s ", subsecond % 2 ? "NICE" : "JOb "); + watch_display_text(WATCH_POSITION_TOP, "W "); + watch_display_text(WATCH_POSITION_BOTTOM, buf); +} + +static bool is_playing(const wordle_state_t *state) { + if (state->attempt > 0) return true; + for (size_t i = 0; i < WORDLE_LENGTH; i++) { + if (state->word_elements[i] != WORDLE_NUM_VALID_LETTERS) + return true; + } + return false; +} + +static void display_result(wordle_state_t *state, uint8_t subsecond) { + char buf[10]; + buf[0] = ' '; + for (size_t i = 0; i < WORDLE_LENGTH; i++) + { + switch (state->word_elements_result[i]) + { + case WORDLE_LETTER_WRONG: + buf[i+1] = '-'; + break; + case WORDLE_LETTER_CORRECT: + buf[i+1] = _valid_letters[state->word_elements[i]]; + break; + case WORDLE_LETTER_WRONG_LOC: + if (subsecond % 2) + buf[i+1] = ' '; + else + buf[i+1] = _valid_letters[state->word_elements[i]]; + default: + break; + } + } + watch_display_text(WATCH_POSITION_BOTTOM, buf); +} + +static bool act_on_btn(wordle_state_t *state, const wordle_pin_enum pin) { + if (state->ignore_btn_ticks > 0) return true; + switch (state->curr_screen) + { + case WORDLE_SCREEN_RESULT: + reset_incorrect_elements(state); + state->position = get_first_pos(state->word_elements_result); + display_playing(state); + return true; + case WORDLE_SCREEN_TITLE: +#if WORDLE_USE_DAILY_STREAK == 2 + if (state->day_last_game_started == get_day_unix_time()) { + display_wait(state); + } + else if (is_playing(state)) + display_playing(state); + else + display_streak(state); +#else + if (is_playing(state)) { + state->continuing = true; + display_continue(state); + } + else + display_streak(state); +#endif + return true; + case WORDLE_SCREEN_STREAK: + state->day_last_game_started = get_day_unix_time(); + reset_board(state); + return true; + case WORDLE_SCREEN_WIN: + case WORDLE_SCREEN_LOSE: + display_title(state); + return true; + case WORDLE_SCREEN_NO_DICT: + case WORDLE_SCREEN_ALREADY_GUESSED: + state->position = get_first_pos(state->word_elements_result); + display_playing(state); + return true; +#if WORDLE_USE_DAILY_STREAK == 2 + case WORDLE_SCREEN_WAIT: + (void) pin; + display_title(state); + return true; +#else + case WORDLE_SCREEN_CONTINUE: + switch (pin) + { + case BTN_ALARM: + if (state->continuing) + display_playing(state); + else { + reset_board(state); + state->streak = 0; + display_streak(state); + } + break; + case BTN_LIGHT: + state->continuing = !state->continuing; + display_continue_result(state->continuing); + break; + default: + break; + } + return true; +#endif + default: + return false; + } + return false; +} + +static void win_lose_shared(wordle_state_t *state) { + reset_all_elements(state); + state->ignore_btn_ticks = WORDLE_TICK_WIN_LOSE; + state->not_to_use[state->not_to_use_position] = state->curr_answer; + state->not_to_use_position = (state->not_to_use_position + 1) % WORDLE_MAX_BETWEEN_REPEATS; +} + +static void get_result(wordle_state_t *state) { +#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES + // Check if it's in the dict + uint16_t in_dict = check_word_in_dict(state->word_elements); + if (in_dict == WORDLE_NUM_WORDS + WORDLE_NUM_POSSIBLE_WORDS) { + display_not_in_dict(state); + return; + } + + // Check if already guessed + for (size_t i = 0; i < WORDLE_MAX_ATTEMPTS; i++) { + if(in_dict == state->guessed_words[i]) { + display_already_guessed(state); + return; + } + } + + state->guessed_words[state->attempt] = in_dict; +#endif + bool exact_match = check_word(state); + if (exact_match) { + state->curr_screen = WORDLE_SCREEN_WIN; + win_lose_shared(state); + if (state->streak < 0x7F) + state->streak++; +#if WORDLE_USE_DAILY_STREAK == 2 + state->day_last_game_started = get_day_unix_time(); // On the edge-case where we solve the puzzle at midnight +#endif + return; + } + if (++state->attempt >= WORDLE_MAX_ATTEMPTS) { + state->curr_screen = WORDLE_SCREEN_LOSE; + win_lose_shared(state); + state->streak = 0; + return; + } + update_known_wrong_letters(state); + state->curr_screen = WORDLE_SCREEN_RESULT; + state->ignore_btn_ticks = WORDLE_TICKS_RESULT; + return; +} + +#if (WORDLE_USE_RANDOM_GUESS != 0) +static void insert_random_guess(wordle_state_t *state) { + uint16_t random_guess; + do { // Don't allow the guess to be the same as the answer + random_guess = get_random(_num_random_guess_words); + } while (random_guess == state->curr_answer); + for (size_t i = 0; i < WORDLE_LENGTH; i++) { + for (size_t j = 0; j < WORDLE_NUM_VALID_LETTERS; j++) + { + if (_valid_words[random_guess][i] == _valid_letters[j]) + state->word_elements[i] = j; + } + } + state->position = WORDLE_LENGTH - 1; + display_all_letters(state); + state->using_random_guess = true; +} +#endif + +static void _activate(wordle_state_t *state) { +#if WORDLE_USE_DAILY_STREAK != 0 + uint32_t now = get_day_unix_time(); + uint32_t one_day = 60 *60 * 24; + if ((WORDLE_USE_DAILY_STREAK == 2 && now >= (state->day_last_game_started + (2*one_day))) + || (now >= (state->day_last_game_started + one_day) && is_playing(state))) { + state->streak = 0; + reset_board(state); + } +#endif + state->using_random_guess = false; + if (is_playing(state) && state->curr_screen >= WORDLE_SCREEN_RESULT) { + reset_incorrect_elements(state); + state->position = get_first_pos(state->word_elements_result); + } + movement_request_tick_frequency(WORDLE_FREQ); + watch_clear_all_indicators(); + watch_clear_colon(); + display_title(state); +} + +void wordle_face_setup(uint8_t watch_face_index, void ** context_ptr) { + (void) watch_face_index; + if (*context_ptr == NULL) { + *context_ptr = malloc(sizeof(wordle_state_t)); + memset(*context_ptr, 0, sizeof(wordle_state_t)); + wordle_state_t *state = (wordle_state_t *)*context_ptr; + state->curr_screen = WORDLE_SCREEN_TITLE; + state->skip_wrong_letter = true; + reset_all_elements(state); + memset(state->not_to_use, 0xff, sizeof(state->not_to_use)); + } + // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep. +} + +void wordle_face_activate(void *context) { + wordle_state_t *state = (wordle_state_t *)context; + _activate(state); + +} + +bool wordle_face_loop(movement_event_t event, void *context) { + wordle_state_t *state = (wordle_state_t *)context; + + switch (event.event_type) { + case EVENT_TICK: + if (state->ignore_btn_ticks > 0) state->ignore_btn_ticks--; + switch (state->curr_screen) + { + case WORDLE_SCREEN_PLAYING: + if (event.subsecond % 2) { + display_letter(state, true); + } else { + watch_display_character(' ', state->position + 5); + } + break; + case WORDLE_SCREEN_RESULT: + display_result(state, event.subsecond); + break; + case WORDLE_SCREEN_LOSE: + display_lose(state, event.subsecond); + break; + case WORDLE_SCREEN_WIN: + display_win(state, event.subsecond); + break; + default: + break; + } + break; + case EVENT_LIGHT_BUTTON_UP: + if (act_on_btn(state, BTN_LIGHT)) break; + get_next_letter(state->position, state->word_elements, state->known_wrong_letters, state->skip_wrong_letter); + display_letter(state, true); + break; + case EVENT_LIGHT_LONG_PRESS: + if (state->curr_screen < WORDLE_SCREEN_PLAYING) { + state->skip_wrong_letter = !state->skip_wrong_letter; + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); + break; + } + if (state->curr_screen != WORDLE_SCREEN_PLAYING) break; + get_prev_letter(state->position, state->word_elements, state->known_wrong_letters, state->skip_wrong_letter); + display_letter(state, true); + break; + case EVENT_ALARM_BUTTON_UP: + if (act_on_btn(state, BTN_ALARM)) break; + display_letter(state, true); + if (state->word_elements[state->position] == WORDLE_NUM_VALID_LETTERS) break; +#if (WORDLE_USE_RANDOM_GUESS != 0) + if (HAL_GPIO_BTN_LIGHT_read() && + (state->using_random_guess || (state->attempt == 0 && state->position == 0))) { + insert_random_guess(state); + break; + } +#endif + state->position = get_next_pos(state->position, state->word_elements_result); + if (state->position >= WORDLE_LENGTH) { + get_result(state); + state->using_random_guess = false; + } + break; + case EVENT_ALARM_LONG_PRESS: + if (state->curr_screen != WORDLE_SCREEN_PLAYING) break; + display_letter(state, true); + state->position = get_prev_pos(state->position, state->word_elements_result); + break; + case EVENT_LIGHT_BUTTON_DOWN: + case EVENT_ACTIVATE: + break; + case EVENT_TIMEOUT: + if (state->curr_screen >= WORDLE_SCREEN_RESULT) { + reset_incorrect_elements(state); + state->position = get_first_pos(state->word_elements_result); + display_title(state); + } + break; + case EVENT_LOW_ENERGY_UPDATE: + if (state->curr_screen != WORDLE_SCREEN_TITLE) + display_title(state); + break; + case EVENT_MODE_LONG_PRESS: + if (state->curr_screen >= WORDLE_SCREEN_PLAYING) { + _activate(state); + } else { + movement_move_to_face(0); + } + break; + default: + return movement_default_loop_handler(event); + } + return true; +} + +void wordle_face_resign(void *context) { + (void) context; +} + diff --git a/watch-faces/complication/wordle_face.h b/watch-faces/complication/wordle_face.h new file mode 100644 index 0000000..e4f7d0f --- /dev/null +++ b/watch-faces/complication/wordle_face.h @@ -0,0 +1,165 @@ +/* + * MIT License + * + * Copyright (c) 2024 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WORDLE_FACE_H_ +#define WORDLE_FACE_H_ + +#include "movement.h" + +/* + * Wordle Face + * A port of NY Times' Wordle game (https://www.nytimes.com/games/wordle/index.html) + * A random 5 letter word is chosen and you have WORDLE_MAX_ATTEMPTS attempts to guess it. + * Each guess must be a valid 5-letter word found in _legal_words in the C file. + * The only letters used are _valid_letters, also found in the C file. + * After a guess, the letters in the correct spot will remain, + * and the letters found in the word, but in the incorrect spot will blink. + * The screen after the title screen if a new game is started shows the streak of games won in a row. + * + * If WORDLE_USE_DAILY_STREAK is set to True, then the game can only be played once per day, + * and the streak resets to 0 if a day goes by without playing the game. + * + * Controls: + * Light Press + * If Playing: Next letter + * Else: Next screen + * Light Hold + * If Playing: Previous letter + * Else: Toggle Hard-Mode. This is skipping over letters that have been confirmed + * to not be in the word (indicated via 'H' in the top-right) + * + * Alarm Press + * If Playing: If WORDLE_USE_RANDOM_GUESS is set and Light btn held and + * (on first letter or already used a random guess) + * and first attempt: Use a random 5 letter word with all letters that are different. + * Else: Next position + * Else: Next screen + * Alarm Hold + * If Playing: Previous position + * Else: None + * + * Note: Actual Hard Mode in Wordle game is "Any revealed hints must be used in subsequent guesses" + * But that came off as clunky UX on the Casio. So instead it only removes unused letters from the keyboard + * as that also simplifies the keyboard. + */ + +#define WORDLE_LENGTH 5 +#define WORDLE_MAX_ATTEMPTS 6 +/* WORDLE_USE_DAILY_STREAK + * 0 = Don't ever reset the streak or the puzzle. + * 1 = Reset the streak and puzzle 24hrs after starting a puzzle and not finishing it. + * If the last puzzle was started at 8AM, it'll be considered failed at 8AM the next day. + * 2 = Reset the streak and puzzle if a puzzle goes unsolved or not started a day after the previous one. + * If the last puzzle was started at 8AM, it'll be considered failed at midnight the next day. + * This will not be the case if the puzzle is started at 8AM, continued at 11:59PM and solved at 12:01AM, the game will let that slide. + * Starting a new game instead of continuing is not allowed in this state. +*/ +#define WORDLE_USE_DAILY_STREAK 1 +#define WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES false // This allows non-words to be entered and repeat guesses to be made. It saves ~11.5KB of ROM. +/* WORDLE_USE_RANDOM_GUESS + * 0 = Don't allow quickly choosing a random quess + * 1 = Allow using a random guess of any value that can be an answer + * 2 = Allow using a random guess of any value that can be an answer where all of its letters are unique + * 3 = Allow using a random guess of any value that can be an answer, and it's considered one of the best initial choices. +*/ +#define WORDLE_USE_RANDOM_GUESS 3 +#define WORDLE_FREQ 2 +// To avoid a button press immedietly skipping a screen, we wait this many ticks +#define WORDLE_TICKS_RESULT 4 +#define WORDLE_TICK_WIN_LOSE 2 +#define WORDLE_TICK_BAD_GUESS 0 + +// Store this many words in our list of words that were already used to avoid too much repetition of guesses +#define WORDLE_MAX_BETWEEN_REPEATS 50 +#include "wordle_face_dict.h" + +#define WORDLE_NUM_WORDS (sizeof(_valid_words) / sizeof(_valid_words[0])) +#define WORDLE_NUM_POSSIBLE_WORDS (sizeof(_possible_words) / sizeof(_possible_words[0])) +#define WORDLE_NUM_VALID_LETTERS (sizeof(_valid_letters) / sizeof(_valid_letters[0])) + +typedef enum { + WORDLE_LETTER_WRONG = 0, + WORDLE_LETTER_WRONG_LOC, + WORDLE_LETTER_CORRECT, + WORDLE_LETTER_COUNT +} wordle_letter_result; + +typedef enum { + WORDLE_SCREEN_TITLE = 0, + WORDLE_SCREEN_STREAK, + WORDLE_SCREEN_CONTINUE, +#if WORDLE_USE_DAILY_STREAK + WORDLE_SCREEN_WAIT, +#endif + WORDLE_SCREEN_PLAYING, + WORDLE_SCREEN_RESULT, + WORDLE_SCREEN_WIN, + WORDLE_SCREEN_LOSE, + WORDLE_SCREEN_NO_DICT, + WORDLE_SCREEN_ALREADY_GUESSED, + WORDLE_SCREEN_COUNT +} wordle_screen; + +typedef enum { + BTN_MODE = 0, + BTN_ALARM, + BTN_LIGHT, +} wordle_pin_enum; + +typedef struct { + // Anything you need to keep track of, put it here! + uint8_t word_elements[WORDLE_LENGTH]; + wordle_letter_result word_elements_result[WORDLE_LENGTH]; +#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES + uint16_t guessed_words[WORDLE_MAX_ATTEMPTS]; +#endif + uint8_t attempt : 4; + uint8_t position : 3; + bool using_random_guess : 1; + uint16_t curr_answer : 14; + bool continuing : 1; + bool skip_wrong_letter : 1; + uint8_t streak; + wordle_screen curr_screen; + bool known_wrong_letters[WORDLE_NUM_VALID_LETTERS]; + uint32_t day_last_game_started; + uint8_t ignore_btn_ticks; + uint16_t not_to_use[WORDLE_MAX_BETWEEN_REPEATS]; + uint8_t not_to_use_position; +} wordle_state_t; + +void wordle_face_setup(uint8_t watch_face_index, void ** context_ptr); +void wordle_face_activate(void *context); +bool wordle_face_loop(movement_event_t event, void *context); +void wordle_face_resign(void *context); + +#define wordle_face ((const watch_face_t){ \ + wordle_face_setup, \ + wordle_face_activate, \ + wordle_face_loop, \ + wordle_face_resign, \ + NULL, \ +}) + +#endif // WORDLE_FACE_H_ diff --git a/watch-faces/complication/wordle_face_dict.h b/watch-faces/complication/wordle_face_dict.h new file mode 100644 index 0000000..6b82976 --- /dev/null +++ b/watch-faces/complication/wordle_face_dict.h @@ -0,0 +1,293 @@ +#ifndef WORDLE_FACE_DICT_H_ +#define WORDLE_FACE_DICT_H_ + +#ifndef WORDLE_LENGTH +#define WORDLE_LENGTH 5 +#endif + +#ifndef WORDLE_USE_RANDOM_GUESS +#define WORDLE_USE_RANDOM_GUESS 2 +#endif + +static const char _valid_letters[] = {'A', 'C', 'E', 'H', 'I', 'L', 'N', 'O', 'P', 'R', 'S', 'T'}; + +// From: https://matthewminer.name/projects/calculators/wordle-words-left/ +// Number of words found: 432 +static const char _valid_words[][WORDLE_LENGTH + 1] = { + "SLATE", "STARE", "SNARE", "SANER", "CRANE", "STALE", "CRATE", "RAISE", "TRACE", + "SHARE", "ARISE", "SCARE", "SPARE", "CHAOS", "TAPIR", "CAIRN", "TENOR", "CLEAN", + "HEART", "SCOPE", "SNARL", "SLEPT", "SINCE", "EPOCH", "SPACE", "RELIC", "SPOIL", + "LITER", "LEAPT", "LANCE", "RANCH", "HORSE", "LEACH", "LATER", "STEAL", "CHEAP", + "SHORT", "ETHIC", "CHANT", "ACTOR", "REACH", "SEPIA", "ONSET", "SPLAT", "LEANT", + "REACT", "OCTAL", "SPORE", "IRATE", "CORAL", "NICER", "SPILT", "SCENT", "PANIC", + "SHIRT", "PECAN", "SLAIN", "SPLIT", "ROACH", "ASCOT", "PHONE", "LITHE", "STOIC", + "STRIP", "RENAL", "POISE", "ENACT", "CHEAT", "PITCH", "NOISE", "INLET", "PEARL", + "POLAR", "PEACH", "STOLE", "CASTE", "CREST", "CRONE", "ETHOS", "THEIR", "STONE", + "SHIRE", "LATCH", "HASTE", "CLOSE", "SPINE", "SLANT", "SPEAR", "SCALE", "CAPER", + "RETCH", "PESTO", "CHIRP", "SPORT", "OPTIC", "SNAIL", "PRICE", "PLANE", "TORCH", + "PASTE", "RECAP", "SOLAR", "CRASH", "LINER", "OPINE", "ASHEN", "PALER", "ECLAT", + "SPELT", "TRIAL", "PERIL", "SLICE", "SCANT", "SAINT", "POSIT", "ATONE", "SPIRE", + "COAST", "INEPT", "SHOAL", "CLASH", "THORN", "PHASE", "SCORE", "TRICE", "PERCH", + "PORCH", "SHEAR", "CHOIR", "RHINO", "PLANT", "SHONE", "CHORE", "LEARN", "ALTER", + "CHAIN", "PANEL", "PLIER", "STEIN", "COPSE", "SONIC", "ALIEN", "CHOSE", "ACORN", + "ANTIC", "CHEST", "OTHER", "CHINA", "TALON", "SCORN", "PLAIN", "PILOT", "RIPEN", + "PATCH", "SPICE", "CLONE", "SCION", "SCONE", "STRAP", "PARSE", "SHALE", "RISEN", + "CANOE", "INTER", "LEASH", "ISLET", "PRINT", "SHINE", "NORTH", "CLEAT", "PLAIT", + "SCRAP", "CLEAR", "SLOTH", "LAPSE", "CHAIR", "SNORT", "SHARP", "OPERA", "STAIN", + "TEACH", "TRAIL", "TRAIN", "LATHE", "PIANO", "PINCH", "PETAL", "STERN", "PRONE", + "PROSE", "PLEAT", "TROPE", "PLACE", "POSER", "INERT", "CHASE", "CAROL", "STAIR", + "SATIN", "SPITE", "LOATH", "ROAST", "ARSON", "SHAPE", "CLASP", "LOSER", "SALON", + "CATER", "SHALT", "INTRO", "ALERT", "PENAL", "SHORE", "RINSE", "CREPT", "APRON", + "SONAR", "AISLE", "AROSE", "HATER", "NICHE", "POINT", "EARTH", "PINTO", "THOSE", + "CLOTH", "NOTCH", "TOPIC", "RESIN", "SCALP", "HEIST", "HERON", "TRIPE", "TONAL", + "TAPER", "SHORN", "TONIC", "HOIST", "SNORE", "STORE", "SLOPE", "OCEAN", "CHART", + "PAINT", "SPENT", "SNIPE", "CRISP", "TRASH", "PATIO", "PLATE", "HOTEL", "LEAST", + "ALONE", "RALPH", "SPIEL", "SIREN", "RATIO", "STOOP", "TROLL", "ATOLL", "SLASH", + "RETRO", "CREEP", "STILT", "SPREE", "TASTE", "CACHE", "CANON", "EATEN", "TEPEE", + "SHEET", "SNEER", "ERROR", "NATAL", "SLEEP", "STINT", "TROOP", "SHALL", "STALL", + "PIPER", "TOAST", "NASAL", "CORER", "THERE", "POOCH", "SCREE", "ELITE", "ALTAR", + "PENCE", "EATER", "ALPHA", "TENTH", "LINEN", "SHEER", "TAINT", "HEATH", "CRIER", + "TENSE", "CARAT", "CANAL", "APNEA", "THESE", "HATCH", "SHELL", "CIRCA", "APART", + "SPILL", "STEEL", "LOCAL", "STOOL", "SHEEN", "RESET", "STEEP", "ELATE", "PRESS", + "SLEET", "CROSS", "TOTAL", "TREAT", "ONION", "STATE", "CINCH", "ASSET", "THREE", + "TORSO", "SNOOP", "PENNE", "SPOON", "SHEEP", "PAPAL", "STILL", "CHILL", "THETA", + "LEECH", "INNER", "HONOR", "LOOSE", "CONIC", "SCENE", "COACH", "CONCH", "LATTE", + "ERASE", "ESTER", "PEACE", "PASTA", "INANE", "SPOOL", "TEASE", "HARSH", "PIECE", + "STEER", "SCOOP", "NINTH", "OTTER", "OCTET", "EERIE", "RISER", "LAPEL", "HIPPO", + "PREEN", "ETHER", "AORTA", "SENSE", "TRACT", "SHOOT", "SLOOP", "REPEL", "TITHE", + "IONIC", "CELLO", "CHESS", "SOOTH", "COCOA", "TITAN", "TOOTH", "TIARA", "CRESS", + "SLOSH", "RARER", "TERSE", "ERECT", "HELLO", "PARER", "RIPER", "NOOSE", "CREPE", + "CACAO", "ILIAC", "POSSE", "CACTI", "EASEL", "LASSO", "ROOST", "ALLOT", "COLON", + "LEPER", "TEETH", "TITLE", "HENCE", "NIECE", "PAPER", "TRITE", "SPELL", "RACER", + "ATTIC", "CRASS", "HITCH", "LEASE", "CEASE", "ROTOR", "ELOPE", "APPLE", "CHILI", + "START", "PHOTO", "SALSA", "STASH", "PRIOR", "TAROT", "COLOR", "CHEER", "CLASS", + "ARENA", "ELECT", "ENTER", "CATCH", "TENET", "TACIT", "TRAIT", "TERRA", "LILAC", +}; + +// These are words that'll never be used, but still need to be in the dictionary for guesses. +// Number of words found: 1898 +static const char _possible_words[][WORDLE_LENGTH + 1] = { +#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES + "AALII", "AARTI", "ACAIS", "ACARI", "ACCAS", "ACERS", "ACETA", "ACHAR", "ACHES", + "ACHOO", "ACINI", "ACNES", "ACRES", "ACROS", "ACTIN", "ACTON", "AECIA", "AEONS", + "AERIE", "AEROS", "AESIR", "AHEAP", "AHENT", "AHINT", "AINEE", "AIOLI", "AIRER", + "AIRNS", "AIRTH", "AIRTS", "AITCH", "ALAAP", "ALANE", "ALANS", "ALANT", "ALAPA", + "ALAPS", "ALATE", "ALCOS", "ALECS", "ALEPH", "ALIAS", "ALINE", "ALIST", "ALLEE", + "ALLEL", "ALLIS", "ALOES", "ALOHA", "ALOIN", "ALOOS", "ALTHO", "ALTOS", "ANANA", + "ANATA", "ANCHO", "ANCLE", "ANCON", "ANEAR", "ANELE", "ANENT", "ANILE", "ANILS", + "ANION", "ANISE", "ANLAS", "ANNAL", "ANNAS", "ANNAT", "ANOAS", "ANOLE", "ANSAE", + "ANTAE", "ANTAR", "ANTAS", "ANTES", "ANTIS", "ANTRA", "ANTRE", "APACE", "APERS", + "APERT", "APHIS", "APIAN", "APIOL", "APISH", "APOOP", "APORT", "APPAL", "APPEL", + "APPRO", "APRES", "APSES", "APSIS", "APSOS", "APTER", "ARARS", "ARCHI", "ARCOS", + "AREAE", "AREAL", "AREAR", "AREAS", "ARECA", "AREIC", "ARENE", "AREPA", "ARERE", + "ARETE", "ARETS", "ARETT", "ARHAT", "ARIAS", "ARIEL", "ARILS", "ARIOT", "ARISH", + "ARLES", "ARNAS", "AROHA", "ARPAS", "ARPEN", "ARRAH", "ARRAS", "ARRET", "ARRIS", + "ARSES", "ARSIS", "ARTAL", "ARTEL", "ARTIC", "ARTIS", "ASANA", "ASCON", "ASHES", + "ASHET", "ASPEN", "ASPER", "ASPIC", "ASPIE", "ASPIS", "ASPRO", "ASSAI", "ASSES", + "ASSOT", "ASTER", "ASTIR", "ATAPS", "ATILT", "ATLAS", "ATOCS", "ATRIA", "ATRIP", + "ATTAP", "ATTAR", "CACAS", "CAECA", "CAESE", "CAINS", "CALLA", "CALLS", "CALOS", + "CALPA", "CALPS", "CANEH", "CANER", "CANES", "CANNA", "CANNS", "CANSO", "CANST", + "CANTO", "CANTS", "CAPAS", "CAPES", "CAPHS", "CAPLE", "CAPON", "CAPOS", "CAPOT", + "CAPRI", "CARAP", "CARER", "CARES", "CARET", "CARLE", "CARLS", "CARNS", "CARON", + "CARPI", "CARPS", "CARRS", "CARSE", "CARTA", "CARTE", "CARTS", "CASAS", "CASCO", + "CASES", "CASTS", "CATES", "CECAL", "CEILI", "CEILS", "CELLA", "CELLI", "CELLS", + "CELTS", "CENSE", "CENTO", "CENTS", "CEORL", "CEPES", "CERCI", "CERES", "CERIA", + "CERIC", "CERNE", "CEROC", "CEROS", "CERTS", "CESSE", "CESTA", "CESTI", "CETES", + "CHACE", "CHACO", "CHAIS", "CHALS", "CHANA", "CHAPE", "CHAPS", "CHAPT", "CHARA", + "CHARE", "CHARR", "CHARS", "CHATS", "CHEEP", "CHELA", "CHELP", "CHERE", "CHERT", + "CHETH", "CHIAO", "CHIAS", "CHICA", "CHICH", "CHICO", "CHICS", "CHIEL", "CHILE", + "CHINE", "CHINO", "CHINS", "CHIPS", "CHIRL", "CHIRO", "CHIRR", "CHIRT", "CHITS", + "CHOCO", "CHOCS", "CHOIL", "CHOLA", "CHOLI", "CHOLO", "CHONS", "CHOON", "CHOPS", + "CHOTA", "CHOTT", "CIELS", "CILIA", "CILLS", "CINCT", "CINES", "CIONS", "CIPPI", + "CIRCS", "CIRES", "CIRLS", "CIRRI", "CISCO", "CISTS", "CITAL", "CITER", "CITES", + "CLACH", "CLAES", "CLANS", "CLAPS", "CLAPT", "CLARO", "CLART", "CLAST", "CLATS", + "CLEEP", "CLEPE", "CLEPT", "CLIES", "CLINE", "CLINT", "CLIPE", "CLIPS", "CLIPT", + "CLITS", "CLONS", "CLOOP", "CLOOT", "CLOPS", "CLOTE", "CLOTS", "COACT", "COALA", + "COALS", "COAPT", "COATE", "COATI", "COATS", "COCAS", "COCCI", "COCCO", "COCOS", + "COHEN", "COHOE", "COHOS", "COILS", "COINS", "COIRS", "COITS", "COLAS", "COLES", + "COLIC", "COLIN", "COLLS", "COLTS", "CONES", "CONIA", "CONIN", "CONNE", "CONNS", + "CONTE", "CONTO", "COOCH", "COOEE", "COOER", "COOLS", "COONS", "COOPS", "COOPT", + "COOST", "COOTS", "COPAL", "COPEN", "COPER", "COPES", "COPRA", "CORES", "CORIA", + "CORNI", "CORNO", "CORNS", "CORPS", "CORSE", "CORSO", "COSEC", "COSES", "COSET", + "COSIE", "COSTA", "COSTE", "COSTS", "COTAN", "COTES", "COTHS", "COTTA", "COTTS", + "CRAAL", "CRAIC", "CRANS", "CRAPE", "CRAPS", "CRARE", "CREEL", "CREES", "CRENA", + "CREPS", "CRIAS", "CRIES", "CRINE", "CRIOS", "CRIPE", "CRIPS", "CRISE", "CRITH", + "CRITS", "CROCI", "CROCS", "CRONS", "CROOL", "CROON", "CROPS", "CRORE", "CROST", + "CTENE", "EALES", "EARLS", "EARNS", "EARNT", "EARST", "EASER", "EASES", "EASLE", + "EASTS", "EATHE", "ECHES", "ECHOS", "EISEL", "ELAIN", "ELANS", "ELCHI", "ELINT", + "ELOIN", "ELOPS", "ELPEE", "ELSIN", "ENATE", "ENIAC", "ENLIT", "ENOLS", "ENROL", + "ENTIA", "EORLS", "EOSIN", "EPACT", "EPEES", "EPHAH", "EPHAS", "EPHOR", "EPICS", + "EPOPT", "EPRIS", "ERICA", "ERICS", "ERNES", "EROSE", "ERSES", "ESCAR", "ESCOT", + "ESILE", "ESNES", "ESSES", "ESTOC", "ESTOP", "ESTRO", "ETAPE", "ETATS", "ETENS", + "ETHAL", "ETHNE", "ETICS", "ETNAS", "ETTIN", "ETTLE", "HAARS", "HAETS", "HAHAS", + "HAILS", "HAINS", "HAINT", "HAIRS", "HAITH", "HALAL", "HALER", "HALES", "HALLO", + "HALLS", "HALON", "HALOS", "HALSE", "HALTS", "HANAP", "HANCE", "HANCH", "HANSA", + "HANSE", "HANTS", "HAOLE", "HAPPI", "HARES", "HARLS", "HARNS", "HAROS", "HARPS", + "HARTS", "HASPS", "HASTA", "HATES", "HATHA", "HEALS", "HEAPS", "HEARE", "HEARS", + "HEAST", "HEATS", "HECHT", "HEELS", "HEILS", "HEIRS", "HELES", "HELIO", "HELLS", + "HELOS", "HELOT", "HELPS", "HENCH", "HENNA", "HENTS", "HEPAR", "HERES", "HERLS", + "HERNS", "HEROS", "HERSE", "HESPS", "HESTS", "HETES", "HETHS", "HIANT", "HILAR", + "HILCH", "HILLO", "HILLS", "HILTS", "HINTS", "HIOIS", "HIREE", "HIRER", "HIRES", + "HISTS", "HITHE", "HOARS", "HOAST", "HOERS", "HOISE", "HOLES", "HOLLA", "HOLLO", + "HOLON", "HOLOS", "HOLTS", "HONAN", "HONER", "HONES", "HOOCH", "HOONS", "HOOPS", + "HOORS", "HOOSH", "HOOTS", "HOPER", "HOPES", "HORAH", "HORAL", "HORAS", "HORIS", + "HORNS", "HORST", "HOSEL", "HOSEN", "HOSER", "HOSES", "HOSTA", "HOSTS", "HOTCH", + "HOTEN", "ICERS", "ICHES", "ICHOR", "ICIER", "ICONS", "ICTAL", "ICTIC", "ILEAC", + "ILEAL", "ILIAL", "ILLER", "ILLTH", "INAPT", "INCEL", "INCLE", "INION", "INNIT", + "INSET", "INSPO", "INTEL", "INTIL", "INTIS", "INTRA", "IOTAS", "IPPON", "IRONE", + "IRONS", "ISHES", "ISLES", "ISNAE", "ISSEI", "ISTLE", "ITHER", "LAARI", "LACER", + "LACES", "LACET", "LAERS", "LAHAL", "LAHAR", "LAICH", "LAICS", "LAIRS", "LAITH", + "LALLS", "LANAI", "LANAS", "LANCH", "LANES", "LANTS", "LAPIN", "LAPIS", "LARCH", + "LAREE", "LARES", "LARIS", "LARNS", "LARNT", "LASER", "LASES", "LASSI", "LASTS", + "LATAH", "LATEN", "LATHI", "LATHS", "LEANS", "LEAPS", "LEARE", "LEARS", "LEATS", + "LEEAR", "LEEPS", "LEERS", "LEESE", "LEETS", "LEHRS", "LEIRS", "LEISH", "LENES", + "LENIS", "LENOS", "LENSE", "LENTI", "LENTO", "LEONE", "LEPRA", "LEPTA", "LERES", + "LERPS", "LESES", "LESTS", "LETCH", "LETHE", "LIANA", "LIANE", "LIARS", "LIART", + "LICHI", "LICHT", "LICIT", "LIENS", "LIERS", "LILLS", "LILOS", "LILTS", "LINAC", + "LINCH", "LINES", "LININ", "LINNS", "LINOS", "LINTS", "LIONS", "LIPAS", "LIPES", + "LIPIN", "LIPOS", "LIRAS", "LIROT", "LISLE", "LISPS", "LISTS", "LITAI", "LITAS", + "LITES", "LITHO", "LITHS", "LITRE", "LLANO", "LOACH", "LOANS", "LOAST", "LOCHE", + "LOCHS", "LOCIE", "LOCIS", "LOCOS", "LOESS", "LOHAN", "LOINS", "LOIPE", "LOIRS", + "LOLLS", "LONER", "LOOIE", "LOONS", "LOOPS", "LOOTS", "LOPER", "LOPES", "LORAL", + "LORAN", "LOREL", "LORES", "LORIC", "LORIS", "LOSEL", "LOSEN", "LOSES", "LOTAH", + "LOTAS", "LOTES", "LOTIC", "LOTOS", "LOTSA", "LOTTA", "LOTTE", "LOTTO", "NAANS", + "NACHE", "NACHO", "NACRE", "NAHAL", "NAILS", "NAIRA", "NALAS", "NALLA", "NANAS", + "NANCE", "NANNA", "NANOS", "NAPAS", "NAPES", "NAPOO", "NAPPA", "NAPPE", "NARAS", + "NARCO", "NARCS", "NARES", "NARIC", "NARIS", "NARRE", "NASHI", "NATCH", "NATES", + "NATIS", "NEALS", "NEAPS", "NEARS", "NEATH", "NEATS", "NEELE", "NEEPS", "NEESE", + "NEIST", "NELIS", "NENES", "NEONS", "NEPER", "NEPIT", "NERAL", "NEROL", "NERTS", + "NESTS", "NETES", "NETOP", "NETTS", "NICHT", "NICOL", "NIHIL", "NILLS", "NINER", + "NINES", "NINON", "NIPAS", "NIRLS", "NISEI", "NISSE", "NITER", "NITES", "NITON", + "NITRE", "NITRO", "NOAHS", "NOELS", "NOILS", "NOINT", "NOIRS", "NOLES", "NOLLS", + "NOLOS", "NONAS", "NONCE", "NONES", "NONET", "NONIS", "NOOIT", "NOONS", "NOOPS", + "NOPAL", "NORIA", "NORIS", "NOSER", "NOSES", "NOTAL", "NOTER", "NOTES", "OASES", + "OASIS", "OASTS", "OATEN", "OATER", "OATHS", "OCHER", "OCHES", "OCHRE", "OCREA", + "OCTAN", "OCTAS", "OHIAS", "OHONE", "OILER", "OINTS", "OLEIC", "OLEIN", "OLENT", + "OLEOS", "OLIOS", "OLLAS", "OLLER", "OLLIE", "OLPAE", "OLPES", "ONCER", "ONCES", + "ONCET", "ONERS", "ONTIC", "OONTS", "OORIE", "OOSES", "OPAHS", "OPALS", "OPENS", + "OPEPE", "OPPOS", "OPSIN", "OPTER", "ORACH", "ORALS", "ORANT", "ORATE", "ORCAS", + "ORCIN", "ORIEL", "ORLES", "ORLON", "ORLOP", "ORNIS", "ORPIN", "ORRIS", "ORTHO", + "OSCAR", "OSHAC", "OSIER", "OSSIA", "OSTIA", "OTTAR", "OTTOS", "PAALS", "PAANS", + "PACAS", "PACER", "PACES", "PACHA", "PACOS", "PACTA", "PACTS", "PAEAN", "PAEON", + "PAILS", "PAINS", "PAIRE", "PAIRS", "PAISA", "PAISE", "PALAS", "PALEA", "PALES", + "PALET", "PALIS", "PALLA", "PALLS", "PALPI", "PALPS", "PALSA", "PANCE", "PANES", + "PANNE", "PANNI", "PANTO", "PANTS", "PAOLI", "PAOLO", "PAPAS", "PAPES", "PAPPI", + "PARAE", "PARAS", "PARCH", "PAREN", "PAREO", "PARES", "PARIS", "PARLE", "PAROL", + "PARPS", "PARRA", "PARRS", "PARTI", "PARTS", "PASEO", "PASES", "PASHA", "PASSE", + "PASTS", "PATEN", "PATER", "PATES", "PATHS", "PATIN", "PATTE", "PEALS", "PEANS", + "PEARE", "PEARS", "PEART", "PEASE", "PEATS", "PECHS", "PEECE", "PEELS", "PEENS", + "PEEPE", "PEEPS", "PEERS", "PEINS", "PEISE", "PELAS", "PELES", "PELLS", "PELON", + "PELTA", "PELTS", "PENES", "PENIE", "PENIS", "PENNA", "PENNI", "PENTS", "PEONS", + "PEPLA", "PEPOS", "PEPSI", "PERAI", "PERCE", "PERCS", "PEREA", "PERES", "PERIS", + "PERNS", "PERPS", "PERSE", "PERST", "PERTS", "PESOS", "PESTS", "PETAR", "PETER", + "PETIT", "PETRE", "PETRI", "PETTI", "PETTO", "PHARE", "PHEER", "PHENE", "PHEON", + "PHESE", "PHIAL", "PHISH", "PHOCA", "PHONO", "PHONS", "PHOTS", "PHPHT", "PIANI", + "PIANS", "PICAL", "PICAS", "PICOT", "PICRA", "PIERS", "PIERT", "PIETA", "PIETS", + "PILAE", "PILAO", "PILAR", "PILCH", "PILEA", "PILEI", "PILER", "PILES", "PILIS", + "PILLS", "PINAS", "PINES", "PINNA", "PINON", "PINOT", "PINTA", "PINTS", "PIONS", + "PIPAL", "PIPAS", "PIPES", "PIPET", "PIPIS", "PIPIT", "PIRAI", "PIRLS", "PIRNS", + "PISCO", "PISES", "PISOS", "PISTE", "PITAS", "PITHS", "PITON", "PITOT", "PITTA", + "PLAAS", "PLANS", "PLAPS", "PLASH", "PLAST", "PLATS", "PLATT", "PLEAS", "PLENA", + "PLEON", "PLESH", "PLICA", "PLIES", "PLOAT", "PLOPS", "PLOTS", "POACH", "POEPS", + "POETS", "POLER", "POLES", "POLIO", "POLIS", "POLLS", "POLOS", "POLTS", "PONCE", + "PONES", "PONTS", "POOHS", "POOLS", "POONS", "POOPS", "POORI", "POORT", "POOTS", + "POPES", "POPPA", "PORAE", "PORAL", "PORER", "PORES", "PORIN", "PORNO", "PORNS", + "PORTA", "PORTS", "POSES", "POSHO", "POSTS", "POTAE", "POTCH", "POTES", "POTIN", + "POTOO", "POTTO", "POTTS", "PRANA", "PRAOS", "PRASE", "PRATE", "PRATS", "PRATT", + "PREES", "PRENT", "PREON", "PREOP", "PREPS", "PRESA", "PRESE", "PREST", "PRIAL", + "PRIER", "PRIES", "PRILL", "PRION", "PRISE", "PRISS", "PROAS", "PROIN", "PROLE", + "PROLL", "PROPS", "PRORE", "PROSO", "PROSS", "PROST", "PROTO", "PSION", "PSOAE", + "PSOAI", "PSOAS", "PSORA", "RACES", "RACHE", "RACON", "RAIAS", "RAILE", "RAILS", + "RAINE", "RAINS", "RAITA", "RAITS", "RALES", "RANAS", "RANCE", "RANEE", "RANIS", + "RANTS", "RAPER", "RAPES", "RAPHE", "RAPPE", "RAREE", "RARES", "RASER", "RASES", + "RASPS", "RASSE", "RASTA", "RATAL", "RATAN", "RATAS", "RATCH", "RATEL", "RATER", + "RATES", "RATHA", "RATHE", "RATHS", "RATOO", "RATOS", "REAIS", "REALO", "REALS", + "REANS", "REAPS", "REARS", "REAST", "REATA", "REATE", "RECAL", "RECCE", "RECCO", + "RECIT", "RECON", "RECTA", "RECTI", "RECTO", "REECH", "REELS", "REENS", "REEST", + "REINS", "REIST", "RELET", "RELIE", "RELIT", "RELLO", "RENIN", "RENNE", "RENOS", + "RENTE", "RENTS", "REOIL", "REPIN", "REPLA", "REPOS", "REPOT", "REPPS", "REPRO", + "RERAN", "RESAT", "RESEE", "RESES", "RESIT", "RESTO", "RESTS", "RETIA", "RETIE", + "RHEAS", "RHIES", "RHINE", "RHONE", "RIALS", "RIANT", "RIATA", "RICER", "RICES", + "RICHT", "RICIN", "RIELS", "RILES", "RILLE", "RILLS", "RINES", "RIOTS", "RIPES", + "RIPPS", "RISES", "RISHI", "RISPS", "RITES", "RITTS", "ROANS", "ROARS", "ROATE", + "ROHES", "ROILS", "ROINS", "ROIST", "ROLES", "ROLLS", "RONEO", "RONES", "RONIN", + "RONNE", "RONTE", "RONTS", "ROONS", "ROOPS", "ROOSA", "ROOSE", "ROOTS", "ROPER", + "ROPES", "RORAL", "RORES", "RORIC", "RORIE", "RORTS", "ROSES", "ROSET", "ROSHI", + "ROSIN", "ROSIT", "ROSTI", "ROSTS", "ROTAL", "ROTAN", "ROTAS", "ROTCH", "ROTES", + "ROTIS", "ROTLS", "ROTON", "ROTOS", "ROTTE", "SACRA", "SAICE", "SAICS", "SAILS", + "SAINE", "SAINS", "SAIRS", "SAIST", "SAITH", "SALAL", "SALAT", "SALEP", "SALES", + "SALET", "SALIC", "SALLE", "SALOL", "SALOP", "SALPA", "SALPS", "SALSE", "SALTO", + "SALTS", "SANES", "SANSA", "SANTO", "SANTS", "SAOLA", "SAPAN", "SAPOR", "SARAN", + "SAREE", "SARIN", "SARIS", "SAROS", "SASER", "SASIN", "SASSE", "SATAI", "SATES", + "SATIS", "SCAIL", "SCALA", "SCALL", "SCANS", "SCAPA", "SCAPE", "SCAPI", "SCARP", + "SCARS", "SCART", "SCATH", "SCATS", "SCATT", "SCEAT", "SCENA", "SCOOT", "SCOPA", + "SCOPS", "SCOTS", "SCRAE", "SCRAN", "SCRAT", "SCRIP", "SEALS", "SEANS", "SEARE", + "SEARS", "SEASE", "SEATS", "SECCO", "SECHS", "SECTS", "SEELS", "SEEPS", "SEERS", + "SEHRI", "SEILS", "SEINE", "SEIRS", "SEISE", "SELAH", "SELES", "SELLA", "SELLE", + "SELLS", "SENAS", "SENES", "SENNA", "SENOR", "SENSA", "SENSI", "SENTE", "SENTI", + "SENTS", "SEPAL", "SEPIC", "SEPTA", "SEPTS", "SERAC", "SERAI", "SERAL", "SERER", + "SERES", "SERIC", "SERIN", "SERON", "SERRA", "SERRE", "SERRS", "SESSA", "SETAE", + "SETAL", "SETON", "SETTS", "SHAHS", "SHANS", "SHAPS", "SHARN", "SHASH", "SHCHI", + "SHEAL", "SHEAS", "SHEEL", "SHENT", "SHEOL", "SHERE", "SHERO", "SHETS", "SHIAI", + "SHIEL", "SHIER", "SHIES", "SHILL", "SHINS", "SHIPS", "SHIRR", "SHIRS", "SHISH", + "SHISO", "SHIST", "SHITE", "SHITS", "SHLEP", "SHOAT", "SHOER", "SHOES", "SHOLA", + "SHOOL", "SHOON", "SHOOS", "SHOPE", "SHOPS", "SHORL", "SHOTE", "SHOTS", "SHOTT", + "SHRIS", "SIALS", "SICES", "SICHT", "SIENS", "SIENT", "SIETH", "SILEN", "SILER", + "SILES", "SILLS", "SILOS", "SILTS", "SINES", "SINHS", "SIPES", "SIREE", "SIRES", + "SIRIH", "SIRIS", "SIROC", "SIRRA", "SISAL", "SISES", "SISTA", "SISTS", "SITAR", + "SITES", "SITHE", "SLAES", "SLANE", "SLAPS", "SLART", "SLATS", "SLEER", "SLIER", + "SLIPE", "SLIPS", "SLIPT", "SLISH", "SLITS", "SLOAN", "SLOES", "SLOOT", "SLOPS", + "SLOTS", "SNAPS", "SNARS", "SNASH", "SNATH", "SNEAP", "SNEES", "SNELL", "SNIES", + "SNIPS", "SNIRT", "SNITS", "SNOEP", "SNOOL", "SNOOT", "SNOTS", "SOAPS", "SOARE", + "SOARS", "SOCAS", "SOCES", "SOCLE", "SOILS", "SOLAH", "SOLAN", "SOLAS", "SOLEI", + "SOLER", "SOLES", "SOLON", "SOLOS", "SONCE", "SONES", "SONNE", "SONSE", "SOOLE", + "SOOLS", "SOOPS", "SOOTE", "SOOTS", "SOPHS", "SOPOR", "SOPRA", "SORAL", "SORAS", + "SOREE", "SOREL", "SORER", "SORES", "SORNS", "SORRA", "SORTA", "SORTS", "SOTHS", + "SOTOL", "SPAER", "SPAES", "SPAHI", "SPAIL", "SPAIN", "SPAIT", "SPALE", "SPALL", + "SPALT", "SPANE", "SPANS", "SPARS", "SPART", "SPATE", "SPATS", "SPEAL", "SPEAN", + "SPEAT", "SPECS", "SPECT", "SPEEL", "SPEER", "SPEIL", "SPEIR", "SPEOS", "SPETS", + "SPIAL", "SPICA", "SPICS", "SPIER", "SPIES", "SPILE", "SPINA", "SPINS", "SPIRT", + "SPITS", "SPOOR", "SPOOT", "SPOSH", "SPOTS", "SPRAT", "SPRIT", "STANE", "STAPH", + "STAPS", "STARN", "STARR", "STARS", "STATS", "STEAN", "STEAR", "STEEN", "STEIL", + "STELA", "STELE", "STELL", "STENO", "STENS", "STENT", "STEPS", "STEPT", "STERE", + "STETS", "STICH", "STIES", "STILE", "STIPA", "STIPE", "STIRE", "STIRP", "STIRS", + "STOAE", "STOAI", "STOAS", "STOAT", "STOEP", "STOIT", "STOLN", "STONN", "STOOR", + "STOPE", "STOPS", "STOPT", "STOSS", "STOTS", "STOTT", "STRAE", "STREP", "STRIA", + "STROP", "TAALS", "TAATA", "TACAN", "TACES", "TACET", "TACHE", "TACHO", "TACHS", + "TACOS", "TACTS", "TAELS", "TAHAS", "TAHRS", "TAILS", "TAINS", "TAIRA", "TAISH", + "TAITS", "TALAR", "TALAS", "TALCS", "TALEA", "TALER", "TALES", "TALLS", "TALPA", + "TANAS", "TANHS", "TANNA", "TANTI", "TANTO", "TAPAS", "TAPEN", "TAPES", "TAPET", + "TAPIS", "TAPPA", "TARAS", "TARES", "TARNS", "TAROC", "TAROS", "TARPS", "TARRE", + "TARSI", "TARTS", "TASAR", "TASER", "TASES", "TASSA", "TASSE", "TASSO", "TATAR", + "TATER", "TATES", "TATHS", "TATIE", "TATTS", "TEALS", "TEARS", "TEATS", "TECHS", + "TECTA", "TEELS", "TEENE", "TEENS", "TEERS", "TEHRS", "TEILS", "TEINS", "TELAE", + "TELCO", "TELES", "TELIA", "TELIC", "TELLS", "TELOI", "TELOS", "TENCH", "TENES", + "TENIA", "TENNE", "TENNO", "TENON", "TENTS", "TEPAL", "TEPAS", "TERAI", "TERAS", + "TERCE", "TERES", "TERNE", "TERNS", "TERTS", "TESLA", "TESTA", "TESTE", "TESTS", + "TETES", "TETHS", "TETRA", "TETRI", "THALE", "THALI", "THANA", "THANE", "THANS", + "THARS", "THECA", "THEES", "THEIC", "THEIN", "THENS", "THESP", "THETE", "THILL", + "THINE", "THINS", "THIOL", "THIRL", "THOLE", "THOLI", "THORO", "THORP", "THRAE", + "THRIP", "THROE", "TIANS", "TIARS", "TICAL", "TICCA", "TICES", "TIERS", "TILER", + "TILES", "TILLS", "TILTH", "TILTS", "TINAS", "TINCT", "TINEA", "TINES", "TINTS", + "TIPIS", "TIRES", "TIRLS", "TIROS", "TIRRS", "TITCH", "TITER", "TITIS", "TITRE", + "TOCOS", "TOEAS", "TOHOS", "TOILE", "TOILS", "TOISE", "TOITS", "TOLAN", "TOLAR", + "TOLAS", "TOLES", "TOLLS", "TOLTS", "TONER", "TONES", "TONNE", "TOOLS", "TOONS", + "TOOTS", "TOPEE", "TOPER", "TOPES", "TOPHE", "TOPHI", "TOPHS", "TOPIS", "TOPOI", + "TOPOS", "TORAH", "TORAN", "TORAS", "TORCS", "TORES", "TORIC", "TORII", "TOROS", + "TOROT", "TORRS", "TORSE", "TORSI", "TORTA", "TORTE", "TORTS", "TOSAS", "TOSES", + "TOTER", "TOTES", "TRANS", "TRANT", "TRAPE", "TRAPS", "TRAPT", "TRASS", "TRATS", + "TRATT", "TREEN", "TREES", "TRESS", "TREST", "TRETS", "TRIAC", "TRIER", "TRIES", + "TRILL", "TRINE", "TRINS", "TRIOL", "TRIOR", "TRIOS", "TRIPS", "TRIST", "TROAT", + "TROIS", "TRONA", "TRONC", "TRONE", "TRONS", "TROTH", "TROTS", "TSARS", +#endif +}; + +#if (WORDLE_USE_RANDOM_GUESS == 3) +static const uint16_t _num_random_guess_words = 13; // The valid_words array begins with this many words that are considered the top 3% best options. +#elif (WORDLE_USE_RANDOM_GUESS == 2) +static const uint16_t _num_random_guess_words = 257; // The valid_words array begins with this many words where each letter is different. +#elif (WORDLE_USE_RANDOM_GUESS == 1) +static const uint16_t _num_random_guess_words = _num_words; +#endif + +#endif // WORDLE_FACE_DICT_H_ \ No newline at end of file