+++ /dev/null
-/*
- * MIT License
- *
- * Copyright (c) 2024 <David Volovskiy>
- *
- * 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 <stdlib.h>
-#include <string.h>
-#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;
-}
-
+++ /dev/null
-/*
- * MIT License
- *
- * Copyright (c) 2024 <David Volovskiy>
- *
- * 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_
-
+++ /dev/null
-#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
#include "tally_face.h"
#include "probability_face.h"
#include "ke_decimal_time_face.h"
+#include "wordle_face.h"
// New includes go above this line.
./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 \
--- /dev/null
+/*
+ * MIT License
+ *
+ * Copyright (c) 2024 <David Volovskiy>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#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;
+}
+
--- /dev/null
+/*
+ * MIT License
+ *
+ * Copyright (c) 2024 <David Volovskiy>
+ *
+ * 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_
--- /dev/null
+#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