From a831ed33364edb9b94f0ce43365a3956fa45c903 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 25 Jun 2023 20:43:01 +0100 Subject: [PATCH 001/161] Hi-lo: Initial game face commit --- movement/make/Makefile | 1 + movement/movement_faces.h | 1 + .../complication/higher_lower_game_face.c | 371 ++++++++++++++++++ .../complication/higher_lower_game_face.h | 55 +++ 4 files changed, 428 insertions(+) create mode 100755 movement/watch_faces/complication/higher_lower_game_face.c create mode 100755 movement/watch_faces/complication/higher_lower_game_face.h diff --git a/movement/make/Makefile b/movement/make/Makefile index 8b056a0..c761c8f 100644 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -116,6 +116,7 @@ SRCS += \ ../watch_faces/complication/geomancy_face.c \ ../watch_faces/clock/simple_clock_bin_led_face.c \ ../watch_faces/complication/flashlight_face.c \ + ../watch_faces/complication/higher_lower_game_face.c \ # New watch faces go above this line. # Leave this line at the bottom of the file; it has all the targets for making your project. diff --git a/movement/movement_faces.h b/movement/movement_faces.h index bf63732..4b510da 100644 --- a/movement/movement_faces.h +++ b/movement/movement_faces.h @@ -93,6 +93,7 @@ #include "dual_timer_face.h" #include "simple_clock_bin_led_face.h" #include "flashlight_face.h" +#include "higher_lower_game_face.h" // New includes go above this line. #endif // MOVEMENT_FACES_H_ diff --git a/movement/watch_faces/complication/higher_lower_game_face.c b/movement/watch_faces/complication/higher_lower_game_face.c new file mode 100755 index 0000000..3c35bc2 --- /dev/null +++ b/movement/watch_faces/complication/higher_lower_game_face.c @@ -0,0 +1,371 @@ +/* + * MIT License + * + * Copyright (c) 2023 Chris Ellis + * + * 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. + */ + +// TODO: Win animation? +// TODO: Save highscore? +// TODO: Add sounds? +// Add sound option +// TODO: Flip board direction? + +// Future Ideas: +// - Use lap indicator for larger score improvement? + +// Emulator only: need time() to seed the random number generator. +#if __EMSCRIPTEN__ +#include +#endif + +#include +#include +#include "higher_lower_game_face.h" +#include "watch_private_display.h" + +#define TITLE_TEXT "Hi-Lo" +#define GAME_BOARD_SIZE 6 +#define MAX_BOARDS 3 //40 +#define GUESSES_PER_SCREEN 5 +#define WIN_SCORE MAX_BOARDS * GUESSES_PER_SCREEN +#define STATUS_DISPLAY_START 0 +#define BOARD_SCORE_DISPLAY_START 2 +#define BOARD_DISPLAY_START 4 +#define BOARD_DISPLAY_END 9 +#define MIN_CARD_VALUE 2 +#define MAX_CARD_VALUE 14 + +typedef struct card_t { + uint8_t value; + bool revealed; +} card_t; + +typedef enum { + A, B, C, D, E, F, G +} segment_t; + +typedef enum { + HL_GUESS_EQUAL, + HL_GUESS_HIGHER, + HL_GUESS_LOWER +} guess_t; + +typedef enum { + HL_GS_TITLE_SCREEN, + HL_GS_GUESSING, + HL_GS_WIN, + HL_GS_LOSE, + HL_GS_SHOW_SCORE, +} game_state_t; + +static game_state_t game_state = HL_GS_TITLE_SCREEN; +static card_t game_board[GAME_BOARD_SIZE] = {0}; +static uint8_t guess_position = 0; +static uint8_t score = 0; +static uint8_t completed_board_count = 0; + +static uint8_t generate_random_number(uint8_t num_values) { + // Emulator: use rand. Hardware: use arc4random. +#if __EMSCRIPTEN__ + return rand() % num_values; +#else + return arc4random_uniform(num_values); +#endif +} + +static void reset_board(bool first_round) { + // First card is random on the first board, and carried over from the last position on subsequent boards + const uint8_t first_card_value = first_round + ? generate_random_number(MAX_CARD_VALUE - MIN_CARD_VALUE + 1) + MIN_CARD_VALUE + : game_board[GAME_BOARD_SIZE - 1].value; + + game_board[0].value = first_card_value; + game_board[0].revealed = true; // Always reveal first card + + // Fill remainder of board + for (size_t i = 1; i < GAME_BOARD_SIZE; ++i) { + game_board[i] = (card_t) { + //.value = generate_random_number(MAX_CARD_VALUE - MIN_CARD_VALUE + 1) + MIN_CARD_VALUE, + .value = i + MIN_CARD_VALUE, + .revealed = false + }; + } +} + +static void init_game(void) { + watch_clear_display(); + watch_display_string(TITLE_TEXT, BOARD_DISPLAY_START); + watch_display_string("GA", STATUS_DISPLAY_START); + reset_board(true); + score = 0; + completed_board_count = 0; + guess_position = 1; +} + +static void set_segment_at_position(segment_t segment, uint8_t position) { + const uint64_t position_segment_data = (Segment_Map[position] >> (8 * (uint8_t) segment)) & 0xFF; + const uint8_t com_pin = position_segment_data >> 6; + const uint8_t seg = position_segment_data & 0x3F; + watch_set_pixel(com_pin, seg); +} + +static void render_board_position(size_t board_position) { + const size_t display_position = BOARD_DISPLAY_END - board_position; + const bool revealed = game_board[board_position].revealed; + + //if (board_position == guess_position) { + // // Current spot + // watch_display_character('-', display_position); + // return; + //} + + if (!revealed) { + // Higher or lower indicator (currently just an empty space) + watch_display_character(' ', display_position); + //set_segment_at_position(F, display_position); + return; + } + + const uint8_t value = game_board[board_position].value; + switch (value) { + case 14: // A + watch_display_character('H', display_position); + break; + case 13: // K (≡) + watch_display_character(' ', display_position); + set_segment_at_position(A, display_position); + set_segment_at_position(D, display_position); + set_segment_at_position(G, display_position); + break; + case 12: // Q (=) + watch_display_character(' ', display_position); + set_segment_at_position(A, display_position); + set_segment_at_position(D, display_position); + break; + case 11: // J (-) + watch_display_character('-', display_position); + break; + case 10: // 10 (0) + watch_display_character('0', display_position); + break; + default: { + const char display_char = value + '0'; + watch_display_character(display_char, display_position); + } + } +} + +static void render_board() { + for (size_t i = 0; i < GAME_BOARD_SIZE; ++i) { + render_board_position(i); + } +} + +static void render_board_count(void) { + // Render completed boards (screens) + char buf[3] = {0}; + snprintf(buf, sizeof(buf), "%2hhu", completed_board_count); + watch_display_string(buf, BOARD_SCORE_DISPLAY_START); +} + +static void render_final_score(void) { + watch_display_string("SC", STATUS_DISPLAY_START); + char buf[7] = {0}; + const uint8_t complete_boards = score / GUESSES_PER_SCREEN; + snprintf(buf, sizeof(buf), "%2hu %03hu", complete_boards, score); + watch_set_colon(); + watch_display_string(buf, BOARD_DISPLAY_START); +} + +static guess_t get_answer() { + if (guess_position < 1 || guess_position > GAME_BOARD_SIZE) + return HL_GUESS_EQUAL; // Maybe add an error state, shouldn't ever hit this. + + game_board[guess_position].revealed = true; + const uint8_t previous_value = game_board[guess_position - 1].value; + const uint8_t current_value = game_board[guess_position].value; + + if (current_value > previous_value) + return HL_GUESS_HIGHER; + else if (current_value < previous_value) + return HL_GUESS_LOWER; + else + return HL_GUESS_EQUAL; +} + +static void do_game_loop(guess_t user_guess) { + switch (game_state) { + case HL_GS_TITLE_SCREEN: + init_game(); + render_board(); + render_board_count(); + game_state = HL_GS_GUESSING; + break; + case HL_GS_GUESSING: { + const guess_t answer = get_answer(); + + // Render answer indicator + switch (answer) { + case HL_GUESS_EQUAL: + watch_display_string("==", STATUS_DISPLAY_START); + break; + case HL_GUESS_HIGHER: + watch_display_string("HI", STATUS_DISPLAY_START); + break; + case HL_GUESS_LOWER: + watch_display_string("LO", STATUS_DISPLAY_START); + break; + } + + // Scoring + if (answer == user_guess) { + score++; + } else if (answer == HL_GUESS_EQUAL) { + // No score for two consecutive identical cards + } else { + // Incorrect guess, game over + watch_display_string("GO", STATUS_DISPLAY_START); + game_board[guess_position].revealed = true; + render_board_position(guess_position); + game_state = HL_GS_LOSE; + return; + } + + if (score >= WIN_SCORE) { + // Win, perhaps some kind of animation sequence? + watch_display_string("WI", STATUS_DISPLAY_START); + watch_display_string(" ", BOARD_SCORE_DISPLAY_START); + watch_display_string("------", BOARD_DISPLAY_START); + game_state = HL_GS_WIN; + return; + } + + // Next guess position + const bool final_board_guess = guess_position == GAME_BOARD_SIZE - 1; + if (final_board_guess) { + // Seed new board + completed_board_count++; + render_board_count(); + guess_position = 1; + reset_board(false); + render_board(); + } else { + guess_position++; + render_board_position(guess_position - 1); + render_board_position(guess_position); + } + break; + } + case HL_GS_WIN: + case HL_GS_LOSE: + // Show score screen on button press from either state + watch_clear_display(); + render_final_score(); + game_state = HL_GS_SHOW_SCORE; + break; + case HL_GS_SHOW_SCORE: + watch_clear_display(); + watch_display_string(TITLE_TEXT, BOARD_DISPLAY_START); + watch_display_string("GA", STATUS_DISPLAY_START); + game_state = HL_GS_TITLE_SCREEN; + break; + default: + watch_display_string("ERROR", BOARD_DISPLAY_START); + break; + } +} + +static void light_button_handler(void) { + do_game_loop(HL_GUESS_HIGHER); +} + +static void alarm_button_handler(void) { + do_game_loop(HL_GUESS_LOWER); +} + +void higher_lower_game_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr) { + (void) settings; + (void) watch_face_index; + + if (*context_ptr == NULL) { + *context_ptr = malloc(sizeof(higher_lower_game_face_state_t)); + memset(*context_ptr, 0, sizeof(higher_lower_game_face_state_t)); + // Do any one-time tasks in here; the inside of this conditional happens only at boot. + memset(game_board, 0, sizeof(game_board)); + } + // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep. +} + +void higher_lower_game_face_activate(movement_settings_t *settings, void *context) { + (void) settings; + higher_lower_game_face_state_t *state = (higher_lower_game_face_state_t *) context; + (void) state; + // Handle any tasks related to your watch face coming on screen. + game_state = HL_GS_TITLE_SCREEN; +} + +bool higher_lower_game_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { + higher_lower_game_face_state_t *state = (higher_lower_game_face_state_t *) context; + (void) state; + + switch (event.event_type) { + case EVENT_ACTIVATE: + // Show your initial UI here. + watch_display_string(TITLE_TEXT, BOARD_DISPLAY_START); + watch_display_string("GA", STATUS_DISPLAY_START); + break; + case EVENT_TICK: + // If needed, update your display here. + break; + case EVENT_LIGHT_BUTTON_UP: + light_button_handler(); + break; + case EVENT_LIGHT_BUTTON_DOWN: + // Don't trigger light + break; + case EVENT_ALARM_BUTTON_UP: + alarm_button_handler(); + break; + case EVENT_TIMEOUT: + // Your watch face will receive this event after a period of inactivity. If it makes sense to resign, + // you may uncomment this line to move back to the first watch face in the list: + // movement_move_to_face(0); + break; + default: + return movement_default_loop_handler(event, settings); + } + + // return true if the watch can enter standby mode. Generally speaking, you should always return true. + // Exceptions: + // * If you are displaying a color using the low-level watch_set_led_color function, you should return false. + // * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false. + // Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or + // movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions. + return true; +} + +void higher_lower_game_face_resign(movement_settings_t *settings, void *context) { + (void) settings; + (void) context; + + // handle any cleanup before your watch face goes off-screen. +} + diff --git a/movement/watch_faces/complication/higher_lower_game_face.h b/movement/watch_faces/complication/higher_lower_game_face.h new file mode 100755 index 0000000..1cef05d --- /dev/null +++ b/movement/watch_faces/complication/higher_lower_game_face.h @@ -0,0 +1,55 @@ +/* + * MIT License + * + * Copyright (c) 2023 Chris Ellis + * + * 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 HIGHER_LOWER_GAME_FACE_H_ +#define HIGHER_LOWER_GAME_FACE_H_ + +#include "movement.h" + +/* + * A DESCRIPTION OF YOUR WATCH FACE + * + * and a description of how use it + * + */ + +typedef struct { + // Anything you need to keep track of, put it here! +} higher_lower_game_face_state_t; + +void higher_lower_game_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); +void higher_lower_game_face_activate(movement_settings_t *settings, void *context); +bool higher_lower_game_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void higher_lower_game_face_resign(movement_settings_t *settings, void *context); + +#define higher_lower_game_face ((const watch_face_t){ \ + higher_lower_game_face_setup, \ + higher_lower_game_face_activate, \ + higher_lower_game_face_loop, \ + higher_lower_game_face_resign, \ + NULL, \ +}) + +#endif // HIGHER_LOWER_GAME_FACE_H_ + From 739ad64cc10d8889c0564e75cf3faa378726d3ba Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 2 Jul 2023 11:40:11 +0100 Subject: [PATCH 002/161] Hi-lo: Allow flipped board rendering option --- movement/watch_faces/complication/higher_lower_game_face.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/movement/watch_faces/complication/higher_lower_game_face.c b/movement/watch_faces/complication/higher_lower_game_face.c index 3c35bc2..1edcaa0 100755 --- a/movement/watch_faces/complication/higher_lower_game_face.c +++ b/movement/watch_faces/complication/higher_lower_game_face.c @@ -52,6 +52,7 @@ #define BOARD_DISPLAY_END 9 #define MIN_CARD_VALUE 2 #define MAX_CARD_VALUE 14 +#define FLIP_BOARD_DIRECTION false typedef struct card_t { uint8_t value; @@ -128,11 +129,13 @@ static void set_segment_at_position(segment_t segment, uint8_t position) { } static void render_board_position(size_t board_position) { - const size_t display_position = BOARD_DISPLAY_END - board_position; + size_t display_position = FLIP_BOARD_DIRECTION + ? BOARD_DISPLAY_START + board_position + : BOARD_DISPLAY_END - board_position; const bool revealed = game_board[board_position].revealed; + //// Current position indicator spot //if (board_position == guess_position) { - // // Current spot // watch_display_character('-', display_position); // return; //} From b147ac0c672f95a04a72fd1254b4e686d47e43f0 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 9 Jul 2023 15:52:54 +0100 Subject: [PATCH 003/161] Hi-lo: Update face description - Remove test code --- .../complication/higher_lower_game_face.c | 17 ++---- .../complication/higher_lower_game_face.h | 54 +++++++++++++++++-- 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/movement/watch_faces/complication/higher_lower_game_face.c b/movement/watch_faces/complication/higher_lower_game_face.c index 1edcaa0..aed6eee 100755 --- a/movement/watch_faces/complication/higher_lower_game_face.c +++ b/movement/watch_faces/complication/higher_lower_game_face.c @@ -22,15 +22,6 @@ * SOFTWARE. */ -// TODO: Win animation? -// TODO: Save highscore? -// TODO: Add sounds? -// Add sound option -// TODO: Flip board direction? - -// Future Ideas: -// - Use lap indicator for larger score improvement? - // Emulator only: need time() to seed the random number generator. #if __EMSCRIPTEN__ #include @@ -43,7 +34,7 @@ #define TITLE_TEXT "Hi-Lo" #define GAME_BOARD_SIZE 6 -#define MAX_BOARDS 3 //40 +#define MAX_BOARDS 40 #define GUESSES_PER_SCREEN 5 #define WIN_SCORE MAX_BOARDS * GUESSES_PER_SCREEN #define STATUS_DISPLAY_START 0 @@ -104,8 +95,7 @@ static void reset_board(bool first_round) { // Fill remainder of board for (size_t i = 1; i < GAME_BOARD_SIZE; ++i) { game_board[i] = (card_t) { - //.value = generate_random_number(MAX_CARD_VALUE - MIN_CARD_VALUE + 1) + MIN_CARD_VALUE, - .value = i + MIN_CARD_VALUE, + .value = generate_random_number(MAX_CARD_VALUE - MIN_CARD_VALUE + 1) + MIN_CARD_VALUE, .revealed = false }; } @@ -129,7 +119,7 @@ static void set_segment_at_position(segment_t segment, uint8_t position) { } static void render_board_position(size_t board_position) { - size_t display_position = FLIP_BOARD_DIRECTION + const size_t display_position = FLIP_BOARD_DIRECTION ? BOARD_DISPLAY_START + board_position : BOARD_DISPLAY_END - board_position; const bool revealed = game_board[board_position].revealed; @@ -371,4 +361,3 @@ void higher_lower_game_face_resign(movement_settings_t *settings, void *context) // handle any cleanup before your watch face goes off-screen. } - diff --git a/movement/watch_faces/complication/higher_lower_game_face.h b/movement/watch_faces/complication/higher_lower_game_face.h index 1cef05d..d093680 100755 --- a/movement/watch_faces/complication/higher_lower_game_face.h +++ b/movement/watch_faces/complication/higher_lower_game_face.h @@ -28,10 +28,59 @@ #include "movement.h" /* - * A DESCRIPTION OF YOUR WATCH FACE + * Higher-Lower game face + * ====================== * - * and a description of how use it + * A game face based on the "higher-lower" card game where the objective is to correctly guess if the next card will + * be higher or lower than the last revealed cards. * + * Game Flow: + * - When the face is selected, the "Hi-Lo" "Title" screen will be displayed, and the status indicator will display "GA" for game + * - Pressing `ALARM` or `LIGHT` will start the game and proceed to the "Guessing" screen + * - The first card will be revealed and the player must now make a guess + * - A player can guess `Higher` by pressing the `LIGHT` button, and `Lower` by pressing the `ALARM` button + * - The status indicator will show the result of the guess: HI (Higher), LO (Lower), or == (Equal) + * - There are five guesses to make on each game screen, once the end of the screen is reached, a new screen + * will be started, with the last revealed card carried over + * - The number of completed screens is displayed in the top right (see Scoring) + * - If the player has guessed correctly, the score is updated and play continues (see Scoring) + * - If the player has guessed incorrectly, the status will change to GO (Game Over) + * - The current card will be revealed + * - Pressing `ALARM` or `LIGHT` will transition to the "Score" screen + * - If the game is won, the status indicator will display "WI" and the "Win" screen will be displayed + * - Pressing `ALARM` or `LIGHT` will transition to the "Score" screen + * - The status indicator will change to "SC" when the final score is displayed + * - The number of completed game screens will be displayed on using the first two digits + * - The number of correct guesses will be displayed using the final three digits + * - E.g. "13: 063" represents 13 completed screens, with 63 correct guesses + * - Pressing `ALARM` or `LIGHT` while on the "Score" screen will transition to back to the "Title" screen + * + * Scoring: + * - If the player guesses correctly (HI/LO) a point is gained + * - If the player guesses incorrectly the game ends + * - Unless the revealed card is equal (==) to the last card, in which case play continues, but no point is gained + * - If the player completes 40 screens full of cards, the game ends and a win screen is displayed + * + * Misc: + * The face tries to remain true to the spirit of using "cards"; to cope with the display limitations I've arrived at + * the following mapping of card values to screen display, but am open to better suggestions: + * + * | Cards | | + * |---------|--------------------------| + * | Value |2|3|4|5|6|7|8|9|10|J|Q|K|A| + * | Display |2|3|4|5|6|7|8|9| 0|-|=|≡|H| + * + * The following may more legible choice: + * | Cards | | + * |---------|--------------------------| + * | Value |2|3|4|5|6|7|8|9|10|J|Q|K|A| + * | Display |0|1|2|3|4|5|6|7|8 |9|-|=|≡| + * + * Future Ideas: + * - Add sounds + * - Save/Display high score + * - Add a "Win" animation + * - Consider using lap indicator for larger score limit */ typedef struct { @@ -52,4 +101,3 @@ void higher_lower_game_face_resign(movement_settings_t *settings, void *context) }) #endif // HIGHER_LOWER_GAME_FACE_H_ - From 43e94ca0f2317ec105d2638c9616ca1c9ef46375 Mon Sep 17 00:00:00 2001 From: Konrad Rieck Date: Wed, 9 Aug 2023 22:34:01 +0200 Subject: [PATCH 004/161] Watch face for tracking deadlines. You can enter and monitor up to four different deadlines by providing their respective date and time. The watch face displays the remaining time at matching granularity, ranging from years to seconds. --- movement/make/Makefile | 1 + movement/movement_faces.h | 1 + .../watch_faces/complication/deadline_face.c | 531 ++++++++++++++++++ .../watch_faces/complication/deadline_face.h | 61 ++ 4 files changed, 594 insertions(+) create mode 100644 movement/watch_faces/complication/deadline_face.c create mode 100644 movement/watch_faces/complication/deadline_face.h diff --git a/movement/make/Makefile b/movement/make/Makefile index 625c772..74c630b 100644 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -118,6 +118,7 @@ SRCS += \ ../watch_faces/complication/flashlight_face.c \ ../watch_faces/clock/decimal_time_face.c \ ../watch_faces/clock/wyoscan_face.c \ + ../watch_faces/complication/deadline_face.c # New watch faces go above this line. # Leave this line at the bottom of the file; it has all the targets for making your project. diff --git a/movement/movement_faces.h b/movement/movement_faces.h index ff34c06..b96bfbb 100644 --- a/movement/movement_faces.h +++ b/movement/movement_faces.h @@ -95,6 +95,7 @@ #include "flashlight_face.h" #include "decimal_time_face.h" #include "wyoscan_face.h" +#include "deadline_face.h" // New includes go above this line. #endif // MOVEMENT_FACES_H_ diff --git a/movement/watch_faces/complication/deadline_face.c b/movement/watch_faces/complication/deadline_face.c new file mode 100644 index 0000000..01ddc12 --- /dev/null +++ b/movement/watch_faces/complication/deadline_face.c @@ -0,0 +1,531 @@ +/* + * MIT License + * + * Copyright (c) 2023 Konrad Rieck + * + * 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. + */ + +/* + * # Deadline Face + * + * This is a watch face for tracking deadlines. It draws inspiration from + * other watch faces of the project but focuses on keeping track of + * deadlines. You can enter and monitor up to four different deadlines by + * providing their respective date and time. The face has two modes: + * *running mode* and *settings mode*. + * + * ## Running Mode + * + * When the watch face is activated, it defaults to running mode. The top + * right corner shows the current deadline number, and the main display + * presents the time left until the deadline. The format of the display + * varies depending on the remaining time. + * + * - When less than a day is left, the display shows the remaining hours, + * minutes, and seconds in the form `HH:MM:SS`. + * + * - When less than a month is left, the display shows the remaining days + * and hours in the form `DD:HH` with the unit `dy` for days. + * + * - When less than a year is left, the display shows the remaining months + * and days in the form `MM:DD` with the unit `mo` for months. + * + * - When more than a year is left, the years and months are displayed in + * the form `YY:MM` with the unit `yr` for years. + * + * - When a deadline has passed in the last 24 hours, the display shows + * `over` to indicate that the deadline has just recently been reached. + * + * - When no deadline is set for a particular slot, or if a deadline has + * already passed by more than 24 hours, `--:--` is displayed. + * + * The user can navigate in running mode using the following buttons: + * + * - The *alarm button* moves the next deadline. There are currently four + * slots available for deadlines. When the last slot has been reached, + * pressing the button moves to the first slot. + * + * - A *long press* on the *alarm button* activates settings mode and + * enables configuring the currently selected deadline. + * + * ## Settings Mode + * + * In settings mode, the currently selected slot for a deadline can be + * configured by providing the date and the time. Like running mode, the + * top right corner of the display indicates the current deadline number. + * The main display shows the date and, on the next page, the time to be + * configured. + * + * The user can use the following buttons in settings mode. + * + * - The *light button* navigates through the different date and time + * settings, going from year, month, day, hour, to minute. The selected + * position is blinking. + * + * - A *long press* on the light button resets the date and time to the next + * day at midnight. This is the default deadline. + * + * - The *alarm button* increments the currently selected position. A *long + * press* on the *alarm button* changes the value faster. + * + * - The *mode button* exists setting mode and returns to *running mode*. + * Here the selected deadline slot can be changed. + * + */ + +#include +#include +#include "deadline_face.h" +#include "watch.h" +#include "watch_utility.h" + +#define SETTINGS_NUM (5) +const char settings_titles[SETTINGS_NUM][3] = { "YR", "MO", "DA", "HR", "M1" }; + +static uint8_t tick_freq; + +/* Local functions */ +static void _running_init(movement_settings_t *settings, deadline_state_t * state); +static bool _running_loop(movement_event_t event, movement_settings_t *settings, void *context); +static void _running_display(movement_event_t event, movement_settings_t *settings, deadline_state_t * state); +static void _setting_init(movement_settings_t *settings, deadline_state_t * state); +static bool _setting_loop(movement_event_t event, movement_settings_t *settings, void *context); +static void _setting_display(movement_event_t event, movement_settings_t *settings, deadline_state_t * state, watch_date_time date); + +/* Utility functions */ +static void _increment_date(movement_settings_t *settings, deadline_state_t * state, watch_date_time date_time); +static inline int32_t _get_tz_offset(movement_settings_t *settings); +static inline void _change_tick_freq(uint8_t freq); +static inline bool _is_leap(int16_t y); +static inline int _days_in_month(int16_t mpnth, int16_t y); +static inline unsigned int _mod(int a, int b); +static inline void _beep_button(movement_settings_t *settings); +static inline void _beep_enable(movement_settings_t *settings); +static inline void _beep_disable(movement_settings_t *settings); +static inline void _reset_deadline(movement_settings_t *settings, deadline_state_t * state); + +/* Check for leap year */ +static inline bool _is_leap(int16_t y) +{ + y += 1900; + return !(y % 4) && ((y % 100) || !(y % 400)); +} + +/* Modulo function */ +static inline unsigned int _mod(int a, int b) +{ + int r = a % b; + return r < 0 ? r + b : r; +} + +/* Return days in month */ +static inline int _days_in_month(int16_t month, int16_t year) +{ + uint8_t days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + month = _mod(month - 1, 12); + + if (month == 1 && _is_leap(year)) { + return days[month] + 1; + } else { + return days[month]; + } +} + +/* Calculate time zone offset */ +static inline int32_t _get_tz_offset(movement_settings_t *settings) +{ + return movement_timezone_offsets[settings->bit.time_zone] * 60; +} + +/* Beep for a button press*/ +static inline void _beep_button(movement_settings_t *settings) +{ + // play a beep as confirmation for a button press (if applicable) + if (!settings->bit.button_should_sound) + return; + + watch_buzzer_play_note(BUZZER_NOTE_C7, 50); +} + +/* Beep for entering settings */ +static inline void _beep_enable(movement_settings_t *settings) +{ + if (!settings->bit.button_should_sound) + return; + + watch_buzzer_play_note(BUZZER_NOTE_G7, 50); + watch_buzzer_play_note(BUZZER_NOTE_REST, 75); + watch_buzzer_play_note(BUZZER_NOTE_C8, 75); +} + +/* Beep for leaving settings */ +static inline void _beep_disable(movement_settings_t *settings) +{ + if (!settings->bit.button_should_sound) + return; + + watch_buzzer_play_note(BUZZER_NOTE_C8, 50); + watch_buzzer_play_note(BUZZER_NOTE_REST, 75); + watch_buzzer_play_note(BUZZER_NOTE_G7, 75); +} + +/* Change tick frequency */ +static inline void _change_tick_freq(uint8_t freq) +{ + if (tick_freq != freq) { + movement_request_tick_frequency(freq); + tick_freq = freq; + } +} + +/* Reset deadline to tomorrow */ +static inline void _reset_deadline(movement_settings_t *settings, deadline_state_t *state) +{ + /* Get current time and reset hours/minutes/seconds */ + watch_date_time date_time = watch_rtc_get_date_time(); + date_time.unit.second = 0; + date_time.unit.minute = 0; + date_time.unit.hour = 0; + + /* Add 24 hours to obtain first second of tomorrow */ + uint32_t ts = watch_utility_date_time_to_unix_time(date_time, _get_tz_offset(settings)); + ts += 24 * 60 * 60; + + state->deadlines[state->current_index] = ts; +} + +/* Increment date in settings mode. Function taken from `set_time_face.c` */ +static void _increment_date(movement_settings_t *settings, deadline_state_t *state, watch_date_time date_time) +{ + const uint8_t days_in_month[12] = { 31, 28, 31, 30, 31, 30, 30, 31, 30, 31, 30, 31 }; + + switch (state->current_page) { + case 0: + /* Only 10 years covered. Fix this sometime next decade */ + date_time.unit.year = ((date_time.unit.year % 10) + 1); + break; + case 1: + date_time.unit.month = (date_time.unit.month % 12) + 1; + break; + case 2: + date_time.unit.day = date_time.unit.day + 1; + + /* Check for leap years */ + int8_t days = days_in_month[date_time.unit.month - 1]; + if (date_time.unit.month == 2 && _is_leap(date_time.unit.year)) + days++; + + if (date_time.unit.day > days) + date_time.unit.day = 1; + break; + case 3: + date_time.unit.hour = (date_time.unit.hour + 1) % 24; + break; + case 4: + date_time.unit.minute = (date_time.unit.minute + 1) % 60; + break; + } + + uint32_t ts = watch_utility_date_time_to_unix_time(date_time, _get_tz_offset(settings)); + state->deadlines[state->current_index] = ts; +} + +/* Update display in running mode */ +static void _running_display(movement_event_t event, movement_settings_t *settings, deadline_state_t *state) +{ + (void) event; + (void) settings; + + /* seconds, minutes, hours, days, months, years */ + int16_t unit[] = { 0, 0, 0, 0, 0, 0 }; + uint8_t i, range[] = { 60, 60, 24, 30, 12, 0 }; + char buf[16]; + + watch_date_time now = watch_rtc_get_date_time(); + uint32_t now_ts = watch_utility_date_time_to_unix_time(now, _get_tz_offset(settings)); + + /* Deadline expired */ + if (state->deadlines[state->current_index] < now_ts) { + if (state->deadlines[state->current_index] + 24 * 60 * 60 > now_ts) + sprintf(buf, "DL%2dOVER ", state->current_index + 1); + else + sprintf(buf, "DL%2d---- ", state->current_index + 1); + + //watch_clear_indicator(WATCH_INDICATOR_BELL); + watch_display_string(buf, 0); + return; + } + + /* Get date time structs */ + watch_date_time deadline = watch_utility_date_time_from_unix_time(state->deadlines[state->current_index], _get_tz_offset(settings) + ); + + /* Calculate naive difference of dates */ + unit[0] = deadline.unit.second - now.unit.second; + unit[1] = deadline.unit.minute - now.unit.minute; + unit[2] = deadline.unit.hour - now.unit.hour; + unit[3] = deadline.unit.day - now.unit.day; + unit[4] = deadline.unit.month - now.unit.month; + unit[5] = deadline.unit.year - now.unit.year; + + /* Correct errors of naive difference */ + for (i = 0; i < 6; i++) { + if (unit[i] < 0) { + /* Correct remaining units */ + if (i == 3) + unit[i] += _days_in_month(deadline.unit.month - 1, deadline.unit.year); + else + unit[i] += range[i]; + + /* Carry over change to next unit if non-zero */ + if (i < 5 && unit[i + 1] != 0) + unit[i + 1] -= 1; + } + } + + /* Set range */ + i = state->current_index + 1; + if (unit[5] > 0) { + /* years:months */ + sprintf(buf, "DL%2d%02d%02dYR", i, unit[5] % 100, unit[4] % 12); + } else if (unit[4] > 0) { + /* months:days */ + sprintf(buf, "DL%2d%02d%02dMO", i, (unit[5] * 12 + unit[4]) % 100, unit[3] % 32); + } else if (unit[3] > 0) { + /* days:hours */ + sprintf(buf, "DL%2d%02d%02ddY", i, unit[3] % 32, unit[2] % 24); + } else { + /* hours:minutes:seconds */ + sprintf(buf, "DL%2d%02d%02d%02d", i, unit[2] % 24, unit[1] % 60, unit[0] % 60); + } + + //watch_set_indicator(WATCH_INDICATOR_BELL); + watch_display_string(buf, 0); +} + +/* Init running mode */ +static void _running_init(movement_settings_t *settings, deadline_state_t *state) +{ + (void) settings; + (void) state; + + watch_clear_indicator(WATCH_INDICATOR_24H); + watch_clear_indicator(WATCH_INDICATOR_PM); + watch_set_colon(); +} + +/* Loop of running mode */ +static bool _running_loop(movement_event_t event, movement_settings_t *settings, void *context) +{ + deadline_state_t *state = (deadline_state_t *) context; + _running_display(event, settings, state); + + switch (event.event_type) { + case EVENT_ALARM_BUTTON_UP: + _beep_button(settings); + state->current_index = (state->current_index + 1) % DEADLINE_FACE_DATES; + _running_display(event, settings, state); + break; + case EVENT_ALARM_LONG_PRESS: + _beep_enable(settings); + _setting_init(settings, state); + state->mode = DEADLINE_FACE_SETTING; + break; + case EVENT_MODE_BUTTON_UP: + movement_move_to_next_face(); + return false; + case EVENT_TIMEOUT: + movement_move_to_face(0); + break; + case EVENT_LOW_ENERGY_UPDATE: + break; + default: + return movement_default_loop_handler(event, settings); + } + + /* Slow down frequency after first loop for snappiness */ + _change_tick_freq(1); + return true; +} + +/* Update display in settings mode */ +static void _setting_display(movement_event_t event, movement_settings_t *settings, deadline_state_t *state, watch_date_time date_time) +{ + char buf[11]; + + int i = state->current_index + 1; + if (state->current_page > 2) { + watch_set_colon(); + if (settings->bit.clock_mode_24h) { + watch_set_indicator(WATCH_INDICATOR_24H); + sprintf(buf, "%s%2d%2d%02d ", settings_titles[state->current_page], i, date_time.unit.hour, date_time.unit.minute); + } else { + sprintf(buf, "%s%2d%2d%02d ", settings_titles[state->current_page], i, (date_time.unit.hour % 12) ? (date_time.unit.hour % 12) : 12, + date_time.unit.minute); + if (date_time.unit.hour < 12) + watch_clear_indicator(WATCH_INDICATOR_PM); + else + watch_set_indicator(WATCH_INDICATOR_PM); + } + } else { + watch_clear_colon(); + watch_clear_indicator(WATCH_INDICATOR_24H); + watch_clear_indicator(WATCH_INDICATOR_PM); + sprintf(buf, "%s%2d%2d%02d%02d", settings_titles[state->current_page], i, date_time.unit.year + 20, date_time.unit.month, date_time.unit.day); + } + + /* Blink up the parameter we are setting */ + if (event.subsecond % 2) { + switch (state->current_page) { + case 0: + case 3: + buf[4] = buf[5] = ' '; + break; + case 1: + case 4: + buf[6] = buf[7] = ' '; + break; + case 2: + buf[8] = buf[9] = ' '; + break; + } + } + + watch_display_string(buf, 0); +} + +/* Init setting mode */ +static void _setting_init(movement_settings_t *settings, deadline_state_t *state) +{ + _change_tick_freq(4); + state->current_page = 0; + + /* Init fresh deadline to next day */ + if (state->deadlines[state->current_index] == 0) { + _reset_deadline(settings, state); + } +} + +/* Loop of setting mode */ +static bool _setting_loop(movement_event_t event, movement_settings_t *settings, void *context) +{ + deadline_state_t *state = (deadline_state_t *) context; + watch_date_time date_time; + date_time = watch_utility_date_time_from_unix_time(state->deadlines[state->current_index], _get_tz_offset(settings)); + + _setting_display(event, settings, state, date_time); + + switch (event.event_type) { + case EVENT_TICK: + if (tick_freq == 8) { + if (watch_get_pin_level(BTN_ALARM)) { + _increment_date(settings, state, date_time); + _setting_display(event, settings, state, date_time); + } else { + _change_tick_freq(4); + } + } + break; + case EVENT_ALARM_LONG_PRESS: + _change_tick_freq(8); + break; + case EVENT_ALARM_LONG_UP: + _change_tick_freq(4); + break; + case EVENT_LIGHT_LONG_PRESS: + _beep_button(settings); + _reset_deadline(settings, state); + break; + case EVENT_LIGHT_BUTTON_DOWN: + break; + case EVENT_LIGHT_BUTTON_UP: + state->current_page = (state->current_page + 1) % SETTINGS_NUM; + _setting_display(event, settings, state, date_time); + break; + case EVENT_ALARM_BUTTON_UP: + _change_tick_freq(4); + _increment_date(settings, state, date_time); + _setting_display(event, settings, state, date_time); + break; + case EVENT_TIMEOUT: + _beep_button(settings); + _change_tick_freq(1); + movement_move_to_face(0); + break; + case EVENT_MODE_BUTTON_UP: + _beep_disable(settings); + _running_init(settings, state); + state->mode = DEADLINE_FACE_RUNNING; + break; + default: + return movement_default_loop_handler(event, settings); + } + + return true; +} + +/* Setup face */ +void deadline_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr) +{ + (void) settings; + (void) watch_face_index; + if (*context_ptr != NULL) + return; /* Skip setup if context available */ + + *context_ptr = malloc(sizeof(deadline_state_t)); + memset(*context_ptr, 0, sizeof(deadline_state_t)); +} + +/* Activate face */ +void deadline_face_activate(movement_settings_t *settings, void *context) +{ + (void) settings; + deadline_state_t *state = (deadline_state_t *) context; + + /* Set display options */ + _running_init(settings, state); + state->mode = DEADLINE_FACE_RUNNING; +} + +/* Loop face */ +bool deadline_face_loop(movement_event_t event, movement_settings_t *settings, void *context) +{ + (void) settings; + deadline_state_t *state = (deadline_state_t *) context; + switch (state->mode) { + case DEADLINE_FACE_SETTING: + _setting_loop(event, settings, context); + break; + default: + case DEADLINE_FACE_RUNNING: + _running_loop(event, settings, context); + break; + } + + return true; +} + +/* Resign face */ +void deadline_face_resign(movement_settings_t *settings, void *context) +{ + (void) settings; + (void) context; +} diff --git a/movement/watch_faces/complication/deadline_face.h b/movement/watch_faces/complication/deadline_face.h new file mode 100644 index 0000000..b560ab2 --- /dev/null +++ b/movement/watch_faces/complication/deadline_face.h @@ -0,0 +1,61 @@ +/* + * MIT License + * + * Copyright (c) 2023 Konrad Rieck + * + * 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 DEADLINE_FACE_H_ +#define DEADLINE_FACE_H_ + +#include "movement.h" + +/* Modes of face */ +typedef enum { + DEADLINE_FACE_RUNNING = 0, + DEADLINE_FACE_SETTING +} deadline_mode_t; + +/* Number of deadline dates */ +#define DEADLINE_FACE_DATES (4) + +/* Deadline configuration */ +typedef struct { + deadline_mode_t mode:1; + uint8_t current_page:3; + uint8_t current_index:2; + uint32_t deadlines[DEADLINE_FACE_DATES]; +} deadline_state_t; + +void deadline_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr); +void deadline_face_activate(movement_settings_t *settings, void *context); +bool deadline_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void deadline_face_resign(movement_settings_t *settings, void *context); + +#define deadline_face ((const watch_face_t){ \ + deadline_face_setup, \ + deadline_face_activate, \ + deadline_face_loop, \ + deadline_face_resign, \ + NULL, \ +}) + +#endif // DEADLINE_FACE_H_ From f3c28ede9613dcbc9ad3616634ce98c592d0a8ba Mon Sep 17 00:00:00 2001 From: "R. Alex Barbieri" <> Date: Sat, 2 Sep 2023 14:52:37 -0500 Subject: [PATCH 005/161] add a manual dst toggle Uses a simplistic set of jump tables to toggle daylight savings on and off. --- movement/movement.c | 107 +++++++++++++++++- movement/movement.h | 5 +- movement/watch_faces/settings/set_time_face.c | 20 +++- 3 files changed, 125 insertions(+), 7 deletions(-) diff --git a/movement/movement.c b/movement/movement.c index 0a5ac2e..cf2eb27 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -122,6 +122,107 @@ const int16_t movement_timezone_offsets[] = { -60, // 40 : -1:00:00 (Azores Standard Time) }; +/* These are approximate equivalent DST timezones for each + * timezone in the offset table. Unlike the full tzinfo file, + * the time-offsets used above are incomplete, so there are + * cases below where an approximate DST timezone is proposed + * for a timezone where no one observes DST, and cases + * where we can't propose an equivaent DST timezone since + * there isn't an appropriate one in the offset table. + * + * However, this should be good enough for anyone living in + * a DST-observing region to manually toggle DST without + * having to separately change the hour and timezone info + * in the time set face. + */ +const uint8_t movement_dst_jump_table[] = { + 1, // 0 UTC + 1 = CET + 2, // 1 CET + 1 = SAST + 3, // 2 SAST + 1 = AST + 5, // 3 AST + 1 = GST + 6, // 4 IST + 1 = AT + 7, // 5 GST + 1 = PST + 8, // 6 AT + 1 = IST + 10, // 7 PST + 1 = KT + 11, // 8 IST + 1 = MT + 9, // 9 Nepal has no equivalent DST timezone, but they don't observe DST anyway + 12, // 10 KT + 1 = TST + 11, // 11 Myanmar has no equivalent DST timezone, but they don't observe DST anyway + 13, // 12 TST + 1 = CST + 15, // 13 CST + 1 = JST + 14, // 14 ACWST has no equivalent DST timezone, but they don't observe DST anyway + 17, // 15 JST + 1 = AEST + 18, // 16 ACST + 1 = LHST + 19, // 17 AEST + 1 = SIT + 18, // 18 LHST has no equivalent DST timezone, but they don't observe DST anyway + 20, // 19 SIT + 1 = NZST + 22, // 20 NZST + 1 = TT + 23, // 21 CST + 1 = CDT + 24, // 22 TT + 1 = LIT + 23, // 23 CDT is already a daylight timezone + 24, // 24 LIT has no equivalent DST timezone, but they don't observe DST anyway + 26, // 25 BIT + 1 = NT + 27, // 26 NT + 1 = HAST + 29, // 27 HAST + 1 = AST + 28, // 28 MIT has no equivalent DST timezone, but they don't observe DST anyway + 30, // 29 AST + 1 = PST + 31, // 30 PST + 1 = MST + 32, // 31 MST + 1 = CST + 33, // 32 CST + 1 = EST + 35, // 33 EST + 1 = AST + 36, // 34 VST + 1 = NST + 37, // 35 AST + 1 = BT + 38, // 36 NST + 1 = NDT + 39, // 37 BT + 1 = 39 + 38, // 38 NDT is already a daylight timezone + 40, // 39 FNT + 1 = AST + 0 // 40 AST + 1 = UTC +}; + +const uint8_t movement_dst_inverse_jump_table[] = { + 40, // 0 + 0, // 1 + 1, // 2 + 2, // 3 + 4, // 4 + 3, // 5 + 4, // 6 + 5, // 7 + 6, // 8 + 9, // 9 + 7, // 10 + 8, // 11 + 10, // 12 + 12, // 13 + 14, // 14 + 13, // 15 + 16, // 16 + 15, // 17 + 16, // 18 + 17, // 19 + 19, // 20 + 21, // 21 + 20, // 22 + 21, // 23 + 24, // 24 + 25, // 25 + 25, // 26 + 26, // 27 + 28, // 28 + 27, // 29 + 29, // 30 + 30, // 31 + 31, // 32 + 32, // 33 + 34, // 34 + 33, // 35 + 34, // 36 + 35, // 37 + 36, // 38 + 37, // 39 + 39 // 40 +}; + const char movement_valid_position_0_chars[] = " AaBbCcDdEeFGgHhIiJKLMNnOoPQrSTtUuWXYZ-='+\\/0123456789"; const char movement_valid_position_1_chars[] = " ABCDEFHlJLNORTtUX-='01378"; @@ -613,13 +714,13 @@ void cb_fast_tick(void) { // Notice: is it possible that two or more buttons have an identical timestamp? In this case // only one of these buttons would receive the long press event. Don't bother for now... if (movement_state.light_down_timestamp > 0) - if (movement_state.fast_ticks - movement_state.light_down_timestamp == MOVEMENT_LONG_PRESS_TICKS + 1) + if (movement_state.fast_ticks - movement_state.light_down_timestamp == MOVEMENT_LONG_PRESS_TICKS + 1) event.event_type = EVENT_LIGHT_LONG_PRESS; if (movement_state.mode_down_timestamp > 0) - if (movement_state.fast_ticks - movement_state.mode_down_timestamp == MOVEMENT_LONG_PRESS_TICKS + 1) + if (movement_state.fast_ticks - movement_state.mode_down_timestamp == MOVEMENT_LONG_PRESS_TICKS + 1) event.event_type = EVENT_MODE_LONG_PRESS; if (movement_state.alarm_down_timestamp > 0) - if (movement_state.fast_ticks - movement_state.alarm_down_timestamp == MOVEMENT_LONG_PRESS_TICKS + 1) + if (movement_state.fast_ticks - movement_state.alarm_down_timestamp == MOVEMENT_LONG_PRESS_TICKS + 1) event.event_type = EVENT_ALARM_LONG_PRESS; // this is just a fail-safe; fast tick should be disabled as soon as the button is up, the LED times out, and/or the alarm finishes. // but if for whatever reason it isn't, this forces the fast tick off after 20 seconds. diff --git a/movement/movement.h b/movement/movement.h index 66bf6af..d61d510 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -62,7 +62,8 @@ typedef union { bool clock_mode_24h : 1; // indicates whether clock should use 12 or 24 hour mode. bool use_imperial_units : 1; // indicates whether to use metric units (the default) or imperial. bool alarm_enabled : 1; // indicates whether there is at least one alarm enabled. - uint8_t reserved : 6; // room for more preferences if needed. + bool dst_active : 1; // indicates whether daylight savings time is active + uint8_t reserved : 5; // room for more preferences if needed. } bit; uint32_t reg; } movement_settings_t; @@ -128,6 +129,8 @@ typedef struct { } movement_event_t; extern const int16_t movement_timezone_offsets[]; +extern const uint8_t movement_dst_jump_table[]; +extern const uint8_t movement_dst_inverse_jump_table[]; extern const char movement_valid_position_0_chars[]; extern const char movement_valid_position_1_chars[]; diff --git a/movement/watch_faces/settings/set_time_face.c b/movement/watch_faces/settings/set_time_face.c index 84a4d18..16011e3 100644 --- a/movement/watch_faces/settings/set_time_face.c +++ b/movement/watch_faces/settings/set_time_face.c @@ -26,8 +26,8 @@ #include "set_time_face.h" #include "watch.h" -#define SET_TIME_FACE_NUM_SETTINGS (7) -const char set_time_face_titles[SET_TIME_FACE_NUM_SETTINGS][3] = {"HR", "M1", "SE", "YR", "MO", "DA", "ZO"}; +#define SET_TIME_FACE_NUM_SETTINGS (8) +const char set_time_face_titles[SET_TIME_FACE_NUM_SETTINGS][3] = {"HR", "M1", "SE", "YR", "MO", "DA", "ZO", "DS"}; static bool _quick_ticks_running; @@ -66,6 +66,16 @@ static void _handle_alarm_button(movement_settings_t *settings, watch_date_time settings->bit.time_zone++; if (settings->bit.time_zone > 40) settings->bit.time_zone = 0; break; + case 7: // daylight savings time + if (settings->bit.dst_active) { // deactivate DST + date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24; + settings->bit.time_zone = movement_dst_inverse_jump_table[settings->bit.time_zone]; + } else { // activate DST + date_time.unit.hour = (date_time.unit.hour + 1) % 24; + settings->bit.time_zone = movement_dst_jump_table[settings->bit.time_zone]; + } + settings->bit.dst_active = !settings->bit.dst_active; + break; } watch_rtc_set_date_time(date_time); } @@ -146,7 +156,7 @@ bool set_time_face_loop(movement_event_t event, movement_settings_t *settings, v watch_clear_indicator(WATCH_INDICATOR_24H); watch_clear_indicator(WATCH_INDICATOR_PM); sprintf(buf, "%s %2d%02d%02d", set_time_face_titles[current_page], date_time.unit.year + 20, date_time.unit.month, date_time.unit.day); - } else { + } else if (current_page < 7) { // zone if (event.subsecond % 2) { watch_clear_colon(); sprintf(buf, "%s ", set_time_face_titles[current_page]); @@ -154,6 +164,10 @@ bool set_time_face_loop(movement_event_t event, movement_settings_t *settings, v watch_set_colon(); sprintf(buf, "%s %3d%02d ", set_time_face_titles[current_page], (int8_t) (movement_timezone_offsets[settings->bit.time_zone] / 60), (int8_t) (movement_timezone_offsets[settings->bit.time_zone] % 60) * (movement_timezone_offsets[settings->bit.time_zone] < 0 ? -1 : 1)); } + } else { // daylight savings + watch_clear_colon(); + if (settings->bit.dst_active) sprintf(buf, "%s dsT y", set_time_face_titles[current_page]); + else sprintf(buf, "%s dsT n", set_time_face_titles[current_page]); } // blink up the parameter we're setting From 56f76b8c6a74ed67f2f82f803a7bae89643d0696 Mon Sep 17 00:00:00 2001 From: CarpeNoctem Date: Wed, 6 Sep 2023 00:20:55 +1000 Subject: [PATCH 006/161] Initial commit of French Revolutionary (Decimal) Time face. (french_revolutionary_face) --- movement/make/Makefile | 1 + movement/movement_faces.h | 1 + .../clock/french_revolutionary_face.c | 243 ++++++++++++++++++ .../clock/french_revolutionary_face.h | 84 ++++++ 4 files changed, 329 insertions(+) create mode 100644 movement/watch_faces/clock/french_revolutionary_face.c create mode 100644 movement/watch_faces/clock/french_revolutionary_face.h diff --git a/movement/make/Makefile b/movement/make/Makefile index 625c772..1d8ccfe 100644 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -118,6 +118,7 @@ SRCS += \ ../watch_faces/complication/flashlight_face.c \ ../watch_faces/clock/decimal_time_face.c \ ../watch_faces/clock/wyoscan_face.c \ + ../watch_faces/clock/french_revolutionary_face.c \ # New watch faces go above this line. # Leave this line at the bottom of the file; it has all the targets for making your project. diff --git a/movement/movement_faces.h b/movement/movement_faces.h index ff34c06..9ee8737 100644 --- a/movement/movement_faces.h +++ b/movement/movement_faces.h @@ -95,6 +95,7 @@ #include "flashlight_face.h" #include "decimal_time_face.h" #include "wyoscan_face.h" +#include "french_revolutionary_face.h" // New includes go above this line. #endif // MOVEMENT_FACES_H_ diff --git a/movement/watch_faces/clock/french_revolutionary_face.c b/movement/watch_faces/clock/french_revolutionary_face.c new file mode 100644 index 0000000..9801b9c --- /dev/null +++ b/movement/watch_faces/clock/french_revolutionary_face.c @@ -0,0 +1,243 @@ +/* + * MIT License + * + * Copyright (c) 2023 CarpeNoctem + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "french_revolutionary_face.h" + +void french_revolutionary_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { + (void) settings; + if (*context_ptr == NULL) { + *context_ptr = malloc(sizeof(french_revolutionary_state_t)); + memset(*context_ptr, 0, sizeof(french_revolutionary_state_t)); + // Do any one-time tasks in here; the inside of this conditional happens only at boot. + french_revolutionary_state_t *state = (french_revolutionary_state_t *)*context_ptr; + state->use_am_pm = false; + state->show_seconds = true; + state->display_type = 0; + state->colon_set_after_splash = false; + } + // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep. +} + +void french_revolutionary_face_activate(movement_settings_t *settings, void *context) { + (void) settings; + french_revolutionary_state_t *state = (french_revolutionary_state_t *)context; + + // Handle any tasks related to your watch face coming on screen. + state->colon_set_after_splash = false; +} + +bool french_revolutionary_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { + french_revolutionary_state_t *state = (french_revolutionary_state_t *)context; + + char buf[11]; + watch_date_time date_time; + fr_decimal_time decimal_time; + + switch (event.event_type) { + case EVENT_ACTIVATE: + // Initial UI - Show a quick "splash screen" + watch_clear_display(); + watch_display_string("FR dECimL", 0); + break; + case EVENT_TICK: + case EVENT_LOW_ENERGY_UPDATE: + + date_time = watch_rtc_get_date_time(); + + decimal_time = get_decimal_time(&date_time); + + set_display_buffer(buf, state, &decimal_time, &date_time); + + // If we're in low-energy mode, don't write out the seconds. Also start the LE tick animation if it's not already going. + if (event.event_type == EVENT_LOW_ENERGY_UPDATE) { + buf[8] = ' '; + buf[9] = ' '; + if (!watch_tick_animation_is_running()) { watch_start_tick_animation(500); } + } + + // Update the display with our decimal time + watch_display_string(buf, 0); + + // Oh, and a one-off to set the colon after the "splash screen" + if (!state->colon_set_after_splash) { + watch_set_colon(); + state->colon_set_after_splash = true; + } + break; + case EVENT_ALARM_BUTTON_UP: + state->display_type += 1 ; // cycle through the display types + if (state->display_type > 2) { state->display_type = 0; } // but return to 0 after 2 + break; + case EVENT_ALARM_LONG_PRESS: + // I originally had chiming on the decimal-hour enabled, and this would enable/disable that chime, just like on + // the simple clock and decimal time faces. But because decimal seconds don't always line up with normal seconds, + // I assume the (decimal-)hourly chime could sometimes be missed. Additionally, I need this button for other purposes, + // now that I added seconds on/off toggle and upper normal-time with the ability to toggle that between 12/24hr format. + state->show_seconds = !state->show_seconds; + if (!state->show_seconds) { watch_display_string(" ", 8); } + else { watch_display_string("--", 8); } + break; + case EVENT_LIGHT_LONG_PRESS: + // In case anyone really wants that upper time in 12-hour format. I thought about using the global setting (settings->bit.clock_mode_24h) + // for this preference, but thought someone who prefers 12-hour format normally, might prefer 24hr when compared to a 10hr decimal day, + // so this is separate for now. + state->use_am_pm = !state->use_am_pm; + if (state->use_am_pm) { + watch_clear_indicator(WATCH_INDICATOR_24H); + if (date_time.unit.hour < 12) { watch_clear_indicator(WATCH_INDICATOR_PM); } + else { watch_set_indicator(WATCH_INDICATOR_PM); } + } else { + watch_clear_indicator(WATCH_INDICATOR_PM); + watch_set_indicator(WATCH_INDICATOR_24H); + } + break; + default: + // Movement's default loop handler will step in for any cases you don't handle above: + // * EVENT_LIGHT_BUTTON_DOWN lights the LED + // * EVENT_MODE_BUTTON_UP moves to the next watch face in the list + // * EVENT_MODE_LONG_PRESS returns to the first watch face (or skips to the secondary watch face, if configured) + // You can override any of these behaviors by adding a case for these events to this switch statement. + return movement_default_loop_handler(event, settings); + } + + // return true if the watch can enter standby mode. Generally speaking, you should always return true. + // Exceptions: + // * If you are displaying a color using the low-level watch_set_led_color function, you should return false. + // * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false. + // Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or + // movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions. + return true; +} + +void french_revolutionary_face_resign(movement_settings_t *settings, void *context) { + (void) settings; + (void) context; + + // handle any cleanup before your watch face goes off-screen. +} + +// Calculate decimal time from normal (24hr) time +fr_decimal_time get_decimal_time(watch_date_time *date_time) { + uint32_t current_24hr_secs, current_decimal_seconds; + fr_decimal_time decimal_time; + // Current 24-hr time in seconds (There are 86400 of these in a day.) + current_24hr_secs = date_time->unit.hour * 3600 + date_time->unit.minute * 60 + date_time->unit.second; + + // Current Decimal Time in seconds. There are 100000 seconds in a 10-hr decimal-time day. + // current_decimal_seconds = current_24hr_seconds * 100000 / 86400, or = current_24_seconds * 1000 / 864; + // By chopping the extra zeros off the end, we can use uint32 instead of uint64. + current_decimal_seconds = current_24hr_secs * 1000 / 864; + + decimal_time.hour = current_decimal_seconds / 10000; + // Remove the hours from total seconds and keep the remainder for below. + current_decimal_seconds = current_decimal_seconds - decimal_time.hour * 10000; + + decimal_time.minute = current_decimal_seconds / 100; + // Remove the minutes from total seconds and keep the remaining seconds + // Note: I think I used an extra seconds variable here because sprintf or movement weren't liking a uint32... + decimal_time.second = current_decimal_seconds - decimal_time.hour * 100; + return decimal_time; +} + +// Fills in the display buffer, depending on the currently-selected display option (and sub-options): +// - Decimal-time only +// - Decimal-time with date in top-right +// - Decimal-time with normal time in the top (minutes first, then hours, due to display limitations) +// Note: There is some power-saving stuff that simple clock does here around not redrawing characters that haven't changed, but we're not doing that here. +// I'll try to add that optimization could be added in a future commit. +void set_display_buffer(char *buf, french_revolutionary_state_t *state, fr_decimal_time *decimal_time, watch_date_time *date_time) { + switch (state->display_type) { + // Decimal time only + case 0: + // Originally I had the day slot set to "FR" (French Revolutionary time), but my brain kept thinking "Friday" whenever I saw it, + // so I changed it to dT (Decimal Time) to avoid that confusion. Apologies to anyone who has the other decimal_time face and this one + // installed concurrently. Maybe the splash screen will help a little. + sprintf( buf, "dT %2d%02d%02d", decimal_time->hour, decimal_time->minute, decimal_time->second ); + watch_clear_indicator(WATCH_INDICATOR_PM); + watch_clear_indicator(WATCH_INDICATOR_24H); + break; + // Decimal time and date + case 1: + sprintf( buf, "dT%2d%2d%02d%02d", date_time->unit.day, decimal_time->hour, decimal_time->minute, decimal_time->second ); + watch_clear_indicator(WATCH_INDICATOR_PM); + watch_clear_indicator(WATCH_INDICATOR_24H); + break; + // Decimal time on bottom, normal time above + case 2: + if (state->use_am_pm) { + // if we are in 12 hour mode, do some cleanup. + watch_clear_indicator(WATCH_INDICATOR_24H); + if (date_time->unit.hour < 12) { + watch_clear_indicator(WATCH_INDICATOR_PM); + } else { + watch_set_indicator(WATCH_INDICATOR_PM); + } + date_time->unit.hour %= 12; + if (date_time->unit.hour == 0) date_time->unit.hour = 12; + } else { + watch_clear_indicator(WATCH_INDICATOR_PM); + watch_set_indicator(WATCH_INDICATOR_24H); + } + // Note, the date digits don't display a leading zero well, so we don't use it. + sprintf( buf, "%02d%2d%2d%02d%02d", date_time->unit.minute, date_time->unit.hour, decimal_time->hour, decimal_time->minute, decimal_time->second ); + + // Make the second character of the Day area more readable + buf[1] = fix_character_one(buf[1]); + break; + } + // Finally, if show_seconds is disabled, trim those off. + if (!state->show_seconds) { + buf[8] = ' '; + buf[9] = ' '; + } +} + +// Sadly, the second character of the Day field cannot show all numbers, so we make some replacements. +// See https://www.sensorwatch.net/docs/wig/display/#limitations-of-the-weekday-digits +char fix_character_one(char digit) { + char return_char = digit; // We don't need to update this for 0, 1, 3, 7 and 8. + switch(digit) { + case '2': + // Roman numeral / tally representation of 2 + return_char = '|'; // Thanks, Joey, for already having this in the character set. + break; + case '4': + // Looks almost like a 4 - just missing the top-left segment. + // 0b01000110 + return_char = '&'; // Slight hack - I want 0b01000110, but 0b01000100 is already in the character set and will do, since B and C segments are linked in this position. + break; + case '5': + return_char = 'F'; // F for Five + break; + case '6': + return_char = 'E'; // Looks almost like a 6 - just missing the bottom-right segment. Not super happy with it, but liked it best of the options I tried. + break; + case '9': + return_char = 'N'; // N for Nine + break; + } + return return_char; +} \ No newline at end of file diff --git a/movement/watch_faces/clock/french_revolutionary_face.h b/movement/watch_faces/clock/french_revolutionary_face.h new file mode 100644 index 0000000..f18bb1f --- /dev/null +++ b/movement/watch_faces/clock/french_revolutionary_face.h @@ -0,0 +1,84 @@ +/* + * MIT License + * + * Copyright (c) 2023 CarpeNoctem + * + * 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 FRENCH_REVOLUTIONARY_FACE_H_ +#define FRENCH_REVOLUTIONARY_FACE_H_ + +#include "movement.h" + +/* + * French Revolutionary Decimal Time + * + * Similar to the Decimal Time face, but with the day divided into ten hours instead of twenty four. + * Each hour is divided into one hundred minutes, and those minutes are divided into 100 seconds. + * I came across this one the Svalbard watch site here: https://svalbard.watch/pages/about_decimal_time.html + * More info here as well: https://en.wikipedia.org/wiki/Decimal_time + * + * By default, the face just displays the current decimal time. Pressing the alarm button will toggle through other display options: + * 1) Just decimal time (with dT indicator at top) + * 2) Decimal time, with dT indicator and date above. + * 3) Decimal time, with 24-hr time above (where Day and Date would normally be displayed), BUT minutes first then hours. + * Sadly, the first character of the date area only goes up to 3 (see https://www.sensorwatch.net/docs/wig/display/#the-day-digits) + * I was going to begrudgindly leave this display option out when I realized that, but thought it would be better to have this backwards + * representation of the "normal" time than not at all. + * + * A long-press of the light button will toggle the upper time between 12-hr AM/PM and 24-hr mode. I thought of reading the main setting for this, + * but thought that a person could normally prefer 12hr time, but next to a 10hr day want to see the normal time in the 24hr format. + * + * A long-press of the alarm button will toggle the seconds off and on. + * + */ + +typedef struct { + bool use_am_pm; // Use 12-hr AM/PM for upper display instead of 24-hr? (Default is 24-hr) + bool show_seconds; + bool colon_set_after_splash; + uint8_t display_type : 2; +} french_revolutionary_state_t; + +typedef struct { + uint8_t second : 6; // 0-99 + uint8_t minute : 6; // 0-99 + uint8_t hour : 5; // 0-10 +} fr_decimal_time; + +void french_revolutionary_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); +void french_revolutionary_face_activate(movement_settings_t *settings, void *context); +bool french_revolutionary_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void french_revolutionary_face_resign(movement_settings_t *settings, void *context); +char fix_character_one(char digit); +fr_decimal_time get_decimal_time(watch_date_time *date_time); +void set_display_buffer(char *buf, french_revolutionary_state_t *state, fr_decimal_time *decimal_time, watch_date_time *date_time); + + +#define french_revolutionary_face ((const watch_face_t){ \ + french_revolutionary_face_setup, \ + french_revolutionary_face_activate, \ + french_revolutionary_face_loop, \ + french_revolutionary_face_resign, \ + NULL, \ +}) + +#endif // FRENCH_REVOLUTIONARY_FACE_H_ + From 82ed355aba8a367765c1d4ae4dd2b052777910b7 Mon Sep 17 00:00:00 2001 From: CarpeNoctem Date: Wed, 6 Sep 2023 01:21:26 +1000 Subject: [PATCH 007/161] fix two-part bug with decimal seconds introduced during big refactor. --- movement/watch_faces/clock/french_revolutionary_face.c | 4 ++-- movement/watch_faces/clock/french_revolutionary_face.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/movement/watch_faces/clock/french_revolutionary_face.c b/movement/watch_faces/clock/french_revolutionary_face.c index 9801b9c..519cfc3 100644 --- a/movement/watch_faces/clock/french_revolutionary_face.c +++ b/movement/watch_faces/clock/french_revolutionary_face.c @@ -158,7 +158,7 @@ fr_decimal_time get_decimal_time(watch_date_time *date_time) { decimal_time.minute = current_decimal_seconds / 100; // Remove the minutes from total seconds and keep the remaining seconds // Note: I think I used an extra seconds variable here because sprintf or movement weren't liking a uint32... - decimal_time.second = current_decimal_seconds - decimal_time.hour * 100; + decimal_time.second = current_decimal_seconds - decimal_time.minute * 100; return decimal_time; } @@ -166,7 +166,7 @@ fr_decimal_time get_decimal_time(watch_date_time *date_time) { // - Decimal-time only // - Decimal-time with date in top-right // - Decimal-time with normal time in the top (minutes first, then hours, due to display limitations) -// Note: There is some power-saving stuff that simple clock does here around not redrawing characters that haven't changed, but we're not doing that here. +// TODO: There is some power-saving stuff that simple clock does here around not redrawing characters that haven't changed, but we're not doing that here. // I'll try to add that optimization could be added in a future commit. void set_display_buffer(char *buf, french_revolutionary_state_t *state, fr_decimal_time *decimal_time, watch_date_time *date_time) { switch (state->display_type) { diff --git a/movement/watch_faces/clock/french_revolutionary_face.h b/movement/watch_faces/clock/french_revolutionary_face.h index f18bb1f..294f422 100644 --- a/movement/watch_faces/clock/french_revolutionary_face.h +++ b/movement/watch_faces/clock/french_revolutionary_face.h @@ -58,8 +58,8 @@ typedef struct { } french_revolutionary_state_t; typedef struct { - uint8_t second : 6; // 0-99 - uint8_t minute : 6; // 0-99 + uint8_t second : 8; // 0-99 + uint8_t minute : 8; // 0-99 uint8_t hour : 5; // 0-10 } fr_decimal_time; From 57e9f11a0c898e6d174d71e33f1b32efb904eb2b Mon Sep 17 00:00:00 2001 From: Dennisman219 Date: Wed, 27 Sep 2023 22:14:19 +0200 Subject: [PATCH 008/161] Minimal clock face --- movement/make/Makefile | 1 + movement/movement_faces.h | 1 + .../watch_faces/clock/minimal_clock_face.c | 115 ++++++++++++++++++ .../watch_faces/clock/minimal_clock_face.h | 56 +++++++++ 4 files changed, 173 insertions(+) create mode 100644 movement/watch_faces/clock/minimal_clock_face.c create mode 100644 movement/watch_faces/clock/minimal_clock_face.h diff --git a/movement/make/Makefile b/movement/make/Makefile index 625c772..56c21a6 100644 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -118,6 +118,7 @@ SRCS += \ ../watch_faces/complication/flashlight_face.c \ ../watch_faces/clock/decimal_time_face.c \ ../watch_faces/clock/wyoscan_face.c \ + ../watch_faces/clock/minimal_clock_face.c \ # New watch faces go above this line. # Leave this line at the bottom of the file; it has all the targets for making your project. diff --git a/movement/movement_faces.h b/movement/movement_faces.h index ff34c06..92b82fb 100644 --- a/movement/movement_faces.h +++ b/movement/movement_faces.h @@ -95,6 +95,7 @@ #include "flashlight_face.h" #include "decimal_time_face.h" #include "wyoscan_face.h" +#include "minimal_clock_face.h" // New includes go above this line. #endif // MOVEMENT_FACES_H_ diff --git a/movement/watch_faces/clock/minimal_clock_face.c b/movement/watch_faces/clock/minimal_clock_face.c new file mode 100644 index 0000000..195a3ee --- /dev/null +++ b/movement/watch_faces/clock/minimal_clock_face.c @@ -0,0 +1,115 @@ +/* + * MIT License + * + * Copyright (c) 2023 Dennisman219 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "minimal_clock_face.h" + +static void _minimal_clock_face_update_display(movement_settings_t *settings) { + watch_date_time date_time = watch_rtc_get_date_time(); + char buffer[11]; + + if (!settings->bit.clock_mode_24h) { + date_time.unit.hour %= 12; + } + + sprintf(buffer, "%2d%02d ", date_time.unit.hour, date_time.unit.minute); + watch_display_string(buffer, 4); +} + +void minimal_clock_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { + (void) settings; + (void) watch_face_index; + if (*context_ptr == NULL) { + *context_ptr = malloc(sizeof(minimal_clock_state_t)); + memset(*context_ptr, 0, sizeof(minimal_clock_state_t)); + // Do any one-time tasks in here; the inside of this conditional happens only at boot. + } + // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep. +} + +void minimal_clock_face_activate(movement_settings_t *settings, void *context) { + (void) settings; + (void) context; + // Handle any tasks related to your watch face coming on screen. + watch_set_colon(); +} + +bool minimal_clock_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { + (void) context; + + switch (event.event_type) { + case EVENT_ACTIVATE: + // Show your initial UI here. + _minimal_clock_face_update_display(settings); + break; + case EVENT_TICK: + // If needed, update your display here. + _minimal_clock_face_update_display(settings); + break; + case EVENT_LIGHT_BUTTON_UP: + // You can use the Light button for your own purposes. Note that by default, Movement will also + // illuminate the LED in response to EVENT_LIGHT_BUTTON_DOWN; to suppress that behavior, add an + // empty case for EVENT_LIGHT_BUTTON_DOWN. + break; + case EVENT_ALARM_BUTTON_UP: + // Just in case you have need for another button. + break; + case EVENT_TIMEOUT: + // Your watch face will receive this event after a period of inactivity. If it makes sense to resign, + // you may uncomment this line to move back to the first watch face in the list: + // movement_move_to_face(0); + break; + case EVENT_LOW_ENERGY_UPDATE: + // If you did not resign in EVENT_TIMEOUT, you can use this event to update the display once a minute. + // Avoid displaying fast-updating values like seconds, since the display won't update again for 60 seconds. + // You should also consider starting the tick animation, to show the wearer that this is sleep mode: + // watch_start_tick_animation(500); + _minimal_clock_face_update_display(settings); + break; + default: + // Movement's default loop handler will step in for any cases you don't handle above: + // * EVENT_LIGHT_BUTTON_DOWN lights the LED + // * EVENT_MODE_BUTTON_UP moves to the next watch face in the list + // * EVENT_MODE_LONG_PRESS returns to the first watch face (or skips to the secondary watch face, if configured) + // You can override any of these behaviors by adding a case for these events to this switch statement. + return movement_default_loop_handler(event, settings); + } + + // return true if the watch can enter standby mode. Generally speaking, you should always return true. + // Exceptions: + // * If you are displaying a color using the low-level watch_set_led_color function, you should return false. + // * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false. + // Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or + // movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions. + return true; +} + +void minimal_clock_face_resign(movement_settings_t *settings, void *context) { + (void) settings; + (void) context; + + // handle any cleanup before your watch face goes off-screen. +} + diff --git a/movement/watch_faces/clock/minimal_clock_face.h b/movement/watch_faces/clock/minimal_clock_face.h new file mode 100644 index 0000000..1978689 --- /dev/null +++ b/movement/watch_faces/clock/minimal_clock_face.h @@ -0,0 +1,56 @@ +/* + * MIT License + * + * Copyright (c) 2023 Dennisman219 + * + * 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 MINIMAL_CLOCK_FACE_H_ +#define MINIMAL_CLOCK_FACE_H_ + +#include "movement.h" + +/* + * A DESCRIPTION OF YOUR WATCH FACE + * + * and a description of how use it + * + */ + +typedef struct { + // Anything you need to keep track of, put it here! + uint8_t unused; +} minimal_clock_state_t; + +void minimal_clock_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); +void minimal_clock_face_activate(movement_settings_t *settings, void *context); +bool minimal_clock_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void minimal_clock_face_resign(movement_settings_t *settings, void *context); + +#define minimal_clock_face ((const watch_face_t){ \ + minimal_clock_face_setup, \ + minimal_clock_face_activate, \ + minimal_clock_face_loop, \ + minimal_clock_face_resign, \ + NULL, \ +}) + +#endif // MINIMAL_CLOCK_FACE_H_ + From c15b01cd4c44f01f93561ae7081ba98282c2c290 Mon Sep 17 00:00:00 2001 From: Dennisman219 Date: Fri, 29 Sep 2023 14:16:51 +0200 Subject: [PATCH 009/161] Added missing description to header file --- movement/watch_faces/clock/minimal_clock_face.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/movement/watch_faces/clock/minimal_clock_face.h b/movement/watch_faces/clock/minimal_clock_face.h index 1978689..d1f6ddf 100644 --- a/movement/watch_faces/clock/minimal_clock_face.h +++ b/movement/watch_faces/clock/minimal_clock_face.h @@ -28,9 +28,10 @@ #include "movement.h" /* - * A DESCRIPTION OF YOUR WATCH FACE + * MINIMAL CLOCK FACE * - * and a description of how use it + * A minimal clock face that just shows hours and minutes. + * There is nothing to configure. The face follows the 12h/24h setting * */ From 21645934844053c62a349ae827c0eabd27fad1b9 Mon Sep 17 00:00:00 2001 From: Dennisman219 Date: Fri, 29 Sep 2023 14:17:30 +0200 Subject: [PATCH 010/161] Leading 0 for hours <12 in 24h mode --- movement/watch_faces/clock/minimal_clock_face.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/movement/watch_faces/clock/minimal_clock_face.c b/movement/watch_faces/clock/minimal_clock_face.c index 195a3ee..fa4880e 100644 --- a/movement/watch_faces/clock/minimal_clock_face.c +++ b/movement/watch_faces/clock/minimal_clock_face.c @@ -32,9 +32,11 @@ static void _minimal_clock_face_update_display(movement_settings_t *settings) { if (!settings->bit.clock_mode_24h) { date_time.unit.hour %= 12; + sprintf(buffer, "%2d%02d ", date_time.unit.hour, date_time.unit.minute); + } else { + sprintf(buffer, "%02d%02d ", date_time.unit.hour, date_time.unit.minute); } - sprintf(buffer, "%2d%02d ", date_time.unit.hour, date_time.unit.minute); watch_display_string(buffer, 4); } From 30195a2619fe04a9c27ed03a3d116f2c3921688d Mon Sep 17 00:00:00 2001 From: Wesley Aptekar-Cassels Date: Fri, 29 Sep 2023 22:10:14 -0400 Subject: [PATCH 011/161] movement: Add "instant" led_duration option. This illuminates the LED only for the time that the button is pressed, and turns it off as soon as it's released, which is the behaviour of the original watch. I chose a led_duration of zero to represent "instant" and all bits set to represent "no LED", which is arbitrary but seemed more sensible to me. --- movement/movement.c | 23 +++++++++++++++---- movement/movement.h | 4 ++-- .../watch_faces/settings/preferences_face.c | 11 ++++++--- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/movement/movement.c b/movement/movement.c index f086841..17011e0 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -213,14 +213,24 @@ void movement_request_tick_frequency(uint8_t freq) { } void movement_illuminate_led(void) { - if (movement_state.settings.bit.led_duration) { + if (movement_state.settings.bit.led_duration != 0b111) { watch_set_led_color(movement_state.settings.bit.led_red_color ? (0xF | movement_state.settings.bit.led_red_color << 4) : 0, movement_state.settings.bit.led_green_color ? (0xF | movement_state.settings.bit.led_green_color << 4) : 0); - movement_state.light_ticks = (movement_state.settings.bit.led_duration * 2 - 1) * 128; + if (movement_state.settings.bit.led_duration == 0) { + movement_state.light_ticks = 1; + } else { + movement_state.light_ticks = (movement_state.settings.bit.led_duration * 2 - 1) * 128; + } _movement_enable_fast_tick_if_needed(); } } +static void _movement_led_off(void) { + watch_set_led_off(); + movement_state.light_ticks = -1; + _movement_disable_fast_tick_if_possible(); +} + bool movement_default_loop_handler(movement_event_t event, movement_settings_t *settings) { (void)settings; @@ -231,6 +241,11 @@ bool movement_default_loop_handler(movement_event_t event, movement_settings_t * case EVENT_LIGHT_BUTTON_DOWN: movement_illuminate_led(); break; + case EVENT_LIGHT_BUTTON_UP: + if (movement_state.settings.bit.led_duration == 0) { + _movement_led_off(); + } + break; case EVENT_MODE_LONG_PRESS: if (MOVEMENT_SECONDARY_FACE_INDEX && movement_state.current_watch_face == 0) { movement_move_to_face(MOVEMENT_SECONDARY_FACE_INDEX); @@ -465,9 +480,7 @@ bool app_loop(void) { if (watch_get_pin_level(BTN_LIGHT)) { movement_state.light_ticks = 1; } else { - watch_set_led_off(); - movement_state.light_ticks = -1; - _movement_disable_fast_tick_if_possible(); + _movement_led_off(); } } diff --git a/movement/movement.h b/movement/movement.h index 5f30dfb..87fdeba 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -50,7 +50,7 @@ typedef union { uint8_t to_interval : 2; // an inactivity interval for asking the active face to resign. bool to_always : 1; // if true, always time out from the active face to face 0. otherwise only faces that time out will resign (the default). uint8_t le_interval : 3; // 0 to disable low energy mode, or an inactivity interval for going into low energy mode. - uint8_t led_duration : 2; // how many seconds to shine the LED for (x2), or 0 to disable it. + uint8_t led_duration : 3; // how many seconds to shine the LED for (x2), 0 to shine only while the button is depressed, or all bits set to disable the LED altogether. uint8_t led_red_color : 4; // for general purpose illumination, the red LED value (0-15) uint8_t led_green_color : 4; // for general purpose illumination, the green LED value (0-15) uint8_t time_zone : 6; // an integer representing an index in the time zone table. @@ -62,7 +62,7 @@ typedef union { bool clock_mode_24h : 1; // indicates whether clock should use 12 or 24 hour mode. bool use_imperial_units : 1; // indicates whether to use metric units (the default) or imperial. bool alarm_enabled : 1; // indicates whether there is at least one alarm enabled. - uint8_t reserved : 6; // room for more preferences if needed. + uint8_t reserved : 5; // room for more preferences if needed. } bit; uint32_t reg; } movement_settings_t; diff --git a/movement/watch_faces/settings/preferences_face.c b/movement/watch_faces/settings/preferences_face.c index b0e328b..68041d2 100644 --- a/movement/watch_faces/settings/preferences_face.c +++ b/movement/watch_faces/settings/preferences_face.c @@ -84,6 +84,9 @@ bool preferences_face_loop(movement_event_t event, movement_settings_t *settings break; case 4: settings->bit.led_duration = settings->bit.led_duration + 1; + if (settings->bit.led_duration > 3) { + settings->bit.led_duration = 0b111; + } break; case 5: settings->bit.led_green_color = settings->bit.led_green_color + 1; @@ -159,11 +162,13 @@ bool preferences_face_loop(movement_event_t event, movement_settings_t *settings } break; case 4: - if (settings->bit.led_duration) { + if (settings->bit.led_duration == 0) { + watch_display_string("instnt", 4); + } else if (settings->bit.led_duration == 0b111) { + watch_display_string("no LEd", 4); + } else { sprintf(buf, " %1d SeC", settings->bit.led_duration * 2 - 1); watch_display_string(buf, 4); - } else { - watch_display_string("no LEd", 4); } break; case 5: From 4763568900d35003cec03f091203f58132d6b958 Mon Sep 17 00:00:00 2001 From: Wesley Aptekar-Cassels Date: Fri, 29 Sep 2023 22:13:29 -0400 Subject: [PATCH 012/161] movement: default to "instant" led_duration. This is a matter of personal preference, but "instant" is the behaviour of the original watch, and seems like the thing more people would expect. Feel free not to take this commit if you disagree, though. --- movement/movement.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/movement/movement.c b/movement/movement.c index 17011e0..577f199 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -364,7 +364,7 @@ void app_init(void) { movement_state.settings.bit.led_green_color = MOVEMENT_DEFAULT_GREEN_COLOR; movement_state.settings.bit.button_should_sound = true; movement_state.settings.bit.le_interval = 1; - movement_state.settings.bit.led_duration = 1; + movement_state.settings.bit.led_duration = 0; movement_state.light_ticks = -1; movement_state.alarm_ticks = -1; movement_state.next_available_backup_register = 4; From f633b7634b80d0bf08e491606eab3f10f85d6ece Mon Sep 17 00:00:00 2001 From: Jonathan Glines Date: Sun, 24 Sep 2023 14:00:13 -0400 Subject: [PATCH 013/161] Support leading zero representation for 24h clock Toggle between default behavior and leading zero with long-press of alarm button on page with 24h setting. --- movement/movement.h | 3 ++- .../clock/repetition_minute_face.c | 7 ++++++- .../clock/simple_clock_bin_led_face.c | 7 ++++++- .../watch_faces/clock/simple_clock_face.c | 7 ++++++- .../watch_faces/clock/weeknumber_clock_face.c | 7 ++++++- .../watch_faces/clock/world_clock2_face.c | 9 +++++++-- movement/watch_faces/clock/world_clock_face.c | 7 ++++++- .../watch_faces/complication/activity_face.c | 11 ++++++++--- .../watch_faces/complication/alarm_face.c | 7 +++++++ .../complication/planetary_hours_face.c | 7 ++++++- .../complication/planetary_time_face.c | 8 +++++++- .../complication/sunrise_sunset_face.c | 12 +++++++++++- movement/watch_faces/complication/wake_face.c | 11 ++++++++--- .../watch_faces/demo/lis2dw_logging_face.c | 11 ++++++++--- .../sensor/thermistor_logging_face.c | 19 ++++++++++++------- .../watch_faces/settings/preferences_face.c | 14 ++++++++++++-- movement/watch_faces/settings/set_time_face.c | 8 +++++++- .../settings/set_time_hackwatch_face.c | 8 +++++++- 18 files changed, 132 insertions(+), 31 deletions(-) diff --git a/movement/movement.h b/movement/movement.h index 5f30dfb..c9e955c 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -60,9 +60,10 @@ typedef union { // time-oriented complication like a sunrise/sunset timer, and a simple locale preference could tell an // altimeter to display feet or meters as easily as it tells a thermometer to display degrees in F or C. bool clock_mode_24h : 1; // indicates whether clock should use 12 or 24 hour mode. + bool clock_24h_leading_zero : 1; // indicates whether clock should leading zero to indicate 24 hour mode. bool use_imperial_units : 1; // indicates whether to use metric units (the default) or imperial. bool alarm_enabled : 1; // indicates whether there is at least one alarm enabled. - uint8_t reserved : 6; // room for more preferences if needed. + uint8_t reserved : 5; // room for more preferences if needed. } bit; uint32_t reg; } movement_settings_t; diff --git a/movement/watch_faces/clock/repetition_minute_face.c b/movement/watch_faces/clock/repetition_minute_face.c index fc78b2d..e992cdf 100644 --- a/movement/watch_faces/clock/repetition_minute_face.c +++ b/movement/watch_faces/clock/repetition_minute_face.c @@ -68,7 +68,7 @@ void repetition_minute_face_activate(movement_settings_t *settings, void *contex if (watch_tick_animation_is_running()) watch_stop_tick_animation(); - if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H); + if (settings->bit.clock_mode_24h && !settings->bit.clock_24h_leading_zero) watch_set_indicator(WATCH_INDICATOR_24H); // handle chime indicator if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL); @@ -112,6 +112,7 @@ bool repetition_minute_face_loop(movement_event_t event, movement_settings_t *se // ...and set the LAP indicator if low. if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP); + bool set_leading_zero = false; if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) { // everything before seconds is the same, don't waste cycles setting those segments. watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8); @@ -132,6 +133,8 @@ bool repetition_minute_face_loop(movement_event_t event, movement_settings_t *se } date_time.unit.hour %= 12; if (date_time.unit.hour == 0) date_time.unit.hour = 12; + } else if (settings->bit.clock_24h_leading_zero && date_time.unit.hour < 10) { + set_leading_zero = true; } pos = 0; if (event.event_type == EVENT_LOW_ENERGY_UPDATE) { @@ -142,6 +145,8 @@ bool repetition_minute_face_loop(movement_event_t event, movement_settings_t *se } } watch_display_string(buf, pos); + if (set_leading_zero) + watch_display_string("0", 4); // handle alarm indicator if (state->alarm_enabled != settings->bit.alarm_enabled) _update_alarm_indicator(settings->bit.alarm_enabled, state); break; diff --git a/movement/watch_faces/clock/simple_clock_bin_led_face.c b/movement/watch_faces/clock/simple_clock_bin_led_face.c index cf39c18..371f1e1 100644 --- a/movement/watch_faces/clock/simple_clock_bin_led_face.c +++ b/movement/watch_faces/clock/simple_clock_bin_led_face.c @@ -60,7 +60,7 @@ void simple_clock_bin_led_face_activate(movement_settings_t *settings, void *con if (watch_tick_animation_is_running()) watch_stop_tick_animation(); - if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H); + if (settings->bit.clock_mode_24h && !settings->bit.clock_24h_leading_zero) watch_set_indicator(WATCH_INDICATOR_24H); // handle chime indicator if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL); @@ -138,6 +138,7 @@ bool simple_clock_bin_led_face_loop(movement_event_t event, movement_settings_t // ...and set the LAP indicator if low. if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP); + bool set_leading_zero = false; if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) { // everything before seconds is the same, don't waste cycles setting those segments. watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8); @@ -158,6 +159,8 @@ bool simple_clock_bin_led_face_loop(movement_event_t event, movement_settings_t } date_time.unit.hour %= 12; if (date_time.unit.hour == 0) date_time.unit.hour = 12; + } else if (settings->bit.clock_24h_leading_zero && date_time.unit.hour < 10) { + set_leading_zero = true; } pos = 0; if (event.event_type == EVENT_LOW_ENERGY_UPDATE) { @@ -168,6 +171,8 @@ bool simple_clock_bin_led_face_loop(movement_event_t event, movement_settings_t } } watch_display_string(buf, pos); + if (set_leading_zero) + watch_display_string("0", 4); // handle alarm indicator if (state->alarm_enabled != settings->bit.alarm_enabled) _update_alarm_indicator(settings->bit.alarm_enabled, state); } diff --git a/movement/watch_faces/clock/simple_clock_face.c b/movement/watch_faces/clock/simple_clock_face.c index ac9a97b..6f67c36 100644 --- a/movement/watch_faces/clock/simple_clock_face.c +++ b/movement/watch_faces/clock/simple_clock_face.c @@ -51,7 +51,7 @@ void simple_clock_face_activate(movement_settings_t *settings, void *context) { if (watch_tick_animation_is_running()) watch_stop_tick_animation(); - if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H); + if (settings->bit.clock_mode_24h && !settings->bit.clock_24h_leading_zero) watch_set_indicator(WATCH_INDICATOR_24H); // handle chime indicator if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL); @@ -95,6 +95,7 @@ bool simple_clock_face_loop(movement_event_t event, movement_settings_t *setting // ...and set the LAP indicator if low. if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP); + bool set_leading_zero = false; if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) { // everything before seconds is the same, don't waste cycles setting those segments. watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8); @@ -115,6 +116,8 @@ bool simple_clock_face_loop(movement_event_t event, movement_settings_t *setting } date_time.unit.hour %= 12; if (date_time.unit.hour == 0) date_time.unit.hour = 12; + } else if (settings->bit.clock_24h_leading_zero && date_time.unit.hour < 10) { + set_leading_zero = true; } pos = 0; if (event.event_type == EVENT_LOW_ENERGY_UPDATE) { @@ -125,6 +128,8 @@ bool simple_clock_face_loop(movement_event_t event, movement_settings_t *setting } } watch_display_string(buf, pos); + if (set_leading_zero) + watch_display_string("0", 4); // handle alarm indicator if (state->alarm_enabled != settings->bit.alarm_enabled) _update_alarm_indicator(settings->bit.alarm_enabled, state); break; diff --git a/movement/watch_faces/clock/weeknumber_clock_face.c b/movement/watch_faces/clock/weeknumber_clock_face.c index 81df584..ed694e6 100644 --- a/movement/watch_faces/clock/weeknumber_clock_face.c +++ b/movement/watch_faces/clock/weeknumber_clock_face.c @@ -50,7 +50,7 @@ void weeknumber_clock_face_activate(movement_settings_t *settings, void *context if (watch_tick_animation_is_running()) watch_stop_tick_animation(); - if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H); + if (settings->bit.clock_mode_24h && !settings->bit.clock_24h_leading_zero) watch_set_indicator(WATCH_INDICATOR_24H); // handle chime indicator if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL); @@ -94,6 +94,7 @@ bool weeknumber_clock_face_loop(movement_event_t event, movement_settings_t *set // ...and set the LAP indicator if low. if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP); + bool set_leading_zero = false; if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) { // everything before minutes is the same. pos = 6; @@ -109,6 +110,8 @@ bool weeknumber_clock_face_loop(movement_event_t event, movement_settings_t *set } date_time.unit.hour %= 12; if (date_time.unit.hour == 0) date_time.unit.hour = 12; + } else if (settings->bit.clock_24h_leading_zero && date_time.unit.hour < 10) { + set_leading_zero = true; } pos = 0; if (event.event_type == EVENT_LOW_ENERGY_UPDATE) { @@ -119,6 +122,8 @@ bool weeknumber_clock_face_loop(movement_event_t event, movement_settings_t *set } } watch_display_string(buf, pos); + if (set_leading_zero) + watch_display_string("0", 4); // handle alarm indicator if (state->alarm_enabled != settings->bit.alarm_enabled) _update_alarm_indicator(settings->bit.alarm_enabled, state); break; diff --git a/movement/watch_faces/clock/world_clock2_face.c b/movement/watch_faces/clock/world_clock2_face.c index 0077f63..0d4acd0 100644 --- a/movement/watch_faces/clock/world_clock2_face.c +++ b/movement/watch_faces/clock/world_clock2_face.c @@ -247,7 +247,7 @@ static bool mode_display(movement_event_t event, movement_settings_t *settings, if (refresh_face) { watch_clear_indicator(WATCH_INDICATOR_SIGNAL); watch_set_colon(); - if (settings->bit.clock_mode_24h) + if (settings->bit.clock_mode_24h && !settings->bit.clock_24h_leading_zero) watch_set_indicator(WATCH_INDICATOR_24H); state->previous_date_time = REFRESH_TIME; @@ -261,6 +261,7 @@ static bool mode_display(movement_event_t event, movement_settings_t *settings, previous_date_time = state->previous_date_time; state->previous_date_time = date_time.reg; + bool set_leading_zero = false; if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) { /* Everything before seconds is the same, don't waste cycles setting those segments. */ pos = 8; @@ -281,7 +282,9 @@ static bool mode_display(movement_event_t event, movement_settings_t *settings, date_time.unit.hour %= 12; if (date_time.unit.hour == 0) date_time.unit.hour = 12; - } + } else if (settings->bit.clock_24h_leading_zero && date_time.unit.hour < 10) { + set_leading_zero = true; + } pos = 0; if (event.event_type == EVENT_LOW_ENERGY_UPDATE) { @@ -303,6 +306,8 @@ static bool mode_display(movement_event_t event, movement_settings_t *settings, } } watch_display_string(buf, pos); + if (set_leading_zero) + watch_display_string("0", 4); break; case EVENT_ALARM_BUTTON_UP: state->current_zone = find_selected_zone(state, FORWARD); diff --git a/movement/watch_faces/clock/world_clock_face.c b/movement/watch_faces/clock/world_clock_face.c index b12d9cd..6b731e8 100644 --- a/movement/watch_faces/clock/world_clock_face.c +++ b/movement/watch_faces/clock/world_clock_face.c @@ -60,7 +60,7 @@ static bool world_clock_face_do_display_mode(movement_event_t event, movement_se watch_date_time date_time; switch (event.event_type) { case EVENT_ACTIVATE: - if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H); + if (settings->bit.clock_mode_24h && !settings->bit.clock_24h_leading_zero) watch_set_indicator(WATCH_INDICATOR_24H); watch_set_colon(); state->previous_date_time = 0xFFFFFFFF; // fall through @@ -72,6 +72,7 @@ static bool world_clock_face_do_display_mode(movement_event_t event, movement_se previous_date_time = state->previous_date_time; state->previous_date_time = date_time.reg; + bool set_leading_zero = false; if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) { // everything before seconds is the same, don't waste cycles setting those segments. pos = 8; @@ -91,6 +92,8 @@ static bool world_clock_face_do_display_mode(movement_event_t event, movement_se } date_time.unit.hour %= 12; if (date_time.unit.hour == 0) date_time.unit.hour = 12; + } else if (settings->bit.clock_24h_leading_zero && date_time.unit.hour < 10) { + set_leading_zero = true; } pos = 0; if (event.event_type == EVENT_LOW_ENERGY_UPDATE) { @@ -112,6 +115,8 @@ static bool world_clock_face_do_display_mode(movement_event_t event, movement_se } } watch_display_string(buf, pos); + if (set_leading_zero) + watch_display_string("0", 4); break; case EVENT_ALARM_LONG_PRESS: movement_request_tick_frequency(4); diff --git a/movement/watch_faces/complication/activity_face.c b/movement/watch_faces/complication/activity_face.c index f92984c..b26fbeb 100644 --- a/movement/watch_faces/complication/activity_face.c +++ b/movement/watch_faces/complication/activity_face.c @@ -293,6 +293,7 @@ static void _activity_update_logging_screen(movement_settings_t *settings, activ } // Briefly, show time without seconds else { + bool set_leading_zero = false; watch_clear_indicator(WATCH_INDICATOR_LAP); watch_date_time now = watch_rtc_get_date_time(); uint8_t hour = now.unit.hour; @@ -304,14 +305,18 @@ static void _activity_update_logging_screen(movement_settings_t *settings, activ watch_set_indicator(WATCH_INDICATOR_PM); hour %= 12; if (hour == 0) hour = 12; - } - else { - watch_set_indicator(WATCH_INDICATOR_24H); + } else { watch_clear_indicator(WATCH_INDICATOR_PM); + if (!settings->bit.clock_24h_leading_zero) + watch_set_indicator(WATCH_INDICATOR_24H); + else if (hour < 10) + set_leading_zero = true; } sprintf(activity_buf, "%2d%02d ", hour, now.unit.minute); watch_set_colon(); watch_display_string(activity_buf, 4); + if (set_leading_zero) + watch_display_string("0", 4); } } diff --git a/movement/watch_faces/complication/alarm_face.c b/movement/watch_faces/complication/alarm_face.c index 3b7d1e3..6817222 100644 --- a/movement/watch_faces/complication/alarm_face.c +++ b/movement/watch_faces/complication/alarm_face.c @@ -99,6 +99,7 @@ static void _alarm_face_draw(movement_settings_t *settings, alarm_state_t *state i = state->alarm[state->alarm_idx].day + 1; } //handle am/pm for hour display + bool set_leading_zero = false; uint8_t h = state->alarm[state->alarm_idx].hour; if (!settings->bit.clock_mode_24h) { if (h >= 12) { @@ -108,6 +109,10 @@ static void _alarm_face_draw(movement_settings_t *settings, alarm_state_t *state watch_clear_indicator(WATCH_INDICATOR_PM); } if (h == 0) h = 12; + } else if (!settings->bit.clock_24h_leading_zero) { + watch_set_indicator(WATCH_INDICATOR_24H); + } else if (h < 10) { + set_leading_zero = true; } sprintf(buf, "%c%c%2d%2d%02d ", _dow_strings[i][0], _dow_strings[i][1], @@ -119,6 +124,8 @@ static void _alarm_face_draw(movement_settings_t *settings, alarm_state_t *state buf[_blink_idx[state->setting_state]] = buf[_blink_idx2[state->setting_state]] = ' '; } watch_display_string(buf, 0); + if (set_leading_zero) + watch_display_string("0", 4); if (state->is_setting) { // draw pitch level indicator diff --git a/movement/watch_faces/complication/planetary_hours_face.c b/movement/watch_faces/complication/planetary_hours_face.c index acded91..e13466b 100644 --- a/movement/watch_faces/complication/planetary_hours_face.c +++ b/movement/watch_faces/complication/planetary_hours_face.c @@ -228,6 +228,7 @@ static void _planetary_hours(movement_settings_t *settings, planetary_hours_stat uint8_t weekday, planet, planetary_hour; uint32_t current_hour_epoch; watch_date_time scratch_time; + bool set_leading_zero = false; // check if we have a location. If not, display error if ( state->no_location ) { @@ -253,7 +254,7 @@ static void _planetary_hours(movement_settings_t *settings, planetary_hours_stat return; } - if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H); + if (settings->bit.clock_mode_24h && !settings->bit.clock_24h_leading_zero) watch_set_indicator(WATCH_INDICATOR_24H); // roll over hour iterator if ( state->hour < 0 ) state->hour = 23; @@ -313,6 +314,8 @@ static void _planetary_hours(movement_settings_t *settings, planetary_hours_stat } scratch_time.unit.hour %= 12; if (scratch_time.unit.hour == 0) scratch_time.unit.hour = 12; + } else if (settings->bit.clock_24h_leading_zero && scratch_time.unit.hour < 10) { + set_leading_zero = true; } // planetary ruler of the hour @@ -328,6 +331,8 @@ static void _planetary_hours(movement_settings_t *settings, planetary_hours_stat watch_set_colon(); watch_display_string(buf, 0); + if (set_leading_zero) + watch_display_string("0", 4); if ( state->ruler == 2 ) _planetary_icon(planet); } diff --git a/movement/watch_faces/complication/planetary_time_face.c b/movement/watch_faces/complication/planetary_time_face.c index 56a18cf..7e7ac6c 100644 --- a/movement/watch_faces/complication/planetary_time_face.c +++ b/movement/watch_faces/complication/planetary_time_face.c @@ -206,6 +206,7 @@ static void _planetary_time(movement_event_t event, movement_settings_t *setting double night_hour_count = 0.0; uint8_t weekday, planet, planetary_hour; double hour_duration, current_hour, current_minute, current_second; + bool set_leading_zero = false; watch_set_colon(); @@ -218,7 +219,7 @@ static void _planetary_time(movement_event_t event, movement_settings_t *setting return; } - if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H); + if (settings->bit.clock_mode_24h && !settings->bit.clock_24h_leading_zero) watch_set_indicator(WATCH_INDICATOR_24H); // PM for night hours, otherwise the night hours are counted from 13 if ( state->night ) { @@ -246,6 +247,9 @@ static void _planetary_time(movement_event_t event, movement_settings_t *setting state->scratch.unit.minute = floor(current_minute); state->scratch.unit.second = (uint8_t)floor(current_second) % 60; + if (settings->bit.clock_mode_24h && settings->bit.clock_24h_leading_zero && state->scratch.unit.hour < 10) + set_leading_zero = true; + // what weekday is it (0 - 6) weekday = watch_utility_get_iso8601_weekday_number(state->scratch.unit.year, state->scratch.unit.month, state->scratch.unit.day) - 1; @@ -263,6 +267,8 @@ static void _planetary_time(movement_event_t event, movement_settings_t *setting else sprintf(buf, "%s h%2d%02d%02d", ruler, state->scratch.unit.hour, state->scratch.unit.minute, state->scratch.unit.second); watch_display_string(buf, 0); + if (set_leading_zero) + watch_display_string("0", 4); if ( state->ruler == 2 ) _planetary_icon(planet); diff --git a/movement/watch_faces/complication/sunrise_sunset_face.c b/movement/watch_faces/complication/sunrise_sunset_face.c index 82de9c6..65dbc55 100644 --- a/movement/watch_faces/complication/sunrise_sunset_face.c +++ b/movement/watch_faces/complication/sunrise_sunset_face.c @@ -85,7 +85,7 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s } watch_set_colon(); - if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H); + if (settings->bit.clock_mode_24h && !settings->bit.clock_24h_leading_zero) watch_set_indicator(WATCH_INDICATOR_24H); rise += hours_from_utc; set += hours_from_utc; @@ -105,12 +105,17 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s if (date_time.reg < scratch_time.reg || show_next_match) { if (state->rise_index == 0 || show_next_match) { + bool set_leading_zero = false; if (!settings->bit.clock_mode_24h) { if (watch_utility_convert_to_12_hour(&scratch_time)) watch_set_indicator(WATCH_INDICATOR_PM); else watch_clear_indicator(WATCH_INDICATOR_PM); + } else if (settings->bit.clock_24h_leading_zero && scratch_time.unit.hour < 10) { + set_leading_zero = true; } sprintf(buf, "rI%2d%2d%02d ", scratch_time.unit.day, scratch_time.unit.hour, scratch_time.unit.minute); watch_display_string(buf, 0); + if (set_leading_zero) + watch_display_string("0", 4); return; } else { show_next_match = true; @@ -132,12 +137,17 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s if (date_time.reg < scratch_time.reg || show_next_match) { if (state->rise_index == 0 || show_next_match) { + bool set_leading_zero = false; if (!settings->bit.clock_mode_24h) { if (watch_utility_convert_to_12_hour(&scratch_time)) watch_set_indicator(WATCH_INDICATOR_PM); else watch_clear_indicator(WATCH_INDICATOR_PM); + } else if (settings->bit.clock_24h_leading_zero && scratch_time.unit.hour < 10) { + set_leading_zero = true; } sprintf(buf, "SE%2d%2d%02d ", scratch_time.unit.day, scratch_time.unit.hour, scratch_time.unit.minute); watch_display_string(buf, 0); + if (set_leading_zero) + watch_display_string("0", 4); return; } else { show_next_match = true; diff --git a/movement/watch_faces/complication/wake_face.c b/movement/watch_faces/complication/wake_face.c index 5c5f86b..99a7f94 100644 --- a/movement/watch_faces/complication/wake_face.c +++ b/movement/watch_faces/complication/wake_face.c @@ -50,12 +50,15 @@ void _wake_face_update_display(movement_settings_t *settings, wake_face_state_t uint8_t hour = state->hour; watch_clear_display(); - if ( settings->bit.clock_mode_24h ) - watch_set_indicator(WATCH_INDICATOR_24H); - else { + bool set_leading_zero = false; + if ( !settings->bit.clock_mode_24h ) { if ( hour >= 12 ) watch_set_indicator(WATCH_INDICATOR_PM); hour = hour % 12 ? hour % 12 : 12; + } else if ( !settings->bit.clock_24h_leading_zero ) { + watch_set_indicator(WATCH_INDICATOR_24H); + } else if ( hour < 10 ) { + set_leading_zero = true; } if ( state->mode ) @@ -66,6 +69,8 @@ void _wake_face_update_display(movement_settings_t *settings, wake_face_state_t watch_set_colon(); watch_display_string(lcdbuf, 0); + if ( set_leading_zero ) + watch_display_string("0", 4); } // diff --git a/movement/watch_faces/demo/lis2dw_logging_face.c b/movement/watch_faces/demo/lis2dw_logging_face.c index 0a957bb..cbae54b 100644 --- a/movement/watch_faces/demo/lis2dw_logging_face.c +++ b/movement/watch_faces/demo/lis2dw_logging_face.c @@ -41,6 +41,7 @@ static void _lis2dw_logging_face_update_display(movement_settings_t *settings, l char time_indication_character; int8_t pos; watch_date_time date_time; + bool set_leading_zero = false; if (logger_state->log_ticks) { pos = (logger_state->data_points - 1 - logger_state->display_index) % LIS2DW_LOGGING_NUM_DATA_POINTS; @@ -50,12 +51,14 @@ static void _lis2dw_logging_face_update_display(movement_settings_t *settings, l } else { date_time = logger_state->data[pos].timestamp; watch_set_colon(); - if (settings->bit.clock_mode_24h) { - watch_set_indicator(WATCH_INDICATOR_24H); - } else { + if (!settings->bit.clock_mode_24h) { if (date_time.unit.hour > 11) watch_set_indicator(WATCH_INDICATOR_PM); date_time.unit.hour %= 12; if (date_time.unit.hour == 0) date_time.unit.hour = 12; + } else if (!settings->bit.clock_24h_leading_zero) { + watch_set_indicator(WATCH_INDICATOR_24H); + } else if (date_time.unit.hour < 10) { + set_leading_zero = true; } switch (logger_state->axis_index) { case 0: @@ -89,6 +92,8 @@ static void _lis2dw_logging_face_update_display(movement_settings_t *settings, l logger_state->interrupts[2]); } watch_display_string(buf, 0); + if (set_leading_zero) + watch_display_string("0", 4); } static void _lis2dw_logging_face_log_data(lis2dw_logger_state_t *logger_state) { diff --git a/movement/watch_faces/sensor/thermistor_logging_face.c b/movement/watch_faces/sensor/thermistor_logging_face.c index 64f605e..18625cc 100644 --- a/movement/watch_faces/sensor/thermistor_logging_face.c +++ b/movement/watch_faces/sensor/thermistor_logging_face.c @@ -40,9 +40,10 @@ static void _thermistor_logging_face_log_data(thermistor_logger_state_t *logger_ thermistor_driver_disable(); } -static void _thermistor_logging_face_update_display(thermistor_logger_state_t *logger_state, bool in_fahrenheit, bool clock_mode_24h) { +static void _thermistor_logging_face_update_display(thermistor_logger_state_t *logger_state, bool in_fahrenheit, bool clock_mode_24h, bool clock_24h_leading_zero) { int8_t pos = (logger_state->data_points - 1 - logger_state->display_index) % THERMISTOR_LOGGING_NUM_DATA_POINTS; char buf[14]; + bool set_leading_zero = false; watch_clear_indicator(WATCH_INDICATOR_24H); watch_clear_indicator(WATCH_INDICATOR_PM); @@ -53,12 +54,14 @@ static void _thermistor_logging_face_update_display(thermistor_logger_state_t *l } else if (logger_state->ts_ticks) { watch_date_time date_time = logger_state->data[pos].timestamp; watch_set_colon(); - if (clock_mode_24h) { - watch_set_indicator(WATCH_INDICATOR_24H); - } else { + if (!clock_mode_24h) { if (date_time.unit.hour > 11) watch_set_indicator(WATCH_INDICATOR_PM); date_time.unit.hour %= 12; if (date_time.unit.hour == 0) date_time.unit.hour = 12; + } else if (!clock_24h_leading_zero) { + watch_set_indicator(WATCH_INDICATOR_24H); + } else if (date_time.unit.hour < 10) { + set_leading_zero = true; } sprintf(buf, "AT%2d%2d%02d%02d", date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second); } else { @@ -70,6 +73,8 @@ static void _thermistor_logging_face_update_display(thermistor_logger_state_t *l } watch_display_string(buf, 0); + if (set_leading_zero) + watch_display_string("0", 4); } void thermistor_logging_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { @@ -100,18 +105,18 @@ bool thermistor_logging_face_loop(movement_event_t event, movement_settings_t *s break; case EVENT_LIGHT_BUTTON_DOWN: logger_state->ts_ticks = 2; - _thermistor_logging_face_update_display(logger_state, settings->bit.use_imperial_units, settings->bit.clock_mode_24h); + _thermistor_logging_face_update_display(logger_state, settings->bit.use_imperial_units, settings->bit.clock_mode_24h, settings->bit.clock_24h_leading_zero); break; case EVENT_ALARM_BUTTON_DOWN: logger_state->display_index = (logger_state->display_index + 1) % THERMISTOR_LOGGING_NUM_DATA_POINTS; logger_state->ts_ticks = 0; // fall through case EVENT_ACTIVATE: - _thermistor_logging_face_update_display(logger_state, settings->bit.use_imperial_units, settings->bit.clock_mode_24h); + _thermistor_logging_face_update_display(logger_state, settings->bit.use_imperial_units, settings->bit.clock_mode_24h, settings->bit.clock_24h_leading_zero); break; case EVENT_TICK: if (logger_state->ts_ticks && --logger_state->ts_ticks == 0) { - _thermistor_logging_face_update_display(logger_state, settings->bit.use_imperial_units, settings->bit.clock_mode_24h); + _thermistor_logging_face_update_display(logger_state, settings->bit.use_imperial_units, settings->bit.clock_mode_24h, settings->bit.clock_24h_leading_zero); } break; case EVENT_BACKGROUND_TASK: diff --git a/movement/watch_faces/settings/preferences_face.c b/movement/watch_faces/settings/preferences_face.c index b0e328b..1cbffc1 100644 --- a/movement/watch_faces/settings/preferences_face.c +++ b/movement/watch_faces/settings/preferences_face.c @@ -93,6 +93,14 @@ bool preferences_face_loop(movement_event_t event, movement_settings_t *settings break; } break; + case EVENT_ALARM_LONG_PRESS: + switch (current_page) { + case 0: + if (settings->bit.clock_mode_24h) + settings->bit.clock_24h_leading_zero = !(settings->bit.clock_24h_leading_zero); + break; + } + break; case EVENT_TIMEOUT: movement_move_to_face(0); break; @@ -107,8 +115,10 @@ bool preferences_face_loop(movement_event_t event, movement_settings_t *settings char buf[8]; switch (current_page) { case 0: - if (settings->bit.clock_mode_24h) watch_display_string("24h", 4); - else watch_display_string("12h", 4); + if (settings->bit.clock_mode_24h) { + if (settings->bit.clock_24h_leading_zero) watch_display_string("024h", 4); + else watch_display_string("24h", 4); + } else watch_display_string("12h", 4); break; case 1: if (settings->bit.button_should_sound) watch_display_string("y", 9); diff --git a/movement/watch_faces/settings/set_time_face.c b/movement/watch_faces/settings/set_time_face.c index 84a4d18..9308071 100644 --- a/movement/watch_faces/settings/set_time_face.c +++ b/movement/watch_faces/settings/set_time_face.c @@ -131,10 +131,14 @@ bool set_time_face_loop(movement_event_t event, movement_settings_t *settings, v } char buf[11]; + bool set_leading_zero = false; if (current_page < 3) { watch_set_colon(); if (settings->bit.clock_mode_24h) { - watch_set_indicator(WATCH_INDICATOR_24H); + if (!settings->bit.clock_24h_leading_zero) + watch_set_indicator(WATCH_INDICATOR_24H); + else if (date_time.unit.hour < 10) + set_leading_zero = true; sprintf(buf, "%s %2d%02d%02d", set_time_face_titles[current_page], date_time.unit.hour, date_time.unit.minute, date_time.unit.second); } else { sprintf(buf, "%s %2d%02d%02d", set_time_face_titles[current_page], (date_time.unit.hour % 12) ? (date_time.unit.hour % 12) : 12, date_time.unit.minute, date_time.unit.second); @@ -175,6 +179,8 @@ bool set_time_face_loop(movement_event_t event, movement_settings_t *settings, v } watch_display_string(buf, 0); + if (set_leading_zero) + watch_display_string("0", 4); return true; } diff --git a/movement/watch_faces/settings/set_time_hackwatch_face.c b/movement/watch_faces/settings/set_time_hackwatch_face.c index c65e2ff..863dcc2 100644 --- a/movement/watch_faces/settings/set_time_hackwatch_face.c +++ b/movement/watch_faces/settings/set_time_hackwatch_face.c @@ -209,10 +209,14 @@ bool set_time_hackwatch_face_loop(movement_event_t event, movement_settings_t *s } char buf[11]; + bool set_leading_zero = false; if (current_page < 3) { watch_set_colon(); if (settings->bit.clock_mode_24h) { - watch_set_indicator(WATCH_INDICATOR_24H); + if (!settings->bit.clock_24h_leading_zero) + watch_set_indicator(WATCH_INDICATOR_24H); + else if (date_time_settings.unit.hour < 10) + set_leading_zero = true; sprintf(buf, "%s %2d%02d%02d", set_time_hackwatch_face_titles[current_page], @@ -278,6 +282,8 @@ bool set_time_hackwatch_face_loop(movement_event_t event, movement_settings_t *s } watch_display_string(buf, 0); + if (set_leading_zero) + watch_display_string("0", 4); return true; } From 7eb725a5f60d4a64058ca19ed655e912e58d14b7 Mon Sep 17 00:00:00 2001 From: PrimmR Date: Fri, 24 Nov 2023 18:58:07 +0000 Subject: [PATCH 014/161] Periodic Table Face --- movement/make/Makefile | 1 + movement/movement_faces.h | 1 + .../watch_faces/complication/periodic_face.c | 357 ++++++++++++++++++ .../watch_faces/complication/periodic_face.h | 61 +++ 4 files changed, 420 insertions(+) create mode 100644 movement/watch_faces/complication/periodic_face.c create mode 100644 movement/watch_faces/complication/periodic_face.h diff --git a/movement/make/Makefile b/movement/make/Makefile index c294b06..2e623e6 100644 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -121,6 +121,7 @@ SRCS += \ ../watch_faces/complication/couch_to_5k_face.c \ ../watch_faces/clock/minute_repeater_decimal_face.c \ ../watch_faces/complication/tuning_tones_face.c \ + ../watch_faces/complication/periodic_face.c \ # New watch faces go above this line. # Leave this line at the bottom of the file; it has all the targets for making your project. diff --git a/movement/movement_faces.h b/movement/movement_faces.h index cb58612..23c5c35 100644 --- a/movement/movement_faces.h +++ b/movement/movement_faces.h @@ -98,6 +98,7 @@ #include "couch_to_5k_face.h" #include "minute_repeater_decimal_face.h" #include "tuning_tones_face.h" +#include "periodic_face.h" // New includes go above this line. #endif // MOVEMENT_FACES_H_ diff --git a/movement/watch_faces/complication/periodic_face.c b/movement/watch_faces/complication/periodic_face.c new file mode 100644 index 0000000..6746c26 --- /dev/null +++ b/movement/watch_faces/complication/periodic_face.c @@ -0,0 +1,357 @@ +/* + * MIT License + * + * Copyright (c) 2023 PrimmR + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "periodic_face.h" + +void periodic_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr) +{ + (void)settings; + (void)watch_face_index; + if (*context_ptr == NULL) + { + *context_ptr = malloc(sizeof(periodic_state_t)); + memset(*context_ptr, 0, sizeof(periodic_state_t)); + } +} + +void periodic_face_activate(movement_settings_t *settings, void *context) +{ + (void)settings; + periodic_state_t *state = (periodic_state_t *)context; + + state->atomic_num = 1; + state->mode = 0; + state->selection_index = 0; + + movement_request_tick_frequency(2); +} + +typedef struct +{ + char name[3]; + uint16_t atomic_mass; + char group[3]; +} element; + +// Comments on the table denote symbols that cannot be displayed +#define MAX_ELEMENT 118 +const element table[MAX_ELEMENT] = { + {"H ", 1, " "}, + {"He", 4, " 0"}, + {"Li", 7, " 1"}, + {"Be", 9, " 2"}, + {"B ", 11, " 3"}, + {"C ", 12, " 4"}, + {"N ", 14, " 5"}, + {"O ", 16, " 6"}, + {"F ", 19, " 7"}, + {"Ne", 20, " 0"}, + {"Na", 23, " 1"}, + {"Mg", 24, " 2"}, // + {"Al", 27, " 3"}, + {"Si", 28, " 4"}, + {"P ", 31, " 5"}, + {"S ", 32, " 6"}, + {"Cl", 355, " 7"}, + {"Ar", 40, " 0"}, + {"K ", 39, " 1"}, + {"Ca", 40, " 2"}, + {"Sc", 45, " T"}, + {"Ti", 48, " T"}, + {" W", 51, " T"}, // "V" + {"Cr", 52, " T"}, + {"Mn", 55, " T"}, + {"Fe", 56, " T"}, + {"Co", 59, " T"}, + {"Ni", 59, " T"}, + {"Cu", 635, " T"}, + {"Zn", 65, " T"}, + {"Ga", 70, " 3"}, + {"Ge", 73, " 4"}, + {"As", 75, " 5"}, // + {"Se", 79, " 6"}, + {"Br", 80, " 7"}, + {"Kr", 84, " 0"}, + {"Rb", 85, " 1"}, + {"Sr", 88, " 2"}, + {"Y ", 89, " T"}, + {"Zr", 91, " T"}, + {"Nb", 93, " T"}, + {"Mo", 96, " T"}, + {"Tc", 97, " T"}, + {"Ru", 101, " T"}, + {"Rh", 103, " T"}, + {"Pd", 106, " T"}, + {"Ag", 108, " T"}, // + {"Cd", 112, " T"}, + {"In", 115, " 3"}, + {"Sn", 119, " 4"}, + {"Sb", 122, " 5"}, + {"Te", 128, " 6"}, + {"I ", 127, " 7"}, + {"Xe", 131, " 0"}, + {"CS", 133, " 1"}, + {"Ba", 137, " 2"}, + {"La", 139, "1a"}, // La + {"Ce", 140, "1a"}, + {"Pr", 141, "1a"}, + {"Nd", 144, "1a"}, + {"Pm", 145, "1a"}, + {"Sm", 150, "1a"}, + {"Eu", 152, "1a"}, + {"Gd", 157, "1a"}, + {"Tb", 159, "1a"}, + {"Dy", 163, "1a"}, // .5 Rounded up due to space constraints + {"Ho", 165, "1a"}, + {"Er", 167, "1a"}, + {"Tm", 169, "1a"}, + {"Yb", 173, "1a"}, + {"Lu", 175, "1a"}, + {"Hf", 179, " T"}, + {"Ta", 181, " T"}, + {"W ", 184, " T"}, + {"Re", 186, " T"}, + {"OS", 190, " T"}, + {"Ir", 192, " T"}, + {"Pt", 195, " T"}, + {"Au", 197, " T"}, + {"Hg", 201, " T"}, // + {"Tl", 204, " 3"}, + {"Pb", 207, " 4"}, + {"Bi", 209, " 5"}, + {"Po", 209, " 6"}, + {"At", 210, " 7"}, + {"Rn", 222, " 0"}, + {"Fr", 223, " 1"}, + {"Ra", 226, " 2"}, + {"Ac", 227, "Ac"}, + {"Th", 232, "Ac"}, + {"Pa", 231, "Ac"}, + {"U ", 238, "Ac"}, + {"Np", 237, "Ac"}, // + {"Pu", 244, "Ac"}, + {"Am", 243, "Ac"}, + {"Cm", 247, "Ac"}, + {"Bk", 247, "Ac"}, // + {"Cf", 251, "Ac"}, + {"Es", 252, "Ac"}, // + {"Fm", 257, "Ac"}, + {"Md", 258, "Ac"}, + {"No", 259, "Ac"}, + {"Lr", 262, "Ac"}, + {"Rf", 267, " T"}, + {"Db", 262, " T"}, + {"Sg", 269, " T"}, // + {"Bh", 264, " T"}, + {"Hs", 269, " T"}, + {"Mt", 278, " T"}, + {"Ds", 281, " T"}, // + {"Rg", 282, " T"}, // + {"Cn", 285, " T"}, + {"Nh", 286, " 3"}, + {"Fl", 289, " 4"}, + {"Mc", 289, " 5"}, + {"LW", 293, " 6"}, // Lv + {"Ts", 294, " 7"}, // + {"Og", 294, " 0"}, // +}; + +// Warning light for symbols that can't be displayed +static void _warning(periodic_state_t *state) +{ + char second_char = table[state->atomic_num - 1].name[1]; + if (second_char == 'p' || second_char == 'g' || second_char == 'y' || second_char == 's') + { + watch_set_indicator(WATCH_INDICATOR_BELL); + } + else + { + watch_clear_indicator(WATCH_INDICATOR_BELL); + } +} + +// Regular mode display +static void _periodic_face_update_lcd(periodic_state_t *state) +{ + // Colon as a decimal for Cl & Cu + if (state->atomic_num == 17 || state->atomic_num == 29) + { + watch_set_colon(); + } + else + { + watch_clear_colon(); + } + + _warning(state); + + char buf[11]; + sprintf(buf, "%s%s%-3d%3d", table[state->atomic_num - 1].name, table[state->atomic_num - 1].group, table[state->atomic_num - 1].atomic_mass, state->atomic_num); + watch_display_string(buf, 0); +} + +// Selection mode logic +static void _periodic_face_selection_increment(periodic_state_t *state) +{ + uint8_t digit0 = (state->atomic_num / 100) % 10; + uint8_t digit1 = (state->atomic_num / 10) % 10; + uint8_t digit2 = (state->atomic_num) % 10; + + // Increment the selected digit by 1 + switch (state->selection_index) + { + case 0: + digit0 ^= 1; + break; + case 1: + if (digit0 == MAX_ELEMENT / 100 && digit1 == (MAX_ELEMENT / 10) % 10) + digit1 = 0; + else + digit1 = (digit1 + 1) % 10; + break; + case 2: + if (digit0 == MAX_ELEMENT / 100 && digit1 == (MAX_ELEMENT / 10) % 10 && digit2 == MAX_ELEMENT % 10) + digit2 = 0; + else + digit2 = (digit2 + 1) % 10; + break; + } + + // Prevent 000 + if (digit0 == 0 && digit1 == 0 && digit2 == 0) { + digit2 = 1; + } + + // Prevent Overflow + if (digit0 == (MAX_ELEMENT / 100) % 10 && digit1 > (MAX_ELEMENT / 10) % 10) + { + digit2 = MAX_ELEMENT % 10; + digit1 = (MAX_ELEMENT / 10) % 10; + } + + state->atomic_num = digit0 * 100 + digit1 * 10 + digit2; +} + +// Selection mode display +static void _periodic_face_selection(periodic_state_t *state, uint8_t subsec) +{ + uint8_t digit0 = (state->atomic_num / 100) % 10; + uint8_t digit1 = (state->atomic_num / 10) % 10; + uint8_t digit2 = (state->atomic_num) % 10; + + watch_display_string(" ", 0); + + char buf[2] = {'\0'}; + + buf[0] = (state->selection_index == 0 && subsec == 0) ? ' ' : digit0 + '0'; + watch_display_string(buf, 5); + + buf[0] = (state->selection_index == 1 && subsec == 0) ? ' ' : digit1 + '0'; + watch_display_string(buf, 6); + + buf[0] = (state->selection_index == 2 && subsec == 0) ? ' ' : digit2 + '0'; + watch_display_string(buf, 7); + + char buf2[3]; + sprintf(buf2, "%s", table[state->atomic_num - 1].name); + watch_display_string(buf2, 0); + + _warning(state); +} + +bool periodic_face_loop(movement_event_t event, movement_settings_t *settings, void *context) +{ + periodic_state_t *state = (periodic_state_t *)context; + + switch (event.event_type) + { + case EVENT_ACTIVATE: + _periodic_face_update_lcd(state); + break; + case EVENT_TICK: + if (state->mode != 0) + { + _periodic_face_selection(state, event.subsecond % 2); + } + break; + case EVENT_LIGHT_BUTTON_UP: + // Only light LED when in regular mode + if (state->mode != MODE_VIEW) + { + state->selection_index = (state->selection_index + 1) % 3; + _periodic_face_selection(state, event.subsecond % 2); + } + break; + case EVENT_LIGHT_BUTTON_DOWN: + if (state->mode != MODE_SELECT) + movement_illuminate_led(); + break; + case EVENT_ALARM_BUTTON_UP: + if (state->mode == MODE_VIEW) + { + state->atomic_num = (state->atomic_num % MAX_ELEMENT) + 1; // Wraps back to 1 + _periodic_face_update_lcd(state); + if (settings->bit.button_should_sound) watch_buzzer_play_note(BUZZER_NOTE_C7, 50); + } + else + { + _periodic_face_selection_increment(state); + _periodic_face_selection(state, event.subsecond % 2); + } + break; + case EVENT_ALARM_LONG_PRESS: + // Toggle between selection mode and regular + if (state->mode == MODE_VIEW) + { + state->mode = MODE_SELECT; + _periodic_face_selection(state, event.subsecond % 2); + } + else + { + state->mode = MODE_VIEW; + _periodic_face_update_lcd(state); + } + if (settings->bit.button_should_sound) watch_buzzer_play_note(BUZZER_NOTE_C8, 50); + + break; + case EVENT_TIMEOUT: + break; + case EVENT_LOW_ENERGY_UPDATE: + break; + default: + return movement_default_loop_handler(event, settings); + } + + return true; +} + +void periodic_face_resign(movement_settings_t *settings, void *context) +{ + (void)settings; + (void)context; + + // handle any cleanup before your watch face goes off-screen. +} diff --git a/movement/watch_faces/complication/periodic_face.h b/movement/watch_faces/complication/periodic_face.h new file mode 100644 index 0000000..56476ca --- /dev/null +++ b/movement/watch_faces/complication/periodic_face.h @@ -0,0 +1,61 @@ +/* + * MIT License + * + * Copyright (c) 2023 PrimmR + * + * 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 PERIODIC_FACE_H_ +#define PERIODIC_FACE_H_ + +#include "movement.h" + +/* + * Periodic Table Face + * + * Elements can be viewed sequentially with a short press of the alarm button, + * or the atomic number can be input directly after holding down the alarm button. + * + */ + +#define MODE_VIEW 0 +#define MODE_SELECT 1 + +typedef struct { + uint8_t atomic_num; + uint8_t mode; + uint8_t selection_index; +} periodic_state_t; + +void periodic_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); +void periodic_face_activate(movement_settings_t *settings, void *context); +bool periodic_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void periodic_face_resign(movement_settings_t *settings, void *context); + +#define periodic_face ((const watch_face_t){ \ + periodic_face_setup, \ + periodic_face_activate, \ + periodic_face_loop, \ + periodic_face_resign, \ + NULL, \ +}) + +#endif // PERIODIC_FACE_H_ + From 0e70adf4d4430a106a34b90c707630c6c6f1a519 Mon Sep 17 00:00:00 2001 From: Wesley Black Date: Wed, 20 Mar 2024 17:53:45 -0300 Subject: [PATCH 015/161] Update movement_faces.h --- movement/movement_faces.h | 1 + 1 file changed, 1 insertion(+) diff --git a/movement/movement_faces.h b/movement/movement_faces.h index 3557110..76bf023 100644 --- a/movement/movement_faces.h +++ b/movement/movement_faces.h @@ -104,6 +104,7 @@ #include "minute_repeater_decimal_face.h" #include "tuning_tones_face.h" #include "kitchen_conversions_face.h" +#include "beeps_face.h" // New includes go above this line. #endif // MOVEMENT_FACES_H_ From 69f25f10165b7d0521f6605991b5d94a49db7332 Mon Sep 17 00:00:00 2001 From: Wesley Black Date: Wed, 20 Mar 2024 17:54:23 -0300 Subject: [PATCH 016/161] Add files via upload --- movement/watch_faces/demo/beeps_face.c | 249 +++++++++++++++++++++++++ movement/watch_faces/demo/beeps_face.h | 61 ++++++ 2 files changed, 310 insertions(+) create mode 100644 movement/watch_faces/demo/beeps_face.c create mode 100644 movement/watch_faces/demo/beeps_face.h diff --git a/movement/watch_faces/demo/beeps_face.c b/movement/watch_faces/demo/beeps_face.c new file mode 100644 index 0000000..07df627 --- /dev/null +++ b/movement/watch_faces/demo/beeps_face.c @@ -0,0 +1,249 @@ +/* + * MIT License + * + * Copyright (c) 2024 Wesley + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "beeps_face.h" + +void beeps_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { + (void) settings; + (void) watch_face_index; + if (*context_ptr == NULL) { + *context_ptr = malloc(sizeof(beeps_state_t)); + memset(*context_ptr, 0, sizeof(beeps_state_t)); + // Do any one-time tasks in here; the inside of this conditional happens only at boot. + } +} + +void beeps_face_activate(movement_settings_t *settings, void *context) { + (void) settings; + beeps_state_t *state = (beeps_state_t *)context; +} + +static void _beep_face_update_lcd(beeps_state_t *state) { + char buf[11]; + const char buzzernote[][7] = {" 5500", " 5827", " 6174"," 6541"," 6930"," 7342"," 7778"," 8241"," 8731"," 9250"," 9800"," 10383"," 11000"," 11654"," 12347"," 13081"," 13859"," 14683"," 15556"," 16481"," 17461"," 18500"," 19600"," 20765"," 22000"," 23308"," 24694"," 26163"," 27718"," 29366"," 31113"," 32963"," 34923"," 36999"," 39200"," 41530"," 44000"," 46616"," 49388"," 52325"," 55437"," 58733"," 62225"," 65925"," 69846"," 73999"," 78399"," 83061"," 88000"," 93233"," 98777"," 104650"," 110873"," 117466"," 124451"," 131851"," 139691"," 147998"," 156798"," 166122"," 176000"," 186466"," 197553"," 209300"," 221746"," 234932"," 248902"," 263702"," 279383"," 295996"," 313596"," 332244"," 352000"," 372931"," 395107"," 418601"," 443492"," 469863"," 497803"," 527404"," 558765"," 591991"," 627193"," 664488"," 704000"," 745862"," 790213"}; + sprintf(buf, "HZ %s", buzzernote[state->frequency]); + watch_display_string(buf, 0); +} + +bool beeps_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { + beeps_state_t *state = (beeps_state_t *)context; + + switch (event.event_type) { + case EVENT_ACTIVATE: + _beep_face_update_lcd(state); + break; + case EVENT_LIGHT_BUTTON_DOWN: + state->frequency = (state->frequency + 1) % 87; + _beep_face_update_lcd(state); + break; + case EVENT_ALARM_BUTTON_DOWN: + if (state->frequency == 0) { + watch_buzzer_play_note(BUZZER_NOTE_A1, 500); + } else if (state->frequency == 1) { + watch_buzzer_play_note(BUZZER_NOTE_A1SHARP_B1FLAT, 500); + } else if (state->frequency == 2) { + watch_buzzer_play_note(BUZZER_NOTE_B1, 500); + } else if (state->frequency == 3) { + watch_buzzer_play_note(BUZZER_NOTE_C2, 500); + } else if (state->frequency == 4) { + watch_buzzer_play_note(BUZZER_NOTE_C2SHARP_D2FLAT, 500); + } else if (state->frequency == 5) { + watch_buzzer_play_note(BUZZER_NOTE_D2, 500); + } else if (state->frequency == 6) { + watch_buzzer_play_note(BUZZER_NOTE_D2SHARP_E2FLAT, 500); + } else if (state->frequency == 7) { + watch_buzzer_play_note(BUZZER_NOTE_E2, 500); + } else if (state->frequency == 8) { + watch_buzzer_play_note(BUZZER_NOTE_F2, 500); + } else if (state->frequency == 9) { + watch_buzzer_play_note(BUZZER_NOTE_F2SHARP_G2FLAT, 500); + } else if (state->frequency == 10) { + watch_buzzer_play_note(BUZZER_NOTE_G2, 500); + } else if (state->frequency == 11) { + watch_buzzer_play_note(BUZZER_NOTE_G2SHARP_A2FLAT, 500); + } else if (state->frequency == 12) { + watch_buzzer_play_note(BUZZER_NOTE_A2, 500); + } else if (state->frequency == 13) { + watch_buzzer_play_note(BUZZER_NOTE_A2SHARP_B2FLAT, 500); + } else if (state->frequency == 14) { + watch_buzzer_play_note(BUZZER_NOTE_B2, 500); + } else if (state->frequency == 15) { + watch_buzzer_play_note(BUZZER_NOTE_C3, 500); + } else if (state->frequency == 16) { + watch_buzzer_play_note(BUZZER_NOTE_C3SHARP_D3FLAT, 500); + } else if (state->frequency == 17) { + watch_buzzer_play_note(BUZZER_NOTE_D3, 500); + } else if (state->frequency == 18) { + watch_buzzer_play_note(BUZZER_NOTE_D3SHARP_E3FLAT, 500); + } else if (state->frequency == 19) { + watch_buzzer_play_note(BUZZER_NOTE_E3, 500); + } else if (state->frequency == 20) { + watch_buzzer_play_note(BUZZER_NOTE_F3, 500); + } else if (state->frequency == 21) { + watch_buzzer_play_note(BUZZER_NOTE_F3SHARP_G3FLAT, 500); + } else if (state->frequency == 22) { + watch_buzzer_play_note(BUZZER_NOTE_G3, 500); + } else if (state->frequency == 23) { + watch_buzzer_play_note(BUZZER_NOTE_G3SHARP_A3FLAT, 500); + } else if (state->frequency == 24) { + watch_buzzer_play_note(BUZZER_NOTE_A3, 500); + } else if (state->frequency == 25) { + watch_buzzer_play_note(BUZZER_NOTE_A3SHARP_B3FLAT, 500); + } else if (state->frequency == 26) { + watch_buzzer_play_note(BUZZER_NOTE_B3, 500); + } else if (state->frequency == 27) { + watch_buzzer_play_note(BUZZER_NOTE_C4, 500); + } else if (state->frequency == 28) { + watch_buzzer_play_note(BUZZER_NOTE_C4SHARP_D4FLAT, 500); + } else if (state->frequency == 29) { + watch_buzzer_play_note(BUZZER_NOTE_D4, 500); + } else if (state->frequency == 30) { + watch_buzzer_play_note(BUZZER_NOTE_D4SHARP_E4FLAT, 500); + } else if (state->frequency == 31) { + watch_buzzer_play_note(BUZZER_NOTE_E4, 500); + } else if (state->frequency == 32) { + watch_buzzer_play_note(BUZZER_NOTE_F4, 500); + } else if (state->frequency == 33) { + watch_buzzer_play_note(BUZZER_NOTE_F4SHARP_G4FLAT, 500); + } else if (state->frequency == 34) { + watch_buzzer_play_note(BUZZER_NOTE_G4, 500); + } else if (state->frequency == 35) { + watch_buzzer_play_note(BUZZER_NOTE_G4SHARP_A4FLAT, 500); + } else if (state->frequency == 36) { + watch_buzzer_play_note(BUZZER_NOTE_A4, 500); + } else if (state->frequency == 37) { + watch_buzzer_play_note(BUZZER_NOTE_A4SHARP_B4FLAT, 500); + } else if (state->frequency == 38) { + watch_buzzer_play_note(BUZZER_NOTE_B4, 500); + } else if (state->frequency == 39) { + watch_buzzer_play_note(BUZZER_NOTE_C5, 500); + } else if (state->frequency == 40) { + watch_buzzer_play_note(BUZZER_NOTE_C5SHARP_D5FLAT, 500); + } else if (state->frequency == 41) { + watch_buzzer_play_note(BUZZER_NOTE_D5, 500); + } else if (state->frequency == 42) { + watch_buzzer_play_note(BUZZER_NOTE_D5SHARP_E5FLAT, 500); + } else if (state->frequency == 43) { + watch_buzzer_play_note(BUZZER_NOTE_E5, 500); + } else if (state->frequency == 44) { + watch_buzzer_play_note(BUZZER_NOTE_F5, 500); + } else if (state->frequency == 45) { + watch_buzzer_play_note(BUZZER_NOTE_F5SHARP_G5FLAT, 500); + } else if (state->frequency == 46) { + watch_buzzer_play_note(BUZZER_NOTE_G5, 500); + } else if (state->frequency == 47) { + watch_buzzer_play_note(BUZZER_NOTE_G5SHARP_A5FLAT, 500); + } else if (state->frequency == 48) { + watch_buzzer_play_note(BUZZER_NOTE_A5, 500); + } else if (state->frequency == 49) { + watch_buzzer_play_note(BUZZER_NOTE_A5SHARP_B5FLAT, 500); + } else if (state->frequency == 50) { + watch_buzzer_play_note(BUZZER_NOTE_B5, 500); + } else if (state->frequency == 51) { + watch_buzzer_play_note(BUZZER_NOTE_C6, 500); + } else if (state->frequency == 52) { + watch_buzzer_play_note(BUZZER_NOTE_C6SHARP_D6FLAT, 500); + } else if (state->frequency == 53) { + watch_buzzer_play_note(BUZZER_NOTE_D6, 500); + } else if (state->frequency == 54) { + watch_buzzer_play_note(BUZZER_NOTE_D6SHARP_E6FLAT, 500); + } else if (state->frequency == 55) { + watch_buzzer_play_note(BUZZER_NOTE_E6, 500); + } else if (state->frequency == 56) { + watch_buzzer_play_note(BUZZER_NOTE_F6, 500); + } else if (state->frequency == 57) { + watch_buzzer_play_note(BUZZER_NOTE_F6SHARP_G6FLAT, 500); + } else if (state->frequency == 58) { + watch_buzzer_play_note(BUZZER_NOTE_G6, 500); + } else if (state->frequency == 59) { + watch_buzzer_play_note(BUZZER_NOTE_G6SHARP_A6FLAT, 500); + } else if (state->frequency == 60) { + watch_buzzer_play_note(BUZZER_NOTE_A6, 500); + } else if (state->frequency == 61) { + watch_buzzer_play_note(BUZZER_NOTE_A6SHARP_B6FLAT, 500); + } else if (state->frequency == 62) { + watch_buzzer_play_note(BUZZER_NOTE_B6, 500); + } else if (state->frequency == 63) { + watch_buzzer_play_note(BUZZER_NOTE_C7, 500); + } else if (state->frequency == 64) { + watch_buzzer_play_note(BUZZER_NOTE_C7SHARP_D7FLAT, 500); + } else if (state->frequency == 65) { + watch_buzzer_play_note(BUZZER_NOTE_D7, 500); + } else if (state->frequency == 66) { + watch_buzzer_play_note(BUZZER_NOTE_D7SHARP_E7FLAT, 500); + } else if (state->frequency == 67) { + watch_buzzer_play_note(BUZZER_NOTE_E7, 500); + } else if (state->frequency == 68) { + watch_buzzer_play_note(BUZZER_NOTE_F7, 500); + } else if (state->frequency == 69) { + watch_buzzer_play_note(BUZZER_NOTE_F7SHARP_G7FLAT, 500); + } else if (state->frequency == 70) { + watch_buzzer_play_note(BUZZER_NOTE_G7, 500); + } else if (state->frequency == 71) { + watch_buzzer_play_note(BUZZER_NOTE_G7SHARP_A7FLAT, 500); + } else if (state->frequency == 72) { + watch_buzzer_play_note(BUZZER_NOTE_A7, 500); + } else if (state->frequency == 73) { + watch_buzzer_play_note(BUZZER_NOTE_A7SHARP_B7FLAT, 500); + } else if (state->frequency == 74) { + watch_buzzer_play_note(BUZZER_NOTE_B7, 500); + } else if (state->frequency == 75) { + watch_buzzer_play_note(BUZZER_NOTE_C8, 500); + } else if (state->frequency == 76) { + watch_buzzer_play_note(BUZZER_NOTE_C8SHARP_D8FLAT, 500); + } else if (state->frequency == 77) { + watch_buzzer_play_note(BUZZER_NOTE_D8, 500); + } else if (state->frequency == 78) { + watch_buzzer_play_note(BUZZER_NOTE_D8SHARP_E8FLAT, 500); + } else if (state->frequency == 79) { + watch_buzzer_play_note(BUZZER_NOTE_E8, 500); + } else if (state->frequency == 80) { + watch_buzzer_play_note(BUZZER_NOTE_F8, 500); + } else if (state->frequency == 81) { + watch_buzzer_play_note(BUZZER_NOTE_F8SHARP_G8FLAT, 500); + } else if (state->frequency == 82) { + watch_buzzer_play_note(BUZZER_NOTE_G8, 500); + } else if (state->frequency == 83) { + watch_buzzer_play_note(BUZZER_NOTE_G8SHARP_A8FLAT, 500); + } else if (state->frequency == 84) { + watch_buzzer_play_note(BUZZER_NOTE_A8, 500); + } else if (state->frequency == 85) { + watch_buzzer_play_note(BUZZER_NOTE_A8SHARP_B8FLAT, 500); + } else if (state->frequency == 86) { + watch_buzzer_play_note(BUZZER_NOTE_B8, 500); + } + break; + default: + return movement_default_loop_handler(event, settings); + } + return true; +} + +void beeps_face_resign(movement_settings_t *settings, void *context) { + (void) settings; + (void) context; +} + diff --git a/movement/watch_faces/demo/beeps_face.h b/movement/watch_faces/demo/beeps_face.h new file mode 100644 index 0000000..73606fd --- /dev/null +++ b/movement/watch_faces/demo/beeps_face.h @@ -0,0 +1,61 @@ +/* + * MIT License + * + * Copyright (c) 2024 Wesley + * + * 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 BEEPS_FACE_H_ +#define BEEPS_FACE_H_ + +#include "movement.h" + +/* + * A simple watch face to test the different Buzzer Notes. + * + * Press the Light button to play a sound. + * Press the Alarm button to change the frequency. + * + * The watch face displays the frequency of the buzzer it will play + * this allows you to reference the watch_buzzer.h file to find the + * corresponding note. + * + * The watch_buzzer.h file is found at watch-library/shared/watch/watch_buzzer.h + */ + +typedef struct { + uint8_t frequency; +} beeps_state_t; + +void beeps_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); +void beeps_face_activate(movement_settings_t *settings, void *context); +bool beeps_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void beeps_face_resign(movement_settings_t *settings, void *context); + +#define beeps_face ((const watch_face_t){ \ + beeps_face_setup, \ + beeps_face_activate, \ + beeps_face_loop, \ + beeps_face_resign, \ + NULL, \ +}) + +#endif // BEEPS_FACE_H_ + From 2ce07f9539eaa2b20c69d078052a9b90e1ae1f67 Mon Sep 17 00:00:00 2001 From: "R. Alex Barbieri" <> Date: Sat, 27 Apr 2024 13:33:32 -0500 Subject: [PATCH 017/161] add blinking to DST toggle in settings page --- movement/watch_faces/settings/set_time_face.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/movement/watch_faces/settings/set_time_face.c b/movement/watch_faces/settings/set_time_face.c index 16011e3..fb0c806 100644 --- a/movement/watch_faces/settings/set_time_face.c +++ b/movement/watch_faces/settings/set_time_face.c @@ -185,6 +185,9 @@ bool set_time_face_loop(movement_event_t event, movement_settings_t *settings, v case 5: buf[8] = buf[9] = ' '; break; + case 7: + buf[9] = ' '; + break; } } From 9794f86430d8d824b30af3e7e348d9573d8cbb03 Mon Sep 17 00:00:00 2001 From: Matt Greer Date: Sun, 5 May 2024 09:47:47 -0400 Subject: [PATCH 018/161] simon_face: Simon game complication --- movement/make/Makefile | 1 + movement/movement_faces.h | 1 + .../watch_faces/complication/simon_face.c | 247 ++++++++++++++++++ .../watch_faces/complication/simon_face.h | 97 +++++++ 4 files changed, 346 insertions(+) create mode 100644 movement/watch_faces/complication/simon_face.c create mode 100644 movement/watch_faces/complication/simon_face.h diff --git a/movement/make/Makefile b/movement/make/Makefile index da5486b..09fdb33 100644 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -129,6 +129,7 @@ SRCS += \ ../watch_faces/clock/minute_repeater_decimal_face.c \ ../watch_faces/complication/tuning_tones_face.c \ ../watch_faces/complication/kitchen_conversions_face.c \ + ../watch_faces/complication/simon_face.c \ # New watch faces go above this line. # Leave this line at the bottom of the file; it has all the targets for making your project. diff --git a/movement/movement_faces.h b/movement/movement_faces.h index 3557110..72aae1b 100644 --- a/movement/movement_faces.h +++ b/movement/movement_faces.h @@ -104,6 +104,7 @@ #include "minute_repeater_decimal_face.h" #include "tuning_tones_face.h" #include "kitchen_conversions_face.h" +#include "simon_face.h" // New includes go above this line. #endif // MOVEMENT_FACES_H_ diff --git a/movement/watch_faces/complication/simon_face.c b/movement/watch_faces/complication/simon_face.c new file mode 100644 index 0000000..99fc160 --- /dev/null +++ b/movement/watch_faces/complication/simon_face.c @@ -0,0 +1,247 @@ +/* + * MIT License + * + * Copyright (c) 2024 <#author_name#> + * + * 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 "simon_face.h" +#include +#include +#include + +// Emulator only: need time() to seed the random number generator +#if __EMSCRIPTEN__ +#include +#endif + +static char _simon_display_buf[12]; + +static inline uint8_t _simon_get_rand_num(uint8_t num_values) { +#if __EMSCRIPTEN__ + return rand() % num_values; +#else + return arc4random_uniform(num_values); +#endif +} + +static void _simon_clear_display(simon_state_t *state) { + if (state->playing_state == SIMON_NOT_PLAYING) { + watch_display_string(" ", 0); + } else { + sprintf(_simon_display_buf, " %2d ", state->sequence_length); + watch_display_string(_simon_display_buf, 0); + } +} + +static void _simon_not_playing_display(simon_state_t *state) { + _simon_clear_display(state); + + sprintf(_simon_display_buf, "SI %d", state->best_score); + watch_display_string(_simon_display_buf, 0); +} + +static void _simon_reset(simon_state_t *state) { + state->playing_state = SIMON_NOT_PLAYING; + state->listen_index = 0; + state->sequence_length = 0; + _simon_not_playing_display(state); +} + + +static void _simon_display_note(SimonNote note, simon_state_t *state) { + char *ndtemplate = NULL; + + switch (note) { + case SIMON_LED_NOTE: + ndtemplate = "LI%2d "; + break; + case SIMON_ALARM_NOTE: + ndtemplate = " %2d AL"; + break; + case SIMON_MODE_NOTE: + ndtemplate = " %2dDE "; + break; + case SIMON_WRONG_NOTE: + ndtemplate = "OH NOOOOO"; + } + + sprintf(_simon_display_buf, ndtemplate, state->sequence_length); + watch_display_string(_simon_display_buf, 0); +} + +static void _simon_play_note(SimonNote note, simon_state_t *state, bool skip_rest) { + _simon_display_note(note, state); + switch (note) { + case SIMON_LED_NOTE: + watch_set_led_yellow(); + watch_buzzer_play_note(BUZZER_NOTE_D3, 300); + break; + case SIMON_MODE_NOTE: + watch_set_led_red(); + watch_buzzer_play_note(BUZZER_NOTE_E4, 300); + break; + case SIMON_ALARM_NOTE: + watch_set_led_green(); + watch_buzzer_play_note(BUZZER_NOTE_C3, 300); + break; + case SIMON_WRONG_NOTE: + watch_buzzer_play_note(BUZZER_NOTE_A1, 800); + break; + } + watch_set_led_off(); + + if (note != SIMON_WRONG_NOTE) { + _simon_clear_display(state); + if (!skip_rest) { + watch_buzzer_play_note(BUZZER_NOTE_REST, 200); + } + } +} + + +static void _simon_setup_next_note(simon_state_t *state) { + if (state->sequence_length > state->best_score) { + state->best_score = state->sequence_length; + } + + _simon_clear_display(state); + state->playing_state = SIMON_TEACHING; + state->sequence[state->sequence_length] = _simon_get_rand_num(3) + 1; + state->sequence_length = state->sequence_length + 1; + state->teaching_index = 0; + state->listen_index = 0; +} + +static void _simon_listen(SimonNote note, simon_state_t *state) { + if (state->sequence[state->listen_index] == note) { + _simon_play_note(note, state, true); + state->listen_index++; + + if (state->listen_index == state->sequence_length) { + state->playing_state = SIMON_READY_FOR_NEXT_NOTE; + } + } else { + _simon_play_note(SIMON_WRONG_NOTE, state, true); + _simon_reset(state); + } +} + +static void _simon_begin_listening(simon_state_t *state) { + state->playing_state = SIMON_LISTENING_BACK; + state->listen_index = 0; +} + +void simon_face_setup(movement_settings_t *settings, uint8_t watch_face_index, + void **context_ptr) { + (void)settings; + (void)watch_face_index; + if (*context_ptr == NULL) { + *context_ptr = malloc(sizeof(simon_state_t)); + memset(*context_ptr, 0, sizeof(simon_state_t)); + // Do any one-time tasks in here; the inside of this conditional happens + // only at boot. + } + // Do any pin or peripheral setup here; this will be called whenever the watch + // wakes from deep sleep. +#if __EMSCRIPTEN__ + // simulator only: seed the randon number generator + time_t t; + srand((unsigned)time(&t)); +#endif +} + +void simon_face_activate(movement_settings_t *settings, void *context) { + (void) settings; + (void) context; +} + +bool simon_face_loop(movement_event_t event, movement_settings_t *settings, + void *context) { + simon_state_t *state = (simon_state_t *)context; + + switch (event.event_type) { + case EVENT_ACTIVATE: + // Show your initial UI here. + _simon_reset(state); + break; + case EVENT_TICK: + if (state->playing_state == SIMON_READY_FOR_NEXT_NOTE) { + _simon_setup_next_note(state); + } else if (state->playing_state == SIMON_TEACHING) { + SimonNote note = state->sequence[state->teaching_index]; + // if this is the final note in the sequence, don't play the rest to let + // the player jump in faster + _simon_play_note(note, state, state->teaching_index == (state->sequence_length - 1)); + state->teaching_index++; + + if (state->teaching_index == state->sequence_length) { + _simon_begin_listening(state); + } + } + break; + case EVENT_LIGHT_BUTTON_DOWN: + break; + case EVENT_LIGHT_BUTTON_UP: + if (state->playing_state == SIMON_NOT_PLAYING) { + state->sequence_length = 0; + _simon_setup_next_note(state); + } else if (state->playing_state == SIMON_LISTENING_BACK) { + _simon_listen(SIMON_LED_NOTE, state); + } + break; + case EVENT_MODE_LONG_PRESS: + if (state->playing_state == SIMON_NOT_PLAYING) { + movement_move_to_face(0); + } else { + state->playing_state = SIMON_NOT_PLAYING; + _simon_reset(state); + } + break; + case EVENT_MODE_BUTTON_UP: + if (state->playing_state == SIMON_NOT_PLAYING) { + movement_move_to_next_face(); + } else if (state->playing_state == SIMON_LISTENING_BACK) { + _simon_listen(SIMON_MODE_NOTE, state); + } + break; + case EVENT_ALARM_BUTTON_UP: + if (state->playing_state == SIMON_LISTENING_BACK) { + _simon_listen(SIMON_ALARM_NOTE, state); + } + break; + case EVENT_TIMEOUT: + movement_move_to_face(0); + break; + case EVENT_LOW_ENERGY_UPDATE: + break; + default: + return movement_default_loop_handler(event, settings); + } + + return true; +} + +void simon_face_resign(movement_settings_t *settings, void *context) { + (void)settings; + (void)context; + watch_set_led_off(); + watch_set_buzzer_off(); +} diff --git a/movement/watch_faces/complication/simon_face.h b/movement/watch_faces/complication/simon_face.h new file mode 100644 index 0000000..b59d56c --- /dev/null +++ b/movement/watch_faces/complication/simon_face.h @@ -0,0 +1,97 @@ +/* + * MIT License + * + * Copyright (c) 2024 <#author_name#> + * + * 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 SIMON_FACE_H_ +#define SIMON_FACE_H_ + +#include "movement.h" + +/* + * simon_face + * ----------- + * The classic electronic game, Simon, reduced to be played on a Sensor-Watch + * + * How to play: + * + * When first arriving at the face, it will show your best score. + * + * Press the light button to start the game. + * + * A sequence will be played, starting with length 1. The sequence can be + * made up of tones corresponding to any of the three buttons. + * + * light button: "LI" will display at the top of the screen, the LED will be yellow, and a high D will play + * mode button: "DE" will display at the left of the screen, the LED will be red, and a high E will play + * alarm button: "AL" will display on the right of the screen, the LED will be green, and a high C will play + * + * Once the sequence has finished, press the same buttons to recreate the sequence. + * + * If correct, the sequence will get one tone longer and play again. See how long of a sequence you can get. + * + * If you recreate the sequence incorrectly, a low note will play with "OH NOOOOO" displayed and the game is over. + * Press light to play again. + * + * Once playing, long press the mode button when it is your turn to exit the game early. + */ + +#define MAX_SEQUENCE 99 + +typedef enum SimonNote { + SIMON_LED_NOTE = 1, + SIMON_MODE_NOTE, + SIMON_ALARM_NOTE, + SIMON_WRONG_NOTE +} SimonNote; + +typedef enum SimonPlayingState { + SIMON_NOT_PLAYING = 0, + SIMON_TEACHING, + SIMON_LISTENING_BACK, + SIMON_READY_FOR_NEXT_NOTE +} SimonPlayingState; + +typedef struct { + uint8_t best_score; + SimonNote sequence[MAX_SEQUENCE]; + uint8_t sequence_length; + uint8_t teaching_index; + uint8_t listen_index; + SimonPlayingState playing_state; +} simon_state_t; + +void simon_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr); +void simon_face_activate(movement_settings_t *settings, void *context); +bool simon_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void simon_face_resign(movement_settings_t *settings, void *context); + +#define simon_face \ + ((const watch_face_t){ \ + simon_face_setup, \ + simon_face_activate, \ + simon_face_loop, \ + simon_face_resign, \ + NULL, \ + }) + +#endif // SIMON_FACE_H_ From fa35b8bb77f687fdd0eba09a6d8e3b49e72ab272 Mon Sep 17 00:00:00 2001 From: Ruben Nic Date: Mon, 6 May 2024 20:32:59 -0400 Subject: [PATCH 019/161] Add close enough clock face --- movement/make/Makefile | 1 + movement/movement_faces.h | 1 + .../clock/close_enough_clock_face.c | 199 ++++++++++++++++++ .../clock/close_enough_clock_face.h | 61 ++++++ 4 files changed, 262 insertions(+) create mode 100644 movement/watch_faces/clock/close_enough_clock_face.c create mode 100644 movement/watch_faces/clock/close_enough_clock_face.h diff --git a/movement/make/Makefile b/movement/make/Makefile index da5486b..690ab81 100644 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -52,6 +52,7 @@ SRCS += \ ../shell.c \ ../shell_cmd_list.c \ ../watch_faces/clock/simple_clock_face.c \ + ../watch_faces/clock/close_enough_clock_face.c \ ../watch_faces/clock/clock_face.c \ ../watch_faces/clock/world_clock_face.c \ ../watch_faces/clock/beats_face.c \ diff --git a/movement/movement_faces.h b/movement/movement_faces.h index 3557110..c6b2958 100644 --- a/movement/movement_faces.h +++ b/movement/movement_faces.h @@ -26,6 +26,7 @@ #define MOVEMENT_FACES_H_ #include "simple_clock_face.h" +#include "close_enough_clock_face.h" #include "clock_face.h" #include "world_clock_face.h" #include "preferences_face.h" diff --git a/movement/watch_faces/clock/close_enough_clock_face.c b/movement/watch_faces/clock/close_enough_clock_face.c new file mode 100644 index 0000000..58db37a --- /dev/null +++ b/movement/watch_faces/clock/close_enough_clock_face.c @@ -0,0 +1,199 @@ +/* + * MIT License + * + * Copyright (c) 2024 Ruben Nic + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "close_enough_clock_face.h" +#include "watch.h" +#include "watch_utility.h" +#include "watch_private_display.h" + +static const char first_words[12][3] = { + " ", // "HH OC", + " 5", // " 5 past HH", + "10", // "10 past HH", + "15", // "15 past HH", + "20", // "20 past HH", + "25", // "25 past HH", + "30", // "30 past HH", + "35", // "35 past HH", + "40", // "20 two HH+1", + "15", // "15 two HH+1", + "10", // "10 two HH+1", + " 5" // " 5 two HH+1" +}; + +static const char second_words[12][3] = { + "OC", // "HH OC", + " P", // " 5 past HH", + " P", // "10 past HH", + " P", // "15 past HH", + " P", // "20 past HH", + " P", // "25 past HH", + " P", // "30 past HH", + " P", // "35 past HH", + " 2", // "20 two HH+1", + " 2", // "15 two HH+1", + " 2", // "10 two HH+1", + " 2" // " 5 two HH+1" +}; + +static const int hour_switch_index = 8; + +static void _update_alarm_indicator(bool settings_alarm_enabled, close_enough_clock_state_t *state) { + state->alarm_enabled = settings_alarm_enabled; + if (state->alarm_enabled) { + watch_set_indicator(WATCH_INDICATOR_SIGNAL); + } else { + watch_clear_indicator(WATCH_INDICATOR_SIGNAL); + }; +} + +void close_enough_clock_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { + (void) settings; + (void) watch_face_index; + + if (*context_ptr == NULL) { + *context_ptr = malloc(sizeof(close_enough_clock_state_t)); + } +} + +void close_enough_clock_face_activate(movement_settings_t *settings, void *context) { + close_enough_clock_state_t *state = (close_enough_clock_state_t *)context; + + if (watch_tick_animation_is_running()) { + watch_stop_tick_animation(); + } + + if (settings->bit.clock_mode_24h) { + watch_set_indicator(WATCH_INDICATOR_24H); + } + + // show alarm indicator if there is an active alarm + _update_alarm_indicator(settings->bit.alarm_enabled, state); + + // this ensures that none of the five_minute_periods will match, so we always rerender when the face activates + state->prev_five_minute_period = -1; +} + +bool close_enough_clock_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { + close_enough_clock_state_t *state = (close_enough_clock_state_t *)context; + char buf[11]; + + watch_date_time date_time; + int prev_five_minute_period; + switch (event.event_type) { + case EVENT_ACTIVATE: + case EVENT_TICK: + case EVENT_LOW_ENERGY_UPDATE: + date_time = watch_rtc_get_date_time(); + prev_five_minute_period = state->prev_five_minute_period; + + // check the battery voltage once a day... + if (date_time.unit.day != state->last_battery_check) { + state->last_battery_check = date_time.unit.day; + watch_enable_adc(); + uint16_t voltage = watch_get_vcc_voltage(); + watch_disable_adc(); + // 2.2 volts will happen when the battery has maybe 5-10% remaining? + // we can refine this later. + state->battery_low = (voltage < 2200); + } + + // ...and set the LAP indicator if low. + if (state->battery_low) { + watch_set_indicator(WATCH_INDICATOR_LAP); + } + + int five_minute_period = (date_time.unit.minute / 5) % 12; + + // same five_minute_period, skip update + if (five_minute_period == prev_five_minute_period) { + break; + } + + // move from "x mins past y" to "x mins to y+1" + if (five_minute_period >= hour_switch_index) { + date_time.unit.hour += 1; + date_time.unit.hour %= 24; + } + + if (!settings->bit.clock_mode_24h) { + // if we are in 12 hour mode, do some cleanup. + if (date_time.unit.hour < 12) { + watch_clear_indicator(WATCH_INDICATOR_PM); + } else { + watch_set_indicator(WATCH_INDICATOR_PM); + } + + date_time.unit.hour %= 12; + if (date_time.unit.hour == 0) { + date_time.unit.hour = 12; + } + } + + char first_word[3]; + char second_word[3]; + char third_word[3]; + if (five_minute_period == 0) { + sprintf(first_word, "%2d", date_time.unit.hour); + strncpy(second_word, first_words[five_minute_period], 3); + strncpy(third_word, second_words[five_minute_period], 3); + } else { + strncpy(first_word, first_words[five_minute_period], 3); + strncpy(second_word, second_words[five_minute_period], 3); + sprintf(third_word, "%2d", date_time.unit.hour); + } + + sprintf( + buf, + "%s%2d%s%s%s", + watch_utility_get_weekday(date_time), + date_time.unit.day, + first_word, + second_word, + third_word + ); + + watch_display_string(buf, 0); + state->prev_five_minute_period = five_minute_period; + + // handle alarm indicator + if (state->alarm_enabled != settings->bit.alarm_enabled) { + _update_alarm_indicator(settings->bit.alarm_enabled, state); + } + + break; + + default: + return movement_default_loop_handler(event, settings); + } + + return true; +} + +void close_enough_clock_face_resign(movement_settings_t *settings, void *context) { + (void) settings; + (void) context; +} diff --git a/movement/watch_faces/clock/close_enough_clock_face.h b/movement/watch_faces/clock/close_enough_clock_face.h new file mode 100644 index 0000000..341357b --- /dev/null +++ b/movement/watch_faces/clock/close_enough_clock_face.h @@ -0,0 +1,61 @@ +/* + * MIT License + * + * Copyright (c) 2024 Ruben Nic + * + * 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 CLOSE_ENOUGH_CLOCK_FACE_H_ +#define CLOSE_ENOUGH_CLOCK_FACE_H_ + +/* + * CLOSE ENOUGH CLOCK FACE + * + * Displays the current time; but only in periods of 5. + * Just in the in the formats of: + * - "10 past 5" + * - "15 to 7" + * - "6 o'clock" + * + */ + +#include "movement.h" + +typedef struct { + int prev_five_minute_period; + uint8_t last_battery_check; + bool battery_low; + bool alarm_enabled; +} close_enough_clock_state_t; + +void close_enough_clock_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); +void close_enough_clock_face_activate(movement_settings_t *settings, void *context); +bool close_enough_clock_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void close_enough_clock_face_resign(movement_settings_t *settings, void *context); + +#define close_enough_clock_face ((const watch_face_t){ \ + close_enough_clock_face_setup, \ + close_enough_clock_face_activate, \ + close_enough_clock_face_loop, \ + close_enough_clock_face_resign, \ + NULL, \ +}) + +#endif // CLOSE_ENOUGH_CLOCK_FACE_H_ From 53f11cbd1e42f888c4de7af19075133f77346b9d Mon Sep 17 00:00:00 2001 From: Ruben Nic Date: Wed, 15 May 2024 12:30:00 -0400 Subject: [PATCH 020/161] have close enough to update less and clean up code --- .../clock/close_enough_clock_face.c | 101 ++++++++++-------- .../clock/close_enough_clock_face.h | 1 + 2 files changed, 59 insertions(+), 43 deletions(-) diff --git a/movement/watch_faces/clock/close_enough_clock_face.c b/movement/watch_faces/clock/close_enough_clock_face.c index 58db37a..19bde69 100644 --- a/movement/watch_faces/clock/close_enough_clock_face.c +++ b/movement/watch_faces/clock/close_enough_clock_face.c @@ -24,42 +24,27 @@ #include #include +#include #include "close_enough_clock_face.h" #include "watch.h" #include "watch_utility.h" -#include "watch_private_display.h" -static const char first_words[12][3] = { - " ", // "HH OC", - " 5", // " 5 past HH", - "10", // "10 past HH", - "15", // "15 past HH", - "20", // "20 past HH", - "25", // "25 past HH", - "30", // "30 past HH", - "35", // "35 past HH", - "40", // "20 two HH+1", - "15", // "15 two HH+1", - "10", // "10 two HH+1", - " 5" // " 5 two HH+1" +const char *words[12][2] = { + {" ", "OC"}, // "HH OC", + {" 5", " P"}, // " 5 past HH", + {"10", " P"}, // "10 past HH", + {"15", " P"}, // "15 past HH", + {"20", " P"}, // "20 past HH", + {"25", " P"}, // "25 past HH", + {"30", " P"}, // "30 past HH", + {"35", " P"}, // "35 past HH", + {"40", " P"}, // "40 past HH", + {"15", " 2"}, // "15 two HH+1", + {"10", " 2"}, // "10 two HH+1", + {" 5", " 2"}, // " 5 two HH+1", }; -static const char second_words[12][3] = { - "OC", // "HH OC", - " P", // " 5 past HH", - " P", // "10 past HH", - " P", // "15 past HH", - " P", // "20 past HH", - " P", // "25 past HH", - " P", // "30 past HH", - " P", // "35 past HH", - " 2", // "20 two HH+1", - " 2", // "15 two HH+1", - " 2", // "10 two HH+1", - " 2" // " 5 two HH+1" -}; - -static const int hour_switch_index = 8; +static const int hour_switch_index = 9; static void _update_alarm_indicator(bool settings_alarm_enabled, close_enough_clock_state_t *state) { state->alarm_enabled = settings_alarm_enabled; @@ -95,20 +80,26 @@ void close_enough_clock_face_activate(movement_settings_t *settings, void *conte // this ensures that none of the five_minute_periods will match, so we always rerender when the face activates state->prev_five_minute_period = -1; + state->prev_min_checked = -1; } bool close_enough_clock_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { close_enough_clock_state_t *state = (close_enough_clock_state_t *)context; - char buf[11]; + char buf[11]; watch_date_time date_time; + bool show_next_hour = false; int prev_five_minute_period; + int prev_min_checked; + int close_enough_hour; + switch (event.event_type) { case EVENT_ACTIVATE: case EVENT_TICK: case EVENT_LOW_ENERGY_UPDATE: date_time = watch_rtc_get_date_time(); prev_five_minute_period = state->prev_five_minute_period; + prev_min_checked = state->prev_min_checked; // check the battery voltage once a day... if (date_time.unit.day != state->last_battery_check) { @@ -126,27 +117,51 @@ bool close_enough_clock_face_loop(movement_event_t event, movement_settings_t *s watch_set_indicator(WATCH_INDICATOR_LAP); } + // same minute, skip update + if (date_time.unit.minute == prev_min_checked) { + break; + } else { + state->prev_min_checked = date_time.unit.minute; + } + int five_minute_period = (date_time.unit.minute / 5) % 12; + // If we are 60% to the next 5 interval, move up to the next period + if (fmodf(date_time.unit.minute / 5.0f, 1.0f) > 0.5f) { + // If we are on the last 5 interval and moving to the next period we need to display the next hour because we are wrapping around + if (five_minute_period == 11) { + show_next_hour = true; + } + + five_minute_period = (five_minute_period + 1) % 12; + } + // same five_minute_period, skip update if (five_minute_period == prev_five_minute_period) { break; } - // move from "x mins past y" to "x mins to y+1" - if (five_minute_period >= hour_switch_index) { - date_time.unit.hour += 1; - date_time.unit.hour %= 24; + // we don't want to modify date_time.unit.hour just in case other watch faces use it + close_enough_hour = date_time.unit.hour; + + // move from "MM(mins) P HH" to "MM(mins) 2 HH+1" + if (five_minute_period >= hour_switch_index || show_next_hour) { + close_enough_hour = (close_enough_hour + 1) % 24; } if (!settings->bit.clock_mode_24h) { // if we are in 12 hour mode, do some cleanup. - if (date_time.unit.hour < 12) { + if (close_enough_hour < 12) { watch_clear_indicator(WATCH_INDICATOR_PM); } else { watch_set_indicator(WATCH_INDICATOR_PM); } + close_enough_hour %= 12; + if (close_enough_hour == 0) { + close_enough_hour = 12; + } + date_time.unit.hour %= 12; if (date_time.unit.hour == 0) { date_time.unit.hour = 12; @@ -156,14 +171,14 @@ bool close_enough_clock_face_loop(movement_event_t event, movement_settings_t *s char first_word[3]; char second_word[3]; char third_word[3]; - if (five_minute_period == 0) { - sprintf(first_word, "%2d", date_time.unit.hour); - strncpy(second_word, first_words[five_minute_period], 3); - strncpy(third_word, second_words[five_minute_period], 3); + if (five_minute_period == 0) { // "HH OC", + sprintf(first_word, "%2d", close_enough_hour); + strncpy(second_word, words[five_minute_period][0], 3); + strncpy(third_word, words[five_minute_period][1], 3); } else { - strncpy(first_word, first_words[five_minute_period], 3); - strncpy(second_word, second_words[five_minute_period], 3); - sprintf(third_word, "%2d", date_time.unit.hour); + strncpy(first_word, words[five_minute_period][0], 3); + strncpy(second_word, words[five_minute_period][1], 3); + sprintf(third_word, "%2d", close_enough_hour); } sprintf( diff --git a/movement/watch_faces/clock/close_enough_clock_face.h b/movement/watch_faces/clock/close_enough_clock_face.h index 341357b..736a6c6 100644 --- a/movement/watch_faces/clock/close_enough_clock_face.h +++ b/movement/watch_faces/clock/close_enough_clock_face.h @@ -40,6 +40,7 @@ typedef struct { int prev_five_minute_period; + int prev_min_checked; uint8_t last_battery_check; bool battery_low; bool alarm_enabled; From af0f8d273259e8898ff4dafdcaff8e067ecb0c53 Mon Sep 17 00:00:00 2001 From: Ruben Nic Date: Sat, 18 May 2024 10:44:43 -0400 Subject: [PATCH 021/161] If the alarm is enabled show bell not signal --- movement/watch_faces/clock/close_enough_clock_face.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/movement/watch_faces/clock/close_enough_clock_face.c b/movement/watch_faces/clock/close_enough_clock_face.c index 19bde69..341942e 100644 --- a/movement/watch_faces/clock/close_enough_clock_face.c +++ b/movement/watch_faces/clock/close_enough_clock_face.c @@ -49,9 +49,9 @@ static const int hour_switch_index = 9; static void _update_alarm_indicator(bool settings_alarm_enabled, close_enough_clock_state_t *state) { state->alarm_enabled = settings_alarm_enabled; if (state->alarm_enabled) { - watch_set_indicator(WATCH_INDICATOR_SIGNAL); + watch_set_indicator(WATCH_INDICATOR_BELL); } else { - watch_clear_indicator(WATCH_INDICATOR_SIGNAL); + watch_clear_indicator(WATCH_INDICATOR_BELL); }; } From 7a2ecad334319acd4ea5ae585ad82b92990a1fca Mon Sep 17 00:00:00 2001 From: Ruben Nic Date: Sat, 25 May 2024 18:16:50 -0400 Subject: [PATCH 022/161] Custom setting of switch from past to to index --- .../clock/close_enough_clock_face.c | 55 +++++++++++++------ 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/movement/watch_faces/clock/close_enough_clock_face.c b/movement/watch_faces/clock/close_enough_clock_face.c index 341942e..cbd62e2 100644 --- a/movement/watch_faces/clock/close_enough_clock_face.c +++ b/movement/watch_faces/clock/close_enough_clock_face.c @@ -29,22 +29,28 @@ #include "watch.h" #include "watch_utility.h" -const char *words[12][2] = { - {" ", "OC"}, // "HH OC", - {" 5", " P"}, // " 5 past HH", - {"10", " P"}, // "10 past HH", - {"15", " P"}, // "15 past HH", - {"20", " P"}, // "20 past HH", - {"25", " P"}, // "25 past HH", - {"30", " P"}, // "30 past HH", - {"35", " P"}, // "35 past HH", - {"40", " P"}, // "40 past HH", - {"15", " 2"}, // "15 two HH+1", - {"10", " 2"}, // "10 two HH+1", - {" 5", " 2"}, // " 5 two HH+1", +const char *words[12] = { + " ", + " 5", + "10", + "15", + "20", + "25", + "30", + "35", + "40", + "45", + "50", + "55", }; -static const int hour_switch_index = 9; +static const char *past_word = " P"; +static const char *to_word = " 2"; +static const char *oclock_word = "OC"; + +// sets when in the five minute period we switch +// from "X past HH" to "X to HH+1" +static const int hour_switch_index = 8; static void _update_alarm_indicator(bool settings_alarm_enabled, close_enough_clock_state_t *state) { state->alarm_enabled = settings_alarm_enabled; @@ -173,11 +179,24 @@ bool close_enough_clock_face_loop(movement_event_t event, movement_settings_t *s char third_word[3]; if (five_minute_period == 0) { // "HH OC", sprintf(first_word, "%2d", close_enough_hour); - strncpy(second_word, words[five_minute_period][0], 3); - strncpy(third_word, words[five_minute_period][1], 3); + strncpy(second_word, words[five_minute_period], 3); + strncpy(third_word, oclock_word, 3); } else { - strncpy(first_word, words[five_minute_period][0], 3); - strncpy(second_word, words[five_minute_period][1], 3); + int words_length = sizeof(words) / sizeof(words[0]); + + strncpy( + first_word, + five_minute_period >= hour_switch_index ? + words[words_length - five_minute_period] : + words[five_minute_period], + 3 + ); + strncpy( + second_word, + five_minute_period >= hour_switch_index ? + to_word : past_word, + 3 + ); sprintf(third_word, "%2d", close_enough_hour); } From 3eaf807590539e6f182bac9204b46f5b6681fccc Mon Sep 17 00:00:00 2001 From: voloved <36523934+voloved@users.noreply.github.com> Date: Sun, 7 Jul 2024 19:23:31 -0400 Subject: [PATCH 023/161] Added Timeout; Ability to turn off LED and Sound; Added doublespeed mode. (#1) * Check that color is valid Instead of merely checking that COLOR is set, check that it is one of RED, BLUE or GREEN * Added ability to turn off sound and timer with modes * Added enum for mode --------- Co-authored-by: Wesley Ellis --- make.mk | 6 + .../watch_faces/complication/simon_face.c | 110 ++++++++++++++++-- .../watch_faces/complication/simon_face.h | 14 +++ 3 files changed, 119 insertions(+), 11 deletions(-) diff --git a/make.mk b/make.mk index bb2d153..00933d0 100644 --- a/make.mk +++ b/make.mk @@ -213,6 +213,12 @@ ifndef COLOR $(error Set the COLOR variable to RED, BLUE, or GREEN depending on what board you have.) endif +COLOR_VALID := $(filter $(COLOR),RED BLUE GREEN) + +ifeq ($(COLOR_VALID),) +$(error COLOR must be RED, BLUE, or GREEN) +endif + ifeq ($(COLOR), BLUE) CFLAGS += -DWATCH_IS_BLUE_BOARD endif diff --git a/movement/watch_faces/complication/simon_face.c b/movement/watch_faces/complication/simon_face.c index 99fc160..eb08f17 100644 --- a/movement/watch_faces/complication/simon_face.c +++ b/movement/watch_faces/complication/simon_face.c @@ -33,6 +33,10 @@ #endif static char _simon_display_buf[12]; +static uint8_t _timer; +static uint16_t _delay_beep; +static uint16_t _timeout; +static uint8_t _secSub; static inline uint8_t _simon_get_rand_num(uint8_t num_values) { #if __EMSCRIPTEN__ @@ -55,7 +59,26 @@ static void _simon_not_playing_display(simon_state_t *state) { _simon_clear_display(state); sprintf(_simon_display_buf, "SI %d", state->best_score); + if (!state->soundOff) + watch_set_indicator(WATCH_INDICATOR_BELL); + else + watch_clear_indicator(WATCH_INDICATOR_BELL); + if (!state->lightOff) + watch_set_indicator(WATCH_INDICATOR_SIGNAL); + else + watch_clear_indicator(WATCH_INDICATOR_SIGNAL); watch_display_string(_simon_display_buf, 0); + switch (state->mode) + { + case SIMON_MODE_EASY: + watch_display_string("E", 9); + break; + case SIMON_MODE_HARD: + watch_display_string("H", 9); + break; + default: + break; + } } static void _simon_reset(simon_state_t *state) { @@ -91,19 +114,31 @@ static void _simon_play_note(SimonNote note, simon_state_t *state, bool skip_res _simon_display_note(note, state); switch (note) { case SIMON_LED_NOTE: - watch_set_led_yellow(); - watch_buzzer_play_note(BUZZER_NOTE_D3, 300); + if (!state->lightOff) watch_set_led_yellow(); + if (state->soundOff) + delay_ms(_delay_beep); + else + watch_buzzer_play_note(BUZZER_NOTE_D3, _delay_beep); break; case SIMON_MODE_NOTE: - watch_set_led_red(); - watch_buzzer_play_note(BUZZER_NOTE_E4, 300); + if (!state->lightOff) watch_set_led_red(); + if (state->soundOff) + delay_ms(_delay_beep); + else + watch_buzzer_play_note(BUZZER_NOTE_E4, _delay_beep); break; case SIMON_ALARM_NOTE: - watch_set_led_green(); - watch_buzzer_play_note(BUZZER_NOTE_C3, 300); + if (!state->lightOff) watch_set_led_green(); + if (state->soundOff) + delay_ms(_delay_beep); + else + watch_buzzer_play_note(BUZZER_NOTE_C3, _delay_beep); break; case SIMON_WRONG_NOTE: - watch_buzzer_play_note(BUZZER_NOTE_A1, 800); + if (state->soundOff) + delay_ms(800); + else + watch_buzzer_play_note(BUZZER_NOTE_A1, 800); break; } watch_set_led_off(); @@ -111,7 +146,7 @@ static void _simon_play_note(SimonNote note, simon_state_t *state, bool skip_res if (note != SIMON_WRONG_NOTE) { _simon_clear_display(state); if (!skip_rest) { - watch_buzzer_play_note(BUZZER_NOTE_REST, 200); + watch_buzzer_play_note(BUZZER_NOTE_REST, (_delay_beep * 2)/3); } } } @@ -134,6 +169,7 @@ static void _simon_listen(SimonNote note, simon_state_t *state) { if (state->sequence[state->listen_index] == note) { _simon_play_note(note, state, true); state->listen_index++; + _timer = 0; if (state->listen_index == state->sequence_length) { state->playing_state = SIMON_READY_FOR_NEXT_NOTE; @@ -149,6 +185,22 @@ static void _simon_begin_listening(simon_state_t *state) { state->listen_index = 0; } +static void _simon_change_speed(simon_state_t *state){ + switch (state->mode) + { + case SIMON_MODE_HARD: + _delay_beep = DELAY_FOR_TONE_MS / 2; + _secSub = SIMON_FACE_FREQUENCY / 2; + _timeout = (TIMER_MAX * SIMON_FACE_FREQUENCY) / 2; + break; + default: + _delay_beep = DELAY_FOR_TONE_MS; + _secSub = SIMON_FACE_FREQUENCY; + _timeout = TIMER_MAX * SIMON_FACE_FREQUENCY; + break; + } +} + void simon_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr) { (void)settings; @@ -171,6 +223,10 @@ void simon_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void simon_face_activate(movement_settings_t *settings, void *context) { (void) settings; (void) context; + simon_state_t *state = (simon_state_t *)context; + _simon_change_speed(state); + movement_request_tick_frequency(SIMON_FACE_FREQUENCY); + _timer = 0; } bool simon_face_loop(movement_event_t event, movement_settings_t *settings, @@ -183,9 +239,16 @@ bool simon_face_loop(movement_event_t event, movement_settings_t *settings, _simon_reset(state); break; case EVENT_TICK: - if (state->playing_state == SIMON_READY_FOR_NEXT_NOTE) { - _simon_setup_next_note(state); - } else if (state->playing_state == SIMON_TEACHING) { + if (state->playing_state == SIMON_LISTENING_BACK && state->mode != SIMON_MODE_EASY) + { + _timer++; + if(_timer >= (_timeout)){ + _timer = 0; + _simon_play_note(SIMON_WRONG_NOTE, state, true); + _simon_reset(state); + } + } + else if (state->playing_state == SIMON_TEACHING && event.subsecond == 0) { SimonNote note = state->sequence[state->teaching_index]; // if this is the final note in the sequence, don't play the rest to let // the player jump in faster @@ -196,12 +259,32 @@ bool simon_face_loop(movement_event_t event, movement_settings_t *settings, _simon_begin_listening(state); } } + else if (state->playing_state == SIMON_READY_FOR_NEXT_NOTE && (event.subsecond % _secSub) == 0) { + _timer = 0; + _simon_setup_next_note(state); + } break; case EVENT_LIGHT_BUTTON_DOWN: break; + case EVENT_LIGHT_LONG_PRESS: + if (state->playing_state == SIMON_NOT_PLAYING) { + state->lightOff = !state->lightOff; + _simon_not_playing_display(state); + } + break; + case EVENT_ALARM_LONG_PRESS: + if (state->playing_state == SIMON_NOT_PLAYING) { + state->soundOff = !state->soundOff; + _simon_not_playing_display(state); + if (!state->soundOff) + watch_buzzer_play_note(BUZZER_NOTE_D3, _delay_beep); + } + break; case EVENT_LIGHT_BUTTON_UP: if (state->playing_state == SIMON_NOT_PLAYING) { state->sequence_length = 0; + watch_clear_indicator(WATCH_INDICATOR_BELL); + watch_clear_indicator(WATCH_INDICATOR_SIGNAL); _simon_setup_next_note(state); } else if (state->playing_state == SIMON_LISTENING_BACK) { _simon_listen(SIMON_LED_NOTE, state); @@ -226,6 +309,11 @@ bool simon_face_loop(movement_event_t event, movement_settings_t *settings, if (state->playing_state == SIMON_LISTENING_BACK) { _simon_listen(SIMON_ALARM_NOTE, state); } + else if (state->playing_state == SIMON_NOT_PLAYING){ + state->mode = (state->mode + 1) % SIMON_MODE_TOTAL; + _simon_change_speed(state); + _simon_not_playing_display(state); + } break; case EVENT_TIMEOUT: movement_move_to_face(0); diff --git a/movement/watch_faces/complication/simon_face.h b/movement/watch_faces/complication/simon_face.h index b59d56c..44bc9f3 100644 --- a/movement/watch_faces/complication/simon_face.h +++ b/movement/watch_faces/complication/simon_face.h @@ -71,12 +71,22 @@ typedef enum SimonPlayingState { SIMON_READY_FOR_NEXT_NOTE } SimonPlayingState; +typedef enum SimonMode { + SIMON_MODE_NORMAL = 0, // 5 Second timeout if nothing is input + SIMON_MODE_EASY, // There is no timeout in this mode + SIMON_MODE_HARD, // The speed of the teaching is doubled and th etimeout is halved + SIMON_MODE_TOTAL +} SimonMode; + typedef struct { uint8_t best_score; SimonNote sequence[MAX_SEQUENCE]; uint8_t sequence_length; uint8_t teaching_index; uint8_t listen_index; + bool soundOff; + bool lightOff; + uint8_t mode:6; SimonPlayingState playing_state; } simon_state_t; @@ -94,4 +104,8 @@ void simon_face_resign(movement_settings_t *settings, void *context); NULL, \ }) +#define TIMER_MAX 5 +#define SIMON_FACE_FREQUENCY 8 +#define DELAY_FOR_TONE_MS 300 + #endif // SIMON_FACE_H_ From 5f1a6517320dabb13c65c8311e9a88281b15764e Mon Sep 17 00:00:00 2001 From: James Haggerty Date: Wed, 17 Apr 2024 20:54:46 +1000 Subject: [PATCH 024/161] Keep light on if interacting This makes it possible to do a bunch of things without having to keep touching the light button. I don't really see any downside with this. If you want the light to go off, just stop touching buttons. --- movement/movement.c | 11 +++++++++++ watch-library/simulator/main.c | 1 + 2 files changed, 12 insertions(+) diff --git a/movement/movement.c b/movement/movement.c index cb3dcf7..8f9a45d 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -543,6 +543,17 @@ bool app_loop(void) { event.subsecond = movement_state.subsecond; // the first trip through the loop overrides the can_sleep state can_sleep = wf->loop(event, &movement_state.settings, watch_face_contexts[movement_state.current_face_idx]); + + // Keep light on if user is still interacting with the watch. + if (movement_state.light_ticks > 0) { + switch (event.event_type) { + case EVENT_LIGHT_BUTTON_DOWN: + case EVENT_MODE_BUTTON_DOWN: + case EVENT_ALARM_BUTTON_DOWN: + movement_illuminate_led(); + } + } + event.event_type = EVENT_NONE; } diff --git a/watch-library/simulator/main.c b/watch-library/simulator/main.c index 6898fd0..5e5070f 100644 --- a/watch-library/simulator/main.c +++ b/watch-library/simulator/main.c @@ -89,6 +89,7 @@ void main_loop_sleep(uint32_t ms) { main_loop_set_sleeping(true); emscripten_sleep(ms); main_loop_set_sleeping(false); + animation_frame_id = ANIMATION_FRAME_ID_INVALID; } bool main_loop_is_sleeping(void) { From c8702d346e4c2e30f1f789844db127f6bb369d2f Mon Sep 17 00:00:00 2001 From: voloved <36523934+voloved@users.noreply.github.com> Date: Tue, 23 Jul 2024 05:35:38 -0400 Subject: [PATCH 025/161] Added subscreens to periodic table face; added title and faster scrolling (#1) * Added subscreens to periodic table face; added title and faster scrolling * Resized buf for element display * Fixed scrolling to work on actual hardware * Added delay before _loop_text at title and bugfix on elements shorter than 6 char * Title screen displays when le_mode starts * Added documentation on usage and removed unneeded variable --- .../watch_faces/complication/periodic_face.c | 599 +++++++++++------- .../watch_faces/complication/periodic_face.h | 29 +- 2 files changed, 388 insertions(+), 240 deletions(-) diff --git a/movement/watch_faces/complication/periodic_face.c b/movement/watch_faces/complication/periodic_face.c index 6746c26..cee0033 100644 --- a/movement/watch_faces/complication/periodic_face.c +++ b/movement/watch_faces/complication/periodic_face.c @@ -26,6 +26,15 @@ #include #include "periodic_face.h" +#define FREQ_FAST 8 +#define FREQ 2 + +static bool _quick_ticks_running; +static uint8_t _ts_ticks = 0; +static int16_t _text_pos; +static const char* _text_looping; +static const char title_text[] = "Periodic Table"; + void periodic_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr) { (void)settings; @@ -42,304 +51,418 @@ void periodic_face_activate(movement_settings_t *settings, void *context) (void)settings; periodic_state_t *state = (periodic_state_t *)context; - state->atomic_num = 1; + state->atomic_num = 0; state->mode = 0; state->selection_index = 0; - - movement_request_tick_frequency(2); + _quick_ticks_running = false; + movement_request_tick_frequency(FREQ); } typedef struct { - char name[3]; - uint16_t atomic_mass; + char symbol[3]; + char name[14]; // Longest is Rutherfordium + int16_t year_discovered; // Negative is BC + uint16_t atomic_mass; // In units of 0.01 AMU + uint16_t electronegativity; // In units of 0.01 char group[3]; } element; +typedef enum { + SCREEN_TITLE = 0, + SCREEN_ELEMENT, + SCREEN_ATOMIC_MASS, + SCREEN_DISCOVER_YEAR, + SCREEN_ELECTRONEGATIVITY, + SCREEN_FULL_NAME, + SCREENS_COUNT +} PeriodicScreens; + +const char screen_name[SCREENS_COUNT][3] = { + [SCREEN_ATOMIC_MASS] = "am", + [SCREEN_DISCOVER_YEAR] = " y", + [SCREEN_ELECTRONEGATIVITY] = "EL", + [SCREEN_FULL_NAME] = " n", +}; + // Comments on the table denote symbols that cannot be displayed #define MAX_ELEMENT 118 const element table[MAX_ELEMENT] = { - {"H ", 1, " "}, - {"He", 4, " 0"}, - {"Li", 7, " 1"}, - {"Be", 9, " 2"}, - {"B ", 11, " 3"}, - {"C ", 12, " 4"}, - {"N ", 14, " 5"}, - {"O ", 16, " 6"}, - {"F ", 19, " 7"}, - {"Ne", 20, " 0"}, - {"Na", 23, " 1"}, - {"Mg", 24, " 2"}, // - {"Al", 27, " 3"}, - {"Si", 28, " 4"}, - {"P ", 31, " 5"}, - {"S ", 32, " 6"}, - {"Cl", 355, " 7"}, - {"Ar", 40, " 0"}, - {"K ", 39, " 1"}, - {"Ca", 40, " 2"}, - {"Sc", 45, " T"}, - {"Ti", 48, " T"}, - {" W", 51, " T"}, // "V" - {"Cr", 52, " T"}, - {"Mn", 55, " T"}, - {"Fe", 56, " T"}, - {"Co", 59, " T"}, - {"Ni", 59, " T"}, - {"Cu", 635, " T"}, - {"Zn", 65, " T"}, - {"Ga", 70, " 3"}, - {"Ge", 73, " 4"}, - {"As", 75, " 5"}, // - {"Se", 79, " 6"}, - {"Br", 80, " 7"}, - {"Kr", 84, " 0"}, - {"Rb", 85, " 1"}, - {"Sr", 88, " 2"}, - {"Y ", 89, " T"}, - {"Zr", 91, " T"}, - {"Nb", 93, " T"}, - {"Mo", 96, " T"}, - {"Tc", 97, " T"}, - {"Ru", 101, " T"}, - {"Rh", 103, " T"}, - {"Pd", 106, " T"}, - {"Ag", 108, " T"}, // - {"Cd", 112, " T"}, - {"In", 115, " 3"}, - {"Sn", 119, " 4"}, - {"Sb", 122, " 5"}, - {"Te", 128, " 6"}, - {"I ", 127, " 7"}, - {"Xe", 131, " 0"}, - {"CS", 133, " 1"}, - {"Ba", 137, " 2"}, - {"La", 139, "1a"}, // La - {"Ce", 140, "1a"}, - {"Pr", 141, "1a"}, - {"Nd", 144, "1a"}, - {"Pm", 145, "1a"}, - {"Sm", 150, "1a"}, - {"Eu", 152, "1a"}, - {"Gd", 157, "1a"}, - {"Tb", 159, "1a"}, - {"Dy", 163, "1a"}, // .5 Rounded up due to space constraints - {"Ho", 165, "1a"}, - {"Er", 167, "1a"}, - {"Tm", 169, "1a"}, - {"Yb", 173, "1a"}, - {"Lu", 175, "1a"}, - {"Hf", 179, " T"}, - {"Ta", 181, " T"}, - {"W ", 184, " T"}, - {"Re", 186, " T"}, - {"OS", 190, " T"}, - {"Ir", 192, " T"}, - {"Pt", 195, " T"}, - {"Au", 197, " T"}, - {"Hg", 201, " T"}, // - {"Tl", 204, " 3"}, - {"Pb", 207, " 4"}, - {"Bi", 209, " 5"}, - {"Po", 209, " 6"}, - {"At", 210, " 7"}, - {"Rn", 222, " 0"}, - {"Fr", 223, " 1"}, - {"Ra", 226, " 2"}, - {"Ac", 227, "Ac"}, - {"Th", 232, "Ac"}, - {"Pa", 231, "Ac"}, - {"U ", 238, "Ac"}, - {"Np", 237, "Ac"}, // - {"Pu", 244, "Ac"}, - {"Am", 243, "Ac"}, - {"Cm", 247, "Ac"}, - {"Bk", 247, "Ac"}, // - {"Cf", 251, "Ac"}, - {"Es", 252, "Ac"}, // - {"Fm", 257, "Ac"}, - {"Md", 258, "Ac"}, - {"No", 259, "Ac"}, - {"Lr", 262, "Ac"}, - {"Rf", 267, " T"}, - {"Db", 262, " T"}, - {"Sg", 269, " T"}, // - {"Bh", 264, " T"}, - {"Hs", 269, " T"}, - {"Mt", 278, " T"}, - {"Ds", 281, " T"}, // - {"Rg", 282, " T"}, // - {"Cn", 285, " T"}, - {"Nh", 286, " 3"}, - {"Fl", 289, " 4"}, - {"Mc", 289, " 5"}, - {"LW", 293, " 6"}, // Lv - {"Ts", 294, " 7"}, // - {"Og", 294, " 0"}, // + { .symbol = "H", .name = "Hydrogen", .year_discovered = 1671, .atomic_mass = 101, .electronegativity = 220, .group = " " }, + { .symbol = "HE", .name = "Helium", .year_discovered = 1868, .atomic_mass = 400, .electronegativity = 0, .group = "0" }, + { .symbol = "LI", .name = "Lithium", .year_discovered = 1817, .atomic_mass = 694, .electronegativity = 98, .group = "1" }, + { .symbol = "BE", .name = "Beryllium", .year_discovered = 1798, .atomic_mass = 901, .electronegativity = 157, .group = "2" }, + { .symbol = "B", .name = "Boron", .year_discovered = 1787, .atomic_mass = 1081, .electronegativity = 204, .group = "3" }, + { .symbol = "C", .name = "Carbon", .year_discovered = -26000, .atomic_mass = 1201, .electronegativity = 255, .group = "4" }, + { .symbol = "N", .name = "Nitrogen", .year_discovered = 1772, .atomic_mass = 1401, .electronegativity = 304, .group = "5" }, + { .symbol = "O", .name = "Oxygen", .year_discovered = 1771, .atomic_mass = 1600, .electronegativity = 344, .group = "6" }, + { .symbol = "F", .name = "Fluorine", .year_discovered = 1771, .atomic_mass = 1900, .electronegativity = 398, .group = "7" }, + { .symbol = "NE", .name = "Neon", .year_discovered = 1898, .atomic_mass = 2018, .electronegativity = 0, .group = "0" }, + { .symbol = "NA", .name = "Sodium", .year_discovered = 1702, .atomic_mass = 2299, .electronegativity = 93, .group = "1" }, + { .symbol = "MG", .name = "Magnesium", .year_discovered = 1755, .atomic_mass = 2431, .electronegativity = 131, .group = "2" }, + { .symbol = "AL", .name = "Aluminium", .year_discovered = 1746, .atomic_mass = 2698, .electronegativity = 161, .group = "3" }, + { .symbol = "SI", .name = "Silicon", .year_discovered = 1739, .atomic_mass = 2809, .electronegativity = 190, .group = "4" }, + { .symbol = "P", .name = "Phosphorus", .year_discovered = 1669, .atomic_mass = 3097, .electronegativity = 219, .group = "5" }, + { .symbol = "S", .name = "Sulfur", .year_discovered = -2000, .atomic_mass = 3206, .electronegativity = 258, .group = "6" }, + { .symbol = "CL", .name = "Chlorine", .year_discovered = 1774, .atomic_mass = 3545., .electronegativity = 316, .group = "7" }, + { .symbol = "AR", .name = "Argon", .year_discovered = 1894, .atomic_mass = 3995., .electronegativity = 0, .group = "0" }, + { .symbol = "K", .name = "Potassium", .year_discovered = 1702, .atomic_mass = 3910, .electronegativity = 82, .group = "1" }, + { .symbol = "CA", .name = "Calcium", .year_discovered = 1739, .atomic_mass = 4008, .electronegativity = 100, .group = "2" }, + { .symbol = "SC", .name = "Scandium", .year_discovered = 1879, .atomic_mass = 4496, .electronegativity = 136, .group = " T" }, + { .symbol = "TI", .name = "Titanium", .year_discovered = 1791, .atomic_mass = 4787, .electronegativity = 154, .group = " T" }, + { .symbol = "W", .name = "Vanadium", .year_discovered = 1801, .atomic_mass = 5094, .electronegativity = 163, .group = " T" }, + { .symbol = "CR", .name = "Chromium", .year_discovered = 1797, .atomic_mass = 5200, .electronegativity = 166, .group = " T" }, + { .symbol = "MN", .name = "Manganese", .year_discovered = 1774, .atomic_mass = 5494, .electronegativity = 155, .group = " T" }, + { .symbol = "FE", .name = "Iron", .year_discovered = -5000, .atomic_mass = 5585, .electronegativity = 183, .group = " T" }, + { .symbol = "CO", .name = "Cobalt", .year_discovered = 1735, .atomic_mass = 5893, .electronegativity = 188, .group = " T" }, + { .symbol = "NI", .name = "Nickel", .year_discovered = 1751, .atomic_mass = 5869, .electronegativity = 191, .group = " T" }, + { .symbol = "CU", .name = "Copper", .year_discovered = -9000, .atomic_mass = 6355, .electronegativity = 190, .group = " T" }, + { .symbol = "ZN", .name = "Zinc", .year_discovered = -1000, .atomic_mass = 6538, .electronegativity = 165, .group = " T" }, + { .symbol = "GA", .name = "Gallium", .year_discovered = 1875, .atomic_mass = 6972, .electronegativity = 181, .group = "3" }, + { .symbol = "GE", .name = "Germanium", .year_discovered = 1886, .atomic_mass = 7263, .electronegativity = 201, .group = "4" }, + { .symbol = "AS", .name = "Arsenic", .year_discovered = 300, .atomic_mass = 7492, .electronegativity = 218, .group = "5" }, + { .symbol = "SE", .name = "Selenium", .year_discovered = 1817, .atomic_mass = 7897, .electronegativity = 255, .group = "6" }, + { .symbol = "BR", .name = "Bromine", .year_discovered = 1825, .atomic_mass = 7990., .electronegativity = 296, .group = "7" }, + { .symbol = "KR", .name = "Krypton", .year_discovered = 1898, .atomic_mass = 8380, .electronegativity = 300, .group = "0" }, + { .symbol = "RB", .name = "Rubidium", .year_discovered = 1861, .atomic_mass = 8547, .electronegativity = 82, .group = "1" }, + { .symbol = "SR", .name = "Strontium", .year_discovered = 1787, .atomic_mass = 8762, .electronegativity = 95, .group = "2" }, + { .symbol = "Y", .name = "Yttrium", .year_discovered = 1794, .atomic_mass = 8891, .electronegativity = 122, .group = " T" }, + { .symbol = "ZR", .name = "Zirconium", .year_discovered = 1789, .atomic_mass = 9122, .electronegativity = 133, .group = " T" }, + { .symbol = "NB", .name = "Niobium", .year_discovered = 1801, .atomic_mass = 9291, .electronegativity = 160, .group = " T" }, + { .symbol = "MO", .name = "Molybdenum", .year_discovered = 1778, .atomic_mass = 9595, .electronegativity = 216, .group = " T" }, + { .symbol = "TC", .name = "Technetium", .year_discovered = 1937, .atomic_mass = 9700, .electronegativity = 190, .group = " T" }, + { .symbol = "RU", .name = "Ruthenium", .year_discovered = 1844, .atomic_mass = 10107, .electronegativity = 220, .group = " T" }, + { .symbol = "RH", .name = "Rhodium", .year_discovered = 1804, .atomic_mass = 10291, .electronegativity = 228, .group = " T" }, + { .symbol = "PD", .name = "Palladium", .year_discovered = 1802, .atomic_mass = 10642, .electronegativity = 220, .group = " T" }, + { .symbol = "AG", .name = "Silver", .year_discovered = -5000, .atomic_mass = 10787, .electronegativity = 193, .group = " T" }, + { .symbol = "CD", .name = "Cadmium", .year_discovered = 1817, .atomic_mass = 11241, .electronegativity = 169, .group = " T" }, + { .symbol = "IN", .name = "Indium", .year_discovered = 1863, .atomic_mass = 11482, .electronegativity = 178, .group = "3" }, + { .symbol = "SN", .name = "Tin", .year_discovered = -3500, .atomic_mass = 11871, .electronegativity = 196, .group = "4" }, + { .symbol = "SB", .name = "Antimony", .year_discovered = -3000, .atomic_mass = 12176, .electronegativity = 205, .group = "5" }, + { .symbol = "TE", .name = "Tellurium", .year_discovered = 1782, .atomic_mass = 12760, .electronegativity = 210, .group = "6" }, + { .symbol = "I", .name = "Iodine", .year_discovered = 1811, .atomic_mass = 12690, .electronegativity = 266, .group = "7" }, + { .symbol = "XE", .name = "Xenon", .year_discovered = 1898, .atomic_mass = 13129, .electronegativity = 260, .group = "0" }, + { .symbol = "CS", .name = "Caesium", .year_discovered = 1860, .atomic_mass = 13291, .electronegativity = 79, .group = "1" }, + { .symbol = "BA", .name = "Barium", .year_discovered = 1772, .atomic_mass = 13733., .electronegativity = 89, .group = "2" }, + { .symbol = "LA", .name = "Lanthanum", .year_discovered = 1838, .atomic_mass = 13891, .electronegativity = 110, .group = "1a" }, + { .symbol = "CE", .name = "Cerium", .year_discovered = 1803, .atomic_mass = 14012, .electronegativity = 112, .group = "1a" }, + { .symbol = "PR", .name = "Praseodymium", .year_discovered = 1885, .atomic_mass = 14091, .electronegativity = 113, .group = "1a" }, + { .symbol = "ND", .name = "Neodymium", .year_discovered = 1841, .atomic_mass = 14424, .electronegativity = 114, .group = "1a" }, + { .symbol = "PM", .name = "Promethium", .year_discovered = 1945, .atomic_mass = 14500, .electronegativity = 113, .group = "1a" }, + { .symbol = "SM", .name = "Samarium", .year_discovered = 1879, .atomic_mass = 15036., .electronegativity = 117, .group = "1a" }, + { .symbol = "EU", .name = "Europium", .year_discovered = 1896, .atomic_mass = 15196, .electronegativity = 120, .group = "1a" }, + { .symbol = "GD", .name = "Gadolinium", .year_discovered = 1880, .atomic_mass = 15725, .electronegativity = 120, .group = "1a" }, + { .symbol = "TB", .name = "Terbium", .year_discovered = 1843, .atomic_mass = 15893, .electronegativity = 120, .group = "1a" }, + { .symbol = "DY", .name = "Dysprosium", .year_discovered = 1886, .atomic_mass = 16250, .electronegativity = 122, .group = "1a" }, + { .symbol = "HO", .name = "Holmium", .year_discovered = 1878, .atomic_mass = 16493, .electronegativity = 123, .group = "1a" }, + { .symbol = "ER", .name = "Erbium", .year_discovered = 1843, .atomic_mass = 16726, .electronegativity = 124, .group = "1a" }, + { .symbol = "TM", .name = "Thulium", .year_discovered = 1879, .atomic_mass = 16893, .electronegativity = 125, .group = "1a" }, + { .symbol = "YB", .name = "Ytterbium", .year_discovered = 1878, .atomic_mass = 17305, .electronegativity = 110, .group = "1a" }, + { .symbol = "LU", .name = "Lutetium", .year_discovered = 1906, .atomic_mass = 17497, .electronegativity = 127, .group = "1a" }, + { .symbol = "HF", .name = "Hafnium", .year_discovered = 1922, .atomic_mass = 17849, .electronegativity = 130, .group = " T" }, + { .symbol = "TA", .name = "Tantalum", .year_discovered = 1802, .atomic_mass = 18095, .electronegativity = 150, .group = " T" }, + { .symbol = "W", .name = "Tungsten", .year_discovered = 1781, .atomic_mass = 18384, .electronegativity = 236, .group = " T" }, + { .symbol = "RE", .name = "Rhenium", .year_discovered = 1908, .atomic_mass = 18621, .electronegativity = 190, .group = " T" }, + { .symbol = "OS", .name = "Osmium", .year_discovered = 1803, .atomic_mass = 19023, .electronegativity = 220, .group = " T" }, + { .symbol = "IR", .name = "Iridium", .year_discovered = 1803, .atomic_mass = 19222, .electronegativity = 220, .group = " T" }, + { .symbol = "PT", .name = "Platinum", .year_discovered = -600, .atomic_mass = 19508, .electronegativity = 228, .group = " T" }, + { .symbol = "AU", .name = "Gold", .year_discovered = -6000, .atomic_mass = 19697, .electronegativity = 254, .group = " T" }, + { .symbol = "HG", .name = "Mercury", .year_discovered = -1500, .atomic_mass = 20059, .electronegativity = 200, .group = " T" }, + { .symbol = "TL", .name = "Thallium", .year_discovered = 1861, .atomic_mass = 20438, .electronegativity = 162, .group = "3" }, + { .symbol = "PB", .name = "Lead", .year_discovered = -7000, .atomic_mass = 20720, .electronegativity = 187, .group = "4" }, + { .symbol = "BI", .name = "Bismuth", .year_discovered = 1500, .atomic_mass = 20898, .electronegativity = 202, .group = "5" }, + { .symbol = "PO", .name = "Polonium", .year_discovered = 1898, .atomic_mass = 20900, .electronegativity = 200, .group = "6" }, + { .symbol = "AT", .name = "Astatine", .year_discovered = 1940, .atomic_mass = 21000, .electronegativity = 220, .group = "7" }, + { .symbol = "RN", .name = "Radon", .year_discovered = 1899, .atomic_mass = 22200, .electronegativity = 220, .group = "0" }, + { .symbol = "FR", .name = "Francium", .year_discovered = 1939, .atomic_mass = 22300, .electronegativity = 79, .group = "1" }, + { .symbol = "RA", .name = "Radium", .year_discovered = 1898, .atomic_mass = 22600, .electronegativity = 90, .group = "2" }, + { .symbol = "AC", .name = "Actinium", .year_discovered = 1902, .atomic_mass = 22700, .electronegativity = 110, .group = "Ac" }, + { .symbol = "TH", .name = "Thorium", .year_discovered = 1829, .atomic_mass = 23204, .electronegativity = 130, .group = "Ac" }, + { .symbol = "PA", .name = "Protactinium", .year_discovered = 1913, .atomic_mass = 23104, .electronegativity = 150, .group = "Ac" }, + { .symbol = "U", .name = "Uranium", .year_discovered = 1789, .atomic_mass = 23803, .electronegativity = 138, .group = "Ac" }, + { .symbol = "NP", .name = "Neptunium", .year_discovered = 1940, .atomic_mass = 23700, .electronegativity = 136, .group = "Ac" }, + { .symbol = "PU", .name = "Plutonium", .year_discovered = 1941, .atomic_mass = 24400, .electronegativity = 128, .group = "Ac" }, + { .symbol = "AM", .name = "Americium", .year_discovered = 1944, .atomic_mass = 24300, .electronegativity = 113, .group = "Ac" }, + { .symbol = "CM", .name = "Curium", .year_discovered = 1944, .atomic_mass = 24700, .electronegativity = 128, .group = "Ac" }, + { .symbol = "BK", .name = "Berkelium", .year_discovered = 1949, .atomic_mass = 24700, .electronegativity = 130, .group = "Ac" }, + { .symbol = "CF", .name = "Californium", .year_discovered = 1950, .atomic_mass = 25100, .electronegativity = 130, .group = "Ac" }, + { .symbol = "ES", .name = "Einsteinium", .year_discovered = 1952, .atomic_mass = 25200, .electronegativity = 130, .group = "Ac" }, + { .symbol = "FM", .name = "Fermium", .year_discovered = 1953, .atomic_mass = 25700, .electronegativity = 130, .group = "Ac" }, + { .symbol = "MD", .name = "Mendelevium", .year_discovered = 1955, .atomic_mass = 25800, .electronegativity = 130, .group = "Ac" }, + { .symbol = "NO", .name = "Nobelium", .year_discovered = 1965, .atomic_mass = 25900, .electronegativity = 130, .group = "Ac" }, + { .symbol = "LR", .name = "Lawrencium", .year_discovered = 1961, .atomic_mass = 26600, .electronegativity = 130, .group = "Ac" }, + { .symbol = "RF", .name = "Rutherfordium", .year_discovered = 1969, .atomic_mass = 26700, .electronegativity = 0, .group = " T" }, + { .symbol = "DB", .name = "Dubnium", .year_discovered = 1970, .atomic_mass = 26800, .electronegativity = 0, .group = " T" }, + { .symbol = "SG", .name = "Seaborgium", .year_discovered = 1974, .atomic_mass = 26700, .electronegativity = 0, .group = " T" }, + { .symbol = "BH", .name = "Bohrium", .year_discovered = 1981, .atomic_mass = 27000, .electronegativity = 0, .group = " T" }, + { .symbol = "HS", .name = "Hassium", .year_discovered = 1984, .atomic_mass = 27100, .electronegativity = 0, .group = " T" }, + { .symbol = "MT", .name = "Meitnerium", .year_discovered = 1982, .atomic_mass = 27800, .electronegativity = 0, .group = " T" }, + { .symbol = "DS", .name = "Darmstadtium", .year_discovered = 1994, .atomic_mass = 28100, .electronegativity = 0, .group = " T" }, + { .symbol = "RG", .name = "Roentgenium", .year_discovered = 1994, .atomic_mass = 28200, .electronegativity = 0, .group = " T" }, + { .symbol = "CN", .name = "Copernicium", .year_discovered = 1996, .atomic_mass = 28500, .electronegativity = 0, .group = " T" }, + { .symbol = "NH", .name = "Nihonium", .year_discovered = 2004, .atomic_mass = 28600, .electronegativity = 0, .group = "3" }, + { .symbol = "FL", .name = "Flerovium", .year_discovered = 1999, .atomic_mass = 28900, .electronegativity = 0, .group = "4" }, + { .symbol = "MC", .name = "Moscovium", .year_discovered = 2003, .atomic_mass = 29000, .electronegativity = 0, .group = "5" }, + { .symbol = "LW", .name = "Livermorium", .year_discovered = 2000, .atomic_mass = 29300, .electronegativity = 0, .group = "6" }, + { .symbol = "TS", .name = "Tennessine", .year_discovered = 2009, .atomic_mass = 29400, .electronegativity = 0, .group = "7" }, + { .symbol = "OG", .name = "Oganesson", .year_discovered = 2002, .atomic_mass = 29400, .electronegativity = 0, .group = "0" }, }; -// Warning light for symbols that can't be displayed -static void _warning(periodic_state_t *state) -{ - char second_char = table[state->atomic_num - 1].name[1]; - if (second_char == 'p' || second_char == 'g' || second_char == 'y' || second_char == 's') - { - watch_set_indicator(WATCH_INDICATOR_BELL); - } - else - { - watch_clear_indicator(WATCH_INDICATOR_BELL); +static void _make_upper(char *string) { + size_t i = 0; + while(string[i] != 0) { + if (string[i] >= 'a' && string[i] <= 'z') + string[i]-=32; // 32 = 'a'-'A' + i++; } } -// Regular mode display -static void _periodic_face_update_lcd(periodic_state_t *state) +static void _display_element(periodic_state_t *state) { - // Colon as a decimal for Cl & Cu - if (state->atomic_num == 17 || state->atomic_num == 29) - { - watch_set_colon(); - } - else - { - watch_clear_colon(); - } - - _warning(state); + char buf[9]; + char ele[3]; + uint8_t atomic_num = state->atomic_num; + strcpy(ele, table[atomic_num - 1].symbol); + _make_upper(ele); + sprintf(buf, "%2s%3d %-2s", table[atomic_num - 1].group, atomic_num, ele); + watch_display_string(buf, 2); +} +static void _display_atomic_mass(periodic_state_t *state) +{ char buf[11]; - sprintf(buf, "%s%s%-3d%3d", table[state->atomic_num - 1].name, table[state->atomic_num - 1].group, table[state->atomic_num - 1].atomic_mass, state->atomic_num); - watch_display_string(buf, 0); + uint16_t mass = table[state->atomic_num - 1].atomic_mass; + uint16_t integer = mass / 100; + uint16_t decimal = mass % 100; + if (decimal == 0) + sprintf(buf, "%-2s%2s%4d", table[state->atomic_num - 1].symbol, screen_name[state->mode], integer); + else + sprintf(buf, "%-2s%2s%3d_%.2d", table[state->atomic_num - 1].symbol, screen_name[state->mode], integer, decimal); + watch_display_string(buf, 0); } -// Selection mode logic -static void _periodic_face_selection_increment(periodic_state_t *state) +static void _display_year_discovered(periodic_state_t *state) { - uint8_t digit0 = (state->atomic_num / 100) % 10; - uint8_t digit1 = (state->atomic_num / 10) % 10; - uint8_t digit2 = (state->atomic_num) % 10; - - // Increment the selected digit by 1 - switch (state->selection_index) - { - case 0: - digit0 ^= 1; - break; - case 1: - if (digit0 == MAX_ELEMENT / 100 && digit1 == (MAX_ELEMENT / 10) % 10) - digit1 = 0; - else - digit1 = (digit1 + 1) % 10; - break; - case 2: - if (digit0 == MAX_ELEMENT / 100 && digit1 == (MAX_ELEMENT / 10) % 10 && digit2 == MAX_ELEMENT % 10) - digit2 = 0; - else - digit2 = (digit2 + 1) % 10; - break; + char buf[11]; + char year_buf[7]; + int16_t year = table[state->atomic_num - 1].year_discovered; + if (abs(year) > 9999) + sprintf(year_buf, "---- "); + else + sprintf(year_buf, "%4d ", abs(year)); + if (year < 0) { + year_buf[4] = 'b'; + year_buf[5] = 'c'; } - - // Prevent 000 - if (digit0 == 0 && digit1 == 0 && digit2 == 0) { - digit2 = 1; - } - - // Prevent Overflow - if (digit0 == (MAX_ELEMENT / 100) % 10 && digit1 > (MAX_ELEMENT / 10) % 10) - { - digit2 = MAX_ELEMENT % 10; - digit1 = (MAX_ELEMENT / 10) % 10; - } - - state->atomic_num = digit0 * 100 + digit1 * 10 + digit2; + sprintf(buf, "%-2s%-2s%s", table[state->atomic_num - 1].symbol, screen_name[state->mode], year_buf); + watch_display_string(buf, 0); } -// Selection mode display -static void _periodic_face_selection(periodic_state_t *state, uint8_t subsec) +static void _display_name(periodic_state_t *state) { - uint8_t digit0 = (state->atomic_num / 100) % 10; - uint8_t digit1 = (state->atomic_num / 10) % 10; - uint8_t digit2 = (state->atomic_num) % 10; + char buf[11]; + _text_looping = table[state->atomic_num - 1].name; + _text_pos = 0; + sprintf(buf, "%-2s%-2s%s", table[state->atomic_num - 1].symbol, screen_name[state->mode], table[state->atomic_num - 1].name); + watch_display_string(buf, 0); +} - watch_display_string(" ", 0); +static void _display_electronegativity(periodic_state_t *state) +{ + char buf[11]; + uint16_t electronegativity = table[state->atomic_num - 1].electronegativity; + uint16_t integer = electronegativity / 100; + uint16_t decimal = electronegativity % 100; + if (decimal == 0) + sprintf(buf, "%-2s%2s%4d", table[state->atomic_num - 1].symbol, screen_name[state->mode], integer); + else + sprintf(buf, "%-2s%2s%3d_%.2d", table[state->atomic_num - 1].symbol, screen_name[state->mode], integer, decimal); + watch_display_string(buf, 0); +} - char buf[2] = {'\0'}; +static void start_quick_cyc(void){ + _quick_ticks_running = true; + movement_request_tick_frequency(FREQ_FAST); +} - buf[0] = (state->selection_index == 0 && subsec == 0) ? ' ' : digit0 + '0'; - watch_display_string(buf, 5); +static void stop_quick_cyc(void){ + _quick_ticks_running = false; + movement_request_tick_frequency(FREQ); +} - buf[0] = (state->selection_index == 1 && subsec == 0) ? ' ' : digit1 + '0'; - watch_display_string(buf, 6); +static int16_t _loop_text(const char* text, int8_t curr_loc, uint8_t char_len){ + // if curr_loc, then use that many ticks as a delay before looping + char buf[15]; + uint8_t next_pos; + uint8_t text_len = strlen(text); + uint8_t pos = 10 - char_len; + if (curr_loc == -1) curr_loc = 0; // To avoid double-showing the 0 + if (char_len >= text_len || curr_loc < 0) { + sprintf(buf, "%s", text); + watch_display_string(buf, pos); + if (curr_loc < 0) return ++curr_loc; + return 0; + } + else if (curr_loc == (text_len + 1)) + curr_loc = 0; + next_pos = curr_loc + 1; + sprintf(buf, "%.6s %.6s", text + curr_loc, text); + watch_display_string(buf, pos); + return next_pos; +} - buf[0] = (state->selection_index == 2 && subsec == 0) ? ' ' : digit2 + '0'; - watch_display_string(buf, 7); +static void _display_title(periodic_state_t *state){ + state->atomic_num = 0; + watch_clear_colon(); + watch_clear_all_indicators(); + _text_looping = title_text; + _text_pos = FREQ * -1; + _text_pos = _loop_text(_text_looping, _text_pos, 5); +} - char buf2[3]; - sprintf(buf2, "%s", table[state->atomic_num - 1].name); - watch_display_string(buf2, 0); +static void _display_screen(periodic_state_t *state, bool should_sound){ + watch_clear_display(); + watch_clear_all_indicators(); + switch (state->mode) + { + case SCREEN_TITLE: + _display_title(state); + break; + case SCREEN_ELEMENT: + case SCREENS_COUNT: + _display_element(state); + break; + case SCREEN_ATOMIC_MASS: + _display_atomic_mass(state); + break; + case SCREEN_DISCOVER_YEAR: + _display_year_discovered(state); + break; + case SCREEN_ELECTRONEGATIVITY: + _display_electronegativity(state); + break; + case SCREEN_FULL_NAME: + _display_name(state); + break; + } + if (should_sound) watch_buzzer_play_note(BUZZER_NOTE_C7, 50); +} - _warning(state); +static void _handle_forward(periodic_state_t *state, bool should_sound){ + state->atomic_num = (state->atomic_num % MAX_ELEMENT) + 1; // Wraps back to 1 + state->mode = SCREEN_ELEMENT; + _display_screen(state, false); + if (should_sound) watch_buzzer_play_note(BUZZER_NOTE_C7, 50); +} + +static void _handle_backward(periodic_state_t *state, bool should_sound){ + if (state->atomic_num <= 1) state->atomic_num = MAX_ELEMENT; + else state->atomic_num = state->atomic_num - 1; + state->mode = SCREEN_ELEMENT; + _display_screen(state, false); + if (should_sound) watch_buzzer_play_note(BUZZER_NOTE_A6, 50); +} + +static void _handle_mode_still_pressed(periodic_state_t *state, bool should_sound) { + if (_ts_ticks != 0){ + if (!watch_get_pin_level(BTN_MODE)) { + _ts_ticks = 0; + return; + } + else if (--_ts_ticks == 0){ + switch (state->mode) + { + case SCREEN_TITLE: + movement_move_to_face(0); + return; + case SCREEN_ELEMENT: + state->mode = SCREEN_TITLE; + _display_screen(state, should_sound); + break; + default: + state->mode = SCREEN_ELEMENT; + _display_screen(state, should_sound); + break; + } + _ts_ticks = 2; + } + } } bool periodic_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { periodic_state_t *state = (periodic_state_t *)context; - switch (event.event_type) { case EVENT_ACTIVATE: - _periodic_face_update_lcd(state); + state->mode = SCREEN_TITLE; + _display_screen(state, false); break; case EVENT_TICK: - if (state->mode != 0) - { - _periodic_face_selection(state, event.subsecond % 2); + if (state->mode == SCREEN_TITLE) _text_pos = _loop_text(_text_looping, _text_pos, 5); + else if (state->mode == SCREEN_FULL_NAME) _text_pos = _loop_text(_text_looping, _text_pos, 6); + if (_quick_ticks_running) { + if (watch_get_pin_level(BTN_LIGHT)) _handle_backward(state, false); + else if (watch_get_pin_level(BTN_ALARM)) _handle_forward(state, false); + else stop_quick_cyc(); } + + _handle_mode_still_pressed(state, settings->bit.button_should_sound); break; case EVENT_LIGHT_BUTTON_UP: - // Only light LED when in regular mode - if (state->mode != MODE_VIEW) - { - state->selection_index = (state->selection_index + 1) % 3; - _periodic_face_selection(state, event.subsecond % 2); - } + _handle_backward(state, settings->bit.button_should_sound); break; case EVENT_LIGHT_BUTTON_DOWN: - if (state->mode != MODE_SELECT) - movement_illuminate_led(); break; case EVENT_ALARM_BUTTON_UP: - if (state->mode == MODE_VIEW) - { - state->atomic_num = (state->atomic_num % MAX_ELEMENT) + 1; // Wraps back to 1 - _periodic_face_update_lcd(state); - if (settings->bit.button_should_sound) watch_buzzer_play_note(BUZZER_NOTE_C7, 50); - } - else - { - _periodic_face_selection_increment(state); - _periodic_face_selection(state, event.subsecond % 2); - } + _handle_forward(state, settings->bit.button_should_sound); break; case EVENT_ALARM_LONG_PRESS: - // Toggle between selection mode and regular - if (state->mode == MODE_VIEW) - { - state->mode = MODE_SELECT; - _periodic_face_selection(state, event.subsecond % 2); + start_quick_cyc(); + _handle_forward(state, settings->bit.button_should_sound); + break; + case EVENT_LIGHT_LONG_PRESS: + start_quick_cyc(); + _handle_backward(state, settings->bit.button_should_sound); + break; + case EVENT_MODE_BUTTON_UP: + if (state->mode == SCREEN_TITLE) movement_move_to_next_face(); + else { + state->mode = (state->mode + 1) % SCREENS_COUNT; + if (state->mode == SCREEN_TITLE) + state->mode = (state->mode + 1) % SCREENS_COUNT; + if (state->mode == SCREEN_ELEMENT){ + _display_screen(state, false); + if (settings->bit.button_should_sound) watch_buzzer_play_note(BUZZER_NOTE_A6, 50); + } + else + _display_screen(state, settings->bit.button_should_sound); } - else + break; + case EVENT_MODE_LONG_PRESS: + switch (state->mode) { - state->mode = MODE_VIEW; - _periodic_face_update_lcd(state); + case SCREEN_TITLE: + movement_move_to_face(0); + return true; + case SCREEN_ELEMENT: + state->mode = SCREEN_TITLE; + _display_screen(state, settings->bit.button_should_sound); + break; + default: + state->mode = SCREEN_ELEMENT; + _display_screen(state, settings->bit.button_should_sound); + break; } - if (settings->bit.button_should_sound) watch_buzzer_play_note(BUZZER_NOTE_C8, 50); - + _ts_ticks = 2; break; case EVENT_TIMEOUT: break; case EVENT_LOW_ENERGY_UPDATE: + // Display title in LE mode. + if (state->mode == SCREEN_TITLE) break; + state->mode = SCREEN_TITLE; + _display_screen(state, false); break; default: return movement_default_loop_handler(event, settings); diff --git a/movement/watch_faces/complication/periodic_face.h b/movement/watch_faces/complication/periodic_face.h index 56476ca..a459e69 100644 --- a/movement/watch_faces/complication/periodic_face.h +++ b/movement/watch_faces/complication/periodic_face.h @@ -29,9 +29,34 @@ /* * Periodic Table Face + * Allows for viewing data of the Periodic Table on your wrist. + * When looking at an element, it'll show you the atomic number on the center of the screen, + * symbol on the right, and it's group on the top-right. + * Pressing the mode button will cycle through the pages. + * Page 1: Atomic Mass + * Page 2: Year Discovered + * Page 3: Electronegativity + * Page 4: Full Name of the Element + * + * Controls: + * Mode Press + * On Title: Next Screen + * Else: Cycle through info of an element + * Mode Hold + * On Title: First Screen + * On Element Symbol Screen: Go to Title Screen + * Else: Go to Symbol Screen of current element + * If you are in a subscreen and just keep holding MODE, you will go through all of these menus without needing to depress. + * + * Light Press + * Previous Element + * Light Hold + * Fast Cycle through Previous Elements * - * Elements can be viewed sequentially with a short press of the alarm button, - * or the atomic number can be input directly after holding down the alarm button. + * Alarm Press + * Next Element + * Alarm Hold + * Fast Cycle through Next Elements * */ From cb90a1980f98d1f5353721382f59a04337dc096b Mon Sep 17 00:00:00 2001 From: PrimmR Date: Wed, 24 Jul 2024 11:18:24 +0100 Subject: [PATCH 026/161] Added LED button combo --- .../watch_faces/complication/periodic_face.c | 38 +++++++++++++++---- .../watch_faces/complication/periodic_face.h | 4 ++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/movement/watch_faces/complication/periodic_face.c b/movement/watch_faces/complication/periodic_face.c index cee0033..162d3e4 100644 --- a/movement/watch_faces/complication/periodic_face.c +++ b/movement/watch_faces/complication/periodic_face.c @@ -2,6 +2,7 @@ * MIT License * * Copyright (c) 2023 PrimmR + * 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 @@ -34,6 +35,7 @@ static uint8_t _ts_ticks = 0; static int16_t _text_pos; static const char* _text_looping; static const char title_text[] = "Periodic Table"; +static bool _led_on = false; void periodic_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr) { @@ -410,20 +412,38 @@ bool periodic_face_loop(movement_event_t event, movement_settings_t *settings, v _handle_mode_still_pressed(state, settings->bit.button_should_sound); break; case EVENT_LIGHT_BUTTON_UP: - _handle_backward(state, settings->bit.button_should_sound); - break; - case EVENT_LIGHT_BUTTON_DOWN: + if (!_led_on) _handle_backward(state, settings->bit.button_should_sound); + if (!watch_get_pin_level(BTN_ALARM)) _led_on = false; break; case EVENT_ALARM_BUTTON_UP: - _handle_forward(state, settings->bit.button_should_sound); + if (!_led_on) _handle_forward(state, settings->bit.button_should_sound); + if (!watch_get_pin_level(BTN_LIGHT)) _led_on = false; break; case EVENT_ALARM_LONG_PRESS: - start_quick_cyc(); - _handle_forward(state, settings->bit.button_should_sound); + if (!_led_on) { + start_quick_cyc(); + _handle_forward(state, settings->bit.button_should_sound); + } break; case EVENT_LIGHT_LONG_PRESS: - start_quick_cyc(); - _handle_backward(state, settings->bit.button_should_sound); + if (!_led_on) { + start_quick_cyc(); + _handle_backward(state, settings->bit.button_should_sound); + } + break; + case EVENT_LIGHT_BUTTON_DOWN: + if (watch_get_pin_level(BTN_ALARM)) _led_on = true; + stop_quick_cyc(); + break; + case EVENT_ALARM_BUTTON_DOWN: + if (watch_get_pin_level(BTN_LIGHT)) _led_on = true; + stop_quick_cyc(); + break; + case EVENT_LIGHT_LONG_UP: + _led_on = false; + break; + case EVENT_ALARM_LONG_UP: + _led_on = false; break; case EVENT_MODE_BUTTON_UP: if (state->mode == SCREEN_TITLE) movement_move_to_next_face(); @@ -468,6 +488,8 @@ bool periodic_face_loop(movement_event_t event, movement_settings_t *settings, v return movement_default_loop_handler(event, settings); } + if (_led_on) movement_illuminate_led(); + return true; } diff --git a/movement/watch_faces/complication/periodic_face.h b/movement/watch_faces/complication/periodic_face.h index a459e69..c310082 100644 --- a/movement/watch_faces/complication/periodic_face.h +++ b/movement/watch_faces/complication/periodic_face.h @@ -2,6 +2,7 @@ * MIT License * * Copyright (c) 2023 PrimmR + * 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 @@ -58,6 +59,9 @@ * Alarm Hold * Fast Cycle through Next Elements * + * Light & Alarm Hold + * Activate LED backlight + * */ #define MODE_VIEW 0 From 6e26c01de042480f5b843b4fce1cefb70fcfe307 Mon Sep 17 00:00:00 2001 From: voloved <36523934+voloved@users.noreply.github.com> Date: Thu, 25 Jul 2024 09:18:56 -0400 Subject: [PATCH 027/161] Holding light button on a non-element screen will turn on the light (#2) * Holding light button on a non-element screen will turn on the light. * The alarm and led button press moves back to the currently-selected element symbol page rather than the next and previous one * Usage update --- .../watch_faces/complication/periodic_face.c | 44 +++++++++---------- .../watch_faces/complication/periodic_face.h | 15 +++---- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/movement/watch_faces/complication/periodic_face.c b/movement/watch_faces/complication/periodic_face.c index 162d3e4..85753ff 100644 --- a/movement/watch_faces/complication/periodic_face.c +++ b/movement/watch_faces/complication/periodic_face.c @@ -35,7 +35,6 @@ static uint8_t _ts_ticks = 0; static int16_t _text_pos; static const char* _text_looping; static const char title_text[] = "Periodic Table"; -static bool _led_on = false; void periodic_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr) { @@ -412,38 +411,39 @@ bool periodic_face_loop(movement_event_t event, movement_settings_t *settings, v _handle_mode_still_pressed(state, settings->bit.button_should_sound); break; case EVENT_LIGHT_BUTTON_UP: - if (!_led_on) _handle_backward(state, settings->bit.button_should_sound); - if (!watch_get_pin_level(BTN_ALARM)) _led_on = false; + if (state->mode <= SCREEN_ELEMENT) { + _handle_backward(state, settings->bit.button_should_sound); + } + else { + state->mode = SCREEN_ELEMENT; + _display_screen(state, settings->bit.button_should_sound); + } + break; + case EVENT_LIGHT_BUTTON_DOWN: break; case EVENT_ALARM_BUTTON_UP: - if (!_led_on) _handle_forward(state, settings->bit.button_should_sound); - if (!watch_get_pin_level(BTN_LIGHT)) _led_on = false; + if (state->mode <= SCREEN_ELEMENT) { + _handle_forward(state, settings->bit.button_should_sound); + } + else { + state->mode = SCREEN_ELEMENT; + _display_screen(state, settings->bit.button_should_sound); + } break; case EVENT_ALARM_LONG_PRESS: - if (!_led_on) { + if (state->mode <= SCREEN_ELEMENT) { start_quick_cyc(); _handle_forward(state, settings->bit.button_should_sound); } break; case EVENT_LIGHT_LONG_PRESS: - if (!_led_on) { + if (state->mode <= SCREEN_ELEMENT) { start_quick_cyc(); _handle_backward(state, settings->bit.button_should_sound); } - break; - case EVENT_LIGHT_BUTTON_DOWN: - if (watch_get_pin_level(BTN_ALARM)) _led_on = true; - stop_quick_cyc(); - break; - case EVENT_ALARM_BUTTON_DOWN: - if (watch_get_pin_level(BTN_LIGHT)) _led_on = true; - stop_quick_cyc(); - break; - case EVENT_LIGHT_LONG_UP: - _led_on = false; - break; - case EVENT_ALARM_LONG_UP: - _led_on = false; + else { + movement_illuminate_led(); + } break; case EVENT_MODE_BUTTON_UP: if (state->mode == SCREEN_TITLE) movement_move_to_next_face(); @@ -488,8 +488,6 @@ bool periodic_face_loop(movement_event_t event, movement_settings_t *settings, v return movement_default_loop_handler(event, settings); } - if (_led_on) movement_illuminate_led(); - return true; } diff --git a/movement/watch_faces/complication/periodic_face.h b/movement/watch_faces/complication/periodic_face.h index c310082..730b0fe 100644 --- a/movement/watch_faces/complication/periodic_face.h +++ b/movement/watch_faces/complication/periodic_face.h @@ -50,18 +50,17 @@ * If you are in a subscreen and just keep holding MODE, you will go through all of these menus without needing to depress. * * Light Press - * Previous Element + * On Title or Element Symbol Screen: Previous Element + * Else: Display currenlt-selected element symbol page * Light Hold - * Fast Cycle through Previous Elements + * On Title Screen or Element Symbol: Fast Cycle through Previous Elements + * Else: Activate LED backlight * * Alarm Press - * Next Element + * On Title or Element Symbol Screen: Next Element + * Else: Display currenlt-selected element symbol page * Alarm Hold - * Fast Cycle through Next Elements - * - * Light & Alarm Hold - * Activate LED backlight - * + * On Title Screen or Element Symbol: Fast Cycle through Next Elements */ #define MODE_VIEW 0 From a9e6b82f0022847ee5257da2158c57b8575e70c8 Mon Sep 17 00:00:00 2001 From: PrimmR Date: Thu, 25 Jul 2024 15:31:25 +0100 Subject: [PATCH 028/161] Update timeout & low energy behaviour --- movement/watch_faces/complication/periodic_face.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/movement/watch_faces/complication/periodic_face.c b/movement/watch_faces/complication/periodic_face.c index 85753ff..69ae4ad 100644 --- a/movement/watch_faces/complication/periodic_face.c +++ b/movement/watch_faces/complication/periodic_face.c @@ -477,13 +477,16 @@ bool periodic_face_loop(movement_event_t event, movement_settings_t *settings, v _ts_ticks = 2; break; case EVENT_TIMEOUT: - break; - case EVENT_LOW_ENERGY_UPDATE: - // Display title in LE mode. + // Display title after timeout if (state->mode == SCREEN_TITLE) break; state->mode = SCREEN_TITLE; _display_screen(state, false); break; + case EVENT_LOW_ENERGY_UPDATE: + // Display static title and tick animation during LE + watch_display_string("Pd Table", 0); + watch_start_tick_animation(500); + break; default: return movement_default_loop_handler(event, settings); } From 149911e4addf203d90ed84f8a6884508637ef6d1 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Fri, 2 Aug 2024 01:23:21 -0400 Subject: [PATCH 029/161] Time now auto-updates with DST --- movement/movement.c | 16 +++++++++ movement/movement.h | 1 + movement/watch_faces/clock/clock_face.c | 24 +++++++++++--- .../watch_faces/clock/simple_clock_face.c | 24 +++++++++++--- movement/watch_faces/settings/set_time_face.c | 10 ++---- watch-library/shared/watch/watch_utility.c | 33 +++++++++++++++++++ watch-library/shared/watch/watch_utility.h | 13 ++++++++ 7 files changed, 105 insertions(+), 16 deletions(-) diff --git a/movement/movement.c b/movement/movement.c index 8b45535..5c804fe 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -34,6 +34,7 @@ #include "filesystem.h" #include "movement.h" #include "shell.h" +#include "watch_utility.h" #ifndef MOVEMENT_FIRMWARE #include "movement_config.h" @@ -467,6 +468,21 @@ uint8_t movement_claim_backup_register(void) { return movement_state.next_available_backup_register++; } +int16_t get_timezone_offset(uint8_t timezone_idx, watch_date_time date_time) { + if (!movement_state.settings.bit.dst_active) return movement_timezone_offsets[timezone_idx]; + uint8_t dst_result = is_dst(date_time); + switch (dst_result) + { + case DST_STARTED: + case DST_OCCURRING: + return movement_timezone_offsets[movement_dst_jump_table[timezone_idx]]; + case DST_ENDING: + case DST_ENDED: + default: + return movement_timezone_offsets[timezone_idx]; + } +} + void app_init(void) { #if defined(NO_FREQCORR) watch_rtc_freqcorr_write(0, 0); diff --git a/movement/movement.h b/movement/movement.h index d19ab3f..a89e377 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -314,5 +314,6 @@ void movement_play_alarm(void); void movement_play_alarm_beeps(uint8_t rounds, BuzzerNote alarm_note); uint8_t movement_claim_backup_register(void); +int16_t get_timezone_offset(uint8_t timezone_idx, watch_date_time date_time); #endif // MOVEMENT_H_ diff --git a/movement/watch_faces/clock/clock_face.c b/movement/watch_faces/clock/clock_face.c index eab5cd8..68019d8 100644 --- a/movement/watch_faces/clock/clock_face.c +++ b/movement/watch_faces/clock/clock_face.c @@ -280,12 +280,28 @@ void clock_face_resign(movement_settings_t *settings, void *context) { (void) context; } -bool clock_face_wants_background_task(movement_settings_t *settings, void *context) { - (void) settings; - clock_state_t *state = (clock_state_t *) context; - if (!state->time_signal_enabled) return false; +static void check_and_act_on_daylight_savings(bool dst_active, watch_date_time date_time) { + if (!dst_active) return; + uint8_t dst_result = is_dst(date_time); + switch (dst_result) + { + case DST_STARTED: + date_time.unit.hour = (date_time.unit.hour + 1) % 24; + break; + case DST_ENDING: + date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24; + break; + default: + return; + } + watch_rtc_set_date_time(date_time); +} +bool clock_face_wants_background_task(movement_settings_t *settings, void *context) { + clock_state_t *state = (clock_state_t *) context; watch_date_time date_time = watch_rtc_get_date_time(); + check_and_act_on_daylight_savings(settings->bit.dst_active, date_time); + if (!state->time_signal_enabled) return false; return date_time.unit.minute == 0; } diff --git a/movement/watch_faces/clock/simple_clock_face.c b/movement/watch_faces/clock/simple_clock_face.c index fbc2c4b..c0f8372 100644 --- a/movement/watch_faces/clock/simple_clock_face.c +++ b/movement/watch_faces/clock/simple_clock_face.c @@ -150,12 +150,28 @@ void simple_clock_face_resign(movement_settings_t *settings, void *context) { (void) context; } -bool simple_clock_face_wants_background_task(movement_settings_t *settings, void *context) { - (void) settings; - simple_clock_state_t *state = (simple_clock_state_t *)context; - if (!state->signal_enabled) return false; +static void check_and_act_on_daylight_savings(bool dst_active, watch_date_time date_time) { + if (!dst_active) return; + uint8_t dst_result = is_dst(date_time); + switch (dst_result) + { + case DST_STARTED: + date_time.unit.hour = (date_time.unit.hour + 1) % 24; + break; + case DST_ENDING: + date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24; + break; + default: + return; + } + watch_rtc_set_date_time(date_time); +} +bool simple_clock_face_wants_background_task(movement_settings_t *settings, void *context) { + simple_clock_state_t *state = (simple_clock_state_t *)context; watch_date_time date_time = watch_rtc_get_date_time(); + check_and_act_on_daylight_savings(settings->bit.dst_active, date_time); + if (!state->signal_enabled) return false; return date_time.unit.minute == 0; } diff --git a/movement/watch_faces/settings/set_time_face.c b/movement/watch_faces/settings/set_time_face.c index fb0c806..90edac2 100644 --- a/movement/watch_faces/settings/set_time_face.c +++ b/movement/watch_faces/settings/set_time_face.c @@ -67,13 +67,6 @@ static void _handle_alarm_button(movement_settings_t *settings, watch_date_time if (settings->bit.time_zone > 40) settings->bit.time_zone = 0; break; case 7: // daylight savings time - if (settings->bit.dst_active) { // deactivate DST - date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24; - settings->bit.time_zone = movement_dst_inverse_jump_table[settings->bit.time_zone]; - } else { // activate DST - date_time.unit.hour = (date_time.unit.hour + 1) % 24; - settings->bit.time_zone = movement_dst_jump_table[settings->bit.time_zone]; - } settings->bit.dst_active = !settings->bit.dst_active; break; } @@ -161,8 +154,9 @@ bool set_time_face_loop(movement_event_t event, movement_settings_t *settings, v watch_clear_colon(); sprintf(buf, "%s ", set_time_face_titles[current_page]); } else { + int16_t tz = get_timezone_offset(settings->bit.time_zone, date_time); watch_set_colon(); - sprintf(buf, "%s %3d%02d ", set_time_face_titles[current_page], (int8_t) (movement_timezone_offsets[settings->bit.time_zone] / 60), (int8_t) (movement_timezone_offsets[settings->bit.time_zone] % 60) * (movement_timezone_offsets[settings->bit.time_zone] < 0 ? -1 : 1)); + sprintf(buf, "%s %3d%02d ", set_time_face_titles[current_page], (int8_t) (tz / 60), (int8_t) (tz % 60) * (tz < 0 ? -1 : 1)); } } else { // daylight savings watch_clear_colon(); diff --git a/watch-library/shared/watch/watch_utility.c b/watch-library/shared/watch/watch_utility.c index 64b3bb7..1410e41 100644 --- a/watch-library/shared/watch/watch_utility.c +++ b/watch-library/shared/watch/watch_utility.c @@ -83,6 +83,39 @@ uint8_t is_leap(uint16_t y) return !(y%4) && ((y%100) || !(y%400)); } +uint8_t is_dst(watch_date_time date_time) { + uint8_t weekday_first_of_month; + watch_date_time dst_start_time; + watch_date_time dst_end_time; + uint32_t unix_dst_start_time; + uint32_t unix_dst_end_time; + uint32_t unix_curr_time; + + dst_start_time.unit.month = 3; + dst_start_time.unit.hour = 2; + dst_start_time.unit.minute = 0; + dst_start_time.unit.second = 0; + weekday_first_of_month = watch_utility_get_iso8601_weekday_number(date_time.unit.year, dst_start_time.unit.month, 1); + dst_start_time.unit.day = 15 - weekday_first_of_month; + unix_dst_start_time = watch_utility_date_time_to_unix_time(dst_start_time, 0); + + dst_end_time.unit.month = 11; + dst_end_time.unit.hour = 2; + dst_end_time.unit.minute = 0; + dst_end_time.unit.second = 0; + weekday_first_of_month = watch_utility_get_iso8601_weekday_number(date_time.unit.year, dst_end_time.unit.month, 1); + dst_end_time.unit.day = 15 - weekday_first_of_month; + unix_dst_end_time = watch_utility_date_time_to_unix_time(dst_end_time, 0); + + date_time.unit.second = 0; + unix_curr_time = watch_utility_date_time_to_unix_time(date_time, 0); + + if (unix_curr_time == unix_dst_start_time) return DST_STARTED; + if (unix_curr_time == unix_dst_end_time) return DST_ENDING; + if (unix_curr_time > unix_dst_end_time || unix_curr_time < unix_dst_start_time) return DST_ENDED; + return DST_OCCURRING; +} + uint16_t watch_utility_days_since_new_year(uint16_t year, uint8_t month, uint8_t day) { uint16_t DAYS_SO_FAR[] = { 0, // Jan diff --git a/watch-library/shared/watch/watch_utility.h b/watch-library/shared/watch/watch_utility.h index e2326d1..d3f3981 100644 --- a/watch-library/shared/watch/watch_utility.h +++ b/watch-library/shared/watch/watch_utility.h @@ -45,6 +45,13 @@ typedef struct { uint32_t days; // 0-4294967295 } watch_duration_t; +typedef enum { + DST_STARTED, + DST_OCCURRING, + DST_ENDING, + DST_ENDED +} dst_t; + /** @brief Returns a two-letter weekday for the given timestamp, suitable for display * in positions 0-1 of the watch face * @param date_time The watch_date_time whose weekday you want. @@ -78,6 +85,12 @@ uint16_t watch_utility_days_since_new_year(uint16_t year, uint8_t month, uint8_t */ uint8_t is_leap(uint16_t year); +/** @brief Returns off of dst_t based off if DST is occurring, srted, ended, or none of those. + * @param date_time The watch_date_time that you wish to convert. + * @return DST_OCCURRING, DST_HAPPENING, DST_ENDING, DST_ENDED + */ +uint8_t is_dst(watch_date_time date_time); + /** @brief Returns the UNIX time (seconds since 1970) for a given date/time in UTC. * @param date_time The watch_date_time that you wish to convert. * @param year The year of the date you wish to convert. From 4c546b14dc9ef8aa65dcee6560417665396c3bf0 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Fri, 2 Aug 2024 01:25:01 -0400 Subject: [PATCH 030/161] The timezone now auto-corrects with DST (but still displays the same offset in the settings screen to the user) --- movement/movement.c | 44 ------------------- movement/movement.h | 1 - movement/watch_faces/clock/beats_face.c | 4 +- .../clock/day_night_percentage_face.c | 5 ++- movement/watch_faces/clock/mars_time_face.c | 2 +- .../watch_faces/clock/world_clock2_face.c | 8 ++-- movement/watch_faces/clock/world_clock_face.c | 14 ++++-- .../watch_faces/complication/astronomy_face.c | 2 +- .../watch_faces/complication/countdown_face.c | 11 ++--- .../complication/moon_phase_face.c | 5 ++- .../watch_faces/complication/orrery_face.c | 2 +- .../complication/planetary_hours_face.c | 7 +-- .../complication/planetary_time_face.c | 8 ++-- .../watch_faces/complication/sailing_face.c | 15 ++++--- .../watch_faces/complication/solstice_face.c | 3 +- .../complication/sunrise_sunset_face.c | 5 ++- .../watch_faces/complication/timer_face.c | 11 ++--- .../watch_faces/complication/tomato_face.c | 11 ++--- movement/watch_faces/complication/totp_face.c | 5 ++- .../watch_faces/complication/totp_face_lfs.c | 3 +- .../accelerometer_data_acquisition_face.c | 2 +- .../settings/set_time_hackwatch_face.c | 5 ++- 22 files changed, 75 insertions(+), 98 deletions(-) diff --git a/movement/movement.c b/movement/movement.c index 5c804fe..52a9ac0 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -208,50 +208,6 @@ const uint8_t movement_dst_jump_table[] = { 0 // 40 AST + 1 = UTC }; -const uint8_t movement_dst_inverse_jump_table[] = { - 40, // 0 - 0, // 1 - 1, // 2 - 2, // 3 - 4, // 4 - 3, // 5 - 4, // 6 - 5, // 7 - 6, // 8 - 9, // 9 - 7, // 10 - 8, // 11 - 10, // 12 - 12, // 13 - 14, // 14 - 13, // 15 - 16, // 16 - 15, // 17 - 16, // 18 - 17, // 19 - 19, // 20 - 21, // 21 - 20, // 22 - 21, // 23 - 24, // 24 - 25, // 25 - 25, // 26 - 26, // 27 - 28, // 28 - 27, // 29 - 29, // 30 - 30, // 31 - 31, // 32 - 32, // 33 - 34, // 34 - 33, // 35 - 34, // 36 - 35, // 37 - 36, // 38 - 37, // 39 - 39 // 40 -}; - const char movement_valid_position_0_chars[] = " AaBbCcDdEeFGgHhIiJKLMNnOoPQrSTtUuWXYZ-='+\\/0123456789"; const char movement_valid_position_1_chars[] = " ABCDEFHlJLNORTtUX-='01378"; diff --git a/movement/movement.h b/movement/movement.h index a89e377..9aa1dbc 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -130,7 +130,6 @@ typedef struct { extern const int16_t movement_timezone_offsets[]; extern const uint8_t movement_dst_jump_table[]; -extern const uint8_t movement_dst_inverse_jump_table[]; extern const char movement_valid_position_0_chars[]; extern const char movement_valid_position_1_chars[]; diff --git a/movement/watch_faces/clock/beats_face.c b/movement/watch_faces/clock/beats_face.c index 85bcbe0..6954396 100644 --- a/movement/watch_faces/clock/beats_face.c +++ b/movement/watch_faces/clock/beats_face.c @@ -61,7 +61,7 @@ bool beats_face_loop(movement_event_t event, movement_settings_t *settings, void case EVENT_ACTIVATE: case EVENT_TICK: date_time = watch_rtc_get_date_time(); - centibeats = clock2beats(date_time.unit.hour, date_time.unit.minute, date_time.unit.second, event.subsecond, movement_timezone_offsets[settings->bit.time_zone]); + centibeats = clock2beats(date_time.unit.hour, date_time.unit.minute, date_time.unit.second, event.subsecond, get_timezone_offset(settings->bit.time_zone, date_time)); if (centibeats == state->last_centibeat_displayed) { // we missed this update, try again next subsecond state->next_subsecond_update = (event.subsecond + 1) % BEAT_REFRESH_FREQUENCY; @@ -76,7 +76,7 @@ bool beats_face_loop(movement_event_t event, movement_settings_t *settings, void case EVENT_LOW_ENERGY_UPDATE: if (!watch_tick_animation_is_running()) watch_start_tick_animation(432); date_time = watch_rtc_get_date_time(); - centibeats = clock2beats(date_time.unit.hour, date_time.unit.minute, date_time.unit.second, event.subsecond, movement_timezone_offsets[settings->bit.time_zone]); + centibeats = clock2beats(date_time.unit.hour, date_time.unit.minute, date_time.unit.second, event.subsecond, get_timezone_offset(settings->bit.time_zone, date_time)); sprintf(buf, "bt %4lu ", centibeats / 100); watch_display_string(buf, 0); diff --git a/movement/watch_faces/clock/day_night_percentage_face.c b/movement/watch_faces/clock/day_night_percentage_face.c index 86f07f9..4041c05 100644 --- a/movement/watch_faces/clock/day_night_percentage_face.c +++ b/movement/watch_faces/clock/day_night_percentage_face.c @@ -62,7 +62,8 @@ void day_night_percentage_face_setup(movement_settings_t *settings, uint8_t watc if (*context_ptr == NULL) { *context_ptr = malloc(sizeof(day_night_percentage_state_t)); day_night_percentage_state_t *state = (day_night_percentage_state_t *)*context_ptr; - watch_date_time utc_now = watch_utility_date_time_convert_zone(watch_rtc_get_date_time(), movement_timezone_offsets[settings->bit.time_zone] * 60, 0); + watch_date_time date_time = watch_rtc_get_date_time(); + watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60, 0); recalculate(utc_now, state); } } @@ -77,7 +78,7 @@ bool day_night_percentage_face_loop(movement_event_t event, movement_settings_t char buf[12]; watch_date_time date_time = watch_rtc_get_date_time(); - watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60, 0); + watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60, 0); switch (event.event_type) { case EVENT_ACTIVATE: diff --git a/movement/watch_faces/clock/mars_time_face.c b/movement/watch_faces/clock/mars_time_face.c index 30f6a34..304c615 100644 --- a/movement/watch_faces/clock/mars_time_face.c +++ b/movement/watch_faces/clock/mars_time_face.c @@ -70,7 +70,7 @@ static void _h_to_hms(mars_clock_hms_t *date_time, double h) { static void _update(movement_settings_t *settings, mars_time_state_t *state) { char buf[11]; watch_date_time date_time = watch_rtc_get_date_time(); - uint32_t now = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); + uint32_t now = watch_utility_date_time_to_unix_time(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60); // TODO: I'm skipping over some steps here. // https://www.giss.nasa.gov/tools/mars24/help/algorithm.html double jdut = 2440587.5 + ((double)now / 86400.0); diff --git a/movement/watch_faces/clock/world_clock2_face.c b/movement/watch_faces/clock/world_clock2_face.c index 2e1e969..6ff806b 100644 --- a/movement/watch_faces/clock/world_clock2_face.c +++ b/movement/watch_faces/clock/world_clock2_face.c @@ -165,6 +165,7 @@ static bool mode_display(movement_event_t event, movement_settings_t *settings, uint32_t timestamp; uint32_t previous_date_time; watch_date_time date_time; + int16_t tz; switch (event.event_type) { case EVENT_ACTIVATE: @@ -183,8 +184,9 @@ static bool mode_display(movement_event_t event, movement_settings_t *settings, /* Determine current time at time zone and store date/time */ date_time = watch_rtc_get_date_time(); - timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); - date_time = watch_utility_date_time_from_unix_time(timestamp, movement_timezone_offsets[state->current_zone] * 60); + tz = get_timezone_offset(settings->bit.time_zone, date_time); + timestamp = watch_utility_date_time_to_unix_time(date_time, tz * 60); + date_time = watch_utility_date_time_from_unix_time(timestamp, tz * 60); previous_date_time = state->previous_date_time; state->previous_date_time = date_time.reg; @@ -284,7 +286,7 @@ static bool mode_settings(movement_event_t event, movement_settings_t *settings, watch_clear_indicator(WATCH_INDICATOR_PM); refresh_face = false; } - result = div(movement_timezone_offsets[state->current_zone], 60); + result = div(get_timezone_offset(state->current_zone, watch_rtc_get_date_time()), 60); hours = result.quot; minutes = result.rem; diff --git a/movement/watch_faces/clock/world_clock_face.c b/movement/watch_faces/clock/world_clock_face.c index b12d9cd..c34db92 100644 --- a/movement/watch_faces/clock/world_clock_face.c +++ b/movement/watch_faces/clock/world_clock_face.c @@ -54,6 +54,7 @@ void world_clock_face_activate(movement_settings_t *settings, void *context) { static bool world_clock_face_do_display_mode(movement_event_t event, movement_settings_t *settings, world_clock_state_t *state) { char buf[11]; uint8_t pos; + int16_t tz; uint32_t timestamp; uint32_t previous_date_time; @@ -67,8 +68,9 @@ static bool world_clock_face_do_display_mode(movement_event_t event, movement_se case EVENT_TICK: case EVENT_LOW_ENERGY_UPDATE: date_time = watch_rtc_get_date_time(); - timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); - date_time = watch_utility_date_time_from_unix_time(timestamp, movement_timezone_offsets[state->settings.bit.timezone_index] * 60); + tz = get_timezone_offset(settings->bit.time_zone, date_time); + timestamp = watch_utility_date_time_to_unix_time(date_time, tz * 60); + date_time = watch_utility_date_time_from_unix_time(timestamp, tz * 60); previous_date_time = state->previous_date_time; state->previous_date_time = date_time.reg; @@ -125,6 +127,8 @@ static bool world_clock_face_do_display_mode(movement_event_t event, movement_se } static bool _world_clock_face_do_settings_mode(movement_event_t event, movement_settings_t *settings, world_clock_state_t *state) { + watch_date_time date_time; + int16_t tz; switch (event.event_type) { case EVENT_MODE_BUTTON_UP: if (state->backup_register) watch_store_backup_data(state->settings.reg, state->backup_register); @@ -168,11 +172,13 @@ static bool _world_clock_face_do_settings_mode(movement_event_t event, movement_ } char buf[13]; + date_time = watch_rtc_get_date_time(); + tz = get_timezone_offset(settings->bit.time_zone, date_time); sprintf(buf, "%c%c %3d%02d ", movement_valid_position_0_chars[state->settings.bit.char_0], movement_valid_position_1_chars[state->settings.bit.char_1], - (int8_t) (movement_timezone_offsets[state->settings.bit.timezone_index] / 60), - (int8_t) (movement_timezone_offsets[state->settings.bit.timezone_index] % 60) * (movement_timezone_offsets[state->settings.bit.timezone_index] < 0 ? -1 : 1)); + (int8_t) (tz / 60), + (int8_t) (tz % 60) * (tz < 0 ? -1 : 1)); watch_set_colon(); watch_clear_indicator(WATCH_INDICATOR_PM); diff --git a/movement/watch_faces/complication/astronomy_face.c b/movement/watch_faces/complication/astronomy_face.c index 204a0a3..8db8760 100644 --- a/movement/watch_faces/complication/astronomy_face.c +++ b/movement/watch_faces/complication/astronomy_face.c @@ -80,7 +80,7 @@ static void _astronomy_face_recalculate(movement_settings_t *settings, astronomy #endif watch_date_time date_time = watch_rtc_get_date_time(); - uint32_t timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); + uint32_t timestamp = watch_utility_date_time_to_unix_time(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60); date_time = watch_utility_date_time_from_unix_time(timestamp, 0); double jd = astro_convert_date_to_julian_date(date_time.unit.year + WATCH_RTC_REFERENCE_YEAR, date_time.unit.month, date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second); diff --git a/movement/watch_faces/complication/countdown_face.c b/movement/watch_faces/complication/countdown_face.c index be04040..04dc592 100644 --- a/movement/watch_faces/complication/countdown_face.c +++ b/movement/watch_faces/complication/countdown_face.c @@ -44,8 +44,8 @@ static void abort_quick_ticks(countdown_state_t *state) { } } -static inline int32_t get_tz_offset(movement_settings_t *settings) { - return movement_timezone_offsets[settings->bit.time_zone] * 60; +static inline int32_t get_tz_offset(movement_settings_t *settings, watch_date_time date_time) { + return get_timezone_offset(settings->bit.time_zone, date_time) * 60; } static inline void store_countdown(countdown_state_t *state) { @@ -70,11 +70,12 @@ static inline void button_beep(movement_settings_t *settings) { static void start(countdown_state_t *state, movement_settings_t *settings) { watch_date_time now = watch_rtc_get_date_time(); + int16_t tz = get_tz_offset(settings, now); state->mode = cd_running; - state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); + state->now_ts = watch_utility_date_time_to_unix_time(now, tz); state->target_ts = watch_utility_offset_timestamp(state->now_ts, state->hours, state->minutes, state->seconds); - watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, get_tz_offset(settings)); + watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, tz); movement_schedule_background_task(target_dt); watch_set_indicator(WATCH_INDICATOR_BELL); } @@ -176,7 +177,7 @@ void countdown_face_activate(movement_settings_t *settings, void *context) { countdown_state_t *state = (countdown_state_t *)context; if(state->mode == cd_running) { watch_date_time now = watch_rtc_get_date_time(); - state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); + state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings, now)); watch_set_indicator(WATCH_INDICATOR_BELL); } watch_set_colon(); diff --git a/movement/watch_faces/complication/moon_phase_face.c b/movement/watch_faces/complication/moon_phase_face.c index f74de64..47ed6e6 100644 --- a/movement/watch_faces/complication/moon_phase_face.c +++ b/movement/watch_faces/complication/moon_phase_face.c @@ -59,8 +59,9 @@ static void _update(movement_settings_t *settings, moon_phase_state_t *state, ui (void)state; char buf[11]; watch_date_time date_time = watch_rtc_get_date_time(); - uint32_t now = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60) + offset; - date_time = watch_utility_date_time_from_unix_time(now, movement_timezone_offsets[settings->bit.time_zone] * 60); + int16_t tz = get_timezone_offset(settings->bit.time_zone, date_time); + uint32_t now = watch_utility_date_time_to_unix_time(date_time, tz * 60) + offset; + date_time = watch_utility_date_time_from_unix_time(now, tz * 60); double currentfrac = fmod(now - FIRST_MOON, LUNAR_SECONDS) / LUNAR_SECONDS; double currentday = currentfrac * LUNAR_DAYS; uint8_t phase_index = 0; diff --git a/movement/watch_faces/complication/orrery_face.c b/movement/watch_faces/complication/orrery_face.c index 42fdf81..0570dd4 100644 --- a/movement/watch_faces/complication/orrery_face.c +++ b/movement/watch_faces/complication/orrery_face.c @@ -48,7 +48,7 @@ static const char orrery_celestial_body_names[NUM_AVAILABLE_BODIES][3] = { static void _orrery_face_recalculate(movement_settings_t *settings, orrery_state_t *state) { watch_date_time date_time = watch_rtc_get_date_time(); - uint32_t timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); + uint32_t timestamp = watch_utility_date_time_to_unix_time(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60); date_time = watch_utility_date_time_from_unix_time(timestamp, 0); double jd = astro_convert_date_to_julian_date(date_time.unit.year + WATCH_RTC_REFERENCE_YEAR, date_time.unit.month, date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second); double et = astro_convert_jd_to_julian_millenia_since_j2000(jd); diff --git a/movement/watch_faces/complication/planetary_hours_face.c b/movement/watch_faces/complication/planetary_hours_face.c index acded91..d9e32c5 100644 --- a/movement/watch_faces/complication/planetary_hours_face.c +++ b/movement/watch_faces/complication/planetary_hours_face.c @@ -134,7 +134,8 @@ static void _planetary_solar_phases(movement_settings_t *settings, planetary_hou state->no_location = false; watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time - watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60, 0); // the current date / time in UTC + int16_t tz = get_timezone_offset(settings->bit.time_zone, date_time); + watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, tz * 60, 0); // the current date / time in UTC watch_date_time scratch_time; // scratchpad, contains different values at different times watch_date_time midnight; scratch_time.reg = midnight.reg = utc_now.reg; @@ -147,7 +148,7 @@ static void _planetary_solar_phases(movement_settings_t *settings, planetary_hou double lon = (double)lon_centi / 100.0; // save UTC offset - state->utc_offset = ((double)movement_timezone_offsets[settings->bit.time_zone]) / 60.0; + state->utc_offset = ((double)tz) / 60.0; // calculate sunrise and sunset of current day in decimal hours after midnight sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset); @@ -237,7 +238,7 @@ static void _planetary_hours(movement_settings_t *settings, planetary_hours_stat // get current time watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time - watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60, 0); // the current date / time in UTC + watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60, 0); // the current date / time in UTC current_hour_epoch = watch_utility_date_time_to_unix_time(utc_now, 0); // set the current planetary hour as default screen diff --git a/movement/watch_faces/complication/planetary_time_face.c b/movement/watch_faces/complication/planetary_time_face.c index 56a18cf..836ada6 100644 --- a/movement/watch_faces/complication/planetary_time_face.c +++ b/movement/watch_faces/complication/planetary_time_face.c @@ -129,7 +129,8 @@ static void _planetary_solar_phase(movement_settings_t *settings, planetary_time state->no_location = false; watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time - watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60, 0); // the current date / time in UTC + int16_t tz = get_timezone_offset(settings->bit.time_zone, date_time); + watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, tz * 60, 0); // the current date / time in UTC watch_date_time scratch_time; // scratchpad, contains different values at different times watch_date_time midnight; scratch_time.reg = midnight.reg = utc_now.reg; @@ -142,7 +143,7 @@ static void _planetary_solar_phase(movement_settings_t *settings, planetary_time double lon = (double)lon_centi / 100.0; // save UTC offset - state->utc_offset = ((double)movement_timezone_offsets[settings->bit.time_zone]) / 60.0; + state->utc_offset = ((double)tz) / 60.0; // get UNIX epoch time now_epoch = watch_utility_date_time_to_unix_time(utc_now, 0); @@ -206,11 +207,12 @@ static void _planetary_time(movement_event_t event, movement_settings_t *setting double night_hour_count = 0.0; uint8_t weekday, planet, planetary_hour; double hour_duration, current_hour, current_minute, current_second; + watch_date_time date_time = watch_rtc_get_date_time(); watch_set_colon(); // get current time and convert to UTC - state->scratch = watch_utility_date_time_convert_zone(watch_rtc_get_date_time(), movement_timezone_offsets[settings->bit.time_zone] * 60, 0); + state->scratch = watch_utility_date_time_convert_zone(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60, 0); // when current phase ends calculate the next phase if ( watch_utility_date_time_to_unix_time(state->scratch, 0) >= state->phase_end ) { diff --git a/movement/watch_faces/complication/sailing_face.c b/movement/watch_faces/complication/sailing_face.c index a6c13fe..16d60c9 100644 --- a/movement/watch_faces/complication/sailing_face.c +++ b/movement/watch_faces/complication/sailing_face.c @@ -33,8 +33,8 @@ #define sl_SELECTIONS 6 #define DEFAULT_MINUTES { 5,4,1,0,0,0 } -static inline int32_t get_tz_offset(movement_settings_t *settings) { - return movement_timezone_offsets[settings->bit.time_zone] * 60; +static inline int32_t get_tz_offset(movement_settings_t *settings, watch_date_time date_time) { + return get_timezone_offset(settings->bit.time_zone, date_time) * 60; } static int lap = 0; @@ -165,7 +165,8 @@ static void ring(sailing_state_t *state, movement_settings_t *settings) { return; } state->nextbeep_ts = state->target_ts - beepseconds[beepflag+1]; - watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->nextbeep_ts, get_tz_offset(settings)); + watch_date_time now = watch_rtc_get_date_time(); + watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->nextbeep_ts, get_tz_offset(settings, now)); movement_schedule_background_task_for_face(state->watch_face_index, target_dt); //background task is set, now we have time to play the tune. If this is cancelled accidentally, the next alarm will still ring. Sound is implemented non-blocking, so that neither buttons nor display output are compromised. for (int i = 0; i < 5; i++) { @@ -194,7 +195,7 @@ static void start(sailing_state_t *state, movement_settings_t *settings) {//gets } if (state->index > 5 || state->minutes[state->index] == 0) { watch_date_time now = watch_rtc_get_date_time(); - state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); + state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings, now)); state->target_ts = state->now_ts; if (alarmflag != 0){ watch_buzzer_play_sequence(long_beep, NULL); @@ -205,7 +206,7 @@ static void start(sailing_state_t *state, movement_settings_t *settings) {//gets movement_request_tick_frequency(1); //synchronises tick with the moment the button was pressed. Solves 1s offset between sound and display, solves up to +-0.5s offset between button action and display. state->mode = sl_running; watch_date_time now = watch_rtc_get_date_time(); - state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); + state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings, now)); state->target_ts = watch_utility_offset_timestamp(state->now_ts, 0, state->minutes[state->index], 0); ring(state, settings); } @@ -253,11 +254,11 @@ void sailing_face_activate(movement_settings_t *settings, void *context) { sailing_state_t *state = (sailing_state_t *)context; if(state->mode == sl_running) { watch_date_time now = watch_rtc_get_date_time(); - state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); + state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings, now)); } if(state->mode == sl_counting) { watch_date_time now = watch_rtc_get_date_time(); - state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); + state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings, now)); watch_set_indicator(WATCH_INDICATOR_LAP); } switch (alarmflag) { diff --git a/movement/watch_faces/complication/solstice_face.c b/movement/watch_faces/complication/solstice_face.c index e74f878..76d0d26 100644 --- a/movement/watch_faces/complication/solstice_face.c +++ b/movement/watch_faces/complication/solstice_face.c @@ -123,9 +123,10 @@ static watch_date_time jde_to_date_time(double JDE) { } static void calculate_datetimes(solstice_state_t *state, movement_settings_t *settings) { + watch_date_time date_time = watch_rtc_get_date_time(); for (int i = 0; i < 4; i++) { // TODO: handle DST changes - state->datetimes[i] = jde_to_date_time(calculate_solstice_equinox(2020 + state->year, i) + (movement_timezone_offsets[settings->bit.time_zone] / (60.0*24.0))); + state->datetimes[i] = jde_to_date_time(calculate_solstice_equinox(2020 + state->year, i) + (get_timezone_offset(settings->bit.time_zone, date_time) / (60.0*24.0))); } } diff --git a/movement/watch_faces/complication/sunrise_sunset_face.c b/movement/watch_faces/complication/sunrise_sunset_face.c index 7330c42..a6ec764 100644 --- a/movement/watch_faces/complication/sunrise_sunset_face.c +++ b/movement/watch_faces/complication/sunrise_sunset_face.c @@ -54,7 +54,8 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s } watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time - watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60, 0); // the current date / time in UTC + int16_t tz = get_timezone_offset(settings->bit.time_zone, date_time); + watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, tz * 60, 0); // the current date / time in UTC watch_date_time scratch_time; // scratchpad, contains different values at different times scratch_time.reg = utc_now.reg; @@ -69,7 +70,7 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s // sunriset returns the rise/set times as signed decimal hours in UTC. // this can mean hours below 0 or above 31, which won't fit into a watch_date_time struct. // to deal with this, we set aside the offset in hours, and add it back before converting it to a watch_date_time. - double hours_from_utc = ((double)movement_timezone_offsets[settings->bit.time_zone]) / 60.0; + double hours_from_utc = ((double)tz) / 60.0; // we loop twice because if it's after sunset today, we need to recalculate to display values for tomorrow. for(int i = 0; i < 2; i++) { diff --git a/movement/watch_faces/complication/timer_face.c b/movement/watch_faces/complication/timer_face.c index 29392d6..5ee2967 100644 --- a/movement/watch_faces/complication/timer_face.c +++ b/movement/watch_faces/complication/timer_face.c @@ -36,8 +36,8 @@ static const int8_t _sound_seq_start[] = {BUZZER_NOTE_C8, 2, 0}; static uint8_t _beeps_to_play; // temporary counter for ring signals playing -static inline int32_t _get_tz_offset(movement_settings_t *settings) { - return movement_timezone_offsets[settings->bit.time_zone] * 60; +static inline int32_t _get_tz_offset(movement_settings_t *settings, watch_date_time date_time) { + return get_timezone_offset(settings->bit.time_zone, date_time) * 60; } static void _signal_callback() { @@ -50,7 +50,8 @@ static void _signal_callback() { static void _start(timer_state_t *state, movement_settings_t *settings, bool with_beep) { if (state->timers[state->current_timer].value == 0) return; watch_date_time now = watch_rtc_get_date_time(); - state->now_ts = watch_utility_date_time_to_unix_time(now, _get_tz_offset(settings)); + int32_t tz = _get_tz_offset(settings, now); + state->now_ts = watch_utility_date_time_to_unix_time(now, tz); if (state->mode == pausing) state->target_ts = state->now_ts + state->paused_left; else @@ -58,7 +59,7 @@ static void _start(timer_state_t *state, movement_settings_t *settings, bool wit state->timers[state->current_timer].unit.hours, state->timers[state->current_timer].unit.minutes, state->timers[state->current_timer].unit.seconds); - watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, _get_tz_offset(settings)); + watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, tz); state->mode = running; movement_schedule_background_task_for_face(state->watch_face_index, target_dt); watch_set_indicator(WATCH_INDICATOR_BELL); @@ -210,7 +211,7 @@ void timer_face_activate(movement_settings_t *settings, void *context) { watch_set_colon(); if(state->mode == running) { watch_date_time now = watch_rtc_get_date_time(); - state->now_ts = watch_utility_date_time_to_unix_time(now, _get_tz_offset(settings)); + state->now_ts = watch_utility_date_time_to_unix_time(now, _get_tz_offset(settings, now)); watch_set_indicator(WATCH_INDICATOR_BELL); } else { state->pausing_seconds = 1; diff --git a/movement/watch_faces/complication/tomato_face.c b/movement/watch_faces/complication/tomato_face.c index 3d46ba9..15b67c6 100644 --- a/movement/watch_faces/complication/tomato_face.c +++ b/movement/watch_faces/complication/tomato_face.c @@ -30,8 +30,8 @@ static uint8_t focus_min = 25; static uint8_t break_min = 5; -static inline int32_t get_tz_offset(movement_settings_t *settings) { - return movement_timezone_offsets[settings->bit.time_zone] * 60; +static inline int32_t get_tz_offset(movement_settings_t *settings, watch_date_time date_time) { + return get_timezone_offset(settings->bit.time_zone, date_time) * 60; } static uint8_t get_length(tomato_state_t *state) { @@ -47,12 +47,13 @@ static uint8_t get_length(tomato_state_t *state) { static void tomato_start(tomato_state_t *state, movement_settings_t *settings) { watch_date_time now = watch_rtc_get_date_time(); + int32_t tz = get_tz_offset(settings, now); int8_t length = (int8_t) get_length(state); state->mode = tomato_run; - state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); + state->now_ts = watch_utility_date_time_to_unix_time(now, tz); state->target_ts = watch_utility_offset_timestamp(state->now_ts, 0, length, 0); - watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, get_tz_offset(settings)); + watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, tz); movement_schedule_background_task(target_dt); watch_set_indicator(WATCH_INDICATOR_BELL); } @@ -126,7 +127,7 @@ void tomato_face_activate(movement_settings_t *settings, void *context) { tomato_state_t *state = (tomato_state_t *)context; if (state->mode == tomato_run) { watch_date_time now = watch_rtc_get_date_time(); - state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); + state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings, now)); watch_set_indicator(WATCH_INDICATOR_BELL); } watch_set_colon(); diff --git a/movement/watch_faces/complication/totp_face.c b/movement/watch_faces/complication/totp_face.c index a593e9c..10a5bfc 100644 --- a/movement/watch_faces/complication/totp_face.c +++ b/movement/watch_faces/complication/totp_face.c @@ -157,8 +157,9 @@ static void totp_generate_and_display(totp_state_t *totp_state) { totp_display(totp_state); } -static inline uint32_t totp_compute_base_timestamp(movement_settings_t *settings) { - return watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), movement_timezone_offsets[settings->bit.time_zone] * 60); +static uint32_t totp_compute_base_timestamp(movement_settings_t *settings) { + watch_date_time date_time = watch_rtc_get_date_time(); + return watch_utility_date_time_to_unix_time(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60); } void totp_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { diff --git a/movement/watch_faces/complication/totp_face_lfs.c b/movement/watch_faces/complication/totp_face_lfs.c index 820ad52..f560627 100644 --- a/movement/watch_faces/complication/totp_face_lfs.c +++ b/movement/watch_faces/complication/totp_face_lfs.c @@ -210,7 +210,8 @@ void totp_face_lfs_activate(movement_settings_t *settings, void *context) { } #endif - totp_state->timestamp = watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), movement_timezone_offsets[settings->bit.time_zone] * 60); + watch_date_time date_time = watch_rtc_get_date_time(); + totp_state->timestamp = watch_utility_date_time_to_unix_time(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60); totp_face_set_record(totp_state, 0); } diff --git a/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c b/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c index 6d174d5..9cadf67 100644 --- a/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c +++ b/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c @@ -442,7 +442,7 @@ static void start_reading(accelerometer_data_acquisition_state_t *state, movemen accelerometer_data_acquisition_record_t record; watch_date_time date_time = watch_rtc_get_date_time(); - state->starting_timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); + state->starting_timestamp = watch_utility_date_time_to_unix_time(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60); record.header.info.record_type = ACCELEROMETER_DATA_ACQUISITION_HEADER; record.header.info.range = ACCELEROMETER_RANGE; record.header.info.temperature = lis2dw_get_temperature(); diff --git a/movement/watch_faces/settings/set_time_hackwatch_face.c b/movement/watch_faces/settings/set_time_hackwatch_face.c index fbe8cbb..bdd1b0a 100644 --- a/movement/watch_faces/settings/set_time_hackwatch_face.c +++ b/movement/watch_faces/settings/set_time_hackwatch_face.c @@ -232,12 +232,13 @@ bool set_time_hackwatch_face_loop(movement_event_t event, movement_settings_t *s watch_clear_colon(); sprintf(buf, "%s ", set_time_hackwatch_face_titles[current_page]); } else { + int16_t tz = get_timezone_offset(settings->bit.time_zone, date_time_settings); watch_set_colon(); sprintf(buf, "%s %3d%02d ", set_time_hackwatch_face_titles[current_page], - (int8_t)(movement_timezone_offsets[settings->bit.time_zone] / 60), - (int8_t)(movement_timezone_offsets[settings->bit.time_zone] % 60) * (movement_timezone_offsets[settings->bit.time_zone] < 0 ? -1 : 1)); + (int8_t)(tz / 60), + (int8_t)(tz % 60) * (tz < 0 ? -1 : 1)); } } From bae8c6582559e2617ceae22ed34f7d0be8e10a9f Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Fri, 2 Aug 2024 07:37:30 -0400 Subject: [PATCH 031/161] Made the faces I care about not need to continuously re-find the timezone --- movement/watch_faces/clock/world_clock2_face.c | 9 ++++----- movement/watch_faces/clock/world_clock2_face.h | 1 + movement/watch_faces/clock/world_clock_face.c | 15 +++++---------- movement/watch_faces/clock/world_clock_face.h | 1 + .../watch_faces/complication/moon_phase_face.c | 1 + .../complication/sunrise_sunset_face.c | 6 +++--- .../complication/sunrise_sunset_face.h | 1 + 7 files changed, 16 insertions(+), 18 deletions(-) diff --git a/movement/watch_faces/clock/world_clock2_face.c b/movement/watch_faces/clock/world_clock2_face.c index 6ff806b..cf37a40 100644 --- a/movement/watch_faces/clock/world_clock2_face.c +++ b/movement/watch_faces/clock/world_clock2_face.c @@ -154,6 +154,7 @@ void world_clock2_face_activate(movement_settings_t *settings, void *context) movement_request_tick_frequency(4); break; } + state->tz = get_timezone_offset(settings->bit.time_zone, watch_rtc_get_date_time()); refresh_face = true; } @@ -165,7 +166,6 @@ static bool mode_display(movement_event_t event, movement_settings_t *settings, uint32_t timestamp; uint32_t previous_date_time; watch_date_time date_time; - int16_t tz; switch (event.event_type) { case EVENT_ACTIVATE: @@ -184,9 +184,8 @@ static bool mode_display(movement_event_t event, movement_settings_t *settings, /* Determine current time at time zone and store date/time */ date_time = watch_rtc_get_date_time(); - tz = get_timezone_offset(settings->bit.time_zone, date_time); - timestamp = watch_utility_date_time_to_unix_time(date_time, tz * 60); - date_time = watch_utility_date_time_from_unix_time(timestamp, tz * 60); + timestamp = watch_utility_date_time_to_unix_time(date_time, state->tz * 60); + date_time = watch_utility_date_time_from_unix_time(timestamp, state->tz * 60); previous_date_time = state->previous_date_time; state->previous_date_time = date_time.reg; @@ -286,7 +285,7 @@ static bool mode_settings(movement_event_t event, movement_settings_t *settings, watch_clear_indicator(WATCH_INDICATOR_PM); refresh_face = false; } - result = div(get_timezone_offset(state->current_zone, watch_rtc_get_date_time()), 60); + result = div(state->tz, 60); hours = result.quot; minutes = result.rem; diff --git a/movement/watch_faces/clock/world_clock2_face.h b/movement/watch_faces/clock/world_clock2_face.h index 0baac21..efc107e 100644 --- a/movement/watch_faces/clock/world_clock2_face.h +++ b/movement/watch_faces/clock/world_clock2_face.h @@ -104,6 +104,7 @@ typedef struct { world_clock2_mode_t current_mode; uint8_t current_zone; uint32_t previous_date_time; + int16_t tz; } world_clock2_state_t; void world_clock2_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr); diff --git a/movement/watch_faces/clock/world_clock_face.c b/movement/watch_faces/clock/world_clock_face.c index c34db92..e7f393e 100644 --- a/movement/watch_faces/clock/world_clock_face.c +++ b/movement/watch_faces/clock/world_clock_face.c @@ -54,13 +54,13 @@ void world_clock_face_activate(movement_settings_t *settings, void *context) { static bool world_clock_face_do_display_mode(movement_event_t event, movement_settings_t *settings, world_clock_state_t *state) { char buf[11]; uint8_t pos; - int16_t tz; uint32_t timestamp; uint32_t previous_date_time; watch_date_time date_time; switch (event.event_type) { case EVENT_ACTIVATE: + state->tz = get_timezone_offset(settings->bit.time_zone, watch_rtc_get_date_time()); if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H); watch_set_colon(); state->previous_date_time = 0xFFFFFFFF; @@ -68,9 +68,8 @@ static bool world_clock_face_do_display_mode(movement_event_t event, movement_se case EVENT_TICK: case EVENT_LOW_ENERGY_UPDATE: date_time = watch_rtc_get_date_time(); - tz = get_timezone_offset(settings->bit.time_zone, date_time); - timestamp = watch_utility_date_time_to_unix_time(date_time, tz * 60); - date_time = watch_utility_date_time_from_unix_time(timestamp, tz * 60); + timestamp = watch_utility_date_time_to_unix_time(date_time, state->tz * 60); + date_time = watch_utility_date_time_from_unix_time(timestamp, state->tz * 60); previous_date_time = state->previous_date_time; state->previous_date_time = date_time.reg; @@ -127,8 +126,6 @@ static bool world_clock_face_do_display_mode(movement_event_t event, movement_se } static bool _world_clock_face_do_settings_mode(movement_event_t event, movement_settings_t *settings, world_clock_state_t *state) { - watch_date_time date_time; - int16_t tz; switch (event.event_type) { case EVENT_MODE_BUTTON_UP: if (state->backup_register) watch_store_backup_data(state->settings.reg, state->backup_register); @@ -172,13 +169,11 @@ static bool _world_clock_face_do_settings_mode(movement_event_t event, movement_ } char buf[13]; - date_time = watch_rtc_get_date_time(); - tz = get_timezone_offset(settings->bit.time_zone, date_time); sprintf(buf, "%c%c %3d%02d ", movement_valid_position_0_chars[state->settings.bit.char_0], movement_valid_position_1_chars[state->settings.bit.char_1], - (int8_t) (tz / 60), - (int8_t) (tz % 60) * (tz < 0 ? -1 : 1)); + (int8_t) (state->tz / 60), + (int8_t) (state->tz % 60) * (state->tz < 0 ? -1 : 1)); watch_set_colon(); watch_clear_indicator(WATCH_INDICATOR_PM); diff --git a/movement/watch_faces/clock/world_clock_face.h b/movement/watch_faces/clock/world_clock_face.h index 92e91a6..e76d85e 100644 --- a/movement/watch_faces/clock/world_clock_face.h +++ b/movement/watch_faces/clock/world_clock_face.h @@ -62,6 +62,7 @@ typedef struct { uint8_t backup_register; uint8_t current_screen; uint32_t previous_date_time; + int16_t tz; } world_clock_state_t; void world_clock_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); diff --git a/movement/watch_faces/complication/moon_phase_face.c b/movement/watch_faces/complication/moon_phase_face.c index 47ed6e6..c571843 100644 --- a/movement/watch_faces/complication/moon_phase_face.c +++ b/movement/watch_faces/complication/moon_phase_face.c @@ -139,6 +139,7 @@ bool moon_phase_face_loop(movement_event_t event, movement_settings_t *settings, switch (event.event_type) { case EVENT_ACTIVATE: + _update(settings, state, state->offset); break; case EVENT_TICK: diff --git a/movement/watch_faces/complication/sunrise_sunset_face.c b/movement/watch_faces/complication/sunrise_sunset_face.c index a6ec764..ad03d88 100644 --- a/movement/watch_faces/complication/sunrise_sunset_face.c +++ b/movement/watch_faces/complication/sunrise_sunset_face.c @@ -54,8 +54,7 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s } watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time - int16_t tz = get_timezone_offset(settings->bit.time_zone, date_time); - watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, tz * 60, 0); // the current date / time in UTC + watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, state->tz * 60, 0); // the current date / time in UTC watch_date_time scratch_time; // scratchpad, contains different values at different times scratch_time.reg = utc_now.reg; @@ -70,7 +69,7 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s // sunriset returns the rise/set times as signed decimal hours in UTC. // this can mean hours below 0 or above 31, which won't fit into a watch_date_time struct. // to deal with this, we set aside the offset in hours, and add it back before converting it to a watch_date_time. - double hours_from_utc = ((double)tz) / 60.0; + double hours_from_utc = ((double)state->tz) / 60.0; // we loop twice because if it's after sunset today, we need to recalculate to display values for tomorrow. for(int i = 0; i < 2; i++) { @@ -317,6 +316,7 @@ void sunrise_sunset_face_activate(movement_settings_t *settings, void *context) movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1); state->working_latitude = _sunrise_sunset_face_struct_from_latlon(movement_location.bit.latitude); state->working_longitude = _sunrise_sunset_face_struct_from_latlon(movement_location.bit.longitude); + state->tz = get_timezone_offset(settings->bit.time_zone, watch_rtc_get_date_time()); } bool sunrise_sunset_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { diff --git a/movement/watch_faces/complication/sunrise_sunset_face.h b/movement/watch_faces/complication/sunrise_sunset_face.h index 16e65b7..71b8462 100644 --- a/movement/watch_faces/complication/sunrise_sunset_face.h +++ b/movement/watch_faces/complication/sunrise_sunset_face.h @@ -52,6 +52,7 @@ typedef struct { uint8_t rise_index; uint8_t active_digit; bool location_changed; + int16_t tz; watch_date_time rise_set_expires; sunrise_sunset_lat_lon_settings_t working_latitude; sunrise_sunset_lat_lon_settings_t working_longitude; From fa2907e098ceccc249e864aad864140208bb4215 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Fri, 2 Aug 2024 17:57:29 -0400 Subject: [PATCH 032/161] Some more cleanup --- movement/movement.c | 111 +++++++++--------- movement/movement.h | 2 +- movement/movement_config.h | 7 ++ movement/watch_faces/settings/set_time_face.c | 9 +- watch-library/shared/watch/watch_utility.h | 9 +- 5 files changed, 76 insertions(+), 62 deletions(-) diff --git a/movement/movement.c b/movement/movement.c index 52a9ac0..4afe273 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -96,6 +96,11 @@ #define MOVEMENT_DEFAULT_LED_DURATION 1 #endif +// Default to having DST get set +#ifndef MOVEMENT_DEFAULT_DST_ACTIVE +#define MOVEMENT_DEFAULT_DST_ACTIVE true +#endif + #if __EMSCRIPTEN__ #include #endif @@ -107,7 +112,8 @@ const int32_t movement_le_inactivity_deadlines[8] = {INT_MAX, 600, 3600, 7200, 2 const int16_t movement_timeout_inactivity_deadlines[4] = {60, 120, 300, 1800}; movement_event_t event; -const int16_t movement_timezone_offsets[] = { +#define NUM_TIME_ZONES 41 +const int16_t movement_timezone_offsets[NUM_TIME_ZONES] = { 0, // 0 : 0:00:00 (UTC) 60, // 1 : 1:00:00 (Central European Time) 120, // 2 : 2:00:00 (South African Standard Time) @@ -164,47 +170,47 @@ const int16_t movement_timezone_offsets[] = { * having to separately change the hour and timezone info * in the time set face. */ -const uint8_t movement_dst_jump_table[] = { - 1, // 0 UTC + 1 = CET - 2, // 1 CET + 1 = SAST - 3, // 2 SAST + 1 = AST - 5, // 3 AST + 1 = GST - 6, // 4 IST + 1 = AT - 7, // 5 GST + 1 = PST - 8, // 6 AT + 1 = IST - 10, // 7 PST + 1 = KT - 11, // 8 IST + 1 = MT - 9, // 9 Nepal has no equivalent DST timezone, but they don't observe DST anyway - 12, // 10 KT + 1 = TST - 11, // 11 Myanmar has no equivalent DST timezone, but they don't observe DST anyway - 13, // 12 TST + 1 = CST - 15, // 13 CST + 1 = JST - 14, // 14 ACWST has no equivalent DST timezone, but they don't observe DST anyway - 17, // 15 JST + 1 = AEST - 18, // 16 ACST + 1 = LHST - 19, // 17 AEST + 1 = SIT - 18, // 18 LHST has no equivalent DST timezone, but they don't observe DST anyway - 20, // 19 SIT + 1 = NZST - 22, // 20 NZST + 1 = TT - 23, // 21 CST + 1 = CDT - 24, // 22 TT + 1 = LIT - 23, // 23 CDT is already a daylight timezone - 24, // 24 LIT has no equivalent DST timezone, but they don't observe DST anyway - 26, // 25 BIT + 1 = NT - 27, // 26 NT + 1 = HAST - 29, // 27 HAST + 1 = AST - 28, // 28 MIT has no equivalent DST timezone, but they don't observe DST anyway - 30, // 29 AST + 1 = PST - 31, // 30 PST + 1 = MST - 32, // 31 MST + 1 = CST - 33, // 32 CST + 1 = EST - 35, // 33 EST + 1 = AST - 36, // 34 VST + 1 = NST - 37, // 35 AST + 1 = BT - 38, // 36 NST + 1 = NDT - 39, // 37 BT + 1 = 39 - 38, // 38 NDT is already a daylight timezone - 40, // 39 FNT + 1 = AST +const int16_t movement_timezone_dst_offsets[NUM_TIME_ZONES] = { + 60, // 0 UTC + 1 = CET + 120, // 1 CET + 1 = SAST + 189, // 2 SAST + 1 = AST + 240, // 3 AST + 1 = GST + 270, // 4 IST + 1 = AT + 300, // 5 GST + 1 = PST + 330, // 6 AT + 1 = IST + 360, // 7 PST + 1 = KT + 390, // 8 IST + 1 = MT + 345, // 9 Nepal has no equivalent DST timezone, but they don't observe DST anyway + 420, // 10 KT + 1 = TST + 390, // 11 Myanmar has no equivalent DST timezone, but they don't observe DST anyway + 480, // 12 TST + 1 = CST + 540, // 13 CST + 1 = JST + 525, // 14 ACWST has no equivalent DST timezone, but they don't observe DST anyway + 600, // 15 JST + 1 = AEST + 630, // 16 ACST + 1 = LHST + 660, // 17 AEST + 1 = SIT + 630, // 18 LHST has no equivalent DST timezone, but they don't observe DST anyway + 720, // 19 SIT + 1 = NZST + 780, // 20 NZST + 1 = TT + 825, // 21 CST + 1 = CDT + 840, // 22 TT + 1 = LIT + 825, // 23 CDT is already a daylight timezone + 840, // 24 LIT has no equivalent DST timezone, but they don't observe DST anyway + -660, // 25 BIT + 1 = NT + -600, // 26 NT + 1 = HAST + -540, // 27 HAST + 1 = AST + -570, // 28 MIT has no equivalent DST timezone, but they don't observe DST anyway + -480, // 29 AST + 1 = PST + -420, // 30 PST + 1 = MST + -360, // 31 MST + 1 = CST + -300, // 32 CST + 1 = EST + -240, // 33 EST + 1 = AST + -210, // 34 VST + 1 = NST + -180, // 35 AST + 1 = BT + -150, // 36 NST + 1 = NDT + -120, // 37 BT + 1 = 39 + -150, // 38 NDT is already a daylight timezone + -60, // 39 FNT + 1 = AST 0 // 40 AST + 1 = UTC }; @@ -426,20 +432,13 @@ uint8_t movement_claim_backup_register(void) { int16_t get_timezone_offset(uint8_t timezone_idx, watch_date_time date_time) { if (!movement_state.settings.bit.dst_active) return movement_timezone_offsets[timezone_idx]; - uint8_t dst_result = is_dst(date_time); - switch (dst_result) - { - case DST_STARTED: - case DST_OCCURRING: - return movement_timezone_offsets[movement_dst_jump_table[timezone_idx]]; - case DST_ENDING: - case DST_ENDED: - default: - return movement_timezone_offsets[timezone_idx]; - } + if (dst_occurring(date_time)) + return movement_timezone_dst_offsets[timezone_idx]; + return movement_timezone_offsets[timezone_idx]; } void app_init(void) { + const int16_t* timezone_offsets; #if defined(NO_FREQCORR) watch_rtc_freqcorr_write(0, 0); #elif defined(WATCH_IS_BLUE_BOARD) @@ -457,6 +456,7 @@ void app_init(void) { movement_state.settings.bit.to_interval = MOVEMENT_DEFAULT_TIMEOUT_INTERVAL; movement_state.settings.bit.le_interval = MOVEMENT_DEFAULT_LOW_ENERGY_INTERVAL; movement_state.settings.bit.led_duration = MOVEMENT_DEFAULT_LED_DURATION; + movement_state.settings.bit.dst_active = MOVEMENT_DEFAULT_DST_ACTIVE; movement_state.light_ticks = -1; movement_state.alarm_ticks = -1; movement_state.next_available_backup_register = 4; @@ -468,8 +468,9 @@ void app_init(void) { int32_t time_zone_offset = EM_ASM_INT({ return -new Date().getTimezoneOffset(); }); - for (int i = 0, count = sizeof(movement_timezone_offsets) / sizeof(movement_timezone_offsets[0]); i < count; i++) { - if (movement_timezone_offsets[i] == time_zone_offset) { + timezone_offsets = dst_occurring(watch_rtc_get_date_time()) ? movement_timezone_dst_offsets : movement_timezone_offsets; + for (int i = 0; i < NUM_TIME_ZONES; i++) { + if (timezone_offsets[i] == time_zone_offset) { movement_state.settings.bit.time_zone = i; break; } diff --git a/movement/movement.h b/movement/movement.h index 9aa1dbc..d0595fb 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -129,7 +129,7 @@ typedef struct { } movement_event_t; extern const int16_t movement_timezone_offsets[]; -extern const uint8_t movement_dst_jump_table[]; +extern const int16_t movement_timezone_dst_offsets[]; extern const char movement_valid_position_0_chars[]; extern const char movement_valid_position_1_chars[]; diff --git a/movement/movement_config.h b/movement/movement_config.h index 10a30af..46e1d2e 100644 --- a/movement/movement_config.h +++ b/movement/movement_config.h @@ -95,4 +95,11 @@ const watch_face_t watch_faces[] = { */ #define MOVEMENT_DEFAULT_LED_DURATION 1 +/* Set if using DST + * Valid values are: + * false: Don't allow the watch to use DST + * true: Allow the watch to use DST + */ +#define MOVEMENT_DEFAULT_DST_ACTIVE true + #endif // MOVEMENT_CONFIG_H_ diff --git a/movement/watch_faces/settings/set_time_face.c b/movement/watch_faces/settings/set_time_face.c index 90edac2..b16cb6d 100644 --- a/movement/watch_faces/settings/set_time_face.c +++ b/movement/watch_faces/settings/set_time_face.c @@ -25,6 +25,7 @@ #include #include "set_time_face.h" #include "watch.h" +#include "watch_utility.h" #define SET_TIME_FACE_NUM_SETTINGS (8) const char set_time_face_titles[SET_TIME_FACE_NUM_SETTINGS][3] = {"HR", "M1", "SE", "YR", "MO", "DA", "ZO", "DS"}; @@ -150,18 +151,18 @@ bool set_time_face_loop(movement_event_t event, movement_settings_t *settings, v watch_clear_indicator(WATCH_INDICATOR_PM); sprintf(buf, "%s %2d%02d%02d", set_time_face_titles[current_page], date_time.unit.year + 20, date_time.unit.month, date_time.unit.day); } else if (current_page < 7) { // zone + char dst_char = (settings->bit.dst_active && dst_occurring(watch_rtc_get_date_time())) ? 'd' : ' '; if (event.subsecond % 2) { watch_clear_colon(); - sprintf(buf, "%s ", set_time_face_titles[current_page]); + sprintf(buf, "%s %c", set_time_face_titles[current_page], dst_char); } else { int16_t tz = get_timezone_offset(settings->bit.time_zone, date_time); watch_set_colon(); - sprintf(buf, "%s %3d%02d ", set_time_face_titles[current_page], (int8_t) (tz / 60), (int8_t) (tz % 60) * (tz < 0 ? -1 : 1)); + sprintf(buf, "%s %3d%02d %c", set_time_face_titles[current_page], (int8_t) (tz / 60), (int8_t) (tz % 60) * (tz < 0 ? -1 : 1), dst_char); } } else { // daylight savings watch_clear_colon(); - if (settings->bit.dst_active) sprintf(buf, "%s dsT y", set_time_face_titles[current_page]); - else sprintf(buf, "%s dsT n", set_time_face_titles[current_page]); + sprintf(buf, "%s dsT %c", set_time_face_titles[current_page], settings->bit.dst_active ? 'y' : 'n'); } // blink up the parameter we're setting diff --git a/watch-library/shared/watch/watch_utility.h b/watch-library/shared/watch/watch_utility.h index d3f3981..bdc2a88 100644 --- a/watch-library/shared/watch/watch_utility.h +++ b/watch-library/shared/watch/watch_utility.h @@ -46,7 +46,7 @@ typedef struct { } watch_duration_t; typedef enum { - DST_STARTED, + DST_STARTING, DST_OCCURRING, DST_ENDING, DST_ENDED @@ -89,7 +89,12 @@ uint8_t is_leap(uint16_t year); * @param date_time The watch_date_time that you wish to convert. * @return DST_OCCURRING, DST_HAPPENING, DST_ENDING, DST_ENDED */ -uint8_t is_dst(watch_date_time date_time); +uint8_t get_dst_status(watch_date_time date_time); + +/** @brief Returns true if it's DST and false otherwise. + * @param date_time The watch_date_time that you wish to convert. + */ +bool dst_occurring(watch_date_time date_time); /** @brief Returns the UNIX time (seconds since 1970) for a given date/time in UTC. * @param date_time The watch_date_time that you wish to convert. From 74421c7e65f9a735bcc394eed027b59baef1cb97 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sat, 3 Aug 2024 08:46:34 -0400 Subject: [PATCH 033/161] Day roll back repeat fix --- movement/watch_faces/clock/clock_face.c | 32 +++++++++-------- .../watch_faces/clock/simple_clock_face.c | 34 +++++++++++-------- movement/watch_faces/settings/set_time_face.c | 1 + .../settings/set_time_hackwatch_face.c | 9 +++-- watch-library/shared/watch/watch_utility.c | 21 +++++++----- 5 files changed, 58 insertions(+), 39 deletions(-) diff --git a/movement/watch_faces/clock/clock_face.c b/movement/watch_faces/clock/clock_face.c index 68019d8..f9ec58a 100644 --- a/movement/watch_faces/clock/clock_face.c +++ b/movement/watch_faces/clock/clock_face.c @@ -280,27 +280,31 @@ void clock_face_resign(movement_settings_t *settings, void *context) { (void) context; } -static void check_and_act_on_daylight_savings(bool dst_active, watch_date_time date_time) { - if (!dst_active) return; - uint8_t dst_result = is_dst(date_time); - switch (dst_result) - { - case DST_STARTED: - date_time.unit.hour = (date_time.unit.hour + 1) % 24; - break; - case DST_ENDING: - date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24; - break; - default: +static void check_and_act_on_daylight_savings(movement_settings_t *settings, watch_date_time date_time) { + if (!settings ->bit.dst_active) return; + uint8_t dst_result = get_dst_status(date_time); + + if (settings ->bit.dst_skip_rolling_back && dst_result == DST_ENDED) { + settings ->bit.dst_skip_rolling_back = false; return; } - watch_rtc_set_date_time(date_time); + else if (dst_result == DST_ENDING && !settings ->bit.dst_skip_rolling_back) { + settings ->bit.dst_skip_rolling_back = true; + date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24; + watch_rtc_set_date_time(date_time); + return; + } + else if (dst_result == DST_STARTING) { + date_time.unit.hour = (date_time.unit.hour + 1) % 24; + watch_rtc_set_date_time(date_time); + return; + } } bool clock_face_wants_background_task(movement_settings_t *settings, void *context) { clock_state_t *state = (clock_state_t *) context; watch_date_time date_time = watch_rtc_get_date_time(); - check_and_act_on_daylight_savings(settings->bit.dst_active, date_time); + check_and_act_on_daylight_savings(settings, date_time); if (!state->time_signal_enabled) return false; return date_time.unit.minute == 0; diff --git a/movement/watch_faces/clock/simple_clock_face.c b/movement/watch_faces/clock/simple_clock_face.c index c0f8372..a7e08a9 100644 --- a/movement/watch_faces/clock/simple_clock_face.c +++ b/movement/watch_faces/clock/simple_clock_face.c @@ -150,27 +150,31 @@ void simple_clock_face_resign(movement_settings_t *settings, void *context) { (void) context; } -static void check_and_act_on_daylight_savings(bool dst_active, watch_date_time date_time) { - if (!dst_active) return; - uint8_t dst_result = is_dst(date_time); - switch (dst_result) - { - case DST_STARTED: - date_time.unit.hour = (date_time.unit.hour + 1) % 24; - break; - case DST_ENDING: - date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24; - break; - default: +static void check_and_act_on_daylight_savings(movement_settings_t *settings, watch_date_time date_time) { + if (!settings ->bit.dst_active) return; + uint8_t dst_result = get_dst_status(date_time); + + if (settings ->bit.dst_skip_rolling_back && (dst_result == DST_ENDED)) { + settings ->bit.dst_skip_rolling_back = false; return; } - watch_rtc_set_date_time(date_time); + else if (dst_result == DST_ENDING && !settings ->bit.dst_skip_rolling_back) { + settings ->bit.dst_skip_rolling_back = true; + date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24; + watch_rtc_set_date_time(date_time); + return; + } + else if (dst_result == DST_STARTING) { + date_time.unit.hour = (date_time.unit.hour + 1) % 24; + watch_rtc_set_date_time(date_time); + return; + } } bool simple_clock_face_wants_background_task(movement_settings_t *settings, void *context) { simple_clock_state_t *state = (simple_clock_state_t *)context; - watch_date_time date_time = watch_rtc_get_date_time(); - check_and_act_on_daylight_savings(settings->bit.dst_active, date_time); + date_time = watch_rtc_get_date_time(); + check_and_act_on_daylight_savings(settings, date_time); if (!state->signal_enabled) return false; return date_time.unit.minute == 0; diff --git a/movement/watch_faces/settings/set_time_face.c b/movement/watch_faces/settings/set_time_face.c index b16cb6d..3f7d0c7 100644 --- a/movement/watch_faces/settings/set_time_face.c +++ b/movement/watch_faces/settings/set_time_face.c @@ -72,6 +72,7 @@ static void _handle_alarm_button(movement_settings_t *settings, watch_date_time break; } watch_rtc_set_date_time(date_time); + settings->bit.dst_skip_rolling_back = false; } static void _abort_quick_ticks() { diff --git a/movement/watch_faces/settings/set_time_hackwatch_face.c b/movement/watch_faces/settings/set_time_hackwatch_face.c index bdd1b0a..be293ab 100644 --- a/movement/watch_faces/settings/set_time_hackwatch_face.c +++ b/movement/watch_faces/settings/set_time_hackwatch_face.c @@ -94,6 +94,7 @@ bool set_time_hackwatch_face_loop(movement_event_t event, movement_settings_t *s } date_time_settings.unit.second = 0; watch_rtc_set_date_time(date_time_settings); + settings->bit.dst_skip_rolling_back = false; } break; case EVENT_ALARM_BUTTON_DOWN: @@ -134,8 +135,10 @@ bool set_time_hackwatch_face_loop(movement_event_t event, movement_settings_t *s } break; } - if (current_page != 2) // Do not set time when we are at seconds, it was already set previously + if (current_page != 2) { // Do not set time when we are at seconds, it was already set previously watch_rtc_set_date_time(date_time_settings); + settings->bit.dst_skip_rolling_back = false; + } break; case EVENT_ALARM_LONG_UP://Setting seconds on long release @@ -178,8 +181,10 @@ bool set_time_hackwatch_face_loop(movement_event_t event, movement_settings_t *s if (settings->bit.time_zone > 40) settings->bit.time_zone = 0; break; } - if (current_page != 2) // Do not set time when we are at seconds, it was already set previously + if (current_page != 2) { // Do not set time when we are at seconds, it was already set previously watch_rtc_set_date_time(date_time_settings); + settings->bit.dst_skip_rolling_back = false; + } //TODO: Do not update whole RTC, just what we are changing break; case EVENT_TIMEOUT: diff --git a/watch-library/shared/watch/watch_utility.c b/watch-library/shared/watch/watch_utility.c index 1410e41..5938b60 100644 --- a/watch-library/shared/watch/watch_utility.c +++ b/watch-library/shared/watch/watch_utility.c @@ -83,38 +83,43 @@ uint8_t is_leap(uint16_t y) return !(y%4) && ((y%100) || !(y%400)); } -uint8_t is_dst(watch_date_time date_time) { - uint8_t weekday_first_of_month; +uint8_t get_dst_status(watch_date_time date_time) { watch_date_time dst_start_time; watch_date_time dst_end_time; uint32_t unix_dst_start_time; uint32_t unix_dst_end_time; uint32_t unix_curr_time; + dst_start_time.unit.year = date_time.unit.year; dst_start_time.unit.month = 3; dst_start_time.unit.hour = 2; dst_start_time.unit.minute = 0; dst_start_time.unit.second = 0; - weekday_first_of_month = watch_utility_get_iso8601_weekday_number(date_time.unit.year, dst_start_time.unit.month, 1); - dst_start_time.unit.day = 15 - weekday_first_of_month; + dst_start_time.unit.day = 15 - watch_utility_get_iso8601_weekday_number(dst_start_time.unit.year + WATCH_RTC_REFERENCE_YEAR, dst_start_time.unit.month, 1); unix_dst_start_time = watch_utility_date_time_to_unix_time(dst_start_time, 0); + dst_end_time.unit.year = date_time.unit.year; dst_end_time.unit.month = 11; dst_end_time.unit.hour = 2; dst_end_time.unit.minute = 0; dst_end_time.unit.second = 0; - weekday_first_of_month = watch_utility_get_iso8601_weekday_number(date_time.unit.year, dst_end_time.unit.month, 1); - dst_end_time.unit.day = 15 - weekday_first_of_month; + dst_end_time.unit.day = 15 - watch_utility_get_iso8601_weekday_number(dst_end_time.unit.year + WATCH_RTC_REFERENCE_YEAR, dst_end_time.unit.month, 1); unix_dst_end_time = watch_utility_date_time_to_unix_time(dst_end_time, 0); + if (date_time.unit.second > 45) // In emu, it's been seen that we may trigger at 59sec rather than exactly 0 each time + date_time.unit.minute = (date_time.unit.minute + 1) % 60; date_time.unit.second = 0; unix_curr_time = watch_utility_date_time_to_unix_time(date_time, 0); - if (unix_curr_time == unix_dst_start_time) return DST_STARTED; + if (unix_curr_time == unix_dst_start_time) return DST_STARTING; if (unix_curr_time == unix_dst_end_time) return DST_ENDING; if (unix_curr_time > unix_dst_end_time || unix_curr_time < unix_dst_start_time) return DST_ENDED; return DST_OCCURRING; -} +} + +bool dst_occurring(watch_date_time date_time) { + return get_dst_status(date_time) <= DST_OCCURRING; +} uint16_t watch_utility_days_since_new_year(uint16_t year, uint8_t month, uint8_t day) { uint16_t DAYS_SO_FAR[] = { From e50390b673fda13a31463509a0d26165f1a4d1ea Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sat, 3 Aug 2024 09:33:49 -0400 Subject: [PATCH 034/161] DST roll is now a global variable in watch_rtc --- movement/movement.c | 20 +++++++++++++ movement/movement.h | 1 + movement/watch_faces/clock/clock_face.c | 29 +++++------------- .../clock/minute_repeater_decimal_face.c | 4 +-- .../clock/repetition_minute_face.c | 4 +-- .../clock/simple_clock_bin_led_face.c | 4 +-- .../watch_faces/clock/simple_clock_face.c | 30 +++++-------------- movement/watch_faces/settings/set_time_face.c | 1 - .../settings/set_time_hackwatch_face.c | 3 -- watch-library/hardware/watch/watch_rtc.c | 14 +++++++++ watch-library/shared/watch/watch_rtc.h | 6 ++++ watch-library/simulator/watch/watch_rtc.c | 14 +++++++++ 12 files changed, 75 insertions(+), 55 deletions(-) diff --git a/movement/movement.c b/movement/movement.c index 4afe273..eabc29c 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -430,6 +430,26 @@ uint8_t movement_claim_backup_register(void) { return movement_state.next_available_backup_register++; } +uint8_t check_and_act_on_daylight_savings(watch_date_time date_time) { + if (movement_state.settings.bit.dst_active) return date_time.unit.hour; + uint8_t dst_result = get_dst_status(date_time); + bool dst_skip_rolling_back = get_dst_skip_rolling_back(); + + if (dst_skip_rolling_back && (dst_result == DST_ENDED)) { + clear_dst_skip_rolling_back(); + } + else if (dst_result == DST_ENDING && !dst_skip_rolling_back) { + set_dst_skip_rolling_back(); + date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24; + watch_rtc_set_date_time(date_time); + } + else if (dst_result == DST_STARTING) { + date_time.unit.hour = (date_time.unit.hour + 1) % 24; + watch_rtc_set_date_time(date_time); + } + return date_time.unit.hour; +} + int16_t get_timezone_offset(uint8_t timezone_idx, watch_date_time date_time) { if (!movement_state.settings.bit.dst_active) return movement_timezone_offsets[timezone_idx]; if (dst_occurring(date_time)) diff --git a/movement/movement.h b/movement/movement.h index d0595fb..6dd38ba 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -313,6 +313,7 @@ void movement_play_alarm(void); void movement_play_alarm_beeps(uint8_t rounds, BuzzerNote alarm_note); uint8_t movement_claim_backup_register(void); +uint8_t check_and_act_on_daylight_savings(watch_date_time date_time); // Returns the currently set hour int16_t get_timezone_offset(uint8_t timezone_idx, watch_date_time date_time); #endif // MOVEMENT_H_ diff --git a/movement/watch_faces/clock/clock_face.c b/movement/watch_faces/clock/clock_face.c index f9ec58a..af601ce 100644 --- a/movement/watch_faces/clock/clock_face.c +++ b/movement/watch_faces/clock/clock_face.c @@ -280,31 +280,16 @@ void clock_face_resign(movement_settings_t *settings, void *context) { (void) context; } -static void check_and_act_on_daylight_savings(movement_settings_t *settings, watch_date_time date_time) { - if (!settings ->bit.dst_active) return; - uint8_t dst_result = get_dst_status(date_time); - - if (settings ->bit.dst_skip_rolling_back && dst_result == DST_ENDED) { - settings ->bit.dst_skip_rolling_back = false; - return; - } - else if (dst_result == DST_ENDING && !settings ->bit.dst_skip_rolling_back) { - settings ->bit.dst_skip_rolling_back = true; - date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24; - watch_rtc_set_date_time(date_time); - return; - } - else if (dst_result == DST_STARTING) { - date_time.unit.hour = (date_time.unit.hour + 1) % 24; - watch_rtc_set_date_time(date_time); - return; - } -} - bool clock_face_wants_background_task(movement_settings_t *settings, void *context) { + (void) settings; clock_state_t *state = (clock_state_t *) context; watch_date_time date_time = watch_rtc_get_date_time(); - check_and_act_on_daylight_savings(settings, date_time); + uint8_t hour_dst = check_and_act_on_daylight_savings(date_time); + if(hour_dst != date_time.unit.hour) { + char buf[3 + 1]; + sprintf(buf, "%2d", hour_dst); + watch_display_string(buf, 4); + } if (!state->time_signal_enabled) return false; return date_time.unit.minute == 0; diff --git a/movement/watch_faces/clock/minute_repeater_decimal_face.c b/movement/watch_faces/clock/minute_repeater_decimal_face.c index 2cedc30..ab0e6de 100644 --- a/movement/watch_faces/clock/minute_repeater_decimal_face.c +++ b/movement/watch_faces/clock/minute_repeater_decimal_face.c @@ -230,9 +230,9 @@ void minute_repeater_decimal_face_resign(movement_settings_t *settings, void *co bool minute_repeater_decimal_face_wants_background_task(movement_settings_t *settings, void *context) { (void) settings; minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)context; - if (!state->signal_enabled) return false; - watch_date_time date_time = watch_rtc_get_date_time(); + check_and_act_on_daylight_savings(date_time); + if (!state->signal_enabled) return false; return date_time.unit.minute == 0; } diff --git a/movement/watch_faces/clock/repetition_minute_face.c b/movement/watch_faces/clock/repetition_minute_face.c index e9e5e31..1afc4ce 100644 --- a/movement/watch_faces/clock/repetition_minute_face.c +++ b/movement/watch_faces/clock/repetition_minute_face.c @@ -213,9 +213,9 @@ void repetition_minute_face_resign(movement_settings_t *settings, void *context) bool repetition_minute_face_wants_background_task(movement_settings_t *settings, void *context) { (void) settings; repetition_minute_state_t *state = (repetition_minute_state_t *)context; - if (!state->signal_enabled) return false; - watch_date_time date_time = watch_rtc_get_date_time(); + check_and_act_on_daylight_savings(date_time); + if (!state->signal_enabled) return false; return date_time.unit.minute == 0; } diff --git a/movement/watch_faces/clock/simple_clock_bin_led_face.c b/movement/watch_faces/clock/simple_clock_bin_led_face.c index cf39c18..1762cfa 100644 --- a/movement/watch_faces/clock/simple_clock_bin_led_face.c +++ b/movement/watch_faces/clock/simple_clock_bin_led_face.c @@ -214,9 +214,9 @@ void simple_clock_bin_led_face_resign(movement_settings_t *settings, void *conte bool simple_clock_bin_led_face_wants_background_task(movement_settings_t *settings, void *context) { (void) settings; simple_clock_bin_led_state_t *state = (simple_clock_bin_led_state_t *)context; - if (!state->signal_enabled) return false; - watch_date_time date_time = watch_rtc_get_date_time(); + check_and_act_on_daylight_savings(date_time); + if (!state->signal_enabled) return false; return date_time.unit.minute == 0; } diff --git a/movement/watch_faces/clock/simple_clock_face.c b/movement/watch_faces/clock/simple_clock_face.c index a7e08a9..3866e2c 100644 --- a/movement/watch_faces/clock/simple_clock_face.c +++ b/movement/watch_faces/clock/simple_clock_face.c @@ -150,31 +150,15 @@ void simple_clock_face_resign(movement_settings_t *settings, void *context) { (void) context; } -static void check_and_act_on_daylight_savings(movement_settings_t *settings, watch_date_time date_time) { - if (!settings ->bit.dst_active) return; - uint8_t dst_result = get_dst_status(date_time); - - if (settings ->bit.dst_skip_rolling_back && (dst_result == DST_ENDED)) { - settings ->bit.dst_skip_rolling_back = false; - return; - } - else if (dst_result == DST_ENDING && !settings ->bit.dst_skip_rolling_back) { - settings ->bit.dst_skip_rolling_back = true; - date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24; - watch_rtc_set_date_time(date_time); - return; - } - else if (dst_result == DST_STARTING) { - date_time.unit.hour = (date_time.unit.hour + 1) % 24; - watch_rtc_set_date_time(date_time); - return; - } -} - bool simple_clock_face_wants_background_task(movement_settings_t *settings, void *context) { simple_clock_state_t *state = (simple_clock_state_t *)context; - date_time = watch_rtc_get_date_time(); - check_and_act_on_daylight_savings(settings, date_time); + watch_date_time date_time = watch_rtc_get_date_time(); + uint8_t hour_dst = check_and_act_on_daylight_savings(date_time); + if(hour_dst != date_time.unit.hour) { + char buf[3 + 1]; + sprintf(buf, "%2d", hour_dst); + watch_display_string(buf, 4); + } if (!state->signal_enabled) return false; return date_time.unit.minute == 0; diff --git a/movement/watch_faces/settings/set_time_face.c b/movement/watch_faces/settings/set_time_face.c index 3f7d0c7..b16cb6d 100644 --- a/movement/watch_faces/settings/set_time_face.c +++ b/movement/watch_faces/settings/set_time_face.c @@ -72,7 +72,6 @@ static void _handle_alarm_button(movement_settings_t *settings, watch_date_time break; } watch_rtc_set_date_time(date_time); - settings->bit.dst_skip_rolling_back = false; } static void _abort_quick_ticks() { diff --git a/movement/watch_faces/settings/set_time_hackwatch_face.c b/movement/watch_faces/settings/set_time_hackwatch_face.c index be293ab..44ae04e 100644 --- a/movement/watch_faces/settings/set_time_hackwatch_face.c +++ b/movement/watch_faces/settings/set_time_hackwatch_face.c @@ -94,7 +94,6 @@ bool set_time_hackwatch_face_loop(movement_event_t event, movement_settings_t *s } date_time_settings.unit.second = 0; watch_rtc_set_date_time(date_time_settings); - settings->bit.dst_skip_rolling_back = false; } break; case EVENT_ALARM_BUTTON_DOWN: @@ -137,7 +136,6 @@ bool set_time_hackwatch_face_loop(movement_event_t event, movement_settings_t *s } if (current_page != 2) { // Do not set time when we are at seconds, it was already set previously watch_rtc_set_date_time(date_time_settings); - settings->bit.dst_skip_rolling_back = false; } break; @@ -183,7 +181,6 @@ bool set_time_hackwatch_face_loop(movement_event_t event, movement_settings_t *s } if (current_page != 2) { // Do not set time when we are at seconds, it was already set previously watch_rtc_set_date_time(date_time_settings); - settings->bit.dst_skip_rolling_back = false; } //TODO: Do not update whole RTC, just what we are changing break; diff --git a/watch-library/hardware/watch/watch_rtc.c b/watch-library/hardware/watch/watch_rtc.c index 93cb9f1..67ecb53 100644 --- a/watch-library/hardware/watch/watch_rtc.c +++ b/watch-library/hardware/watch/watch_rtc.c @@ -30,6 +30,19 @@ ext_irq_cb_t btn_alarm_callback; ext_irq_cb_t a2_callback; ext_irq_cb_t a4_callback; +static bool dst_skip_rolling_back; +bool get_dst_skip_rolling_back(void) { + return dst_skip_rolling_back; +} + +void set_dst_skip_rolling_back(void) { + dst_skip_rolling_back = true; +} + +void clear_dst_skip_rolling_back(void) { + dst_skip_rolling_back = false; +} + bool _watch_rtc_is_enabled(void) { return RTC->MODE2.CTRLA.bit.ENABLE; } @@ -60,6 +73,7 @@ void watch_rtc_set_date_time(watch_date_time date_time) { _sync_rtc(); // Double sync as without it at high Hz faces setting time is unrealiable (specifically, set_time_hackwatch) RTC->MODE2.CLOCK.reg = date_time.reg; _sync_rtc(); + clear_dst_skip_rolling_back(); } watch_date_time watch_rtc_get_date_time(void) { diff --git a/watch-library/shared/watch/watch_rtc.h b/watch-library/shared/watch/watch_rtc.h index 3e63bb5..a9134eb 100644 --- a/watch-library/shared/watch/watch_rtc.h +++ b/watch-library/shared/watch/watch_rtc.h @@ -157,5 +157,11 @@ void watch_rtc_enable(bool en); */ void watch_rtc_freqcorr_write(int16_t value, int16_t sign); +/** @brief Returns if we're currently at a point where the we rolled back for DST and need to ignore the next DST segment + */ +bool get_dst_skip_rolling_back(void); +void set_dst_skip_rolling_back(void); +void clear_dst_skip_rolling_back(void); + /// @} #endif diff --git a/watch-library/simulator/watch/watch_rtc.c b/watch-library/simulator/watch/watch_rtc.c index 2bb6074..9fe9e29 100644 --- a/watch-library/simulator/watch/watch_rtc.c +++ b/watch-library/simulator/watch/watch_rtc.c @@ -39,6 +39,19 @@ ext_irq_cb_t btn_alarm_callback; ext_irq_cb_t a2_callback; ext_irq_cb_t a4_callback; +static bool dst_skip_rolling_back; +bool get_dst_skip_rolling_back(void) { + return dst_skip_rolling_back; +} + +void set_dst_skip_rolling_back(void) { + dst_skip_rolling_back = true; +} + +void clear_dst_skip_rolling_back(void) { + dst_skip_rolling_back = false; +} + bool _watch_rtc_is_enabled(void) { return true; } @@ -57,6 +70,7 @@ void watch_rtc_set_date_time(watch_date_time date_time) { const date = new Date(year, month - 1, day, hour, minute, second); return date - Date.now(); }, date_time.reg); + clear_dst_skip_rolling_back(); } watch_date_time watch_rtc_get_date_time(void) { From aebea960c01772bcb54210b30109eb1b11b4e34d Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sat, 3 Aug 2024 10:08:24 -0400 Subject: [PATCH 035/161] Cleanup to the default branch --- movement/movement.c | 2 +- movement/watch_faces/clock/simple_clock_face.c | 1 + movement/watch_faces/settings/set_time_face.c | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/movement/movement.c b/movement/movement.c index eabc29c..256aa8a 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -458,7 +458,6 @@ int16_t get_timezone_offset(uint8_t timezone_idx, watch_date_time date_time) { } void app_init(void) { - const int16_t* timezone_offsets; #if defined(NO_FREQCORR) watch_rtc_freqcorr_write(0, 0); #elif defined(WATCH_IS_BLUE_BOARD) @@ -485,6 +484,7 @@ void app_init(void) { filesystem_init(); #if __EMSCRIPTEN__ + const int16_t* timezone_offsets; int32_t time_zone_offset = EM_ASM_INT({ return -new Date().getTimezoneOffset(); }); diff --git a/movement/watch_faces/clock/simple_clock_face.c b/movement/watch_faces/clock/simple_clock_face.c index 3866e2c..20dfb54 100644 --- a/movement/watch_faces/clock/simple_clock_face.c +++ b/movement/watch_faces/clock/simple_clock_face.c @@ -151,6 +151,7 @@ void simple_clock_face_resign(movement_settings_t *settings, void *context) { } bool simple_clock_face_wants_background_task(movement_settings_t *settings, void *context) { + (void) settings; simple_clock_state_t *state = (simple_clock_state_t *)context; watch_date_time date_time = watch_rtc_get_date_time(); uint8_t hour_dst = check_and_act_on_daylight_savings(date_time); diff --git a/movement/watch_faces/settings/set_time_face.c b/movement/watch_faces/settings/set_time_face.c index b16cb6d..1b19df8 100644 --- a/movement/watch_faces/settings/set_time_face.c +++ b/movement/watch_faces/settings/set_time_face.c @@ -134,7 +134,7 @@ bool set_time_face_loop(movement_event_t event, movement_settings_t *settings, v return movement_default_loop_handler(event, settings); } - char buf[11]; + char buf[13]; if (current_page < 3) { watch_set_colon(); if (settings->bit.clock_mode_24h) { From 7a15ed35910adb89e1ee197fd05f3928d108ec8f Mon Sep 17 00:00:00 2001 From: Christian Buschau Date: Wed, 26 Jun 2024 10:11:50 +0200 Subject: [PATCH 036/161] faces/alarm_thermometer: new watch face --- movement/make/Makefile | 1 + movement/movement_faces.h | 1 + .../sensor/alarm_thermometer_face.c | 154 ++++++++++++++++++ .../sensor/alarm_thermometer_face.h | 74 +++++++++ 4 files changed, 230 insertions(+) create mode 100644 movement/watch_faces/sensor/alarm_thermometer_face.c create mode 100644 movement/watch_faces/sensor/alarm_thermometer_face.h diff --git a/movement/make/Makefile b/movement/make/Makefile index da5486b..da32a45 100644 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -129,6 +129,7 @@ SRCS += \ ../watch_faces/clock/minute_repeater_decimal_face.c \ ../watch_faces/complication/tuning_tones_face.c \ ../watch_faces/complication/kitchen_conversions_face.c \ + ../watch_faces/sensor/alarm_thermometer_face.c \ # New watch faces go above this line. # Leave this line at the bottom of the file; it has all the targets for making your project. diff --git a/movement/movement_faces.h b/movement/movement_faces.h index 3557110..fda91ac 100644 --- a/movement/movement_faces.h +++ b/movement/movement_faces.h @@ -104,6 +104,7 @@ #include "minute_repeater_decimal_face.h" #include "tuning_tones_face.h" #include "kitchen_conversions_face.h" +#include "alarm_thermometer_face.h" // New includes go above this line. #endif // MOVEMENT_FACES_H_ diff --git a/movement/watch_faces/sensor/alarm_thermometer_face.c b/movement/watch_faces/sensor/alarm_thermometer_face.c new file mode 100644 index 0000000..3b7821e --- /dev/null +++ b/movement/watch_faces/sensor/alarm_thermometer_face.c @@ -0,0 +1,154 @@ +/* + * MIT License + * + * Copyright (c) 2024 Christian Buschau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include "alarm_thermometer_face.h" +#include "thermistor_driver.h" + +static float _alarm_thermometer_face_update(bool in_fahrenheit) { + thermistor_driver_enable(); + float temperature_c = thermistor_driver_get_temperature(); + char buf[14]; + if (in_fahrenheit) { + sprintf(buf, "%4.1f#F", temperature_c * 1.8 + 32.0); + } else { + sprintf(buf, "%4.1f#C", temperature_c); + } + watch_display_string(buf, 4); + thermistor_driver_disable(); + return temperature_c; +} + +static void _alarm_thermometer_face_clear(int last[]) { + for (size_t i = 0; i < LAST_SIZE; i++) { + last[i] = INT_MIN; + } +} + +void alarm_thermometer_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { + (void) settings; + (void) watch_face_index; + if (*context_ptr == NULL) { + *context_ptr = malloc(sizeof(alarm_thermometer_state_t)); + memset(*context_ptr, 0, sizeof(alarm_thermometer_state_t)); + } +} + +void alarm_thermometer_face_activate(movement_settings_t *settings, void *context) { + (void) settings; + alarm_thermometer_state_t *state = (alarm_thermometer_state_t *)context; + state->mode = MODE_NORMAL; + _alarm_thermometer_face_clear(state->last); + watch_display_string("AT", 0); +} + +bool alarm_thermometer_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { + alarm_thermometer_state_t *state = (alarm_thermometer_state_t *)context; + + switch (event.event_type) { + case EVENT_ACTIVATE: + _alarm_thermometer_face_update(settings->bit.use_imperial_units); + break; + case EVENT_TICK: + if (watch_rtc_get_date_time().unit.second % 5 == 0) { + switch (state->mode) { + case MODE_NORMAL: + _alarm_thermometer_face_update(settings->bit.use_imperial_units); + break; + case MODE_ALARM: + for (size_t i = LAST_SIZE - 1; i > 0; i--) { + state->last[i] = state->last[i - 1]; + } + state->last[0] = roundf(_alarm_thermometer_face_update(settings->bit.use_imperial_units) * 10.0f); + bool constant = true; + for (size_t i = 1; i < LAST_SIZE; i++) { + if (state->last[i - 1] != state->last[i]) { + constant = false; + break; + } + } + if (constant) { + state->mode = MODE_FREEZE; + watch_set_indicator(WATCH_INDICATOR_SIGNAL); + movement_play_alarm(); + } + break; + case MODE_FREEZE: + break; + } + } + break; + case EVENT_ALARM_BUTTON_UP: + switch (state->mode) { + case MODE_NORMAL: + state->mode = MODE_ALARM; + watch_set_indicator(WATCH_INDICATOR_BELL); + _alarm_thermometer_face_clear(state->last); + break; + case MODE_FREEZE: + state->mode = MODE_NORMAL; + watch_clear_indicator(WATCH_INDICATOR_BELL); + watch_clear_indicator(WATCH_INDICATOR_SIGNAL); + break; + case MODE_ALARM: + state->mode = MODE_NORMAL; + watch_clear_indicator(WATCH_INDICATOR_BELL); + _alarm_thermometer_face_update(settings->bit.use_imperial_units); + break; + } + if (settings->bit.button_should_sound) { + watch_buzzer_play_note(BUZZER_NOTE_C7, 50); + } + break; + case EVENT_ALARM_LONG_PRESS: + if (state->mode != MODE_FREEZE) { + settings->bit.use_imperial_units = !settings->bit.use_imperial_units; + _alarm_thermometer_face_update(settings->bit.use_imperial_units); + } + break; + case EVENT_LOW_ENERGY_UPDATE: + if (!watch_tick_animation_is_running()) { + state->mode = MODE_NORMAL; + watch_clear_indicator(WATCH_INDICATOR_BELL); + watch_clear_indicator(WATCH_INDICATOR_SIGNAL); + watch_start_tick_animation(1000); + } + if (watch_rtc_get_date_time().unit.minute % 5 == 0) { + _alarm_thermometer_face_update(settings->bit.use_imperial_units); + watch_display_string(" ", 8); + } + break; + default: + return movement_default_loop_handler(event, settings); + } + + return true; +} + +void alarm_thermometer_face_resign(movement_settings_t *settings, void *context) { + (void) settings; + (void) context; +} diff --git a/movement/watch_faces/sensor/alarm_thermometer_face.h b/movement/watch_faces/sensor/alarm_thermometer_face.h new file mode 100644 index 0000000..1b3aabf --- /dev/null +++ b/movement/watch_faces/sensor/alarm_thermometer_face.h @@ -0,0 +1,74 @@ +/* + * MIT License + * + * Copyright (c) 2024 Christian Buschau + * + * 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 ALARM_THERMOMETER_FACE_H_ +#define ALARM_THERMOMETER_FACE_H_ + +#include +#include "movement.h" + +/* + * ALARM THERMOMETER + * + * This watch face shows the current temperature in degrees Celsius. Press and + * hold the alarm button to toggle between Celsius and Fahrenheit. Press and + * release the alarm button to start a "timer". The watch will sound an alarm + * when the temperature remains constant for at least 30 seconds and the + * temperature will stop updating until you press the alarm button. You can + * cancel the alarm by pressing the button again. If the temperature doesn't + * remain constant until the low energy timeout is reached, the alarm will stop. + * This is useful to measure e.g. the room temperature. If you lay off your + * watch from your wrist, it will take some time until it cools down, and will + * notify you when the measurement is constant enough. + * THIS WATCH FACE IS NOT INTENDED TO DIAGNOSE, TREAT, CURE OR PREVENT ANY + * DISEASE. + */ + +#define LAST_SIZE 6 + +typedef enum { + MODE_NORMAL, + MODE_ALARM, + MODE_FREEZE +} alarm_thermometer_mode_t; + +typedef struct { + int last[LAST_SIZE]; + alarm_thermometer_mode_t mode; +} alarm_thermometer_state_t; + +void alarm_thermometer_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); +void alarm_thermometer_face_activate(movement_settings_t *settings, void *context); +bool alarm_thermometer_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void alarm_thermometer_face_resign(movement_settings_t *settings, void *context); + +#define alarm_thermometer_face ((const watch_face_t){ \ + alarm_thermometer_face_setup, \ + alarm_thermometer_face_activate, \ + alarm_thermometer_face_loop, \ + alarm_thermometer_face_resign, \ + NULL, \ +}) + +#endif // ALARM_THERMOMETER_FACE_H_ From 2824a62908b9f7b6e24b25c5ba46e0f3287ae80f Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 4 Aug 2024 09:43:56 -0400 Subject: [PATCH 037/161] Bugfix on not registering the top of an hour --- movement/movement.c | 22 +++++++++++++++---- movement/movement.h | 2 +- movement/watch_faces/clock/clock_face.c | 7 +----- .../watch_faces/clock/simple_clock_face.c | 7 +----- watch-library/shared/watch/watch_utility.c | 6 ++--- 5 files changed, 24 insertions(+), 20 deletions(-) diff --git a/movement/movement.c b/movement/movement.c index 256aa8a..aead621 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -430,8 +430,8 @@ uint8_t movement_claim_backup_register(void) { return movement_state.next_available_backup_register++; } -uint8_t check_and_act_on_daylight_savings(watch_date_time date_time) { - if (movement_state.settings.bit.dst_active) return date_time.unit.hour; +bool check_and_act_on_daylight_savings(watch_date_time date_time) { + if (!movement_state.settings.bit.dst_active) return false; uint8_t dst_result = get_dst_status(date_time); bool dst_skip_rolling_back = get_dst_skip_rolling_back(); @@ -439,15 +439,17 @@ uint8_t check_and_act_on_daylight_savings(watch_date_time date_time) { clear_dst_skip_rolling_back(); } else if (dst_result == DST_ENDING && !dst_skip_rolling_back) { - set_dst_skip_rolling_back(); date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24; watch_rtc_set_date_time(date_time); + set_dst_skip_rolling_back(); + return true; } else if (dst_result == DST_STARTING) { date_time.unit.hour = (date_time.unit.hour + 1) % 24; watch_rtc_set_date_time(date_time); + return true; } - return date_time.unit.hour; + return false; } int16_t get_timezone_offset(uint8_t timezone_idx, watch_date_time date_time) { @@ -476,6 +478,18 @@ void app_init(void) { movement_state.settings.bit.le_interval = MOVEMENT_DEFAULT_LOW_ENERGY_INTERVAL; movement_state.settings.bit.led_duration = MOVEMENT_DEFAULT_LED_DURATION; movement_state.settings.bit.dst_active = MOVEMENT_DEFAULT_DST_ACTIVE; + +#ifdef MAKEFILE_TIMEZONE + timezone_offsets = dst_occurring(watch_rtc_get_date_time()) ? movement_timezone_dst_offsets : movement_timezone_offsets; + for (int i = 0; i < NUM_TIME_ZONES; i++) { + if (timezone_offsets[i] == MAKEFILE_TIMEZONE) { + movement_state.settings.bit.time_zone = i; + break; + } + } +#else + movement_state.settings.bit.time_zone = 35; // Atlantic Time as default +#endif movement_state.light_ticks = -1; movement_state.alarm_ticks = -1; movement_state.next_available_backup_register = 4; diff --git a/movement/movement.h b/movement/movement.h index 6dd38ba..e75f4e8 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -313,7 +313,7 @@ void movement_play_alarm(void); void movement_play_alarm_beeps(uint8_t rounds, BuzzerNote alarm_note); uint8_t movement_claim_backup_register(void); -uint8_t check_and_act_on_daylight_savings(watch_date_time date_time); // Returns the currently set hour +bool check_and_act_on_daylight_savings(watch_date_time date_time); // Returns if the time was changed due to DST int16_t get_timezone_offset(uint8_t timezone_idx, watch_date_time date_time); #endif // MOVEMENT_H_ diff --git a/movement/watch_faces/clock/clock_face.c b/movement/watch_faces/clock/clock_face.c index af601ce..e29d587 100644 --- a/movement/watch_faces/clock/clock_face.c +++ b/movement/watch_faces/clock/clock_face.c @@ -284,12 +284,7 @@ bool clock_face_wants_background_task(movement_settings_t *settings, void *conte (void) settings; clock_state_t *state = (clock_state_t *) context; watch_date_time date_time = watch_rtc_get_date_time(); - uint8_t hour_dst = check_and_act_on_daylight_savings(date_time); - if(hour_dst != date_time.unit.hour) { - char buf[3 + 1]; - sprintf(buf, "%2d", hour_dst); - watch_display_string(buf, 4); - } + check_and_act_on_daylight_savings(date_time); if (!state->time_signal_enabled) return false; return date_time.unit.minute == 0; diff --git a/movement/watch_faces/clock/simple_clock_face.c b/movement/watch_faces/clock/simple_clock_face.c index 20dfb54..118dc32 100644 --- a/movement/watch_faces/clock/simple_clock_face.c +++ b/movement/watch_faces/clock/simple_clock_face.c @@ -154,12 +154,7 @@ bool simple_clock_face_wants_background_task(movement_settings_t *settings, void (void) settings; simple_clock_state_t *state = (simple_clock_state_t *)context; watch_date_time date_time = watch_rtc_get_date_time(); - uint8_t hour_dst = check_and_act_on_daylight_savings(date_time); - if(hour_dst != date_time.unit.hour) { - char buf[3 + 1]; - sprintf(buf, "%2d", hour_dst); - watch_display_string(buf, 4); - } + check_and_act_on_daylight_savings(date_time); if (!state->signal_enabled) return false; return date_time.unit.minute == 0; diff --git a/watch-library/shared/watch/watch_utility.c b/watch-library/shared/watch/watch_utility.c index 5938b60..3dfb37b 100644 --- a/watch-library/shared/watch/watch_utility.c +++ b/watch-library/shared/watch/watch_utility.c @@ -106,10 +106,10 @@ uint8_t get_dst_status(watch_date_time date_time) { dst_end_time.unit.day = 15 - watch_utility_get_iso8601_weekday_number(dst_end_time.unit.year + WATCH_RTC_REFERENCE_YEAR, dst_end_time.unit.month, 1); unix_dst_end_time = watch_utility_date_time_to_unix_time(dst_end_time, 0); - if (date_time.unit.second > 45) // In emu, it's been seen that we may trigger at 59sec rather than exactly 0 each time - date_time.unit.minute = (date_time.unit.minute + 1) % 60; - date_time.unit.second = 0; unix_curr_time = watch_utility_date_time_to_unix_time(date_time, 0); + unix_curr_time -= date_time.unit.second; + if (date_time.unit.second > 45) // In emu, it's been seen that we may trigger at 59sec rather than exactly 0 each time + unix_curr_time += 60; if (unix_curr_time == unix_dst_start_time) return DST_STARTING; if (unix_curr_time == unix_dst_end_time) return DST_ENDING; From 598e87618654c53a5a41e4a77b981b07c738283f Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 4 Aug 2024 10:11:33 -0400 Subject: [PATCH 038/161] check_and_act_on_daylight_savings now only occurs in one spot --- movement/movement.c | 48 ++++++++++--------- movement/movement.h | 1 - movement/watch_faces/clock/clock_face.c | 4 +- .../clock/minute_repeater_decimal_face.c | 4 +- .../clock/repetition_minute_face.c | 4 +- .../clock/simple_clock_bin_led_face.c | 4 +- .../watch_faces/clock/simple_clock_face.c | 4 +- 7 files changed, 36 insertions(+), 33 deletions(-) diff --git a/movement/movement.c b/movement/movement.c index aead621..ebd831f 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -247,6 +247,31 @@ static inline void _movement_disable_fast_tick_if_possible(void) { } } +static bool _check_and_act_on_daylight_savings(void) { + if (!movement_state.settings.bit.dst_active) return false; + watch_date_time date_time = watch_rtc_get_date_time(); + // No need for all of the unix time calculations for times not at the beginning or end of the hour + if (date_time.unit.minute > 1 && date_time.unit.minute < 59) return false; + uint8_t dst_result = get_dst_status(date_time); + bool dst_skip_rolling_back = get_dst_skip_rolling_back(); + + if (dst_skip_rolling_back && (dst_result == DST_ENDED)) { + clear_dst_skip_rolling_back(); + } + else if (dst_result == DST_ENDING && !dst_skip_rolling_back) { + date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24; + watch_rtc_set_date_time(date_time); + set_dst_skip_rolling_back(); + return true; + } + else if (dst_result == DST_STARTING) { + date_time.unit.hour = (date_time.unit.hour + 1) % 24; + watch_rtc_set_date_time(date_time); + return true; + } + return false; +} + static void _movement_handle_background_tasks(void) { for(uint8_t i = 0; i < MOVEMENT_NUM_FACES; i++) { // For each face, if the watch face wants a background task... @@ -256,6 +281,7 @@ static void _movement_handle_background_tasks(void) { watch_faces[i].loop(background_event, &movement_state.settings, watch_face_contexts[i]); } } + _check_and_act_on_daylight_savings(); movement_state.needs_background_tasks_handled = false; } @@ -430,28 +456,6 @@ uint8_t movement_claim_backup_register(void) { return movement_state.next_available_backup_register++; } -bool check_and_act_on_daylight_savings(watch_date_time date_time) { - if (!movement_state.settings.bit.dst_active) return false; - uint8_t dst_result = get_dst_status(date_time); - bool dst_skip_rolling_back = get_dst_skip_rolling_back(); - - if (dst_skip_rolling_back && (dst_result == DST_ENDED)) { - clear_dst_skip_rolling_back(); - } - else if (dst_result == DST_ENDING && !dst_skip_rolling_back) { - date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24; - watch_rtc_set_date_time(date_time); - set_dst_skip_rolling_back(); - return true; - } - else if (dst_result == DST_STARTING) { - date_time.unit.hour = (date_time.unit.hour + 1) % 24; - watch_rtc_set_date_time(date_time); - return true; - } - return false; -} - int16_t get_timezone_offset(uint8_t timezone_idx, watch_date_time date_time) { if (!movement_state.settings.bit.dst_active) return movement_timezone_offsets[timezone_idx]; if (dst_occurring(date_time)) diff --git a/movement/movement.h b/movement/movement.h index e75f4e8..d0595fb 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -313,7 +313,6 @@ void movement_play_alarm(void); void movement_play_alarm_beeps(uint8_t rounds, BuzzerNote alarm_note); uint8_t movement_claim_backup_register(void); -bool check_and_act_on_daylight_savings(watch_date_time date_time); // Returns if the time was changed due to DST int16_t get_timezone_offset(uint8_t timezone_idx, watch_date_time date_time); #endif // MOVEMENT_H_ diff --git a/movement/watch_faces/clock/clock_face.c b/movement/watch_faces/clock/clock_face.c index e29d587..eab5cd8 100644 --- a/movement/watch_faces/clock/clock_face.c +++ b/movement/watch_faces/clock/clock_face.c @@ -283,9 +283,9 @@ void clock_face_resign(movement_settings_t *settings, void *context) { bool clock_face_wants_background_task(movement_settings_t *settings, void *context) { (void) settings; clock_state_t *state = (clock_state_t *) context; - watch_date_time date_time = watch_rtc_get_date_time(); - check_and_act_on_daylight_savings(date_time); if (!state->time_signal_enabled) return false; + watch_date_time date_time = watch_rtc_get_date_time(); + return date_time.unit.minute == 0; } diff --git a/movement/watch_faces/clock/minute_repeater_decimal_face.c b/movement/watch_faces/clock/minute_repeater_decimal_face.c index ab0e6de..2cedc30 100644 --- a/movement/watch_faces/clock/minute_repeater_decimal_face.c +++ b/movement/watch_faces/clock/minute_repeater_decimal_face.c @@ -230,9 +230,9 @@ void minute_repeater_decimal_face_resign(movement_settings_t *settings, void *co bool minute_repeater_decimal_face_wants_background_task(movement_settings_t *settings, void *context) { (void) settings; minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)context; - watch_date_time date_time = watch_rtc_get_date_time(); - check_and_act_on_daylight_savings(date_time); if (!state->signal_enabled) return false; + watch_date_time date_time = watch_rtc_get_date_time(); + return date_time.unit.minute == 0; } diff --git a/movement/watch_faces/clock/repetition_minute_face.c b/movement/watch_faces/clock/repetition_minute_face.c index 1afc4ce..e9e5e31 100644 --- a/movement/watch_faces/clock/repetition_minute_face.c +++ b/movement/watch_faces/clock/repetition_minute_face.c @@ -213,9 +213,9 @@ void repetition_minute_face_resign(movement_settings_t *settings, void *context) bool repetition_minute_face_wants_background_task(movement_settings_t *settings, void *context) { (void) settings; repetition_minute_state_t *state = (repetition_minute_state_t *)context; - watch_date_time date_time = watch_rtc_get_date_time(); - check_and_act_on_daylight_savings(date_time); if (!state->signal_enabled) return false; + watch_date_time date_time = watch_rtc_get_date_time(); + return date_time.unit.minute == 0; } diff --git a/movement/watch_faces/clock/simple_clock_bin_led_face.c b/movement/watch_faces/clock/simple_clock_bin_led_face.c index 1762cfa..cf39c18 100644 --- a/movement/watch_faces/clock/simple_clock_bin_led_face.c +++ b/movement/watch_faces/clock/simple_clock_bin_led_face.c @@ -214,9 +214,9 @@ void simple_clock_bin_led_face_resign(movement_settings_t *settings, void *conte bool simple_clock_bin_led_face_wants_background_task(movement_settings_t *settings, void *context) { (void) settings; simple_clock_bin_led_state_t *state = (simple_clock_bin_led_state_t *)context; - watch_date_time date_time = watch_rtc_get_date_time(); - check_and_act_on_daylight_savings(date_time); if (!state->signal_enabled) return false; + watch_date_time date_time = watch_rtc_get_date_time(); + return date_time.unit.minute == 0; } diff --git a/movement/watch_faces/clock/simple_clock_face.c b/movement/watch_faces/clock/simple_clock_face.c index 118dc32..fbc2c4b 100644 --- a/movement/watch_faces/clock/simple_clock_face.c +++ b/movement/watch_faces/clock/simple_clock_face.c @@ -153,9 +153,9 @@ void simple_clock_face_resign(movement_settings_t *settings, void *context) { bool simple_clock_face_wants_background_task(movement_settings_t *settings, void *context) { (void) settings; simple_clock_state_t *state = (simple_clock_state_t *)context; - watch_date_time date_time = watch_rtc_get_date_time(); - check_and_act_on_daylight_savings(date_time); if (!state->signal_enabled) return false; + watch_date_time date_time = watch_rtc_get_date_time(); + return date_time.unit.minute == 0; } From 18154deef4d3877d3c9c0364a9da631e4e9f7da5 Mon Sep 17 00:00:00 2001 From: mcguirepr89 Date: Sat, 31 Aug 2024 08:31:26 -0400 Subject: [PATCH 039/161] Adds a simple calculator face --- movement/make/Makefile | 1 + movement/movement_faces.h | 1 + .../complication/simple_calculator_face.c | 415 ++++++++++++++++++ .../complication/simple_calculator_face.h | 145 ++++++ 4 files changed, 562 insertions(+) create mode 100644 movement/watch_faces/complication/simple_calculator_face.c create mode 100644 movement/watch_faces/complication/simple_calculator_face.h diff --git a/movement/make/Makefile b/movement/make/Makefile index da5486b..40b5be3 100644 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -129,6 +129,7 @@ SRCS += \ ../watch_faces/clock/minute_repeater_decimal_face.c \ ../watch_faces/complication/tuning_tones_face.c \ ../watch_faces/complication/kitchen_conversions_face.c \ + ../watch_faces/complication/simple_calculator_face.c \ # New watch faces go above this line. # Leave this line at the bottom of the file; it has all the targets for making your project. diff --git a/movement/movement_faces.h b/movement/movement_faces.h index 3557110..55d4124 100644 --- a/movement/movement_faces.h +++ b/movement/movement_faces.h @@ -104,6 +104,7 @@ #include "minute_repeater_decimal_face.h" #include "tuning_tones_face.h" #include "kitchen_conversions_face.h" +#include "simple_calculator_face.h" // New includes go above this line. #endif // MOVEMENT_FACES_H_ diff --git a/movement/watch_faces/complication/simple_calculator_face.c b/movement/watch_faces/complication/simple_calculator_face.c new file mode 100644 index 0000000..95804ce --- /dev/null +++ b/movement/watch_faces/complication/simple_calculator_face.c @@ -0,0 +1,415 @@ +/* + * MIT License + * + * Copyright (c) 2024 Patrick McGuire + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include "simple_calculator_face.h" + +void simple_calculator_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { + (void) settings; + (void) watch_face_index; + if (*context_ptr == NULL) { + *context_ptr = malloc(sizeof(simple_calculator_state_t)); + memset(*context_ptr, 0, sizeof(simple_calculator_state_t)); + } +} + +void simple_calculator_face_activate(movement_settings_t *settings, void *context) { + (void) settings; + simple_calculator_state_t *state = (simple_calculator_state_t *)context; + state->placeholder = PLACEHOLDER_ONES; + state->mode = MODE_ENTERING_FIRST_NUM; + movement_request_tick_frequency(4); +} + +static void increment_placeholder(calculator_number_t *number, calculator_placeholder_t placeholder) { + uint8_t *digits[] = { + &number->hundredths, + &number->tenths, + &number->ones, + &number->tens, + &number->hundreds, + &number->thousands + }; + *digits[placeholder] = (*digits[placeholder] + 1) % 10; +} + +static float convert_to_float(calculator_number_t number) { + float result = 0.0; + + // Add the whole number portion + result += number.thousands * 1000.0f; + result += number.hundreds * 100.0f; + result += number.tens * 10.0f; + result += number.ones * 1.0f; + + // Add the fractional portion + result += number.tenths * 0.1f; + result += number.hundredths * 0.01f; + + // Round to nearest hundredth + result = roundf(result * 100) / 100; + return result; +} + +static char* update_display_number(calculator_number_t *number, char *display_string, uint8_t which_num) { + char sign = ' '; + if (number->negative) sign = '-'; + sprintf(display_string, "CA%d%c%d%d%d%d%d%d", + which_num, + sign, + number->thousands, + number->hundreds, + number->tens, + number->ones, + number->tenths, + number->hundredths); + + return display_string; +} + +static void set_operation(simple_calculator_state_t *state) { + switch (state->operation) { + case 0: + watch_display_string(" Add", 0); + break; + case 1: + watch_display_string(" sub", 0); + break; + case 2: + watch_display_string(" n&ul", 0); + break; + case 3: + watch_display_string(" div", 0); + break; + case 4: + watch_display_string(" root", 0); + break; + case 5: + watch_display_string(" pow", 0); + break; + } +} + +static void cycle_operation(simple_calculator_state_t *state) { + state->operation = (state->operation + 1) % OPERATIONS_COUNT; // Assuming there are 6 operations + printf("Current operation: %d\n", state->operation); // For debugging +} + + +static calculator_number_t convert_to_string(float number) { + calculator_number_t result; + + // Handle negative numbers + bool is_negative = (number < 0); + if (is_negative) { + number = -number; + result.negative = true; + } + + int int_part = (int)number; + float decimal_part_float = ((number - int_part) * 100); // two decimal places + printf("decimal_part_float = %f\n", decimal_part_float); //For debugging + int decimal_part = round(decimal_part_float); + printf("decimal_part = %d\n", decimal_part); //For debugging + + result.thousands = int_part / 1000 % 10; + result.hundreds = int_part / 100 % 10; + result.tens = int_part / 10 % 10; + result.ones = int_part % 10; + + result.tenths = decimal_part / 10 % 10; + result.hundredths = decimal_part % 10; + + return result; +} + +static void reset_to_zero(calculator_number_t *number) { + number->negative = false; + number->hundredths = 0; + number->tenths = 0; + number->ones = 0; + number->tens = 0; + number->hundreds = 0; + number->thousands = 0; +} + +static void set_number(calculator_number_t *number, calculator_placeholder_t placeholder, char *display_string, char *temp_display_string, movement_event_t event, uint8_t which_num) { + uint8_t display_index; + // Update display string with current number + update_display_number(number, display_string, which_num); + + // Copy the updated display string to a temporary buffer + strcpy(temp_display_string, display_string); + + // Determine the display index based on the placeholder + display_index = 9 - placeholder; + + // Blink selected placeholder + // Check if `event.subsecond` is even + if (event.subsecond % 2 == 0) { + // Replace the character at the index corresponding to the current placeholder with a space + temp_display_string[display_index] = ' '; + } + + // Display the (possibly modified) string + watch_display_string(temp_display_string, 0); +} + +static void view_results(simple_calculator_state_t *state, char *display_string) { + float first_num_float, second_num_float, result_float = 0.0f; // For arithmetic operations + // Convert the numbers to float + first_num_float = convert_to_float(state->first_num); + if (state->first_num.negative) first_num_float = first_num_float * -1; + printf("first_num_float = %f\n", first_num_float); // For debugging // For debugging + second_num_float = convert_to_float(state->second_num); + if (state->second_num.negative) second_num_float = second_num_float * -1; + printf("second_num_float = %f\n", second_num_float); // For debugging + + // Perform the calculation based on the selected operation + switch (state->operation) { + case OP_ADD: + result_float = first_num_float + second_num_float; + break; + case OP_SUB: + result_float = first_num_float - second_num_float; + break; + case OP_MULT: + result_float = first_num_float * second_num_float; + break; + case OP_DIV: + if (second_num_float != 0) { + result_float = first_num_float / second_num_float; + } else { + state->mode = MODE_ERROR; + return; + } + break; + case OP_ROOT: + if (first_num_float >= 0) { + result_float = sqrtf(first_num_float); + } else { + state->mode = MODE_ERROR; + return; + } + break; + case OP_POWER: + result_float = powf(first_num_float, second_num_float); // Power operation + break; + default: + result_float = 0.0f; + break; + } + + if (result_float > 9999.99 || result_float < -9999.99) { + state->mode = MODE_ERROR; + return; + } + + result_float = roundf(result_float * 100.0f) / 100.0f; // Might not be needed + printf("result as float = %f\n", result_float); // For debugging + + // Convert the float result to a string + state->result = convert_to_string(result_float); + + // Update the display with the result + update_display_number(&state->result, display_string, 3); + watch_display_string(display_string, 0); +} + +static void reset_from_error(simple_calculator_state_t *state) { + reset_to_zero(&state->first_num); + reset_to_zero(&state->second_num); + state->mode = MODE_ENTERING_FIRST_NUM; +} +bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { + simple_calculator_state_t *state = (simple_calculator_state_t *)context; + char display_string[10]; + char temp_display_string[10]; // Temporary buffer for blinking effect + + switch (event.event_type) { + case EVENT_ACTIVATE: + break; + + case EVENT_TICK: + switch (state->mode) { + case MODE_ENTERING_FIRST_NUM: + set_number(&state->first_num, + state->placeholder, + display_string, + temp_display_string, + event, + 1); + break; + + case MODE_CHOOSING: + set_operation(state); + break; + + case MODE_ENTERING_SECOND_NUM: + // If doing a square root calculation, skip to results + if (state->operation == 4) { + state->mode = MODE_VIEW_RESULTS; + // otherwise, set the second number + } else { + set_number(&state->second_num, + state->placeholder, + display_string, + temp_display_string, + event, + 2); + } + break; + + case MODE_VIEW_RESULTS: + view_results(state, display_string); + break; + case MODE_ERROR: + watch_display_string("CA Error ", 0); + break; + } + break; + + case EVENT_LIGHT_BUTTON_DOWN: + break; + + case EVENT_LIGHT_BUTTON_UP: + switch (state->mode) { + case MODE_ENTERING_FIRST_NUM: + case MODE_ENTERING_SECOND_NUM: + // Move to the next placeholder when the light button is pressed + state->placeholder = (state->placeholder + 1) % MAX_PLACEHOLDERS; // Loop back to the start after PLACEHOLDER_THOUSANDS + break; + case MODE_CHOOSING: + cycle_operation(state); + break; + case MODE_ERROR: + reset_from_error(state); + break; + case MODE_VIEW_RESULTS: + break; + } + break; + + case EVENT_LIGHT_LONG_PRESS: + switch (state->mode) { + case MODE_ENTERING_FIRST_NUM: + // toggle negative on state->first_num + state->first_num.negative = !state->first_num.negative; + break; + case MODE_ENTERING_SECOND_NUM: + // toggle negative on state->second_num + state->first_num.negative = !state->first_num.negative; + break; + case MODE_ERROR: + reset_from_error(state); + break; + case MODE_CHOOSING: + case MODE_VIEW_RESULTS: + break; + } + break; + + case EVENT_ALARM_BUTTON_UP: + switch (state->mode) { + case MODE_ENTERING_FIRST_NUM: + // Increment the digit in the current placeholder + increment_placeholder(&state->first_num, state->placeholder); + update_display_number(&state->first_num, display_string, 1); + break; + case MODE_CHOOSING: + // Confirm and select the current operation + printf("Selected operation: %d\n", state->operation); // For debugging + state->mode = MODE_ENTERING_SECOND_NUM; + break; + case MODE_ENTERING_SECOND_NUM: + // Increment the digit in the current placeholder + increment_placeholder(&state->second_num, state->placeholder); + update_display_number(&state->second_num, display_string, 2); + break; + case MODE_ERROR: + reset_from_error(state); + break; + case MODE_VIEW_RESULTS: + break; + } + break; + + case EVENT_ALARM_LONG_PRESS: + switch (state->mode) { + case MODE_ENTERING_FIRST_NUM: + reset_to_zero(&state->first_num); + break; + case MODE_ENTERING_SECOND_NUM: + reset_to_zero(&state->second_num); + break; + case MODE_ERROR: + reset_from_error(state); + break; + case MODE_CHOOSING: + case MODE_VIEW_RESULTS: + break; + } + break; + + case EVENT_MODE_BUTTON_DOWN: + break; + + case EVENT_MODE_BUTTON_UP: + if (state->mode == MODE_ERROR) { + reset_from_error(state); + } else { + state->placeholder = PLACEHOLDER_ONES; + state->mode = (state->mode + 1) % 4; + if (state->mode == MODE_ENTERING_FIRST_NUM) { + state->first_num = state->result; + reset_to_zero(&state->second_num); + } + printf("Current mode: %d\n", state->mode); // For debugging + } + break; + + case EVENT_MODE_LONG_PRESS: + movement_move_to_next_face(); + break; + + case EVENT_TIMEOUT: + movement_request_tick_frequency(1); + movement_move_to_face(0); + break; + + default: + return movement_default_loop_handler(event, settings); + } + + return true; +} + +void simple_calculator_face_resign(movement_settings_t *settings, void *context) { + (void) settings; + (void) context; + movement_request_tick_frequency(1); +} + diff --git a/movement/watch_faces/complication/simple_calculator_face.h b/movement/watch_faces/complication/simple_calculator_face.h new file mode 100644 index 0000000..1f77dc0 --- /dev/null +++ b/movement/watch_faces/complication/simple_calculator_face.h @@ -0,0 +1,145 @@ +/* + * MIT License + * + * Copyright (c) 2024 Patrick McGuire + * + * 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 SIMPLE_CALCULATOR_FACE_H_ +#define SIMPLE_CALCULATOR_FACE_H_ + +#include "movement.h" + +/* + * Simple Calculator + * + * How to use: + * + * Flow: + * Enter first number -> Select operator -> Enter second number -> View Results + * + * How to read the display: + * - "CA" is displayed at the top to tell you that you're in the CAlculator + * - The top-right digit (1, 2, or 3) lets you know whether you're entering the + * first number (1), entering the second number (2), or viewing the results (3). + * - To the right of the top-right digit will show the number's sign. If the + * number is negative, a "-" will be displayed, otherwise it is empty. + * - The 4 large digits to the left are whole numbers and the 2 smaller digits + * on the right are the tenths and hundredths decimal places. + * + * Entering the first number: + * - Press ALARM to increment the selected (blinking) digit + * - Press LIGHT to move to the next placeholder + * - LONG PRESS the LIGHT button to toggle the number's sign to make it + * negative + * - LONG PRESS the ALARM button to reset the number to 0 + * - Press MODE to proceed to selecting the operator + * + * Selecting the operator: + * - Press the LIGHT button to cycle through available operators. They are: + * + Add + * - Subtract + * * Multiply + * / Divide + * sqrtf() Square root + * powf() Power (exponent calculation) + * - Press MODE or ALARM to proceed to entering the second number + * + * Entering the second number: + * - Everything is the same as setting the first number except that pressing + * MODE here will proceed to viewing the results + * + * Viewing the results: + * - Pressing MODE will start a new calculation with the result as the first + * number. (LONG PRESS ALARM to reset the value to 0) + * + * Errors: + * - An error will be triggered if the result is not able to be displayed, that + * is, if the value is greater than 9,999.99 or less than -9,999.99. + * - An error will also be triggered if an impossible operation is selected, + * for instance trying to divide by 0 or get the square root of a negative + * number. + * - Exit error mode and start over with any button press. + * + */ + +#define OPERATIONS_COUNT 6 +#define MAX_PLACEHOLDERS 6 + +typedef struct { + bool negative; + uint8_t hundredths; + uint8_t tenths; + uint8_t ones; + uint8_t tens; + uint8_t hundreds; + uint8_t thousands; +} calculator_number_t; + +typedef enum { + PLACEHOLDER_HUNDREDTHS, + PLACEHOLDER_TENTHS, + PLACEHOLDER_ONES, + PLACEHOLDER_TENS, + PLACEHOLDER_HUNDREDS, + PLACEHOLDER_THOUSANDS +} calculator_placeholder_t; + +typedef enum { + OP_ADD, + OP_SUB, + OP_MULT, + OP_DIV, + OP_ROOT, + OP_POWER, +} calculator_operation_t; + +typedef enum { + MODE_ENTERING_FIRST_NUM, + MODE_CHOOSING, + MODE_ENTERING_SECOND_NUM, + MODE_VIEW_RESULTS, + MODE_ERROR +} calculator_mode_t; + +typedef struct { + calculator_number_t first_num; + calculator_number_t second_num; + calculator_number_t result; + calculator_operation_t operation; + calculator_mode_t mode; + calculator_placeholder_t placeholder; +} simple_calculator_state_t; + +void simple_calculator_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); +void simple_calculator_face_activate(movement_settings_t *settings, void *context); +bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void simple_calculator_face_resign(movement_settings_t *settings, void *context); + +#define simple_calculator_face ((const watch_face_t){ \ + simple_calculator_face_setup, \ + simple_calculator_face_activate, \ + simple_calculator_face_loop, \ + simple_calculator_face_resign, \ + NULL, \ +}) + +#endif // SIMPLE_CALCULATOR_FACE_H_ + From 28db77f90c0ff21fa9657039ab69f427e91fc99d Mon Sep 17 00:00:00 2001 From: mcguirepr89 Date: Sat, 31 Aug 2024 08:36:02 -0400 Subject: [PATCH 040/161] forgot to mention long press MODE for next face --- movement/watch_faces/complication/simple_calculator_face.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/movement/watch_faces/complication/simple_calculator_face.h b/movement/watch_faces/complication/simple_calculator_face.h index 1f77dc0..4f1e8d5 100644 --- a/movement/watch_faces/complication/simple_calculator_face.h +++ b/movement/watch_faces/complication/simple_calculator_face.h @@ -32,6 +32,8 @@ * * How to use: * + * Important note: LONG PRESS MODE to move to next watch face + * * Flow: * Enter first number -> Select operator -> Enter second number -> View Results * From e13d42b5b5d1234293e67d162affb6499956b180 Mon Sep 17 00:00:00 2001 From: mcguirepr89 Date: Sat, 31 Aug 2024 12:41:57 -0400 Subject: [PATCH 041/161] mode=movement_move_to_next_face & longmode = face0 --- .../watch_faces/complication/simple_calculator_face.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/movement/watch_faces/complication/simple_calculator_face.c b/movement/watch_faces/complication/simple_calculator_face.c index 95804ce..ada289d 100644 --- a/movement/watch_faces/complication/simple_calculator_face.c +++ b/movement/watch_faces/complication/simple_calculator_face.c @@ -380,6 +380,14 @@ bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *se case EVENT_MODE_BUTTON_UP: if (state->mode == MODE_ERROR) { reset_from_error(state); + } else if (state->mode == MODE_ENTERING_FIRST_NUM && + state->first_num.hundredths == 0 && + state->first_num.tenths == 0 && + state->first_num.ones== 0 && + state->first_num.tens == 0 && + state->first_num.hundreds == 0 && + state->first_num.thousands == 0) { + movement_move_to_next_face(); } else { state->placeholder = PLACEHOLDER_ONES; state->mode = (state->mode + 1) % 4; @@ -392,7 +400,7 @@ bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *se break; case EVENT_MODE_LONG_PRESS: - movement_move_to_next_face(); + movement_move_to_face(0); break; case EVENT_TIMEOUT: From b607b6f7a9177f3fc7e16d842f0b7f6f18a86d9e Mon Sep 17 00:00:00 2001 From: mcguirepr89 Date: Sat, 31 Aug 2024 12:44:29 -0400 Subject: [PATCH 042/161] removed note about mode long press --- movement/watch_faces/complication/simple_calculator_face.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/movement/watch_faces/complication/simple_calculator_face.h b/movement/watch_faces/complication/simple_calculator_face.h index 4f1e8d5..1f77dc0 100644 --- a/movement/watch_faces/complication/simple_calculator_face.h +++ b/movement/watch_faces/complication/simple_calculator_face.h @@ -32,8 +32,6 @@ * * How to use: * - * Important note: LONG PRESS MODE to move to next watch face - * * Flow: * Enter first number -> Select operator -> Enter second number -> View Results * From c75a21196f088064d0763d07065df076311e940c Mon Sep 17 00:00:00 2001 From: mcguirepr89 Date: Sat, 31 Aug 2024 14:46:23 -0400 Subject: [PATCH 043/161] commented out debugging printf() statements --- .../complication/simple_calculator_face.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/movement/watch_faces/complication/simple_calculator_face.c b/movement/watch_faces/complication/simple_calculator_face.c index ada289d..3221981 100644 --- a/movement/watch_faces/complication/simple_calculator_face.c +++ b/movement/watch_faces/complication/simple_calculator_face.c @@ -115,7 +115,7 @@ static void set_operation(simple_calculator_state_t *state) { static void cycle_operation(simple_calculator_state_t *state) { state->operation = (state->operation + 1) % OPERATIONS_COUNT; // Assuming there are 6 operations - printf("Current operation: %d\n", state->operation); // For debugging + //printf("Current operation: %d\n", state->operation); // For debugging } @@ -131,9 +131,9 @@ static calculator_number_t convert_to_string(float number) { int int_part = (int)number; float decimal_part_float = ((number - int_part) * 100); // two decimal places - printf("decimal_part_float = %f\n", decimal_part_float); //For debugging + //printf("decimal_part_float = %f\n", decimal_part_float); //For debugging int decimal_part = round(decimal_part_float); - printf("decimal_part = %d\n", decimal_part); //For debugging + //printf("decimal_part = %d\n", decimal_part); //For debugging result.thousands = int_part / 1000 % 10; result.hundreds = int_part / 100 % 10; @@ -183,10 +183,10 @@ static void view_results(simple_calculator_state_t *state, char *display_string) // Convert the numbers to float first_num_float = convert_to_float(state->first_num); if (state->first_num.negative) first_num_float = first_num_float * -1; - printf("first_num_float = %f\n", first_num_float); // For debugging // For debugging + //printf("first_num_float = %f\n", first_num_float); // For debugging // For debugging second_num_float = convert_to_float(state->second_num); if (state->second_num.negative) second_num_float = second_num_float * -1; - printf("second_num_float = %f\n", second_num_float); // For debugging + //printf("second_num_float = %f\n", second_num_float); // For debugging // Perform the calculation based on the selected operation switch (state->operation) { @@ -229,7 +229,7 @@ static void view_results(simple_calculator_state_t *state, char *display_string) } result_float = roundf(result_float * 100.0f) / 100.0f; // Might not be needed - printf("result as float = %f\n", result_float); // For debugging + //printf("result as float = %f\n", result_float); // For debugging // Convert the float result to a string state->result = convert_to_string(result_float); @@ -341,7 +341,7 @@ bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *se break; case MODE_CHOOSING: // Confirm and select the current operation - printf("Selected operation: %d\n", state->operation); // For debugging + //printf("Selected operation: %d\n", state->operation); // For debugging state->mode = MODE_ENTERING_SECOND_NUM; break; case MODE_ENTERING_SECOND_NUM: @@ -395,7 +395,7 @@ bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *se state->first_num = state->result; reset_to_zero(&state->second_num); } - printf("Current mode: %d\n", state->mode); // For debugging + //printf("Current mode: %d\n", state->mode); // For debugging } break; From 70426751f85e9986b063b73aa8aa792b9773329b Mon Sep 17 00:00:00 2001 From: mcguirepr89 Date: Sat, 31 Aug 2024 17:47:15 -0400 Subject: [PATCH 044/161] negative toggle was only for first_num -- fixed --- movement/watch_faces/complication/simple_calculator_face.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/movement/watch_faces/complication/simple_calculator_face.c b/movement/watch_faces/complication/simple_calculator_face.c index 95804ce..3542590 100644 --- a/movement/watch_faces/complication/simple_calculator_face.c +++ b/movement/watch_faces/complication/simple_calculator_face.c @@ -321,7 +321,7 @@ bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *se break; case MODE_ENTERING_SECOND_NUM: // toggle negative on state->second_num - state->first_num.negative = !state->first_num.negative; + state->second_num.negative = !state->second_num.negative; break; case MODE_ERROR: reset_from_error(state); From 12b1432aae00a698665320289a00d2035dee845c Mon Sep 17 00:00:00 2001 From: mcguirepr89 Date: Sun, 1 Sep 2024 10:10:59 -0400 Subject: [PATCH 045/161] mode long press = reset_all() --- .../complication/simple_calculator_face.c | 45 +++++++++++++------ 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/movement/watch_faces/complication/simple_calculator_face.c b/movement/watch_faces/complication/simple_calculator_face.c index 0623525..b3b2d0e 100644 --- a/movement/watch_faces/complication/simple_calculator_face.c +++ b/movement/watch_faces/complication/simple_calculator_face.c @@ -92,22 +92,22 @@ static char* update_display_number(calculator_number_t *number, char *display_st static void set_operation(simple_calculator_state_t *state) { switch (state->operation) { - case 0: + case OP_ADD: watch_display_string(" Add", 0); break; - case 1: + case OP_SUB: watch_display_string(" sub", 0); break; - case 2: + case OP_MULT: watch_display_string(" n&ul", 0); break; - case 3: + case OP_DIV: watch_display_string(" div", 0); break; - case 4: + case OP_ROOT: watch_display_string(" root", 0); break; - case 5: + case OP_POWER: watch_display_string(" pow", 0); break; } @@ -239,10 +239,12 @@ static void view_results(simple_calculator_state_t *state, char *display_string) watch_display_string(display_string, 0); } -static void reset_from_error(simple_calculator_state_t *state) { +static void reset_all(simple_calculator_state_t *state) { reset_to_zero(&state->first_num); reset_to_zero(&state->second_num); state->mode = MODE_ENTERING_FIRST_NUM; + state->operation = OP_ADD; + state->placeholder = PLACEHOLDER_ONES; } bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { simple_calculator_state_t *state = (simple_calculator_state_t *)context; @@ -270,7 +272,7 @@ bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *se case MODE_ENTERING_SECOND_NUM: // If doing a square root calculation, skip to results - if (state->operation == 4) { + if (state->operation == OP_ROOT) { state->mode = MODE_VIEW_RESULTS; // otherwise, set the second number } else { @@ -306,7 +308,7 @@ bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *se cycle_operation(state); break; case MODE_ERROR: - reset_from_error(state); + reset_all(state); break; case MODE_VIEW_RESULTS: break; @@ -324,7 +326,7 @@ bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *se state->second_num.negative = !state->second_num.negative; break; case MODE_ERROR: - reset_from_error(state); + reset_all(state); break; case MODE_CHOOSING: case MODE_VIEW_RESULTS: @@ -350,7 +352,7 @@ bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *se update_display_number(&state->second_num, display_string, 2); break; case MODE_ERROR: - reset_from_error(state); + reset_all(state); break; case MODE_VIEW_RESULTS: break; @@ -366,7 +368,7 @@ bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *se reset_to_zero(&state->second_num); break; case MODE_ERROR: - reset_from_error(state); + reset_all(state); break; case MODE_CHOOSING: case MODE_VIEW_RESULTS: @@ -379,7 +381,7 @@ bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *se case EVENT_MODE_BUTTON_UP: if (state->mode == MODE_ERROR) { - reset_from_error(state); + reset_all(state); } else if (state->mode == MODE_ENTERING_FIRST_NUM && state->first_num.hundredths == 0 && state->first_num.tenths == 0 && @@ -400,7 +402,22 @@ bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *se break; case EVENT_MODE_LONG_PRESS: - movement_move_to_face(0); + if (state->first_num.hundredths == 0 && + state->first_num.tenths == 0 && + state->first_num.ones== 0 && + state->first_num.tens == 0 && + state->first_num.hundreds == 0 && + state->first_num.thousands == 0 && + state->second_num.hundredths == 0 && + state->second_num.tenths == 0 && + state->second_num.ones== 0 && + state->second_num.tens == 0 && + state->second_num.hundreds == 0 && + state->second_num.thousands == 0) { + movement_move_to_face(0); + } else { + reset_all(state); + } break; case EVENT_TIMEOUT: From b774900ae62c6ed7442eec71d0327a81a7da3ed9 Mon Sep 17 00:00:00 2001 From: mcguirepr89 Date: Mon, 2 Sep 2024 13:10:26 -0400 Subject: [PATCH 046/161] finally squashed the bug --- .../complication/simple_calculator_face.c | 95 ++++++++++++------- 1 file changed, 61 insertions(+), 34 deletions(-) diff --git a/movement/watch_faces/complication/simple_calculator_face.c b/movement/watch_faces/complication/simple_calculator_face.c index b3b2d0e..6bfd9e8 100644 --- a/movement/watch_faces/complication/simple_calculator_face.c +++ b/movement/watch_faces/complication/simple_calculator_face.c @@ -36,11 +36,23 @@ void simple_calculator_face_setup(movement_settings_t *settings, uint8_t watch_f } } +static void reset_to_zero(calculator_number_t *number) { + number->negative = false; + number->hundredths = 0; + number->tenths = 0; + number->ones = 0; + number->tens = 0; + number->hundreds = 0; + number->thousands = 0; +} + void simple_calculator_face_activate(movement_settings_t *settings, void *context) { (void) settings; simple_calculator_state_t *state = (simple_calculator_state_t *)context; state->placeholder = PLACEHOLDER_ONES; state->mode = MODE_ENTERING_FIRST_NUM; + reset_to_zero(&state->second_num); + reset_to_zero(&state->result); movement_request_tick_frequency(4); } @@ -71,12 +83,18 @@ static float convert_to_float(calculator_number_t number) { // Round to nearest hundredth result = roundf(result * 100) / 100; + + // Handle negative numbers + if (number.negative) result = -result; + //printf("convert_to_float results = %f\n", result); // For debugging + return result; } static char* update_display_number(calculator_number_t *number, char *display_string, uint8_t which_num) { char sign = ' '; if (number->negative) sign = '-'; + sprintf(display_string, "CA%d%c%d%d%d%d%d%d", which_num, sign, @@ -115,7 +133,6 @@ static void set_operation(simple_calculator_state_t *state) { static void cycle_operation(simple_calculator_state_t *state) { state->operation = (state->operation + 1) % OPERATIONS_COUNT; // Assuming there are 6 operations - //printf("Current operation: %d\n", state->operation); // For debugging } @@ -123,15 +140,17 @@ static calculator_number_t convert_to_string(float number) { calculator_number_t result; // Handle negative numbers - bool is_negative = (number < 0); - if (is_negative) { + if (number < 0) { number = -number; result.negative = true; - } + } else result.negative = false; + // Get each digit from each placeholder int int_part = (int)number; + float decimal_part_float = ((number - int_part) * 100); // two decimal places //printf("decimal_part_float = %f\n", decimal_part_float); //For debugging + int decimal_part = round(decimal_part_float); //printf("decimal_part = %d\n", decimal_part); //For debugging @@ -146,22 +165,15 @@ static calculator_number_t convert_to_string(float number) { return result; } -static void reset_to_zero(calculator_number_t *number) { - number->negative = false; - number->hundredths = 0; - number->tenths = 0; - number->ones = 0; - number->tens = 0; - number->hundreds = 0; - number->thousands = 0; -} - +// This is the main function for setting the first_num and second_num +// WISH: there must be a way to pass less to this function? static void set_number(calculator_number_t *number, calculator_placeholder_t placeholder, char *display_string, char *temp_display_string, movement_event_t event, uint8_t which_num) { + + // Create the display index uint8_t display_index; - // Update display string with current number + + // Update display string with current number and copy into temp string update_display_number(number, display_string, which_num); - - // Copy the updated display string to a temporary buffer strcpy(temp_display_string, display_string); // Determine the display index based on the placeholder @@ -179,14 +191,13 @@ static void set_number(calculator_number_t *number, calculator_placeholder_t pla } static void view_results(simple_calculator_state_t *state, char *display_string) { - float first_num_float, second_num_float, result_float = 0.0f; // For arithmetic operations - // Convert the numbers to float + + // Initialize float variables to do the math + float first_num_float, second_num_float, result_float = 0.0f; + + // Convert the passed numbers to floats first_num_float = convert_to_float(state->first_num); - if (state->first_num.negative) first_num_float = first_num_float * -1; - //printf("first_num_float = %f\n", first_num_float); // For debugging // For debugging second_num_float = convert_to_float(state->second_num); - if (state->second_num.negative) second_num_float = second_num_float * -1; - //printf("second_num_float = %f\n", second_num_float); // For debugging // Perform the calculation based on the selected operation switch (state->operation) { @@ -216,29 +227,37 @@ static void view_results(simple_calculator_state_t *state, char *display_string) } break; case OP_POWER: - result_float = powf(first_num_float, second_num_float); // Power operation + result_float = powf(first_num_float, second_num_float); break; default: result_float = 0.0f; break; } + // Be sure the result can fit on the watch display, else error if (result_float > 9999.99 || result_float < -9999.99) { state->mode = MODE_ERROR; return; } result_float = roundf(result_float * 100.0f) / 100.0f; // Might not be needed + //printf("result as float = %f\n", result_float); // For debugging // Convert the float result to a string + // This isn't strictly necessary, but allows easily reusing the result as + // the next calculation's first_num state->result = convert_to_string(result_float); // Update the display with the result update_display_number(&state->result, display_string, 3); + + //printf("display_string = %s\n", display_string); // For debugging + watch_display_string(display_string, 0); } +// Used both when returning from errors and when long pressing MODE static void reset_all(simple_calculator_state_t *state) { reset_to_zero(&state->first_num); reset_to_zero(&state->second_num); @@ -246,6 +265,7 @@ static void reset_all(simple_calculator_state_t *state) { state->operation = OP_ADD; state->placeholder = PLACEHOLDER_ONES; } + bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { simple_calculator_state_t *state = (simple_calculator_state_t *)context; char display_string[10]; @@ -258,6 +278,7 @@ bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *se case EVENT_TICK: switch (state->mode) { case MODE_ENTERING_FIRST_NUM: + // See the WISH for this function above set_number(&state->first_num, state->placeholder, display_string, @@ -274,8 +295,8 @@ bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *se // If doing a square root calculation, skip to results if (state->operation == OP_ROOT) { state->mode = MODE_VIEW_RESULTS; - // otherwise, set the second number } else { + // See the WISH for this function above set_number(&state->second_num, state->placeholder, display_string, @@ -288,6 +309,7 @@ bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *se case MODE_VIEW_RESULTS: view_results(state, display_string); break; + case MODE_ERROR: watch_display_string("CA Error ", 0); break; @@ -340,16 +362,21 @@ bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *se // Increment the digit in the current placeholder increment_placeholder(&state->first_num, state->placeholder); update_display_number(&state->first_num, display_string, 1); + + //printf("display_string = %s\n", display_string); // For debugging + break; case MODE_CHOOSING: // Confirm and select the current operation - //printf("Selected operation: %d\n", state->operation); // For debugging state->mode = MODE_ENTERING_SECOND_NUM; break; case MODE_ENTERING_SECOND_NUM: // Increment the digit in the current placeholder increment_placeholder(&state->second_num, state->placeholder); update_display_number(&state->second_num, display_string, 2); + + //printf("display_string = %s\n", display_string); // For debugging + break; case MODE_ERROR: reset_all(state); @@ -391,30 +418,30 @@ bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *se state->first_num.thousands == 0) { movement_move_to_next_face(); } else { + // Reset the placeholder and proceed to the next MODE state->placeholder = PLACEHOLDER_ONES; state->mode = (state->mode + 1) % 4; + // When looping back to MODE_ENTERING_FIRST_NUM, reuse the + // previous calculation's results as the next calculation's + // first_num; also reset other numbers if (state->mode == MODE_ENTERING_FIRST_NUM) { state->first_num = state->result; reset_to_zero(&state->second_num); + reset_to_zero(&state->result); } - //printf("Current mode: %d\n", state->mode); // For debugging } break; case EVENT_MODE_LONG_PRESS: + // Move to next face if first number is 0 if (state->first_num.hundredths == 0 && state->first_num.tenths == 0 && state->first_num.ones== 0 && state->first_num.tens == 0 && state->first_num.hundreds == 0 && - state->first_num.thousands == 0 && - state->second_num.hundredths == 0 && - state->second_num.tenths == 0 && - state->second_num.ones== 0 && - state->second_num.tens == 0 && - state->second_num.hundreds == 0 && - state->second_num.thousands == 0) { + state->first_num.thousands == 0) { movement_move_to_face(0); + // otherwise, start over } else { reset_all(state); } From 2e878e146c10474bc1ac8c3f718f3aea76b1330a Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Wed, 14 Aug 2024 22:13:23 -0400 Subject: [PATCH 047/161] Start of Wordle --- movement/make/Makefile | 1 + movement/movement_faces.h | 1 + .../watch_faces/complication/wordle_face.c | 753 ++++++++++++++++++ .../watch_faces/complication/wordle_face.h | 86 ++ 4 files changed, 841 insertions(+) create mode 100644 movement/watch_faces/complication/wordle_face.c create mode 100644 movement/watch_faces/complication/wordle_face.h diff --git a/movement/make/Makefile b/movement/make/Makefile index da5486b..f2b20eb 100644 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -129,6 +129,7 @@ SRCS += \ ../watch_faces/clock/minute_repeater_decimal_face.c \ ../watch_faces/complication/tuning_tones_face.c \ ../watch_faces/complication/kitchen_conversions_face.c \ + ../watch_faces/complication/wordle_face.c \ # New watch faces go above this line. # Leave this line at the bottom of the file; it has all the targets for making your project. diff --git a/movement/movement_faces.h b/movement/movement_faces.h index 3557110..f697e72 100644 --- a/movement/movement_faces.h +++ b/movement/movement_faces.h @@ -104,6 +104,7 @@ #include "minute_repeater_decimal_face.h" #include "tuning_tones_face.h" #include "kitchen_conversions_face.h" +#include "wordle_face.h" // New includes go above this line. #endif // MOVEMENT_FACES_H_ diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c new file mode 100644 index 0000000..8cd993e --- /dev/null +++ b/movement/watch_faces/complication/wordle_face.c @@ -0,0 +1,753 @@ +/* + * MIT License + * + * Copyright (c) 2024 <#author_name#> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "wordle_face.h" + +/* +TODO: +* Add quick iteration (8x freq to get to the letter we want) +* Fix the word matching (if answer is AAAAA and we put in AACAA, the C blinks) +* Verify pressing back always work when the board is G_G_G +* Add daily streak and wait for next day +*/ + + +/* +Letter | Usage in Text +E | 12.7% +T | 9.1% But looks bad across all positions +A | 8.2% +O | 7.5% +I | 7.0% +N | 6.7% Few uses in 5 letter words than C +S | 6.3% +H | 6.1% +R | 6.0% +D | 4.3% +L | 4.0% +C | 2.8% +*/ +static const char _valid_letters[] = {'A', 'C', 'd', 'E', 'H', 'I', 'L', 'O', 'R', 'S'}; + +// From: https://github.com/charlesreid1/five-letter-words/blob/master/sgb-words.txt +static const char _legal_words[][WORDLE_LENGTH + 1] = { +"AAAAA", "SHALL", "HEARd", "ORdER", "CLOSE", "CLASS", "HORSE", "AddEd", "COLOR", "IdEAS", +"HEARd", "ORdER", "CLOSE", "CLASS", "HORSE", "AddEd", "COLOR", "IdEAS", "CRIEd", +"ORdER", "CLOSE", "CLASS", "HORSE", "AddEd", "COLOR", "IdEAS", "CRIEd", "CLEAR", +"CLOSE", "CLASS", "HORSE", "AddEd", "COLOR", "IdEAS", "CRIEd", "CLEAR", "CHILd", +"CLASS", "HORSE", "AddEd", "COLOR", "IdEAS", "CRIEd", "CLEAR", "CHILd", "SIdES", +"HORSE", "AddEd", "COLOR", "IdEAS", "CRIEd", "CLEAR", "CHILd", "SIdES", "AREAS", +"AddEd", "COLOR", "IdEAS", "CRIEd", "CLEAR", "CHILd", "SIdES", "AREAS", "SCALE", +"COLOR", "IdEAS", "CRIEd", "CLEAR", "CHILd", "SIdES", "AREAS", "SCALE", "CELLS", +"IdEAS", "CRIEd", "CLEAR", "CHILd", "SIdES", "AREAS", "SCALE", "CELLS", "AHEAd", +"CRIEd", "CLEAR", "CHILd", "SIdES", "AREAS", "SCALE", "CELLS", "AHEAd", "REACH", +"CLEAR", "CHILd", "SIdES", "AREAS", "SCALE", "CELLS", "AHEAd", "REACH", "RAdIO", +"CHILd", "SIdES", "AREAS", "SCALE", "CELLS", "AHEAd", "REACH", "RAdIO", "LOCAL", +"SIdES", "AREAS", "SCALE", "CELLS", "AHEAd", "REACH", "RAdIO", "LOCAL", "SEEdS", +"AREAS", "SCALE", "CELLS", "AHEAd", "REACH", "RAdIO", "LOCAL", "SEEdS", "CROSS", +"SCALE", "CELLS", "AHEAd", "REACH", "RAdIO", "LOCAL", "SEEdS", "CROSS", "CASES", +"CELLS", "AHEAd", "REACH", "RAdIO", "LOCAL", "SEEdS", "CROSS", "CASES", "OLdER", +"AHEAd", "REACH", "RAdIO", "LOCAL", "SEEdS", "CROSS", "CASES", "OLdER", "SHOES", +"REACH", "RAdIO", "LOCAL", "SEEdS", "CROSS", "CASES", "OLdER", "SHOES", "CHAIR", +"RAdIO", "LOCAL", "SEEdS", "CROSS", "CASES", "OLdER", "SHOES", "CHAIR", "SCORE", +"LOCAL", "SEEdS", "CROSS", "CASES", "OLdER", "SHOES", "CHAIR", "SCORE", "SHORE", +"SEEdS", "CROSS", "CASES", "OLdER", "SHOES", "CHAIR", "SCORE", "SHORE", "HEAdS", +"CROSS", "CASES", "OLdER", "SHOES", "CHAIR", "SCORE", "SHORE", "HEAdS", "dRESS", +"CASES", "OLdER", "SHOES", "CHAIR", "SCORE", "SHORE", "HEAdS", "dRESS", "SHARE", +"OLdER", "SHOES", "CHAIR", "SCORE", "SHORE", "HEAdS", "dRESS", "SHARE", "SOLId", +"SHOES", "CHAIR", "SCORE", "SHORE", "HEAdS", "dRESS", "SHARE", "SOLId", "HILLS", +"CHAIR", "SCORE", "SHORE", "HEAdS", "dRESS", "SHARE", "SOLId", "HILLS", "RAISE", +"SCORE", "SHORE", "HEAdS", "dRESS", "SHARE", "SOLId", "HILLS", "RAISE", "ROAdS", +"SHORE", "HEAdS", "dRESS", "SHARE", "SOLId", "HILLS", "RAISE", "ROAdS", "CHORd", +"HEAdS", "dRESS", "SHARE", "SOLId", "HILLS", "RAISE", "ROAdS", "CHORd", "HOLES", +"dRESS", "SHARE", "SOLId", "HILLS", "RAISE", "ROAdS", "CHORd", "HOLES", "HOLdS", +"SHARE", "SOLId", "HILLS", "RAISE", "ROAdS", "CHORd", "HOLES", "HOLdS", "CALLS", +"SOLId", "HILLS", "RAISE", "ROAdS", "CHORd", "HOLES", "HOLdS", "CALLS", "dOORS", +"HILLS", "RAISE", "ROAdS", "CHORd", "HOLES", "HOLdS", "CALLS", "dOORS", "LOOSE", +"RAISE", "ROAdS", "CHORd", "HOLES", "HOLdS", "CALLS", "dOORS", "LOOSE", "ASIdE", +"ROAdS", "CHORd", "HOLES", "HOLdS", "CALLS", "dOORS", "LOOSE", "ASIdE", "SHELL", +"CHORd", "HOLES", "HOLdS", "CALLS", "dOORS", "LOOSE", "ASIdE", "SHELL", "dRIEd", +"HOLES", "HOLdS", "CALLS", "dOORS", "LOOSE", "ASIdE", "SHELL", "dRIEd", "SHAdE", +"HOLdS", "CALLS", "dOORS", "LOOSE", "ASIdE", "SHELL", "dRIEd", "SHAdE", "CARdS", +"CALLS", "dOORS", "LOOSE", "ASIdE", "SHELL", "dRIEd", "SHAdE", "CARdS", "CHOSE", +"dOORS", "LOOSE", "ASIdE", "SHELL", "dRIEd", "SHAdE", "CARdS", "CHOSE", "SOLAR", +"LOOSE", "ASIdE", "SHELL", "dRIEd", "SHAdE", "CARdS", "CHOSE", "SOLAR", "RISES", +"ASIdE", "SHELL", "dRIEd", "SHAdE", "CARdS", "CHOSE", "SOLAR", "RISES", "SALES", +"SHELL", "dRIEd", "SHAdE", "CARdS", "CHOSE", "SOLAR", "RISES", "SALES", "ACRES", +"dRIEd", "SHAdE", "CARdS", "CHOSE", "SOLAR", "RISES", "SALES", "ACRES", "SLIdE", +"SHAdE", "CARdS", "CHOSE", "SOLAR", "RISES", "SALES", "ACRES", "SLIdE", "ERROR", +"CARdS", "CHOSE", "SOLAR", "RISES", "SALES", "ACRES", "SLIdE", "ERROR", "RACEd", +"CHOSE", "SOLAR", "RISES", "SALES", "ACRES", "SLIdE", "ERROR", "RACEd", "dRILL", +"SOLAR", "RISES", "SALES", "ACRES", "SLIdE", "ERROR", "RACEd", "dRILL", "HELLO", +"RISES", "SALES", "ACRES", "SLIdE", "ERROR", "RACEd", "dRILL", "HELLO", "LEAdS", +"SALES", "ACRES", "SLIdE", "ERROR", "RACEd", "dRILL", "HELLO", "LEAdS", "COACH", +"ACRES", "SLIdE", "ERROR", "RACEd", "dRILL", "HELLO", "LEAdS", "COACH", "REAdS", +"SLIdE", "ERROR", "RACEd", "dRILL", "HELLO", "LEAdS", "COACH", "REAdS", "HERdS", +"ERROR", "RACEd", "dRILL", "HELLO", "LEAdS", "COACH", "REAdS", "HERdS", "AROSE", +"RACEd", "dRILL", "HELLO", "LEAdS", "COACH", "REAdS", "HERdS", "AROSE", "RACES", +"dRILL", "HELLO", "LEAdS", "COACH", "REAdS", "HERdS", "AROSE", "RACES", "HEELS", +"HELLO", "LEAdS", "COACH", "REAdS", "HERdS", "AROSE", "RACES", "HEELS", "RIdER", +"LEAdS", "COACH", "REAdS", "HERdS", "AROSE", "RACES", "HEELS", "RIdER", "ROLLS", +"COACH", "REAdS", "HERdS", "AROSE", "RACES", "HEELS", "RIdER", "ROLLS", "CRASH", +"REAdS", "HERdS", "AROSE", "RACES", "HEELS", "RIdER", "ROLLS", "CRASH", "SAILS", +"HERdS", "AROSE", "RACES", "HEELS", "RIdER", "ROLLS", "CRASH", "SAILS", "ARISE", +"AROSE", "RACES", "HEELS", "RIdER", "ROLLS", "CRASH", "SAILS", "ARISE", "IdEAL", +"RACES", "HEELS", "RIdER", "ROLLS", "CRASH", "SAILS", "ARISE", "IdEAL", "CRIES", +"HEELS", "RIdER", "ROLLS", "CRASH", "SAILS", "ARISE", "IdEAL", "CRIES", "ASHES", +"RIdER", "ROLLS", "CRASH", "SAILS", "ARISE", "IdEAL", "CRIES", "ASHES", "CHASE", +"ROLLS", "CRASH", "SAILS", "ARISE", "IdEAL", "CRIES", "ASHES", "CHASE", "SLICE", +"CRASH", "SAILS", "ARISE", "IdEAL", "CRIES", "ASHES", "CHASE", "SLICE", "CHEER", +"SAILS", "ARISE", "IdEAL", "CRIES", "ASHES", "CHASE", "SLICE", "CHEER", "HIdES", +"ARISE", "IdEAL", "CRIES", "ASHES", "CHASE", "SLICE", "CHEER", "HIdES", "dEEdS", +"IdEAL", "CRIES", "ASHES", "CHASE", "SLICE", "CHEER", "HIdES", "dEEdS", "RIdES", +"CRIES", "ASHES", "CHASE", "SLICE", "CHEER", "HIdES", "dEEdS", "RIdES", "ROSES", +"ASHES", "CHASE", "SLICE", "CHEER", "HIdES", "dEEdS", "RIdES", "ROSES", "HIREd", +"CHASE", "SLICE", "CHEER", "HIdES", "dEEdS", "RIdES", "ROSES", "HIREd", "SALAd", +"SLICE", "CHEER", "HIdES", "dEEdS", "RIdES", "ROSES", "HIREd", "SALAd", "LOAdS", +"CHEER", "HIdES", "dEEdS", "RIdES", "ROSES", "HIREd", "SALAd", "LOAdS", "HEARS", +"HIdES", "dEEdS", "RIdES", "ROSES", "HIREd", "SALAd", "LOAdS", "HEARS", "LOSES", +"dEEdS", "RIdES", "ROSES", "HIREd", "SALAd", "LOAdS", "HEARS", "LOSES", "CORAL", +"RIdES", "ROSES", "HIREd", "SALAd", "LOAdS", "HEARS", "LOSES", "CORAL", "dAREd", +"ROSES", "HIREd", "SALAd", "LOAdS", "HEARS", "LOSES", "CORAL", "dAREd", "RAdAR", +"HIREd", "SALAd", "LOAdS", "HEARS", "LOSES", "CORAL", "dAREd", "RAdAR", "HAIRS", +"SALAd", "LOAdS", "HEARS", "LOSES", "CORAL", "dAREd", "RAdAR", "HAIRS", "dOLLS", +"LOAdS", "HEARS", "LOSES", "CORAL", "dAREd", "RAdAR", "HAIRS", "dOLLS", "CAREd", +"HEARS", "LOSES", "CORAL", "dAREd", "RAdAR", "HAIRS", "dOLLS", "CAREd", "SELLS", +"LOSES", "CORAL", "dAREd", "RAdAR", "HAIRS", "dOLLS", "CAREd", "SELLS", "COOLS", +"CORAL", "dAREd", "RAdAR", "HAIRS", "dOLLS", "CAREd", "SELLS", "COOLS", "HARSH", +"dAREd", "RAdAR", "HAIRS", "dOLLS", "CAREd", "SELLS", "COOLS", "HARSH", "SOILS", +"RAdAR", "HAIRS", "dOLLS", "CAREd", "SELLS", "COOLS", "HARSH", "SOILS", "REEdS", +"HAIRS", "dOLLS", "CAREd", "SELLS", "COOLS", "HARSH", "SOILS", "REEdS", "SHEER", +"dOLLS", "CAREd", "SELLS", "COOLS", "HARSH", "SOILS", "REEdS", "SHEER", "CHILL", +"CAREd", "SELLS", "COOLS", "HARSH", "SOILS", "REEdS", "SHEER", "CHILL", "CORdS", +"SELLS", "COOLS", "HARSH", "SOILS", "REEdS", "SHEER", "CHILL", "CORdS", "RAILS", +"COOLS", "HARSH", "SOILS", "REEdS", "SHEER", "CHILL", "CORdS", "RAILS", "dEALS", +"HARSH", "SOILS", "REEdS", "SHEER", "CHILL", "CORdS", "RAILS", "dEALS", "ACIdS", +"SOILS", "REEdS", "SHEER", "CHILL", "CORdS", "RAILS", "dEALS", "ACIdS", "COCOA", +"REEdS", "SHEER", "CHILL", "CORdS", "RAILS", "dEALS", "ACIdS", "COCOA", "SCARE", +"SHEER", "CHILL", "CORdS", "RAILS", "dEALS", "ACIdS", "COCOA", "SCARE", "CEASE", +"CHILL", "CORdS", "RAILS", "dEALS", "ACIdS", "COCOA", "SCARE", "CEASE", "SEALS", +"CORdS", "RAILS", "dEALS", "ACIdS", "COCOA", "SCARE", "CEASE", "SEALS", "LORdS", +"RAILS", "dEALS", "ACIdS", "COCOA", "SCARE", "CEASE", "SEALS", "LORdS", "HALLS", +"dEALS", "ACIdS", "COCOA", "SCARE", "CEASE", "SEALS", "LORdS", "HALLS", "COALS", +"ACIdS", "COCOA", "SCARE", "CEASE", "SEALS", "LORdS", "HALLS", "COALS", "ROdEO", +"COCOA", "SCARE", "CEASE", "SEALS", "LORdS", "HALLS", "COALS", "ROdEO", "COdES", +"SCARE", "CEASE", "SEALS", "LORdS", "HALLS", "COALS", "ROdEO", "COdES", "ELdER", +"CEASE", "SEALS", "LORdS", "HALLS", "COALS", "ROdEO", "COdES", "ELdER", "ROLES", +"SEALS", "LORdS", "HALLS", "COALS", "ROdEO", "COdES", "ELdER", "ROLES", "dREAd", +"LORdS", "HALLS", "COALS", "ROdEO", "COdES", "ELdER", "ROLES", "dREAd", "CEdAR", +"HALLS", "COALS", "ROdEO", "COdES", "ELdER", "ROLES", "dREAd", "CEdAR", "SLASH", +"COALS", "ROdEO", "COdES", "ELdER", "ROLES", "dREAd", "CEdAR", "SLASH", "CARES", +"ROdEO", "COdES", "ELdER", "ROLES", "dREAd", "CEdAR", "SLASH", "CARES", "IdOLS", +"COdES", "ELdER", "ROLES", "dREAd", "CEdAR", "SLASH", "CARES", "IdOLS", "OdORS", +"ELdER", "ROLES", "dREAd", "CEdAR", "SLASH", "CARES", "IdOLS", "OdORS", "EASEd", +"ROLES", "dREAd", "CEdAR", "SLASH", "CARES", "IdOLS", "OdORS", "EASEd", "CHOIR", +"dREAd", "CEdAR", "SLASH", "CARES", "IdOLS", "OdORS", "EASEd", "CHOIR", "AIdEd", +"CEdAR", "SLASH", "CARES", "IdOLS", "OdORS", "EASEd", "CHOIR", "AIdEd", "CHAOS", +"SLASH", "CARES", "IdOLS", "OdORS", "EASEd", "CHOIR", "AIdEd", "CHAOS", "LEASE", +"CARES", "IdOLS", "OdORS", "EASEd", "CHOIR", "AIdEd", "CHAOS", "LEASE", "SHEAR", +"IdOLS", "OdORS", "EASEd", "CHOIR", "AIdEd", "CHAOS", "LEASE", "SHEAR", "SLEdS", +"OdORS", "EASEd", "CHOIR", "AIdEd", "CHAOS", "LEASE", "SHEAR", "SLEdS", "COILS", +"EASEd", "CHOIR", "AIdEd", "CHAOS", "LEASE", "SHEAR", "SLEdS", "COILS", "ACHEd", +"CHOIR", "AIdEd", "CHAOS", "LEASE", "SHEAR", "SLEdS", "COILS", "ACHEd", "CELLO", +"AIdEd", "CHAOS", "LEASE", "SHEAR", "SLEdS", "COILS", "ACHEd", "CELLO", "dRIES", +"CHAOS", "LEASE", "SHEAR", "SLEdS", "COILS", "ACHEd", "CELLO", "dRIES", "OASIS", +"LEASE", "SHEAR", "SLEdS", "COILS", "ACHEd", "CELLO", "dRIES", "OASIS", "dRIER", +"SHEAR", "SLEdS", "COILS", "ACHEd", "CELLO", "dRIES", "OASIS", "dRIER", "CACAO", +"SLEdS", "COILS", "ACHEd", "CELLO", "dRIES", "OASIS", "dRIER", "CACAO", "EERIE", +"COILS", "ACHEd", "CELLO", "dRIES", "OASIS", "dRIER", "CACAO", "EERIE", "SCARS", +"ACHEd", "CELLO", "dRIES", "OASIS", "dRIER", "CACAO", "EERIE", "SCARS", "RAIdS", +"CELLO", "dRIES", "OASIS", "dRIER", "CACAO", "EERIE", "SCARS", "RAIdS", "SOLES", +"dRIES", "OASIS", "dRIER", "CACAO", "EERIE", "SCARS", "RAIdS", "SOLES", "CAROL", +"OASIS", "dRIER", "CACAO", "EERIE", "SCARS", "RAIdS", "SOLES", "CAROL", "CHESS", +"dRIER", "CACAO", "EERIE", "SCARS", "RAIdS", "SOLES", "CAROL", "CHESS", "OASES", +"CACAO", "EERIE", "SCARS", "RAIdS", "SOLES", "CAROL", "CHESS", "OASES", "ASSES", +"EERIE", "SCARS", "RAIdS", "SOLES", "CAROL", "CHESS", "OASES", "ASSES", "SHEdS", +"SCARS", "RAIdS", "SOLES", "CAROL", "CHESS", "OASES", "ASSES", "SHEdS", "CLASH", +"RAIdS", "SOLES", "CAROL", "CHESS", "OASES", "ASSES", "SHEdS", "CLASH", "dISCS", +"SOLES", "CAROL", "CHESS", "OASES", "ASSES", "SHEdS", "CLASH", "dISCS", "ERASE", +"CAROL", "CHESS", "OASES", "ASSES", "SHEdS", "CLASH", "dISCS", "ERASE", "CIdER", +"CHESS", "OASES", "ASSES", "SHEdS", "CLASH", "dISCS", "ERASE", "CIdER", "SHALE", +"OASES", "ASSES", "SHEdS", "CLASH", "dISCS", "ERASE", "CIdER", "SHALE", "AISLE", +"ASSES", "SHEdS", "CLASH", "dISCS", "ERASE", "CIdER", "SHALE", "AISLE", "HEIRS", +"SHEdS", "CLASH", "dISCS", "ERASE", "CIdER", "SHALE", "AISLE", "HEIRS", "ROARS", +"CLASH", "dISCS", "ERASE", "CIdER", "SHALE", "AISLE", "HEIRS", "ROARS", "SCOLd", +"dISCS", "ERASE", "CIdER", "SHALE", "AISLE", "HEIRS", "ROARS", "SCOLd", "LEASH", +"ERASE", "CIdER", "SHALE", "AISLE", "HEIRS", "ROARS", "SCOLd", "LEASH", "LASSO", +"CIdER", "SHALE", "AISLE", "HEIRS", "ROARS", "SCOLd", "LEASH", "LASSO", "CHORE", +"SHALE", "AISLE", "HEIRS", "ROARS", "SCOLd", "LEASH", "LASSO", "CHORE", "LACEd", +"AISLE", "HEIRS", "ROARS", "SCOLd", "LEASH", "LASSO", "CHORE", "LACEd", "dOSES", +"HEIRS", "ROARS", "SCOLd", "LEASH", "LASSO", "CHORE", "LACEd", "dOSES", "COLdS", +"ROARS", "SCOLd", "LEASH", "LASSO", "CHORE", "LACEd", "dOSES", "COLdS", "CORES", +"SCOLd", "LEASH", "LASSO", "CHORE", "LACEd", "dOSES", "COLdS", "CORES", "CHILI", +"LEASH", "LASSO", "CHORE", "LACEd", "dOSES", "COLdS", "CORES", "CHILI", "EASEL", +"LASSO", "CHORE", "LACEd", "dOSES", "COLdS", "CORES", "CHILI", "EASEL", "LACES", +"CHORE", "LACEd", "dOSES", "COLdS", "CORES", "CHILI", "EASEL", "LACES", "HORdE", +"LACEd", "dOSES", "COLdS", "CORES", "CHILI", "EASEL", "LACES", "HORdE", "LASER", +"dOSES", "COLdS", "CORES", "CHILI", "EASEL", "LACES", "HORdE", "LASER", "HARES", +"COLdS", "CORES", "CHILI", "EASEL", "LACES", "HORdE", "LASER", "HARES", "CREEd", +"CORES", "CHILI", "EASEL", "LACES", "HORdE", "LASER", "HARES", "CREEd", "LILAC", +"CHILI", "EASEL", "LACES", "HORdE", "LASER", "HARES", "CREEd", "LILAC", "HOOdS", +"EASEL", "LACES", "HORdE", "LASER", "HARES", "CREEd", "LILAC", "HOOdS", "ROACH", +"LACES", "HORdE", "LASER", "HARES", "CREEd", "LILAC", "HOOdS", "ROACH", "dIALS", +"HORdE", "LASER", "HARES", "CREEd", "LILAC", "HOOdS", "ROACH", "dIALS", "SOLOS", +"LASER", "HARES", "CREEd", "LILAC", "HOOdS", "ROACH", "dIALS", "SOLOS", "SISAL", +"HARES", "CREEd", "LILAC", "HOOdS", "ROACH", "dIALS", "SOLOS", "SISAL", "HOSES", +"CREEd", "LILAC", "HOOdS", "ROACH", "dIALS", "SOLOS", "SISAL", "HOSES", "dARES", +"LILAC", "HOOdS", "ROACH", "dIALS", "SOLOS", "SISAL", "HOSES", "dARES", "ACHES", +"HOOdS", "ROACH", "dIALS", "SOLOS", "SISAL", "HOSES", "dARES", "ACHES", "LOSER", +"ROACH", "dIALS", "SOLOS", "SISAL", "HOSES", "dARES", "ACHES", "LOSER", "LAdLE", +"dIALS", "SOLOS", "SISAL", "HOSES", "dARES", "ACHES", "LOSER", "LAdLE", "RAdII", +"SOLOS", "SISAL", "HOSES", "dARES", "ACHES", "LOSER", "LAdLE", "RAdII", "SORES", +"SISAL", "HOSES", "dARES", "ACHES", "LOSER", "LAdLE", "RAdII", "SORES", "RELIC", +"HOSES", "dARES", "ACHES", "LOSER", "LAdLE", "RAdII", "SORES", "RELIC", "AIdES", +"dARES", "ACHES", "LOSER", "LAdLE", "RAdII", "SORES", "RELIC", "AIdES", "ALdER", +"ACHES", "LOSER", "LAdLE", "RAdII", "SORES", "RELIC", "AIdES", "ALdER", "COdEd", +"LOSER", "LAdLE", "RAdII", "SORES", "RELIC", "AIdES", "ALdER", "COdEd", "ISLES", +"LAdLE", "RAdII", "SORES", "RELIC", "AIdES", "ALdER", "COdEd", "ISLES", "CLOdS", +"RAdII", "SORES", "RELIC", "AIdES", "ALdER", "COdEd", "ISLES", "CLOdS", "AdORE", +"SORES", "RELIC", "AIdES", "ALdER", "COdEd", "ISLES", "CLOdS", "AdORE", "RARER", +"RELIC", "AIdES", "ALdER", "COdEd", "ISLES", "CLOdS", "AdORE", "RARER", "SILLS", +"AIdES", "ALdER", "COdEd", "ISLES", "CLOdS", "AdORE", "RARER", "SILLS", "SHOAL", +"ALdER", "COdEd", "ISLES", "CLOdS", "AdORE", "RARER", "SILLS", "SHOAL", "CACHE", +"COdEd", "ISLES", "CLOdS", "AdORE", "RARER", "SILLS", "SHOAL", "CACHE", "REELS", +"ISLES", "CLOdS", "AdORE", "RARER", "SILLS", "SHOAL", "CACHE", "REELS", "LIARS", +"CLOdS", "AdORE", "RARER", "SILLS", "SHOAL", "CACHE", "REELS", "LIARS", "SOARS", +"AdORE", "RARER", "SILLS", "SHOAL", "CACHE", "REELS", "LIARS", "SOARS", "SIdEd", +"RARER", "SILLS", "SHOAL", "CACHE", "REELS", "LIARS", "SOARS", "SIdEd", "HEALS", +"SILLS", "SHOAL", "CACHE", "REELS", "LIARS", "SOARS", "SIdEd", "HEALS", "SOdAS", +"SHOAL", "CACHE", "REELS", "LIARS", "SOARS", "SIdEd", "HEALS", "SOdAS", "ERREd", +"CACHE", "REELS", "LIARS", "SOARS", "SIdEd", "HEALS", "SOdAS", "ERREd", "ARdOR", +"REELS", "LIARS", "SOARS", "SIdEd", "HEALS", "SOdAS", "ERREd", "ARdOR", "HIRES", +"LIARS", "SOARS", "SIdEd", "HEALS", "SOdAS", "ERREd", "ARdOR", "HIRES", "LEECH", +"SOARS", "SIdEd", "HEALS", "SOdAS", "ERREd", "ARdOR", "HIRES", "LEECH", "EROdE", +"SIdEd", "HEALS", "SOdAS", "ERREd", "ARdOR", "HIRES", "LEECH", "EROdE", "HOARd", +"HEALS", "SOdAS", "ERREd", "ARdOR", "HIRES", "LEECH", "EROdE", "HOARd", "COOEd", +"SOdAS", "ERREd", "ARdOR", "HIRES", "LEECH", "EROdE", "HOARd", "COOEd", "SHREd", +"ERREd", "ARdOR", "HIRES", "LEECH", "EROdE", "HOARd", "COOEd", "SHREd", "SLOSH", +"ARdOR", "HIRES", "LEECH", "EROdE", "HOARd", "COOEd", "SHREd", "SLOSH", "CHIdE", +"HIRES", "LEECH", "EROdE", "HOARd", "COOEd", "SHREd", "SLOSH", "CHIdE", "EASES", +"LEECH", "EROdE", "HOARd", "COOEd", "SHREd", "SLOSH", "CHIdE", "EASES", "HALOS", +"EROdE", "HOARd", "COOEd", "SHREd", "SLOSH", "CHIdE", "EASES", "HALOS", "ACRId", +"HOARd", "COOEd", "SHREd", "SLOSH", "CHIdE", "EASES", "HALOS", "ACRId", "EIdER", +"COOEd", "SHREd", "SLOSH", "CHIdE", "EASES", "HALOS", "ACRId", "EIdER", "AddER", +"SHREd", "SLOSH", "CHIdE", "EASES", "HALOS", "ACRId", "EIdER", "AddER", "dEARS", +"SLOSH", "CHIdE", "EASES", "HALOS", "ACRId", "EIdER", "AddER", "dEARS", "SEERS", +"CHIdE", "EASES", "HALOS", "ACRId", "EIdER", "AddER", "dEARS", "SEERS", "OddER", +"EASES", "HALOS", "ACRId", "EIdER", "AddER", "dEARS", "SEERS", "OddER", "SIdLE", +"HALOS", "ACRId", "EIdER", "AddER", "dEARS", "SEERS", "OddER", "SIdLE", "dOLEd", +"ACRId", "EIdER", "AddER", "dEARS", "SEERS", "OddER", "SIdLE", "dOLEd", "HAILS", +"EIdER", "AddER", "dEARS", "SEERS", "OddER", "SIdLE", "dOLEd", "HAILS", "AIREd", +"AddER", "dEARS", "SEERS", "OddER", "SIdLE", "dOLEd", "HAILS", "AIREd", "COCCI", +"dEARS", "SEERS", "OddER", "SIdLE", "dOLEd", "HAILS", "AIREd", "COCCI", "SILOS", +"SEERS", "OddER", "SIdLE", "dOLEd", "HAILS", "AIREd", "COCCI", "SILOS", "dOSEd", +"OddER", "SIdLE", "dOLEd", "HAILS", "AIREd", "COCCI", "SILOS", "dOSEd", "RILLS", +"SIdLE", "dOLEd", "HAILS", "AIREd", "COCCI", "SILOS", "dOSEd", "RILLS", "EARLS", +"dOLEd", "HAILS", "AIREd", "COCCI", "SILOS", "dOSEd", "RILLS", "EARLS", "LAIRS", +"HAILS", "AIREd", "COCCI", "SILOS", "dOSEd", "RILLS", "EARLS", "LAIRS", "IdLER", +"AIREd", "COCCI", "SILOS", "dOSEd", "RILLS", "EARLS", "LAIRS", "IdLER", "SCALd", +"COCCI", "SILOS", "dOSEd", "RILLS", "EARLS", "LAIRS", "IdLER", "SCALd", "AdIOS", +"SILOS", "dOSEd", "RILLS", "EARLS", "LAIRS", "IdLER", "SCALd", "AdIOS", "dALES", +"dOSEd", "RILLS", "EARLS", "LAIRS", "IdLER", "SCALd", "AdIOS", "dALES", "HEEdS", +"RILLS", "EARLS", "LAIRS", "IdLER", "SCALd", "AdIOS", "dALES", "HEEdS", "CEdEd", +"EARLS", "LAIRS", "IdLER", "SCALd", "AdIOS", "dALES", "HEEdS", "CEdEd", "LARCH", +"LAIRS", "IdLER", "SCALd", "AdIOS", "dALES", "HEEdS", "CEdEd", "LARCH", "dOLES", +"IdLER", "SCALd", "AdIOS", "dALES", "HEEdS", "CEdEd", "LARCH", "dOLES", "dROOL", +"SCALd", "AdIOS", "dALES", "HEEdS", "CEdEd", "LARCH", "dOLES", "dROOL", "dELLS", +"AdIOS", "dALES", "HEEdS", "CEdEd", "LARCH", "dOLES", "dROOL", "dELLS", "COCOS", +"dALES", "HEEdS", "CEdEd", "LARCH", "dOLES", "dROOL", "dELLS", "COCOS", "LEERS", +"HEEdS", "CEdEd", "LARCH", "dOLES", "dROOL", "dELLS", "COCOS", "LEERS", "ALIAS", +"CEdEd", "LARCH", "dOLES", "dROOL", "dELLS", "COCOS", "LEERS", "ALIAS", "dICEd", +"LARCH", "dOLES", "dROOL", "dELLS", "COCOS", "LEERS", "ALIAS", "dICEd", "LOdES", +"dOLES", "dROOL", "dELLS", "COCOS", "LEERS", "ALIAS", "dICEd", "LOdES", "IdLEd", +"dROOL", "dELLS", "COCOS", "LEERS", "ALIAS", "dICEd", "LOdES", "IdLEd", "RACER", +"dELLS", "COCOS", "LEERS", "ALIAS", "dICEd", "LOdES", "IdLEd", "RACER", "AILEd", +"COCOS", "LEERS", "ALIAS", "dICEd", "LOdES", "IdLEd", "RACER", "AILEd", "HERES", +"LEERS", "ALIAS", "dICEd", "LOdES", "IdLEd", "RACER", "AILEd", "HERES", "IdLES", +"ALIAS", "dICEd", "LOdES", "IdLEd", "RACER", "AILEd", "HERES", "IdLES", "dOdOS", +"dICEd", "LOdES", "IdLEd", "RACER", "AILEd", "HERES", "IdLES", "dOdOS", "HELLS", +"LOdES", "IdLEd", "RACER", "AILEd", "HERES", "IdLES", "dOdOS", "HELLS", "SOLEd", +"IdLEd", "RACER", "AILEd", "HERES", "IdLES", "dOdOS", "HELLS", "SOLEd", "CASEd", +"RACER", "AILEd", "HERES", "IdLES", "dOdOS", "HELLS", "SOLEd", "CASEd", "COEdS", +"AILEd", "HERES", "IdLES", "dOdOS", "HELLS", "SOLEd", "CASEd", "COEdS", "LORES", +"HERES", "IdLES", "dOdOS", "HELLS", "SOLEd", "CASEd", "COEdS", "LORES", "REARS", +"IdLES", "dOdOS", "HELLS", "SOLEd", "CASEd", "COEdS", "LORES", "REARS", "COLIC", +"dOdOS", "HELLS", "SOLEd", "CASEd", "COEdS", "LORES", "REARS", "COLIC", "SIRES", +"HELLS", "SOLEd", "CASEd", "COEdS", "LORES", "REARS", "COLIC", "SIRES", "SORER", +"SOLEd", "CASEd", "COEdS", "LORES", "REARS", "COLIC", "SIRES", "SORER", "LOLLS", +"CASEd", "COEdS", "LORES", "REARS", "COLIC", "SIRES", "SORER", "LOLLS", "SCAdS", +"COEdS", "LORES", "REARS", "COLIC", "SIRES", "SORER", "LOLLS", "SCAdS", "LEACH", +"LORES", "REARS", "COLIC", "SIRES", "SORER", "LOLLS", "SCAdS", "LEACH", "ARCEd", +"REARS", "COLIC", "SIRES", "SORER", "LOLLS", "SCAdS", "LEACH", "ARCEd", "COREd", +"COLIC", "SIRES", "SORER", "LOLLS", "SCAdS", "LEACH", "ARCEd", "COREd", "dEAdS", +"SIRES", "SORER", "LOLLS", "SCAdS", "LEACH", "ARCEd", "COREd", "dEAdS", "dROLL", +"SORER", "LOLLS", "SCAdS", "LEACH", "ARCEd", "COREd", "dEAdS", "dROLL", "dICES", +"LOLLS", "SCAdS", "LEACH", "ARCEd", "COREd", "dEAdS", "dROLL", "dICES", "CEdES", +"SCAdS", "LEACH", "ARCEd", "COREd", "dEAdS", "dROLL", "dICES", "CEdES", "HOSEd", +"LEACH", "ARCEd", "COREd", "dEAdS", "dROLL", "dICES", "CEdES", "HOSEd", "CRESS", +"ARCEd", "COREd", "dEAdS", "dROLL", "dICES", "CEdES", "HOSEd", "CRESS", "ICIER", +"COREd", "dEAdS", "dROLL", "dICES", "CEdES", "HOSEd", "CRESS", "ICIER", "LARdS", +"dEAdS", "dROLL", "dICES", "CEdES", "HOSEd", "CRESS", "ICIER", "LARdS", "HEROS", +"dROLL", "dICES", "CEdES", "HOSEd", "CRESS", "ICIER", "LARdS", "HEROS", "SHOOS", +"dICES", "CEdES", "HOSEd", "CRESS", "ICIER", "LARdS", "HEROS", "SHOOS", "dECAL", +"CEdES", "HOSEd", "CRESS", "ICIER", "LARdS", "HEROS", "SHOOS", "dECAL", "CILIA", +"HOSEd", "CRESS", "ICIER", "LARdS", "HEROS", "SHOOS", "dECAL", "CILIA", "SEARS", +"CRESS", "ICIER", "LARdS", "HEROS", "SHOOS", "dECAL", "CILIA", "SEARS", "SARIS", +"ICIER", "LARdS", "HEROS", "SHOOS", "dECAL", "CILIA", "SEARS", "SARIS", "dILLS", +"LARdS", "HEROS", "SHOOS", "dECAL", "CILIA", "SEARS", "SARIS", "dILLS", "OILEd", +"HEROS", "SHOOS", "dECAL", "CILIA", "SEARS", "SARIS", "dILLS", "OILEd", "dOERS", +"SHOOS", "dECAL", "CILIA", "SEARS", "SARIS", "dILLS", "OILEd", "dOERS", "ALOHA", +"dECAL", "CILIA", "SEARS", "SARIS", "dILLS", "OILEd", "dOERS", "ALOHA", "RISER", +"CILIA", "SEARS", "SARIS", "dILLS", "OILEd", "dOERS", "ALOHA", "RISER", "CRIER", +"SEARS", "SARIS", "dILLS", "OILEd", "dOERS", "ALOHA", "RISER", "CRIER", "LOESS", +"SARIS", "dILLS", "OILEd", "dOERS", "ALOHA", "RISER", "CRIER", "LOESS", "dECOR", +"dILLS", "OILEd", "dOERS", "ALOHA", "RISER", "CRIER", "LOESS", "dECOR", "SHIEd", +"OILEd", "dOERS", "ALOHA", "RISER", "CRIER", "LOESS", "dECOR", "SHIEd", "dROSS", +"dOERS", "ALOHA", "RISER", "CRIER", "LOESS", "dECOR", "SHIEd", "dROSS", "CREdO", +"ALOHA", "RISER", "CRIER", "LOESS", "dECOR", "SHIEd", "dROSS", "CREdO", "ALOES", +"RISER", "CRIER", "LOESS", "dECOR", "SHIEd", "dROSS", "CREdO", "ALOES", "dIOdE", +"CRIER", "LOESS", "dECOR", "SHIEd", "dROSS", "CREdO", "ALOES", "dIOdE", "COHOS", +"LOESS", "dECOR", "SHIEd", "dROSS", "CREdO", "ALOES", "dIOdE", "COHOS", "SIREE", +"dECOR", "SHIEd", "dROSS", "CREdO", "ALOES", "dIOdE", "COHOS", "SIREE", "OCHRE", +"SHIEd", "dROSS", "CREdO", "ALOES", "dIOdE", "COHOS", "SIREE", "OCHRE", "SIREd", +"dROSS", "CREdO", "ALOES", "dIOdE", "COHOS", "SIREE", "OCHRE", "SIREd", "CAdRE", +"CREdO", "ALOES", "dIOdE", "COHOS", "SIREE", "OCHRE", "SIREd", "CAdRE", "ECHOS", +"ALOES", "dIOdE", "COHOS", "SIREE", "OCHRE", "SIREd", "CAdRE", "ECHOS", "RILEd", +"dIOdE", "COHOS", "SIREE", "OCHRE", "SIREd", "CAdRE", "ECHOS", "RILEd", "CIRCA", +"COHOS", "SIREE", "OCHRE", "SIREd", "CAdRE", "ECHOS", "RILEd", "CIRCA", "HALLO", +"SIREE", "OCHRE", "SIREd", "CAdRE", "ECHOS", "RILEd", "CIRCA", "HALLO", "SHIES", +"OCHRE", "SIREd", "CAdRE", "ECHOS", "RILEd", "CIRCA", "HALLO", "SHIES", "HOLEd", +"SIREd", "CAdRE", "ECHOS", "RILEd", "CIRCA", "HALLO", "SHIES", "HOLEd", "SEdER", +"CAdRE", "ECHOS", "RILEd", "CIRCA", "HALLO", "SHIES", "HOLEd", "SEdER", "REALS", +"ECHOS", "RILEd", "CIRCA", "HALLO", "SHIES", "HOLEd", "SEdER", "REALS", "AERIE", +"RILEd", "CIRCA", "HALLO", "SHIES", "HOLEd", "SEdER", "REALS", "AERIE", "CRASS", +"CIRCA", "HALLO", "SHIES", "HOLEd", "SEdER", "REALS", "AERIE", "CRASS", "dACHA", +"HALLO", "SHIES", "HOLEd", "SEdER", "REALS", "AERIE", "CRASS", "dACHA", "ACHOO", +"SHIES", "HOLEd", "SEdER", "REALS", "AERIE", "CRASS", "dACHA", "ACHOO", "dREAR", +"HOLEd", "SEdER", "REALS", "AERIE", "CRASS", "dACHA", "ACHOO", "dREAR", "ARIAS", +"SEdER", "REALS", "AERIE", "CRASS", "dACHA", "ACHOO", "dREAR", "ARIAS", "HOOCH", +"REALS", "AERIE", "CRASS", "dACHA", "ACHOO", "dREAR", "ARIAS", "HOOCH", "SHILL", +"AERIE", "CRASS", "dACHA", "ACHOO", "dREAR", "ARIAS", "HOOCH", "SHILL", "AHHHH", +"CRASS", "dACHA", "ACHOO", "dREAR", "ARIAS", "HOOCH", "SHILL", "AHHHH", "SHISH", +"dACHA", "ACHOO", "dREAR", "ARIAS", "HOOCH", "SHILL", "AHHHH", "SHISH", "RILES", +"ACHOO", "dREAR", "ARIAS", "HOOCH", "SHILL", "AHHHH", "SHISH", "RILES", "HEERd", +"dREAR", "ARIAS", "HOOCH", "SHILL", "AHHHH", "SHISH", "RILES", "HEERd", "CREEL", +"ARIAS", "HOOCH", "SHILL", "AHHHH", "SHISH", "RILES", "HEERd", "CREEL", "RHEAS", +"HOOCH", "SHILL", "AHHHH", "SHISH", "RILES", "HEERd", "CREEL", "RHEAS", "COdER", +"SHILL", "AHHHH", "SHISH", "RILES", "HEERd", "CREEL", "RHEAS", "COdER", "HALEd", +"AHHHH", "SHISH", "RILES", "HEERd", "CREEL", "RHEAS", "COdER", "HALEd", "CHARd", +"SHISH", "RILES", "HEERd", "CREEL", "RHEAS", "COdER", "HALEd", "CHARd", "SHOEd", +"RILES", "HEERd", "CREEL", "RHEAS", "COdER", "HALEd", "CHARd", "SHOEd", "ESSES", +"HEERd", "CREEL", "RHEAS", "COdER", "HALEd", "CHARd", "SHOEd", "ESSES", "EAREd", +"CREEL", "RHEAS", "COdER", "HALEd", "CHARd", "SHOEd", "ESSES", "EAREd", "ELIdE", +"RHEAS", "COdER", "HALEd", "CHARd", "SHOEd", "ESSES", "EAREd", "ELIdE", "SALSA", +"COdER", "HALEd", "CHARd", "SHOEd", "ESSES", "EAREd", "ELIdE", "SALSA", "AHOLd", +"HALEd", "CHARd", "SHOEd", "ESSES", "EAREd", "ELIdE", "SALSA", "AHOLd", "SHAHS", +"CHARd", "SHOEd", "ESSES", "EAREd", "ELIdE", "SALSA", "AHOLd", "SHAHS", "dISCO", +"SHOEd", "ESSES", "EAREd", "ELIdE", "SALSA", "AHOLd", "SHAHS", "dISCO", "SHARd", +"ESSES", "EAREd", "ELIdE", "SALSA", "AHOLd", "SHAHS", "dISCO", "SHARd", "CORER", +"EAREd", "ELIdE", "SALSA", "AHOLd", "SHAHS", "dISCO", "SHARd", "CORER", "COLAS", +"ELIdE", "SALSA", "AHOLd", "SHAHS", "dISCO", "SHARd", "CORER", "COLAS", "dROId", +"SALSA", "AHOLd", "SHAHS", "dISCO", "SHARd", "CORER", "COLAS", "dROId", "OLdIE", +"AHOLd", "SHAHS", "dISCO", "SHARd", "CORER", "COLAS", "dROId", "OLdIE", "COdAS", +"SHAHS", "dISCO", "SHARd", "CORER", "COLAS", "dROId", "OLdIE", "COdAS", "ORCAS", +"dISCO", "SHARd", "CORER", "COLAS", "dROId", "OLdIE", "COdAS", "ORCAS", "HALES", +"SHARd", "CORER", "COLAS", "dROId", "OLdIE", "COdAS", "ORCAS", "HALES", "REdId", +"CORER", "COLAS", "dROId", "OLdIE", "COdAS", "ORCAS", "HALES", "REdId", "dAdOS", +"COLAS", "dROId", "OLdIE", "COdAS", "ORCAS", "HALES", "REdId", "dAdOS", "CLAdS", +"dROId", "OLdIE", "COdAS", "ORCAS", "HALES", "REdId", "dAdOS", "CLAdS", "OOdLE", +"OLdIE", "COdAS", "ORCAS", "HALES", "REdId", "dAdOS", "CLAdS", "OOdLE", "HIdER", +"COdAS", "ORCAS", "HALES", "REdId", "dAdOS", "CLAdS", "OOdLE", "HIdER", "dELIS", +"ORCAS", "HALES", "REdId", "dAdOS", "CLAdS", "OOdLE", "HIdER", "dELIS", "SCROd", +"HALES", "REdId", "dAdOS", "CLAdS", "OOdLE", "HIdER", "dELIS", "SCROd", "SHIRE", +"REdId", "dAdOS", "CLAdS", "OOdLE", "HIdER", "dELIS", "SCROd", "SHIRE", "AddLE", +"dAdOS", "CLAdS", "OOdLE", "HIdER", "dELIS", "SCROd", "SHIRE", "AddLE", "dEICE", +"CLAdS", "OOdLE", "HIdER", "dELIS", "SCROd", "SHIRE", "AddLE", "dEICE", "ORALS", +"OOdLE", "HIdER", "dELIS", "SCROd", "SHIRE", "AddLE", "dEICE", "ORALS", "HIRER", +"HIdER", "dELIS", "SCROd", "SHIRE", "AddLE", "dEICE", "ORALS", "HIRER", "ROILS", +"dELIS", "SCROd", "SHIRE", "AddLE", "dEICE", "ORALS", "HIRER", "ROILS", "dOLOR", +"SCROd", "SHIRE", "AddLE", "dEICE", "ORALS", "HIRER", "ROILS", "dOLOR", "ILIAC", +"SHIRE", "AddLE", "dEICE", "ORALS", "HIRER", "ROILS", "dOLOR", "ILIAC", "CILLS", +"AddLE", "dEICE", "ORALS", "HIRER", "ROILS", "dOLOR", "ILIAC", "CILLS", "LOCOS", +"dEICE", "ORALS", "HIRER", "ROILS", "dOLOR", "ILIAC", "CILLS", "LOCOS", "HILAR", +"ORALS", "HIRER", "ROILS", "dOLOR", "ILIAC", "CILLS", "LOCOS", "HILAR", "CROCS", +"HIRER", "ROILS", "dOLOR", "ILIAC", "CILLS", "LOCOS", "HILAR", "CROCS", "OHHHH", +"ROILS", "dOLOR", "ILIAC", "CILLS", "LOCOS", "HILAR", "CROCS", "OHHHH", "SECCO", +"dOLOR", "ILIAC", "CILLS", "LOCOS", "HILAR", "CROCS", "OHHHH", "SECCO", "SHAdS", +"ILIAC", "CILLS", "LOCOS", "HILAR", "CROCS", "OHHHH", "SECCO", "SHAdS", "CHOOS", +"CILLS", "LOCOS", "HILAR", "CROCS", "OHHHH", "SECCO", "SHAdS", "CHOOS", "AREAL", +"LOCOS", "HILAR", "CROCS", "OHHHH", "SECCO", "SHAdS", "CHOOS", "AREAL", "dIdOS", +"HILAR", "CROCS", "OHHHH", "SECCO", "SHAdS", "CHOOS", "AREAL", "dIdOS", "COOCH", +"CROCS", "OHHHH", "SECCO", "SHAdS", "CHOOS", "AREAL", "dIdOS", "COOCH", "CHILE", +"OHHHH", "SECCO", "SHAdS", "CHOOS", "AREAL", "dIdOS", "COOCH", "CHILE", "OCHER", +"SECCO", "SHAdS", "CHOOS", "AREAL", "dIdOS", "COOCH", "CHILE", "OCHER", "dOLCE", +"SHAdS", "CHOOS", "AREAL", "dIdOS", "COOCH", "CHILE", "OCHER", "dOLCE", "SLOES", +"CHOOS", "AREAL", "dIdOS", "COOCH", "CHILE", "OCHER", "dOLCE", "SLOES", "LAIRd", +"AREAL", "dIdOS", "COOCH", "CHILE", "OCHER", "dOLCE", "SLOES", "LAIRd", "SHERd", +"dIdOS", "COOCH", "CHILE", "OCHER", "dOLCE", "SLOES", "LAIRd", "SHERd", "CHARS", +"COOCH", "CHILE", "OCHER", "dOLCE", "SLOES", "LAIRd", "SHERd", "CHARS", "HAdES", +"CHILE", "OCHER", "dOLCE", "SLOES", "LAIRd", "SHERd", "CHARS", "HAdES", "HOARS", +"OCHER", "dOLCE", "SLOES", "LAIRd", "SHERd", "CHARS", "HAdES", "HOARS", "dARER", +"dOLCE", "SLOES", "LAIRd", "SHERd", "CHARS", "HAdES", "HOARS", "dARER", "ROIdS", +"SLOES", "LAIRd", "SHERd", "CHARS", "HAdES", "HOARS", "dARER", "ROIdS", "ARRAS", +"LAIRd", "SHERd", "CHARS", "HAdES", "HOARS", "dARER", "ROIdS", "ARRAS", "HALER", +"SHERd", "CHARS", "HAdES", "HOARS", "dARER", "ROIdS", "ARRAS", "HALER", "AIRER", +"CHARS", "HAdES", "HOARS", "dARER", "ROIdS", "ARRAS", "HALER", "AIRER", "CASAS", +"HAdES", "HOARS", "dARER", "ROIdS", "ARRAS", "HALER", "AIRER", "CASAS", "CARER", +"HOARS", "dARER", "ROIdS", "ARRAS", "HALER", "AIRER", "CASAS", "CARER", "OAREd", +"dARER", "ROIdS", "ARRAS", "HALER", "AIRER", "CASAS", "CARER", "OAREd", "RILLE", +"ROIdS", "ARRAS", "HALER", "AIRER", "CASAS", "CARER", "OAREd", "RILLE", "RICEd", +"ARRAS", "HALER", "AIRER", "CASAS", "CARER", "OAREd", "RILLE", "RICEd", "LIRAS", +"HALER", "AIRER", "CASAS", "CARER", "OAREd", "RILLE", "RICEd", "LIRAS", "AIdER", +"AIRER", "CASAS", "CARER", "OAREd", "RILLE", "RICEd", "LIRAS", "AIdER", "ICHOR", +"CASAS", "CARER", "OAREd", "RILLE", "RICEd", "LIRAS", "AIdER", "ICHOR", "LOCHS", +"CARER", "OAREd", "RILLE", "RICEd", "LIRAS", "AIdER", "ICHOR", "LOCHS", "CALLA", +"OAREd", "RILLE", "RICEd", "LIRAS", "AIdER", "ICHOR", "LOCHS", "CALLA", "COCAS", +"RILLE", "RICEd", "LIRAS", "AIdER", "ICHOR", "LOCHS", "CALLA", "COCAS", "OILER", +"RICEd", "LIRAS", "AIdER", "ICHOR", "LOCHS", "CALLA", "COCAS", "OILER", "ASSAI", +"LIRAS", "AIdER", "ICHOR", "LOCHS", "CALLA", "COCAS", "OILER", "ASSAI", "LAdES", +"AIdER", "ICHOR", "LOCHS", "CALLA", "COCAS", "OILER", "ASSAI", "LAdES", "SHIER", +"ICHOR", "LOCHS", "CALLA", "COCAS", "OILER", "ASSAI", "LAdES", "SHIER", "dICER", +"LOCHS", "CALLA", "COCAS", "OILER", "ASSAI", "LAdES", "SHIER", "dICER", "OLEOS", +"CALLA", "COCAS", "OILER", "ASSAI", "LAdES", "SHIER", "dICER", "OLEOS", "SHOER", +"COCAS", "OILER", "ASSAI", "LAdES", "SHIER", "dICER", "OLEOS", "SHOER", "HOdAd", +"OILER", "ASSAI", "LAdES", "SHIER", "dICER", "OLEOS", "SHOER", "HOdAd", "IOdIC", +"ASSAI", "LAdES", "SHIER", "dICER", "OLEOS", "SHOER", "HOdAd", "IOdIC", "LIERS", +"LAdES", "SHIER", "dICER", "OLEOS", "SHOER", "HOdAd", "IOdIC", "LIERS", "AIOLI", +"SHIER", "dICER", "OLEOS", "SHOER", "HOdAd", "IOdIC", "LIERS", "AIOLI", "HOERS", +"dICER", "OLEOS", "SHOER", "HOdAd", "IOdIC", "LIERS", "AIOLI", "HOERS", "SLIER", +"OLEOS", "SHOER", "HOdAd", "IOdIC", "LIERS", "AIOLI", "HOERS", "SLIER", "LASES", +"SHOER", "HOdAd", "IOdIC", "LIERS", "AIOLI", "HOERS", "SLIER", "LASES", "RASAE", +"HOdAd", "IOdIC", "LIERS", "AIOLI", "HOERS", "SLIER", "LASES", "RASAE", "SOCLE", +"IOdIC", "LIERS", "AIOLI", "HOERS", "SLIER", "LASES", "RASAE", "SOCLE", "RICER", +"LIERS", "AIOLI", "HOERS", "SLIER", "LASES", "RASAE", "SOCLE", "RICER", "HAddA", +"AIOLI", "HOERS", "SLIER", "LASES", "RASAE", "SOCLE", "RICER", "HAddA", "RICES", +"HOERS", "SLIER", "LASES", "RASAE", "SOCLE", "RICER", "HAddA", "RICES", "ROOdS", +"SLIER", "LASES", "RASAE", "SOCLE", "RICER", "HAddA", "RICES", "ROOdS", "CEdER", +"LASES", "RASAE", "SOCLE", "RICER", "HAddA", "RICES", "ROOdS", "CEdER", "CEILS", +"RASAE", "SOCLE", "RICER", "HAddA", "RICES", "ROOdS", "CEdER", "CEILS", "LISLE", +"SOCLE", "RICER", "HAddA", "RICES", "ROOdS", "CEdER", "CEILS", "LISLE", "OLIOS", +"RICER", "HAddA", "RICES", "ROOdS", "CEdER", "CEILS", "LISLE", "OLIOS", "dOSER", +"HAddA", "RICES", "ROOdS", "CEdER", "CEILS", "LISLE", "OLIOS", "dOSER", "dILdO", +"RICES", "ROOdS", "CEdER", "CEILS", "LISLE", "OLIOS", "dOSER", "dILdO", "SELAH", +"ROOdS", "CEdER", "CEILS", "LISLE", "OLIOS", "dOSER", "dILdO", "SELAH", "RIALS", +"CEdER", "CEILS", "LISLE", "OLIOS", "dOSER", "dILdO", "SELAH", "RIALS", "ICERS", +"CEILS", "LISLE", "OLIOS", "dOSER", "dILdO", "SELAH", "RIALS", "ICERS", "SHIRR", +"LISLE", "OLIOS", "dOSER", "dILdO", "SELAH", "RIALS", "ICERS", "SHIRR", "LAdEd", +"OLIOS", "dOSER", "dILdO", "SELAH", "RIALS", "ICERS", "SHIRR", "LAdEd", "LASEd", +"dOSER", "dILdO", "SELAH", "RIALS", "ICERS", "SHIRR", "LAdEd", "LASEd", "HOLER", +"SELAH", "RIALS", "ICERS", "SHIRR", "LAdEd", "LASEd", "HOLER", "LACER", "ARSES", +"dILdO", "SELAH", "RIALS", "ICERS", "SHIRR", "LAdEd", "LASEd", "HOLER", "LACER", +"RIALS", "ICERS", "SHIRR", "LAdEd", "LASEd", "HOLER", "LACER", "ARSES", "CIRRI", +"ICERS", "SHIRR", "LAdEd", "LASEd", "HOLER", "LACER", "ARSES", "CIRRI", "dIRER", +"SHIRR", "LAdEd", "LASEd", "HOLER", "LACER", "ARSES", "CIRRI", "dIRER", "ASSEd", +"LAdEd", "LASEd", "HOLER", "LACER", "ARSES", "CIRRI", "dIRER", "ASSEd", "OSIER", +}; + +static const uint32_t _num_words = (sizeof(_legal_words) / sizeof(_legal_words[0])); +static const uint8_t _num_valid_letters = (sizeof(_valid_letters) / sizeof(_valid_letters[0])); + +static uint32_t get_random(uint32_t max) { + #if __EMSCRIPTEN__ + return rand() % max; + #else + return arc4random_uniform(max); + #endif +} + +static void display_letter(wordle_state_t *state) { + char buf[1 + 1]; + if (state->word_elements[state->position] >= _num_valid_letters) { + 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; + for (size_t i = 0; i < WORDLE_LENGTH; i++) { + state->position = i; + display_letter(state); + } + state->position = prev_pos; +} + +static bool check_word(wordle_state_t *state) { + + // Exact + bool is_exact_match = true; + for (size_t i = 0; i < WORDLE_LENGTH; i++) { + if (_valid_letters[state->word_elements[i]] == _legal_words[state->curr_answer][i]) + state->word_elements_result[i] = WORDLE_LETTER_CORRECT; + else { + state->word_elements_result[i] = WORDLE_LETTER_WRONG; + is_exact_match = false; + } + } + + // 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 (_valid_letters[state->word_elements[i]] == _legal_words[state->curr_answer][j]) { + printf("me: %c them: %c\r\n", _valid_letters[state->word_elements[i]], _legal_words[state->curr_answer][j]); + state->word_elements_result[j] = WORDLE_LETTER_WRONG_LOC; + break; + } + } + } + + for (size_t i = 0; i < WORDLE_LENGTH; i++) { + printf("%d : %d\r\n", i, state->word_elements_result[i]); + } + + + + return is_exact_match; +} + +static void display_attempt(uint8_t attempt) { + char buf[2]; + sprintf(buf, "%d", attempt); + watch_display_string(buf, 3); +} + +static void reset_board(wordle_state_t *state) { + for (size_t i = 0; i < WORDLE_LENGTH; i++) { + state->word_elements[i] = _num_valid_letters; + state->word_elements_result[i] = WORDLE_LETTER_WRONG; + } + state->curr_answer = get_random(_num_words); + state->position = 0; + state->attempt = 1; + state->curr_screen = SCREEN_PLAYING; + watch_clear_colon(); + watch_display_string(" ", 4); + display_attempt(state->attempt); + display_all_letters(state); + printf("rand: %s\r\n", _legal_words[state->curr_answer]); +} + +static void display_title(wordle_state_t *state) { + char buf[11]; + state->curr_screen = SCREEN_TITLE; + printf("streak %d \r\n", state->streak); + sprintf(buf, "WO St%2ddy", state->streak); + watch_display_string(buf, 0); + watch_set_colon(); +} + +static void display_lose(wordle_state_t *state, uint8_t subsecond) { + char buf[WORDLE_LENGTH + 5]; + sprintf(buf," L %s", subsecond % 2 ? _legal_words[state->curr_answer] : " "); + watch_display_string(buf, 0); +} + +static void display_win(wordle_state_t *state, uint8_t subsecond) { + char buf[11]; + sprintf(buf," W %s ", subsecond % 2 ? "NICE" : "JOb "); + watch_display_string(buf, 0); +} + +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) { + uint8_t pos = curr_pos; + do { + pos++; + if (pos > WORDLE_LENGTH) return WORDLE_LENGTH + 1; + } while (word_elements_result[pos] == WORDLE_LETTER_CORRECT); + return pos; +} + +static uint8_t get_prev_pos(uint8_t curr_pos, WordleLetterResult *word_elements_result) { + int8_t pos = curr_pos; + do { + pos--; + if (pos < 0) return curr_pos; + } while (word_elements_result[pos] == WORDLE_LETTER_CORRECT); + return pos; +} + +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) { + if (state->curr_screen == SCREEN_RESULT) { + for (size_t i = 0; i < WORDLE_LENGTH; i++) { + if (state->word_elements_result[i] != WORDLE_LETTER_CORRECT) + state->word_elements[i] = _num_valid_letters; + } + display_attempt(state->attempt); + display_all_letters(state); + state->position = get_first_pos(state->word_elements_result); + state->curr_screen = SCREEN_PLAYING; + return true; + } + if (state->curr_screen == SCREEN_TITLE) { + reset_board(state); + return true; + } + if (state->curr_screen >= SCREEN_WIN) { + display_title(state); + return true; + } + return false; +} + +void wordle_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { + (void) settings; + (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; + + } + // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep. +} + +void wordle_face_activate(movement_settings_t *settings, void *context) { + (void) settings; + wordle_state_t *state = (wordle_state_t *)context; + movement_request_tick_frequency(2); + if (state->curr_screen == SCREEN_TITLE) + display_title(state); +} + +bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { + wordle_state_t *state = (wordle_state_t *)context; + + switch (event.event_type) { + case EVENT_ACTIVATE: + if (state->curr_screen == SCREEN_PLAYING) + display_all_letters(state); + break; + case EVENT_TICK: + switch (state->curr_screen) + { + case SCREEN_PLAYING: + if (event.subsecond % 2) { + display_letter(state); + } 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)) break; + if (state->word_elements[state->position] >= _num_valid_letters) state->word_elements[state->position] = 0; + else state->word_elements[state->position] = (state->word_elements[state->position] + 1) % _num_valid_letters; + display_letter(state); + break; + case EVENT_LIGHT_LONG_PRESS: + if (state->word_elements[state->position] >= _num_valid_letters) state->word_elements[state->position] = _num_valid_letters - 1; + else state->word_elements[state->position] = (state->word_elements[state->position] + _num_valid_letters - 1) % _num_valid_letters; + display_letter(state); + break; + case EVENT_ALARM_BUTTON_UP: + if (act_on_btn(state)) break; + display_letter(state); + if (state->word_elements[state->position] == _num_valid_letters) break; + state->position = get_next_pos(state->position, state->word_elements_result); + if(WORDLE_LENGTH == (state->position)) { + bool exact_match = check_word(state); + if (exact_match) { + state->curr_screen = SCREEN_WIN; + state->streak++; + break; + } + if (state->attempt++ > WORDLE_MAX_ATTEMPTS) { + state->curr_screen = SCREEN_LOSE; + state->streak = 0; + break; + } + state->curr_screen = SCREEN_RESULT; + break; + } + break; + case EVENT_ALARM_LONG_PRESS: + display_letter(state); + state->position = get_prev_pos(state->position, state->word_elements_result); + break; + case EVENT_LIGHT_BUTTON_DOWN: + break; + case EVENT_TIMEOUT: + case EVENT_LOW_ENERGY_UPDATE: + break; + default: + return movement_default_loop_handler(event, settings); + } + return true; +} + +void wordle_face_resign(movement_settings_t *settings, void *context) { + (void) settings; + (void) context; +} + diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h new file mode 100644 index 0000000..542a01b --- /dev/null +++ b/movement/watch_faces/complication/wordle_face.h @@ -0,0 +1,86 @@ +/* + * MIT License + * + * Copyright (c) 2024 <#author_name#> + * + * 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" + +/* + * A DESCRIPTION OF YOUR WATCH FACE + * + * and a description of how use it + * + */ + +#define WORDLE_LENGTH 5 +#define WORDLE_MAX_ATTEMPTS 5 +#define USE_DAILY_STREAK true + +typedef enum { + WORDLE_LETTER_WRONG = 0, + WORDLE_LETTER_WRONG_LOC, + WORDLE_LETTER_CORRECT, + WORDLE_LETTER_COUNT +} WordleLetterResult; + +typedef enum { + SCREEN_PLAYING = 0, + SCREEN_RESULT, + SCREEN_TITLE, + SCREEN_WIN, + SCREEN_LOSE, + 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]; + uint8_t attempt : 3; + uint8_t position : 3; + uint8_t unused : 2; + uint16_t curr_answer; + uint8_t streak; + WordleScreen curr_screen; +#if USE_DAILY_STREAK + // For the day info +#endif +} wordle_state_t; + +void wordle_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); +void wordle_face_activate(movement_settings_t *settings, void *context); +bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void wordle_face_resign(movement_settings_t *settings, 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_ + From 6bf22edbdcc87e8ca9fc484c55bafe94ff16f414 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Wed, 14 Aug 2024 23:13:34 -0400 Subject: [PATCH 048/161] Continued Wordle dev --- .../watch_faces/complication/wordle_face.c | 552 +++----------- .../watch_faces/complication/wordle_face.h | 1 + utils/wordle_list.py | 697 ++++++++++++++++++ 3 files changed, 803 insertions(+), 447 deletions(-) create mode 100644 utils/wordle_list.py diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 8cd993e..f6f63fd 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -32,439 +32,81 @@ TODO: * Fix the word matching (if answer is AAAAA and we put in AACAA, the C blinks) * Verify pressing back always work when the board is G_G_G * Add daily streak and wait for next day +* Add a way tpo recount previous attempts */ -/* -Letter | Usage in Text -E | 12.7% -T | 9.1% But looks bad across all positions -A | 8.2% -O | 7.5% -I | 7.0% -N | 6.7% Few uses in 5 letter words than C -S | 6.3% -H | 6.1% -R | 6.0% -D | 4.3% -L | 4.0% -C | 2.8% -*/ -static const char _valid_letters[] = {'A', 'C', 'd', 'E', 'H', 'I', 'L', 'O', 'R', 'S'}; - // From: https://github.com/charlesreid1/five-letter-words/blob/master/sgb-words.txt + +/* +Letter | Usage in sgb-words.txt +S | 2674 +E | 2658 +A | 2181 +R | 1799 +O | 1683 +I | 1539 +T | 1462 But looks bad across all positions +L | 1434 +N | 1219 +D | 1100 lowercase d looks like a in certain positions +U | 1068 C has more words with the other letters here (457 total vs 390) +C | 920 +P | 895 +*/ +static const char _valid_letters[] = {'A', 'C', 'E', 'I', 'L', 'N', 'O', 'P', 'R', 'S'}; + static const char _legal_words[][WORDLE_LENGTH + 1] = { -"AAAAA", "SHALL", "HEARd", "ORdER", "CLOSE", "CLASS", "HORSE", "AddEd", "COLOR", "IdEAS", -"HEARd", "ORdER", "CLOSE", "CLASS", "HORSE", "AddEd", "COLOR", "IdEAS", "CRIEd", -"ORdER", "CLOSE", "CLASS", "HORSE", "AddEd", "COLOR", "IdEAS", "CRIEd", "CLEAR", -"CLOSE", "CLASS", "HORSE", "AddEd", "COLOR", "IdEAS", "CRIEd", "CLEAR", "CHILd", -"CLASS", "HORSE", "AddEd", "COLOR", "IdEAS", "CRIEd", "CLEAR", "CHILd", "SIdES", -"HORSE", "AddEd", "COLOR", "IdEAS", "CRIEd", "CLEAR", "CHILd", "SIdES", "AREAS", -"AddEd", "COLOR", "IdEAS", "CRIEd", "CLEAR", "CHILd", "SIdES", "AREAS", "SCALE", -"COLOR", "IdEAS", "CRIEd", "CLEAR", "CHILd", "SIdES", "AREAS", "SCALE", "CELLS", -"IdEAS", "CRIEd", "CLEAR", "CHILd", "SIdES", "AREAS", "SCALE", "CELLS", "AHEAd", -"CRIEd", "CLEAR", "CHILd", "SIdES", "AREAS", "SCALE", "CELLS", "AHEAd", "REACH", -"CLEAR", "CHILd", "SIdES", "AREAS", "SCALE", "CELLS", "AHEAd", "REACH", "RAdIO", -"CHILd", "SIdES", "AREAS", "SCALE", "CELLS", "AHEAd", "REACH", "RAdIO", "LOCAL", -"SIdES", "AREAS", "SCALE", "CELLS", "AHEAd", "REACH", "RAdIO", "LOCAL", "SEEdS", -"AREAS", "SCALE", "CELLS", "AHEAd", "REACH", "RAdIO", "LOCAL", "SEEdS", "CROSS", -"SCALE", "CELLS", "AHEAd", "REACH", "RAdIO", "LOCAL", "SEEdS", "CROSS", "CASES", -"CELLS", "AHEAd", "REACH", "RAdIO", "LOCAL", "SEEdS", "CROSS", "CASES", "OLdER", -"AHEAd", "REACH", "RAdIO", "LOCAL", "SEEdS", "CROSS", "CASES", "OLdER", "SHOES", -"REACH", "RAdIO", "LOCAL", "SEEdS", "CROSS", "CASES", "OLdER", "SHOES", "CHAIR", -"RAdIO", "LOCAL", "SEEdS", "CROSS", "CASES", "OLdER", "SHOES", "CHAIR", "SCORE", -"LOCAL", "SEEdS", "CROSS", "CASES", "OLdER", "SHOES", "CHAIR", "SCORE", "SHORE", -"SEEdS", "CROSS", "CASES", "OLdER", "SHOES", "CHAIR", "SCORE", "SHORE", "HEAdS", -"CROSS", "CASES", "OLdER", "SHOES", "CHAIR", "SCORE", "SHORE", "HEAdS", "dRESS", -"CASES", "OLdER", "SHOES", "CHAIR", "SCORE", "SHORE", "HEAdS", "dRESS", "SHARE", -"OLdER", "SHOES", "CHAIR", "SCORE", "SHORE", "HEAdS", "dRESS", "SHARE", "SOLId", -"SHOES", "CHAIR", "SCORE", "SHORE", "HEAdS", "dRESS", "SHARE", "SOLId", "HILLS", -"CHAIR", "SCORE", "SHORE", "HEAdS", "dRESS", "SHARE", "SOLId", "HILLS", "RAISE", -"SCORE", "SHORE", "HEAdS", "dRESS", "SHARE", "SOLId", "HILLS", "RAISE", "ROAdS", -"SHORE", "HEAdS", "dRESS", "SHARE", "SOLId", "HILLS", "RAISE", "ROAdS", "CHORd", -"HEAdS", "dRESS", "SHARE", "SOLId", "HILLS", "RAISE", "ROAdS", "CHORd", "HOLES", -"dRESS", "SHARE", "SOLId", "HILLS", "RAISE", "ROAdS", "CHORd", "HOLES", "HOLdS", -"SHARE", "SOLId", "HILLS", "RAISE", "ROAdS", "CHORd", "HOLES", "HOLdS", "CALLS", -"SOLId", "HILLS", "RAISE", "ROAdS", "CHORd", "HOLES", "HOLdS", "CALLS", "dOORS", -"HILLS", "RAISE", "ROAdS", "CHORd", "HOLES", "HOLdS", "CALLS", "dOORS", "LOOSE", -"RAISE", "ROAdS", "CHORd", "HOLES", "HOLdS", "CALLS", "dOORS", "LOOSE", "ASIdE", -"ROAdS", "CHORd", "HOLES", "HOLdS", "CALLS", "dOORS", "LOOSE", "ASIdE", "SHELL", -"CHORd", "HOLES", "HOLdS", "CALLS", "dOORS", "LOOSE", "ASIdE", "SHELL", "dRIEd", -"HOLES", "HOLdS", "CALLS", "dOORS", "LOOSE", "ASIdE", "SHELL", "dRIEd", "SHAdE", -"HOLdS", "CALLS", "dOORS", "LOOSE", "ASIdE", "SHELL", "dRIEd", "SHAdE", "CARdS", -"CALLS", "dOORS", "LOOSE", "ASIdE", "SHELL", "dRIEd", "SHAdE", "CARdS", "CHOSE", -"dOORS", "LOOSE", "ASIdE", "SHELL", "dRIEd", "SHAdE", "CARdS", "CHOSE", "SOLAR", -"LOOSE", "ASIdE", "SHELL", "dRIEd", "SHAdE", "CARdS", "CHOSE", "SOLAR", "RISES", -"ASIdE", "SHELL", "dRIEd", "SHAdE", "CARdS", "CHOSE", "SOLAR", "RISES", "SALES", -"SHELL", "dRIEd", "SHAdE", "CARdS", "CHOSE", "SOLAR", "RISES", "SALES", "ACRES", -"dRIEd", "SHAdE", "CARdS", "CHOSE", "SOLAR", "RISES", "SALES", "ACRES", "SLIdE", -"SHAdE", "CARdS", "CHOSE", "SOLAR", "RISES", "SALES", "ACRES", "SLIdE", "ERROR", -"CARdS", "CHOSE", "SOLAR", "RISES", "SALES", "ACRES", "SLIdE", "ERROR", "RACEd", -"CHOSE", "SOLAR", "RISES", "SALES", "ACRES", "SLIdE", "ERROR", "RACEd", "dRILL", -"SOLAR", "RISES", "SALES", "ACRES", "SLIdE", "ERROR", "RACEd", "dRILL", "HELLO", -"RISES", "SALES", "ACRES", "SLIdE", "ERROR", "RACEd", "dRILL", "HELLO", "LEAdS", -"SALES", "ACRES", "SLIdE", "ERROR", "RACEd", "dRILL", "HELLO", "LEAdS", "COACH", -"ACRES", "SLIdE", "ERROR", "RACEd", "dRILL", "HELLO", "LEAdS", "COACH", "REAdS", -"SLIdE", "ERROR", "RACEd", "dRILL", "HELLO", "LEAdS", "COACH", "REAdS", "HERdS", -"ERROR", "RACEd", "dRILL", "HELLO", "LEAdS", "COACH", "REAdS", "HERdS", "AROSE", -"RACEd", "dRILL", "HELLO", "LEAdS", "COACH", "REAdS", "HERdS", "AROSE", "RACES", -"dRILL", "HELLO", "LEAdS", "COACH", "REAdS", "HERdS", "AROSE", "RACES", "HEELS", -"HELLO", "LEAdS", "COACH", "REAdS", "HERdS", "AROSE", "RACES", "HEELS", "RIdER", -"LEAdS", "COACH", "REAdS", "HERdS", "AROSE", "RACES", "HEELS", "RIdER", "ROLLS", -"COACH", "REAdS", "HERdS", "AROSE", "RACES", "HEELS", "RIdER", "ROLLS", "CRASH", -"REAdS", "HERdS", "AROSE", "RACES", "HEELS", "RIdER", "ROLLS", "CRASH", "SAILS", -"HERdS", "AROSE", "RACES", "HEELS", "RIdER", "ROLLS", "CRASH", "SAILS", "ARISE", -"AROSE", "RACES", "HEELS", "RIdER", "ROLLS", "CRASH", "SAILS", "ARISE", "IdEAL", -"RACES", "HEELS", "RIdER", "ROLLS", "CRASH", "SAILS", "ARISE", "IdEAL", "CRIES", -"HEELS", "RIdER", "ROLLS", "CRASH", "SAILS", "ARISE", "IdEAL", "CRIES", "ASHES", -"RIdER", "ROLLS", "CRASH", "SAILS", "ARISE", "IdEAL", "CRIES", "ASHES", "CHASE", -"ROLLS", "CRASH", "SAILS", "ARISE", "IdEAL", "CRIES", "ASHES", "CHASE", "SLICE", -"CRASH", "SAILS", "ARISE", "IdEAL", "CRIES", "ASHES", "CHASE", "SLICE", "CHEER", -"SAILS", "ARISE", "IdEAL", "CRIES", "ASHES", "CHASE", "SLICE", "CHEER", "HIdES", -"ARISE", "IdEAL", "CRIES", "ASHES", "CHASE", "SLICE", "CHEER", "HIdES", "dEEdS", -"IdEAL", "CRIES", "ASHES", "CHASE", "SLICE", "CHEER", "HIdES", "dEEdS", "RIdES", -"CRIES", "ASHES", "CHASE", "SLICE", "CHEER", "HIdES", "dEEdS", "RIdES", "ROSES", -"ASHES", "CHASE", "SLICE", "CHEER", "HIdES", "dEEdS", "RIdES", "ROSES", "HIREd", -"CHASE", "SLICE", "CHEER", "HIdES", "dEEdS", "RIdES", "ROSES", "HIREd", "SALAd", -"SLICE", "CHEER", "HIdES", "dEEdS", "RIdES", "ROSES", "HIREd", "SALAd", "LOAdS", -"CHEER", "HIdES", "dEEdS", "RIdES", "ROSES", "HIREd", "SALAd", "LOAdS", "HEARS", -"HIdES", "dEEdS", "RIdES", "ROSES", "HIREd", "SALAd", "LOAdS", "HEARS", "LOSES", -"dEEdS", "RIdES", "ROSES", "HIREd", "SALAd", "LOAdS", "HEARS", "LOSES", "CORAL", -"RIdES", "ROSES", "HIREd", "SALAd", "LOAdS", "HEARS", "LOSES", "CORAL", "dAREd", -"ROSES", "HIREd", "SALAd", "LOAdS", "HEARS", "LOSES", "CORAL", "dAREd", "RAdAR", -"HIREd", "SALAd", "LOAdS", "HEARS", "LOSES", "CORAL", "dAREd", "RAdAR", "HAIRS", -"SALAd", "LOAdS", "HEARS", "LOSES", "CORAL", "dAREd", "RAdAR", "HAIRS", "dOLLS", -"LOAdS", "HEARS", "LOSES", "CORAL", "dAREd", "RAdAR", "HAIRS", "dOLLS", "CAREd", -"HEARS", "LOSES", "CORAL", "dAREd", "RAdAR", "HAIRS", "dOLLS", "CAREd", "SELLS", -"LOSES", "CORAL", "dAREd", "RAdAR", "HAIRS", "dOLLS", "CAREd", "SELLS", "COOLS", -"CORAL", "dAREd", "RAdAR", "HAIRS", "dOLLS", "CAREd", "SELLS", "COOLS", "HARSH", -"dAREd", "RAdAR", "HAIRS", "dOLLS", "CAREd", "SELLS", "COOLS", "HARSH", "SOILS", -"RAdAR", "HAIRS", "dOLLS", "CAREd", "SELLS", "COOLS", "HARSH", "SOILS", "REEdS", -"HAIRS", "dOLLS", "CAREd", "SELLS", "COOLS", "HARSH", "SOILS", "REEdS", "SHEER", -"dOLLS", "CAREd", "SELLS", "COOLS", "HARSH", "SOILS", "REEdS", "SHEER", "CHILL", -"CAREd", "SELLS", "COOLS", "HARSH", "SOILS", "REEdS", "SHEER", "CHILL", "CORdS", -"SELLS", "COOLS", "HARSH", "SOILS", "REEdS", "SHEER", "CHILL", "CORdS", "RAILS", -"COOLS", "HARSH", "SOILS", "REEdS", "SHEER", "CHILL", "CORdS", "RAILS", "dEALS", -"HARSH", "SOILS", "REEdS", "SHEER", "CHILL", "CORdS", "RAILS", "dEALS", "ACIdS", -"SOILS", "REEdS", "SHEER", "CHILL", "CORdS", "RAILS", "dEALS", "ACIdS", "COCOA", -"REEdS", "SHEER", "CHILL", "CORdS", "RAILS", "dEALS", "ACIdS", "COCOA", "SCARE", -"SHEER", "CHILL", "CORdS", "RAILS", "dEALS", "ACIdS", "COCOA", "SCARE", "CEASE", -"CHILL", "CORdS", "RAILS", "dEALS", "ACIdS", "COCOA", "SCARE", "CEASE", "SEALS", -"CORdS", "RAILS", "dEALS", "ACIdS", "COCOA", "SCARE", "CEASE", "SEALS", "LORdS", -"RAILS", "dEALS", "ACIdS", "COCOA", "SCARE", "CEASE", "SEALS", "LORdS", "HALLS", -"dEALS", "ACIdS", "COCOA", "SCARE", "CEASE", "SEALS", "LORdS", "HALLS", "COALS", -"ACIdS", "COCOA", "SCARE", "CEASE", "SEALS", "LORdS", "HALLS", "COALS", "ROdEO", -"COCOA", "SCARE", "CEASE", "SEALS", "LORdS", "HALLS", "COALS", "ROdEO", "COdES", -"SCARE", "CEASE", "SEALS", "LORdS", "HALLS", "COALS", "ROdEO", "COdES", "ELdER", -"CEASE", "SEALS", "LORdS", "HALLS", "COALS", "ROdEO", "COdES", "ELdER", "ROLES", -"SEALS", "LORdS", "HALLS", "COALS", "ROdEO", "COdES", "ELdER", "ROLES", "dREAd", -"LORdS", "HALLS", "COALS", "ROdEO", "COdES", "ELdER", "ROLES", "dREAd", "CEdAR", -"HALLS", "COALS", "ROdEO", "COdES", "ELdER", "ROLES", "dREAd", "CEdAR", "SLASH", -"COALS", "ROdEO", "COdES", "ELdER", "ROLES", "dREAd", "CEdAR", "SLASH", "CARES", -"ROdEO", "COdES", "ELdER", "ROLES", "dREAd", "CEdAR", "SLASH", "CARES", "IdOLS", -"COdES", "ELdER", "ROLES", "dREAd", "CEdAR", "SLASH", "CARES", "IdOLS", "OdORS", -"ELdER", "ROLES", "dREAd", "CEdAR", "SLASH", "CARES", "IdOLS", "OdORS", "EASEd", -"ROLES", "dREAd", "CEdAR", "SLASH", "CARES", "IdOLS", "OdORS", "EASEd", "CHOIR", -"dREAd", "CEdAR", "SLASH", "CARES", "IdOLS", "OdORS", "EASEd", "CHOIR", "AIdEd", -"CEdAR", "SLASH", "CARES", "IdOLS", "OdORS", "EASEd", "CHOIR", "AIdEd", "CHAOS", -"SLASH", "CARES", "IdOLS", "OdORS", "EASEd", "CHOIR", "AIdEd", "CHAOS", "LEASE", -"CARES", "IdOLS", "OdORS", "EASEd", "CHOIR", "AIdEd", "CHAOS", "LEASE", "SHEAR", -"IdOLS", "OdORS", "EASEd", "CHOIR", "AIdEd", "CHAOS", "LEASE", "SHEAR", "SLEdS", -"OdORS", "EASEd", "CHOIR", "AIdEd", "CHAOS", "LEASE", "SHEAR", "SLEdS", "COILS", -"EASEd", "CHOIR", "AIdEd", "CHAOS", "LEASE", "SHEAR", "SLEdS", "COILS", "ACHEd", -"CHOIR", "AIdEd", "CHAOS", "LEASE", "SHEAR", "SLEdS", "COILS", "ACHEd", "CELLO", -"AIdEd", "CHAOS", "LEASE", "SHEAR", "SLEdS", "COILS", "ACHEd", "CELLO", "dRIES", -"CHAOS", "LEASE", "SHEAR", "SLEdS", "COILS", "ACHEd", "CELLO", "dRIES", "OASIS", -"LEASE", "SHEAR", "SLEdS", "COILS", "ACHEd", "CELLO", "dRIES", "OASIS", "dRIER", -"SHEAR", "SLEdS", "COILS", "ACHEd", "CELLO", "dRIES", "OASIS", "dRIER", "CACAO", -"SLEdS", "COILS", "ACHEd", "CELLO", "dRIES", "OASIS", "dRIER", "CACAO", "EERIE", -"COILS", "ACHEd", "CELLO", "dRIES", "OASIS", "dRIER", "CACAO", "EERIE", "SCARS", -"ACHEd", "CELLO", "dRIES", "OASIS", "dRIER", "CACAO", "EERIE", "SCARS", "RAIdS", -"CELLO", "dRIES", "OASIS", "dRIER", "CACAO", "EERIE", "SCARS", "RAIdS", "SOLES", -"dRIES", "OASIS", "dRIER", "CACAO", "EERIE", "SCARS", "RAIdS", "SOLES", "CAROL", -"OASIS", "dRIER", "CACAO", "EERIE", "SCARS", "RAIdS", "SOLES", "CAROL", "CHESS", -"dRIER", "CACAO", "EERIE", "SCARS", "RAIdS", "SOLES", "CAROL", "CHESS", "OASES", -"CACAO", "EERIE", "SCARS", "RAIdS", "SOLES", "CAROL", "CHESS", "OASES", "ASSES", -"EERIE", "SCARS", "RAIdS", "SOLES", "CAROL", "CHESS", "OASES", "ASSES", "SHEdS", -"SCARS", "RAIdS", "SOLES", "CAROL", "CHESS", "OASES", "ASSES", "SHEdS", "CLASH", -"RAIdS", "SOLES", "CAROL", "CHESS", "OASES", "ASSES", "SHEdS", "CLASH", "dISCS", -"SOLES", "CAROL", "CHESS", "OASES", "ASSES", "SHEdS", "CLASH", "dISCS", "ERASE", -"CAROL", "CHESS", "OASES", "ASSES", "SHEdS", "CLASH", "dISCS", "ERASE", "CIdER", -"CHESS", "OASES", "ASSES", "SHEdS", "CLASH", "dISCS", "ERASE", "CIdER", "SHALE", -"OASES", "ASSES", "SHEdS", "CLASH", "dISCS", "ERASE", "CIdER", "SHALE", "AISLE", -"ASSES", "SHEdS", "CLASH", "dISCS", "ERASE", "CIdER", "SHALE", "AISLE", "HEIRS", -"SHEdS", "CLASH", "dISCS", "ERASE", "CIdER", "SHALE", "AISLE", "HEIRS", "ROARS", -"CLASH", "dISCS", "ERASE", "CIdER", "SHALE", "AISLE", "HEIRS", "ROARS", "SCOLd", -"dISCS", "ERASE", "CIdER", "SHALE", "AISLE", "HEIRS", "ROARS", "SCOLd", "LEASH", -"ERASE", "CIdER", "SHALE", "AISLE", "HEIRS", "ROARS", "SCOLd", "LEASH", "LASSO", -"CIdER", "SHALE", "AISLE", "HEIRS", "ROARS", "SCOLd", "LEASH", "LASSO", "CHORE", -"SHALE", "AISLE", "HEIRS", "ROARS", "SCOLd", "LEASH", "LASSO", "CHORE", "LACEd", -"AISLE", "HEIRS", "ROARS", "SCOLd", "LEASH", "LASSO", "CHORE", "LACEd", "dOSES", -"HEIRS", "ROARS", "SCOLd", "LEASH", "LASSO", "CHORE", "LACEd", "dOSES", "COLdS", -"ROARS", "SCOLd", "LEASH", "LASSO", "CHORE", "LACEd", "dOSES", "COLdS", "CORES", -"SCOLd", "LEASH", "LASSO", "CHORE", "LACEd", "dOSES", "COLdS", "CORES", "CHILI", -"LEASH", "LASSO", "CHORE", "LACEd", "dOSES", "COLdS", "CORES", "CHILI", "EASEL", -"LASSO", "CHORE", "LACEd", "dOSES", "COLdS", "CORES", "CHILI", "EASEL", "LACES", -"CHORE", "LACEd", "dOSES", "COLdS", "CORES", "CHILI", "EASEL", "LACES", "HORdE", -"LACEd", "dOSES", "COLdS", "CORES", "CHILI", "EASEL", "LACES", "HORdE", "LASER", -"dOSES", "COLdS", "CORES", "CHILI", "EASEL", "LACES", "HORdE", "LASER", "HARES", -"COLdS", "CORES", "CHILI", "EASEL", "LACES", "HORdE", "LASER", "HARES", "CREEd", -"CORES", "CHILI", "EASEL", "LACES", "HORdE", "LASER", "HARES", "CREEd", "LILAC", -"CHILI", "EASEL", "LACES", "HORdE", "LASER", "HARES", "CREEd", "LILAC", "HOOdS", -"EASEL", "LACES", "HORdE", "LASER", "HARES", "CREEd", "LILAC", "HOOdS", "ROACH", -"LACES", "HORdE", "LASER", "HARES", "CREEd", "LILAC", "HOOdS", "ROACH", "dIALS", -"HORdE", "LASER", "HARES", "CREEd", "LILAC", "HOOdS", "ROACH", "dIALS", "SOLOS", -"LASER", "HARES", "CREEd", "LILAC", "HOOdS", "ROACH", "dIALS", "SOLOS", "SISAL", -"HARES", "CREEd", "LILAC", "HOOdS", "ROACH", "dIALS", "SOLOS", "SISAL", "HOSES", -"CREEd", "LILAC", "HOOdS", "ROACH", "dIALS", "SOLOS", "SISAL", "HOSES", "dARES", -"LILAC", "HOOdS", "ROACH", "dIALS", "SOLOS", "SISAL", "HOSES", "dARES", "ACHES", -"HOOdS", "ROACH", "dIALS", "SOLOS", "SISAL", "HOSES", "dARES", "ACHES", "LOSER", -"ROACH", "dIALS", "SOLOS", "SISAL", "HOSES", "dARES", "ACHES", "LOSER", "LAdLE", -"dIALS", "SOLOS", "SISAL", "HOSES", "dARES", "ACHES", "LOSER", "LAdLE", "RAdII", -"SOLOS", "SISAL", "HOSES", "dARES", "ACHES", "LOSER", "LAdLE", "RAdII", "SORES", -"SISAL", "HOSES", "dARES", "ACHES", "LOSER", "LAdLE", "RAdII", "SORES", "RELIC", -"HOSES", "dARES", "ACHES", "LOSER", "LAdLE", "RAdII", "SORES", "RELIC", "AIdES", -"dARES", "ACHES", "LOSER", "LAdLE", "RAdII", "SORES", "RELIC", "AIdES", "ALdER", -"ACHES", "LOSER", "LAdLE", "RAdII", "SORES", "RELIC", "AIdES", "ALdER", "COdEd", -"LOSER", "LAdLE", "RAdII", "SORES", "RELIC", "AIdES", "ALdER", "COdEd", "ISLES", -"LAdLE", "RAdII", "SORES", "RELIC", "AIdES", "ALdER", "COdEd", "ISLES", "CLOdS", -"RAdII", "SORES", "RELIC", "AIdES", "ALdER", "COdEd", "ISLES", "CLOdS", "AdORE", -"SORES", "RELIC", "AIdES", "ALdER", "COdEd", "ISLES", "CLOdS", "AdORE", "RARER", -"RELIC", "AIdES", "ALdER", "COdEd", "ISLES", "CLOdS", "AdORE", "RARER", "SILLS", -"AIdES", "ALdER", "COdEd", "ISLES", "CLOdS", "AdORE", "RARER", "SILLS", "SHOAL", -"ALdER", "COdEd", "ISLES", "CLOdS", "AdORE", "RARER", "SILLS", "SHOAL", "CACHE", -"COdEd", "ISLES", "CLOdS", "AdORE", "RARER", "SILLS", "SHOAL", "CACHE", "REELS", -"ISLES", "CLOdS", "AdORE", "RARER", "SILLS", "SHOAL", "CACHE", "REELS", "LIARS", -"CLOdS", "AdORE", "RARER", "SILLS", "SHOAL", "CACHE", "REELS", "LIARS", "SOARS", -"AdORE", "RARER", "SILLS", "SHOAL", "CACHE", "REELS", "LIARS", "SOARS", "SIdEd", -"RARER", "SILLS", "SHOAL", "CACHE", "REELS", "LIARS", "SOARS", "SIdEd", "HEALS", -"SILLS", "SHOAL", "CACHE", "REELS", "LIARS", "SOARS", "SIdEd", "HEALS", "SOdAS", -"SHOAL", "CACHE", "REELS", "LIARS", "SOARS", "SIdEd", "HEALS", "SOdAS", "ERREd", -"CACHE", "REELS", "LIARS", "SOARS", "SIdEd", "HEALS", "SOdAS", "ERREd", "ARdOR", -"REELS", "LIARS", "SOARS", "SIdEd", "HEALS", "SOdAS", "ERREd", "ARdOR", "HIRES", -"LIARS", "SOARS", "SIdEd", "HEALS", "SOdAS", "ERREd", "ARdOR", "HIRES", "LEECH", -"SOARS", "SIdEd", "HEALS", "SOdAS", "ERREd", "ARdOR", "HIRES", "LEECH", "EROdE", -"SIdEd", "HEALS", "SOdAS", "ERREd", "ARdOR", "HIRES", "LEECH", "EROdE", "HOARd", -"HEALS", "SOdAS", "ERREd", "ARdOR", "HIRES", "LEECH", "EROdE", "HOARd", "COOEd", -"SOdAS", "ERREd", "ARdOR", "HIRES", "LEECH", "EROdE", "HOARd", "COOEd", "SHREd", -"ERREd", "ARdOR", "HIRES", "LEECH", "EROdE", "HOARd", "COOEd", "SHREd", "SLOSH", -"ARdOR", "HIRES", "LEECH", "EROdE", "HOARd", "COOEd", "SHREd", "SLOSH", "CHIdE", -"HIRES", "LEECH", "EROdE", "HOARd", "COOEd", "SHREd", "SLOSH", "CHIdE", "EASES", -"LEECH", "EROdE", "HOARd", "COOEd", "SHREd", "SLOSH", "CHIdE", "EASES", "HALOS", -"EROdE", "HOARd", "COOEd", "SHREd", "SLOSH", "CHIdE", "EASES", "HALOS", "ACRId", -"HOARd", "COOEd", "SHREd", "SLOSH", "CHIdE", "EASES", "HALOS", "ACRId", "EIdER", -"COOEd", "SHREd", "SLOSH", "CHIdE", "EASES", "HALOS", "ACRId", "EIdER", "AddER", -"SHREd", "SLOSH", "CHIdE", "EASES", "HALOS", "ACRId", "EIdER", "AddER", "dEARS", -"SLOSH", "CHIdE", "EASES", "HALOS", "ACRId", "EIdER", "AddER", "dEARS", "SEERS", -"CHIdE", "EASES", "HALOS", "ACRId", "EIdER", "AddER", "dEARS", "SEERS", "OddER", -"EASES", "HALOS", "ACRId", "EIdER", "AddER", "dEARS", "SEERS", "OddER", "SIdLE", -"HALOS", "ACRId", "EIdER", "AddER", "dEARS", "SEERS", "OddER", "SIdLE", "dOLEd", -"ACRId", "EIdER", "AddER", "dEARS", "SEERS", "OddER", "SIdLE", "dOLEd", "HAILS", -"EIdER", "AddER", "dEARS", "SEERS", "OddER", "SIdLE", "dOLEd", "HAILS", "AIREd", -"AddER", "dEARS", "SEERS", "OddER", "SIdLE", "dOLEd", "HAILS", "AIREd", "COCCI", -"dEARS", "SEERS", "OddER", "SIdLE", "dOLEd", "HAILS", "AIREd", "COCCI", "SILOS", -"SEERS", "OddER", "SIdLE", "dOLEd", "HAILS", "AIREd", "COCCI", "SILOS", "dOSEd", -"OddER", "SIdLE", "dOLEd", "HAILS", "AIREd", "COCCI", "SILOS", "dOSEd", "RILLS", -"SIdLE", "dOLEd", "HAILS", "AIREd", "COCCI", "SILOS", "dOSEd", "RILLS", "EARLS", -"dOLEd", "HAILS", "AIREd", "COCCI", "SILOS", "dOSEd", "RILLS", "EARLS", "LAIRS", -"HAILS", "AIREd", "COCCI", "SILOS", "dOSEd", "RILLS", "EARLS", "LAIRS", "IdLER", -"AIREd", "COCCI", "SILOS", "dOSEd", "RILLS", "EARLS", "LAIRS", "IdLER", "SCALd", -"COCCI", "SILOS", "dOSEd", "RILLS", "EARLS", "LAIRS", "IdLER", "SCALd", "AdIOS", -"SILOS", "dOSEd", "RILLS", "EARLS", "LAIRS", "IdLER", "SCALd", "AdIOS", "dALES", -"dOSEd", "RILLS", "EARLS", "LAIRS", "IdLER", "SCALd", "AdIOS", "dALES", "HEEdS", -"RILLS", "EARLS", "LAIRS", "IdLER", "SCALd", "AdIOS", "dALES", "HEEdS", "CEdEd", -"EARLS", "LAIRS", "IdLER", "SCALd", "AdIOS", "dALES", "HEEdS", "CEdEd", "LARCH", -"LAIRS", "IdLER", "SCALd", "AdIOS", "dALES", "HEEdS", "CEdEd", "LARCH", "dOLES", -"IdLER", "SCALd", "AdIOS", "dALES", "HEEdS", "CEdEd", "LARCH", "dOLES", "dROOL", -"SCALd", "AdIOS", "dALES", "HEEdS", "CEdEd", "LARCH", "dOLES", "dROOL", "dELLS", -"AdIOS", "dALES", "HEEdS", "CEdEd", "LARCH", "dOLES", "dROOL", "dELLS", "COCOS", -"dALES", "HEEdS", "CEdEd", "LARCH", "dOLES", "dROOL", "dELLS", "COCOS", "LEERS", -"HEEdS", "CEdEd", "LARCH", "dOLES", "dROOL", "dELLS", "COCOS", "LEERS", "ALIAS", -"CEdEd", "LARCH", "dOLES", "dROOL", "dELLS", "COCOS", "LEERS", "ALIAS", "dICEd", -"LARCH", "dOLES", "dROOL", "dELLS", "COCOS", "LEERS", "ALIAS", "dICEd", "LOdES", -"dOLES", "dROOL", "dELLS", "COCOS", "LEERS", "ALIAS", "dICEd", "LOdES", "IdLEd", -"dROOL", "dELLS", "COCOS", "LEERS", "ALIAS", "dICEd", "LOdES", "IdLEd", "RACER", -"dELLS", "COCOS", "LEERS", "ALIAS", "dICEd", "LOdES", "IdLEd", "RACER", "AILEd", -"COCOS", "LEERS", "ALIAS", "dICEd", "LOdES", "IdLEd", "RACER", "AILEd", "HERES", -"LEERS", "ALIAS", "dICEd", "LOdES", "IdLEd", "RACER", "AILEd", "HERES", "IdLES", -"ALIAS", "dICEd", "LOdES", "IdLEd", "RACER", "AILEd", "HERES", "IdLES", "dOdOS", -"dICEd", "LOdES", "IdLEd", "RACER", "AILEd", "HERES", "IdLES", "dOdOS", "HELLS", -"LOdES", "IdLEd", "RACER", "AILEd", "HERES", "IdLES", "dOdOS", "HELLS", "SOLEd", -"IdLEd", "RACER", "AILEd", "HERES", "IdLES", "dOdOS", "HELLS", "SOLEd", "CASEd", -"RACER", "AILEd", "HERES", "IdLES", "dOdOS", "HELLS", "SOLEd", "CASEd", "COEdS", -"AILEd", "HERES", "IdLES", "dOdOS", "HELLS", "SOLEd", "CASEd", "COEdS", "LORES", -"HERES", "IdLES", "dOdOS", "HELLS", "SOLEd", "CASEd", "COEdS", "LORES", "REARS", -"IdLES", "dOdOS", "HELLS", "SOLEd", "CASEd", "COEdS", "LORES", "REARS", "COLIC", -"dOdOS", "HELLS", "SOLEd", "CASEd", "COEdS", "LORES", "REARS", "COLIC", "SIRES", -"HELLS", "SOLEd", "CASEd", "COEdS", "LORES", "REARS", "COLIC", "SIRES", "SORER", -"SOLEd", "CASEd", "COEdS", "LORES", "REARS", "COLIC", "SIRES", "SORER", "LOLLS", -"CASEd", "COEdS", "LORES", "REARS", "COLIC", "SIRES", "SORER", "LOLLS", "SCAdS", -"COEdS", "LORES", "REARS", "COLIC", "SIRES", "SORER", "LOLLS", "SCAdS", "LEACH", -"LORES", "REARS", "COLIC", "SIRES", "SORER", "LOLLS", "SCAdS", "LEACH", "ARCEd", -"REARS", "COLIC", "SIRES", "SORER", "LOLLS", "SCAdS", "LEACH", "ARCEd", "COREd", -"COLIC", "SIRES", "SORER", "LOLLS", "SCAdS", "LEACH", "ARCEd", "COREd", "dEAdS", -"SIRES", "SORER", "LOLLS", "SCAdS", "LEACH", "ARCEd", "COREd", "dEAdS", "dROLL", -"SORER", "LOLLS", "SCAdS", "LEACH", "ARCEd", "COREd", "dEAdS", "dROLL", "dICES", -"LOLLS", "SCAdS", "LEACH", "ARCEd", "COREd", "dEAdS", "dROLL", "dICES", "CEdES", -"SCAdS", "LEACH", "ARCEd", "COREd", "dEAdS", "dROLL", "dICES", "CEdES", "HOSEd", -"LEACH", "ARCEd", "COREd", "dEAdS", "dROLL", "dICES", "CEdES", "HOSEd", "CRESS", -"ARCEd", "COREd", "dEAdS", "dROLL", "dICES", "CEdES", "HOSEd", "CRESS", "ICIER", -"COREd", "dEAdS", "dROLL", "dICES", "CEdES", "HOSEd", "CRESS", "ICIER", "LARdS", -"dEAdS", "dROLL", "dICES", "CEdES", "HOSEd", "CRESS", "ICIER", "LARdS", "HEROS", -"dROLL", "dICES", "CEdES", "HOSEd", "CRESS", "ICIER", "LARdS", "HEROS", "SHOOS", -"dICES", "CEdES", "HOSEd", "CRESS", "ICIER", "LARdS", "HEROS", "SHOOS", "dECAL", -"CEdES", "HOSEd", "CRESS", "ICIER", "LARdS", "HEROS", "SHOOS", "dECAL", "CILIA", -"HOSEd", "CRESS", "ICIER", "LARdS", "HEROS", "SHOOS", "dECAL", "CILIA", "SEARS", -"CRESS", "ICIER", "LARdS", "HEROS", "SHOOS", "dECAL", "CILIA", "SEARS", "SARIS", -"ICIER", "LARdS", "HEROS", "SHOOS", "dECAL", "CILIA", "SEARS", "SARIS", "dILLS", -"LARdS", "HEROS", "SHOOS", "dECAL", "CILIA", "SEARS", "SARIS", "dILLS", "OILEd", -"HEROS", "SHOOS", "dECAL", "CILIA", "SEARS", "SARIS", "dILLS", "OILEd", "dOERS", -"SHOOS", "dECAL", "CILIA", "SEARS", "SARIS", "dILLS", "OILEd", "dOERS", "ALOHA", -"dECAL", "CILIA", "SEARS", "SARIS", "dILLS", "OILEd", "dOERS", "ALOHA", "RISER", -"CILIA", "SEARS", "SARIS", "dILLS", "OILEd", "dOERS", "ALOHA", "RISER", "CRIER", -"SEARS", "SARIS", "dILLS", "OILEd", "dOERS", "ALOHA", "RISER", "CRIER", "LOESS", -"SARIS", "dILLS", "OILEd", "dOERS", "ALOHA", "RISER", "CRIER", "LOESS", "dECOR", -"dILLS", "OILEd", "dOERS", "ALOHA", "RISER", "CRIER", "LOESS", "dECOR", "SHIEd", -"OILEd", "dOERS", "ALOHA", "RISER", "CRIER", "LOESS", "dECOR", "SHIEd", "dROSS", -"dOERS", "ALOHA", "RISER", "CRIER", "LOESS", "dECOR", "SHIEd", "dROSS", "CREdO", -"ALOHA", "RISER", "CRIER", "LOESS", "dECOR", "SHIEd", "dROSS", "CREdO", "ALOES", -"RISER", "CRIER", "LOESS", "dECOR", "SHIEd", "dROSS", "CREdO", "ALOES", "dIOdE", -"CRIER", "LOESS", "dECOR", "SHIEd", "dROSS", "CREdO", "ALOES", "dIOdE", "COHOS", -"LOESS", "dECOR", "SHIEd", "dROSS", "CREdO", "ALOES", "dIOdE", "COHOS", "SIREE", -"dECOR", "SHIEd", "dROSS", "CREdO", "ALOES", "dIOdE", "COHOS", "SIREE", "OCHRE", -"SHIEd", "dROSS", "CREdO", "ALOES", "dIOdE", "COHOS", "SIREE", "OCHRE", "SIREd", -"dROSS", "CREdO", "ALOES", "dIOdE", "COHOS", "SIREE", "OCHRE", "SIREd", "CAdRE", -"CREdO", "ALOES", "dIOdE", "COHOS", "SIREE", "OCHRE", "SIREd", "CAdRE", "ECHOS", -"ALOES", "dIOdE", "COHOS", "SIREE", "OCHRE", "SIREd", "CAdRE", "ECHOS", "RILEd", -"dIOdE", "COHOS", "SIREE", "OCHRE", "SIREd", "CAdRE", "ECHOS", "RILEd", "CIRCA", -"COHOS", "SIREE", "OCHRE", "SIREd", "CAdRE", "ECHOS", "RILEd", "CIRCA", "HALLO", -"SIREE", "OCHRE", "SIREd", "CAdRE", "ECHOS", "RILEd", "CIRCA", "HALLO", "SHIES", -"OCHRE", "SIREd", "CAdRE", "ECHOS", "RILEd", "CIRCA", "HALLO", "SHIES", "HOLEd", -"SIREd", "CAdRE", "ECHOS", "RILEd", "CIRCA", "HALLO", "SHIES", "HOLEd", "SEdER", -"CAdRE", "ECHOS", "RILEd", "CIRCA", "HALLO", "SHIES", "HOLEd", "SEdER", "REALS", -"ECHOS", "RILEd", "CIRCA", "HALLO", "SHIES", "HOLEd", "SEdER", "REALS", "AERIE", -"RILEd", "CIRCA", "HALLO", "SHIES", "HOLEd", "SEdER", "REALS", "AERIE", "CRASS", -"CIRCA", "HALLO", "SHIES", "HOLEd", "SEdER", "REALS", "AERIE", "CRASS", "dACHA", -"HALLO", "SHIES", "HOLEd", "SEdER", "REALS", "AERIE", "CRASS", "dACHA", "ACHOO", -"SHIES", "HOLEd", "SEdER", "REALS", "AERIE", "CRASS", "dACHA", "ACHOO", "dREAR", -"HOLEd", "SEdER", "REALS", "AERIE", "CRASS", "dACHA", "ACHOO", "dREAR", "ARIAS", -"SEdER", "REALS", "AERIE", "CRASS", "dACHA", "ACHOO", "dREAR", "ARIAS", "HOOCH", -"REALS", "AERIE", "CRASS", "dACHA", "ACHOO", "dREAR", "ARIAS", "HOOCH", "SHILL", -"AERIE", "CRASS", "dACHA", "ACHOO", "dREAR", "ARIAS", "HOOCH", "SHILL", "AHHHH", -"CRASS", "dACHA", "ACHOO", "dREAR", "ARIAS", "HOOCH", "SHILL", "AHHHH", "SHISH", -"dACHA", "ACHOO", "dREAR", "ARIAS", "HOOCH", "SHILL", "AHHHH", "SHISH", "RILES", -"ACHOO", "dREAR", "ARIAS", "HOOCH", "SHILL", "AHHHH", "SHISH", "RILES", "HEERd", -"dREAR", "ARIAS", "HOOCH", "SHILL", "AHHHH", "SHISH", "RILES", "HEERd", "CREEL", -"ARIAS", "HOOCH", "SHILL", "AHHHH", "SHISH", "RILES", "HEERd", "CREEL", "RHEAS", -"HOOCH", "SHILL", "AHHHH", "SHISH", "RILES", "HEERd", "CREEL", "RHEAS", "COdER", -"SHILL", "AHHHH", "SHISH", "RILES", "HEERd", "CREEL", "RHEAS", "COdER", "HALEd", -"AHHHH", "SHISH", "RILES", "HEERd", "CREEL", "RHEAS", "COdER", "HALEd", "CHARd", -"SHISH", "RILES", "HEERd", "CREEL", "RHEAS", "COdER", "HALEd", "CHARd", "SHOEd", -"RILES", "HEERd", "CREEL", "RHEAS", "COdER", "HALEd", "CHARd", "SHOEd", "ESSES", -"HEERd", "CREEL", "RHEAS", "COdER", "HALEd", "CHARd", "SHOEd", "ESSES", "EAREd", -"CREEL", "RHEAS", "COdER", "HALEd", "CHARd", "SHOEd", "ESSES", "EAREd", "ELIdE", -"RHEAS", "COdER", "HALEd", "CHARd", "SHOEd", "ESSES", "EAREd", "ELIdE", "SALSA", -"COdER", "HALEd", "CHARd", "SHOEd", "ESSES", "EAREd", "ELIdE", "SALSA", "AHOLd", -"HALEd", "CHARd", "SHOEd", "ESSES", "EAREd", "ELIdE", "SALSA", "AHOLd", "SHAHS", -"CHARd", "SHOEd", "ESSES", "EAREd", "ELIdE", "SALSA", "AHOLd", "SHAHS", "dISCO", -"SHOEd", "ESSES", "EAREd", "ELIdE", "SALSA", "AHOLd", "SHAHS", "dISCO", "SHARd", -"ESSES", "EAREd", "ELIdE", "SALSA", "AHOLd", "SHAHS", "dISCO", "SHARd", "CORER", -"EAREd", "ELIdE", "SALSA", "AHOLd", "SHAHS", "dISCO", "SHARd", "CORER", "COLAS", -"ELIdE", "SALSA", "AHOLd", "SHAHS", "dISCO", "SHARd", "CORER", "COLAS", "dROId", -"SALSA", "AHOLd", "SHAHS", "dISCO", "SHARd", "CORER", "COLAS", "dROId", "OLdIE", -"AHOLd", "SHAHS", "dISCO", "SHARd", "CORER", "COLAS", "dROId", "OLdIE", "COdAS", -"SHAHS", "dISCO", "SHARd", "CORER", "COLAS", "dROId", "OLdIE", "COdAS", "ORCAS", -"dISCO", "SHARd", "CORER", "COLAS", "dROId", "OLdIE", "COdAS", "ORCAS", "HALES", -"SHARd", "CORER", "COLAS", "dROId", "OLdIE", "COdAS", "ORCAS", "HALES", "REdId", -"CORER", "COLAS", "dROId", "OLdIE", "COdAS", "ORCAS", "HALES", "REdId", "dAdOS", -"COLAS", "dROId", "OLdIE", "COdAS", "ORCAS", "HALES", "REdId", "dAdOS", "CLAdS", -"dROId", "OLdIE", "COdAS", "ORCAS", "HALES", "REdId", "dAdOS", "CLAdS", "OOdLE", -"OLdIE", "COdAS", "ORCAS", "HALES", "REdId", "dAdOS", "CLAdS", "OOdLE", "HIdER", -"COdAS", "ORCAS", "HALES", "REdId", "dAdOS", "CLAdS", "OOdLE", "HIdER", "dELIS", -"ORCAS", "HALES", "REdId", "dAdOS", "CLAdS", "OOdLE", "HIdER", "dELIS", "SCROd", -"HALES", "REdId", "dAdOS", "CLAdS", "OOdLE", "HIdER", "dELIS", "SCROd", "SHIRE", -"REdId", "dAdOS", "CLAdS", "OOdLE", "HIdER", "dELIS", "SCROd", "SHIRE", "AddLE", -"dAdOS", "CLAdS", "OOdLE", "HIdER", "dELIS", "SCROd", "SHIRE", "AddLE", "dEICE", -"CLAdS", "OOdLE", "HIdER", "dELIS", "SCROd", "SHIRE", "AddLE", "dEICE", "ORALS", -"OOdLE", "HIdER", "dELIS", "SCROd", "SHIRE", "AddLE", "dEICE", "ORALS", "HIRER", -"HIdER", "dELIS", "SCROd", "SHIRE", "AddLE", "dEICE", "ORALS", "HIRER", "ROILS", -"dELIS", "SCROd", "SHIRE", "AddLE", "dEICE", "ORALS", "HIRER", "ROILS", "dOLOR", -"SCROd", "SHIRE", "AddLE", "dEICE", "ORALS", "HIRER", "ROILS", "dOLOR", "ILIAC", -"SHIRE", "AddLE", "dEICE", "ORALS", "HIRER", "ROILS", "dOLOR", "ILIAC", "CILLS", -"AddLE", "dEICE", "ORALS", "HIRER", "ROILS", "dOLOR", "ILIAC", "CILLS", "LOCOS", -"dEICE", "ORALS", "HIRER", "ROILS", "dOLOR", "ILIAC", "CILLS", "LOCOS", "HILAR", -"ORALS", "HIRER", "ROILS", "dOLOR", "ILIAC", "CILLS", "LOCOS", "HILAR", "CROCS", -"HIRER", "ROILS", "dOLOR", "ILIAC", "CILLS", "LOCOS", "HILAR", "CROCS", "OHHHH", -"ROILS", "dOLOR", "ILIAC", "CILLS", "LOCOS", "HILAR", "CROCS", "OHHHH", "SECCO", -"dOLOR", "ILIAC", "CILLS", "LOCOS", "HILAR", "CROCS", "OHHHH", "SECCO", "SHAdS", -"ILIAC", "CILLS", "LOCOS", "HILAR", "CROCS", "OHHHH", "SECCO", "SHAdS", "CHOOS", -"CILLS", "LOCOS", "HILAR", "CROCS", "OHHHH", "SECCO", "SHAdS", "CHOOS", "AREAL", -"LOCOS", "HILAR", "CROCS", "OHHHH", "SECCO", "SHAdS", "CHOOS", "AREAL", "dIdOS", -"HILAR", "CROCS", "OHHHH", "SECCO", "SHAdS", "CHOOS", "AREAL", "dIdOS", "COOCH", -"CROCS", "OHHHH", "SECCO", "SHAdS", "CHOOS", "AREAL", "dIdOS", "COOCH", "CHILE", -"OHHHH", "SECCO", "SHAdS", "CHOOS", "AREAL", "dIdOS", "COOCH", "CHILE", "OCHER", -"SECCO", "SHAdS", "CHOOS", "AREAL", "dIdOS", "COOCH", "CHILE", "OCHER", "dOLCE", -"SHAdS", "CHOOS", "AREAL", "dIdOS", "COOCH", "CHILE", "OCHER", "dOLCE", "SLOES", -"CHOOS", "AREAL", "dIdOS", "COOCH", "CHILE", "OCHER", "dOLCE", "SLOES", "LAIRd", -"AREAL", "dIdOS", "COOCH", "CHILE", "OCHER", "dOLCE", "SLOES", "LAIRd", "SHERd", -"dIdOS", "COOCH", "CHILE", "OCHER", "dOLCE", "SLOES", "LAIRd", "SHERd", "CHARS", -"COOCH", "CHILE", "OCHER", "dOLCE", "SLOES", "LAIRd", "SHERd", "CHARS", "HAdES", -"CHILE", "OCHER", "dOLCE", "SLOES", "LAIRd", "SHERd", "CHARS", "HAdES", "HOARS", -"OCHER", "dOLCE", "SLOES", "LAIRd", "SHERd", "CHARS", "HAdES", "HOARS", "dARER", -"dOLCE", "SLOES", "LAIRd", "SHERd", "CHARS", "HAdES", "HOARS", "dARER", "ROIdS", -"SLOES", "LAIRd", "SHERd", "CHARS", "HAdES", "HOARS", "dARER", "ROIdS", "ARRAS", -"LAIRd", "SHERd", "CHARS", "HAdES", "HOARS", "dARER", "ROIdS", "ARRAS", "HALER", -"SHERd", "CHARS", "HAdES", "HOARS", "dARER", "ROIdS", "ARRAS", "HALER", "AIRER", -"CHARS", "HAdES", "HOARS", "dARER", "ROIdS", "ARRAS", "HALER", "AIRER", "CASAS", -"HAdES", "HOARS", "dARER", "ROIdS", "ARRAS", "HALER", "AIRER", "CASAS", "CARER", -"HOARS", "dARER", "ROIdS", "ARRAS", "HALER", "AIRER", "CASAS", "CARER", "OAREd", -"dARER", "ROIdS", "ARRAS", "HALER", "AIRER", "CASAS", "CARER", "OAREd", "RILLE", -"ROIdS", "ARRAS", "HALER", "AIRER", "CASAS", "CARER", "OAREd", "RILLE", "RICEd", -"ARRAS", "HALER", "AIRER", "CASAS", "CARER", "OAREd", "RILLE", "RICEd", "LIRAS", -"HALER", "AIRER", "CASAS", "CARER", "OAREd", "RILLE", "RICEd", "LIRAS", "AIdER", -"AIRER", "CASAS", "CARER", "OAREd", "RILLE", "RICEd", "LIRAS", "AIdER", "ICHOR", -"CASAS", "CARER", "OAREd", "RILLE", "RICEd", "LIRAS", "AIdER", "ICHOR", "LOCHS", -"CARER", "OAREd", "RILLE", "RICEd", "LIRAS", "AIdER", "ICHOR", "LOCHS", "CALLA", -"OAREd", "RILLE", "RICEd", "LIRAS", "AIdER", "ICHOR", "LOCHS", "CALLA", "COCAS", -"RILLE", "RICEd", "LIRAS", "AIdER", "ICHOR", "LOCHS", "CALLA", "COCAS", "OILER", -"RICEd", "LIRAS", "AIdER", "ICHOR", "LOCHS", "CALLA", "COCAS", "OILER", "ASSAI", -"LIRAS", "AIdER", "ICHOR", "LOCHS", "CALLA", "COCAS", "OILER", "ASSAI", "LAdES", -"AIdER", "ICHOR", "LOCHS", "CALLA", "COCAS", "OILER", "ASSAI", "LAdES", "SHIER", -"ICHOR", "LOCHS", "CALLA", "COCAS", "OILER", "ASSAI", "LAdES", "SHIER", "dICER", -"LOCHS", "CALLA", "COCAS", "OILER", "ASSAI", "LAdES", "SHIER", "dICER", "OLEOS", -"CALLA", "COCAS", "OILER", "ASSAI", "LAdES", "SHIER", "dICER", "OLEOS", "SHOER", -"COCAS", "OILER", "ASSAI", "LAdES", "SHIER", "dICER", "OLEOS", "SHOER", "HOdAd", -"OILER", "ASSAI", "LAdES", "SHIER", "dICER", "OLEOS", "SHOER", "HOdAd", "IOdIC", -"ASSAI", "LAdES", "SHIER", "dICER", "OLEOS", "SHOER", "HOdAd", "IOdIC", "LIERS", -"LAdES", "SHIER", "dICER", "OLEOS", "SHOER", "HOdAd", "IOdIC", "LIERS", "AIOLI", -"SHIER", "dICER", "OLEOS", "SHOER", "HOdAd", "IOdIC", "LIERS", "AIOLI", "HOERS", -"dICER", "OLEOS", "SHOER", "HOdAd", "IOdIC", "LIERS", "AIOLI", "HOERS", "SLIER", -"OLEOS", "SHOER", "HOdAd", "IOdIC", "LIERS", "AIOLI", "HOERS", "SLIER", "LASES", -"SHOER", "HOdAd", "IOdIC", "LIERS", "AIOLI", "HOERS", "SLIER", "LASES", "RASAE", -"HOdAd", "IOdIC", "LIERS", "AIOLI", "HOERS", "SLIER", "LASES", "RASAE", "SOCLE", -"IOdIC", "LIERS", "AIOLI", "HOERS", "SLIER", "LASES", "RASAE", "SOCLE", "RICER", -"LIERS", "AIOLI", "HOERS", "SLIER", "LASES", "RASAE", "SOCLE", "RICER", "HAddA", -"AIOLI", "HOERS", "SLIER", "LASES", "RASAE", "SOCLE", "RICER", "HAddA", "RICES", -"HOERS", "SLIER", "LASES", "RASAE", "SOCLE", "RICER", "HAddA", "RICES", "ROOdS", -"SLIER", "LASES", "RASAE", "SOCLE", "RICER", "HAddA", "RICES", "ROOdS", "CEdER", -"LASES", "RASAE", "SOCLE", "RICER", "HAddA", "RICES", "ROOdS", "CEdER", "CEILS", -"RASAE", "SOCLE", "RICER", "HAddA", "RICES", "ROOdS", "CEdER", "CEILS", "LISLE", -"SOCLE", "RICER", "HAddA", "RICES", "ROOdS", "CEdER", "CEILS", "LISLE", "OLIOS", -"RICER", "HAddA", "RICES", "ROOdS", "CEdER", "CEILS", "LISLE", "OLIOS", "dOSER", -"HAddA", "RICES", "ROOdS", "CEdER", "CEILS", "LISLE", "OLIOS", "dOSER", "dILdO", -"RICES", "ROOdS", "CEdER", "CEILS", "LISLE", "OLIOS", "dOSER", "dILdO", "SELAH", -"ROOdS", "CEdER", "CEILS", "LISLE", "OLIOS", "dOSER", "dILdO", "SELAH", "RIALS", -"CEdER", "CEILS", "LISLE", "OLIOS", "dOSER", "dILdO", "SELAH", "RIALS", "ICERS", -"CEILS", "LISLE", "OLIOS", "dOSER", "dILdO", "SELAH", "RIALS", "ICERS", "SHIRR", -"LISLE", "OLIOS", "dOSER", "dILdO", "SELAH", "RIALS", "ICERS", "SHIRR", "LAdEd", -"OLIOS", "dOSER", "dILdO", "SELAH", "RIALS", "ICERS", "SHIRR", "LAdEd", "LASEd", -"dOSER", "dILdO", "SELAH", "RIALS", "ICERS", "SHIRR", "LAdEd", "LASEd", "HOLER", -"SELAH", "RIALS", "ICERS", "SHIRR", "LAdEd", "LASEd", "HOLER", "LACER", "ARSES", -"dILdO", "SELAH", "RIALS", "ICERS", "SHIRR", "LAdEd", "LASEd", "HOLER", "LACER", -"RIALS", "ICERS", "SHIRR", "LAdEd", "LASEd", "HOLER", "LACER", "ARSES", "CIRRI", -"ICERS", "SHIRR", "LAdEd", "LASEd", "HOLER", "LACER", "ARSES", "CIRRI", "dIRER", -"SHIRR", "LAdEd", "LASEd", "HOLER", "LACER", "ARSES", "CIRRI", "dIRER", "ASSEd", -"LAdEd", "LASEd", "HOLER", "LACER", "ARSES", "CIRRI", "dIRER", "ASSEd", "OSIER", + "PLACE", "SINCE", "PAPER", "LINES", "LEARN", "SPACE", "CLOSE", "CLASS", "PIECE", + "COLOR", "ALONE", "PLANE", "SPELL", "CLEAR", "AREAS", "SENSE", "OCEAN", "SCALE", + "CELLS", "SLEEP", "LOCAL", "CLEAN", "PEACE", "CROSS", "CASES", "CROPS", "PLAIN", + "PAIRS", "SCORE", "NOISE", "PIANO", "PLANS", "PRICE", "RAISE", "SCENE", "PRESS", + "APPLE", "CALLS", "POLES", "LOOSE", "OPERA", "INNER", "SOLAR", "RISES", "SALES", + "ACRES", "ERROR", "NAILS", "COINS", "SLOPE", "CANOE", "CANAL", "LIONS", "AROSE", + "RACES", "SPARE", "PIPES", "RAINS", "ROLLS", "SAILS", "ARISE", "ROPES", "CRIES", + "OPENS", "APRON", "SPEAR", "SLICE", "SPOON", "ROSES", "LINEN", "POLAR", "PEARL", + "LOSES", "CORAL", "SPOIL", "PANEL", "SELLS", "COOLS", "SOILS", "POOLS", "RISEN", + "PILES", "SLAIN", "PANIC", "CRISP", "RAILS", "SCOPE", "CONES", "COCOA", "REINS", + "NOSES", "SCARE", "CEASE", "PRIOR", "POLIO", "SEALS", "COALS", "LOANS", "SPINS", + "PAINS", "ONION", "SCRAP", "ROLES", "SNAIL", "LOOPS", "CREEP", "CARES", "ALIEN", + "CRANE", "SLIPS", "SPINE", "LEAPS", "PROSE", "SNARE", "PINES", "SCALP", "LEASE", + "COILS", "ARENA", "PEARS", "CLIPS", "LANES", "RESIN", "SONAR", "CORPS", "NIECE", + "CELLO", "REPEL", "SPICE", "OASIS", "PACES", "COLON", "CACAO", "RINSE", "SPOOL", + "SPILL", "SNAPS", "EERIE", "EARNS", "PERIL", "LINER", "SCARS", "SOLES", "PAILS", + "CAROL", "CANON", "POLLS", "NASAL", "SCORN", "OASES", "ASSES", "PILLS", "SPORE", + "SPIES", "ERASE", "AISLE", "LOINS", "LEANS", "LANCE", "PANES", "PORES", "POISE", + "IRONS", "ROARS", "SCOOP", "LASSO", "PRONE", "NICER", "SNARL", "CORES", "RIPEN", + "PEERS", "EASEL", "LACES", "SOAPS", "SNIPS", "PIERS", "LASER", "LILAC", "SIREN", + "CLASP", "POSSE", "POSES", "SLOOP", "SLAPS", "SOLOS", "SISAL", "SEEPS", "SPANS", + "CLAPS", "ACORN", "LOSER", "LAPSE", "ASPEN", "SNORE", "PROPS", "PESOS", "SONIC", + "SPARS", "SORES", "RELIC", "NOOSE", "NEARS", "CAPES", "CANES", "SPIRE", "ISLES", + "SNEER", "RARER", "NINES", "PECAN", "PENCE", "SILLS", "COPRA", "POPES", "SPREE", + "SCANS", "REELS", "LIARS", "LEPER", "SOARS", "PLEAS", "PALER", "EPICS", "CAPER", + "CONIC", "OPALS", "EASES", "ARSON", "CLANS", "PLIES", "CROON", "PREEN", "SEERS", + "COCCI", "SILOS", "SNIPE", "SANER", "RILLS", "CORNS", "PRIES", "LOONS", "EARLS", + "PEONS", "PALES", "LAIRS", "PEELS", "PEALS", "CRONE", "ENROL", "COOPS", "LAPEL", + "RASPS", "ASPIC", "PENIS", "INANE", "SLOPS", "COCOS", "LEERS", "LOPES", "ALIAS", + "RACER", "COPSE", "PALLS", "COPES", "ICONS", "REAPS", "SNOOP", "LORES", "REARS", + "COLIC", "PLOPS", "SIRES", "CARPS", "LISPS", "PEEPS", "SORER", "LOLLS", "PARES", + "ACNES", "NEONS", "NOONS", "PAPAS", "RIPER", "ELOPE", "CRESS", "NAPES", "ICIER", + "CILIA", "SEARS", "SARIS", "PAPAL", "ROSIN", "CREPE", "RISER", "PACER", "SALON", + "CRIER", "LOESS", "PIPER", "COONS", "SEINE", "IONIC", "SCRIP", "PENAL", "ALOES", + "APACE", "SIREE", "ROPER", "ANION", "LONER", "CIRCA", "CARNE", "ANISE", "SPECS", + "ANNAS", "PICAS", "REALS", "AERIE", "ORLON", "CRASS", "SPIEL", "LAPIS", "ARIAS", + "PAEAN", "SINES", "SCION", "RAPES", "SCARP", "SEPIA", "POSER", "LIENS", "RILES", + "APSES", "NONCE", "ANOLE", "RECAP", "CARON", "PORNO", "CREEL", "CAPOS", "OPINE", + "NISEI", "RERAN", "NARCO", "CLOPS", "ESSES", "SCONE", "SALSA", "ROANS", "RAPER", + "CORER", "COLAS", "CAIRN", "CRAPS", "CLONE", "NOELS", "ORCAS", "PARSE", "EPEES", + "LANAI", "SEPAL", "CAPON", "PREPS", "NARCS", "REPRO", "ORALS", "ROILS", "ILIAC", + "CILLS", "LOCOS", "RENAL", "CROCS", "PARAS", "SECCO", "PIONS", "PARER", "PLEIN", + "AREAL", "SOLON", "PSOAS", "SCOPS", "SLOES", "NOIRE", "POLIS", "PASSE", "NONES", + "SARAN", "POLOS", "APERS", "ARRAS", "PRISE", "SPIER", "AIRER", "APIAN", "CASAS", + "CARER", "POOPS", "SPINA", "PENES", "RILLE", "CANER", "LIRAS", "PRIER", "LOPER", + "CALLA", "PONES", "COCAS", "OILER", "ASSAI", "LAPIN", "ASANA", "OLEOS", "LIERS", + "ANILE", "PLENA", "AIOLI", "SLIER", "CANNA", "PEASE", "LASES", "RASAE", "PAREN", + "SOCLE", "RICER", "RICES", "ELANS", "CEILS", "LISLE", "OLIOS", "APSOS", "RIALS", + "ICERS", "COPER", "PEENS", "POLER", "LACER", "ARSES", "SPOOR", "CIRRI", "APNEA", + "NARES", "OSIER", }; static const uint32_t _num_words = (sizeof(_legal_words) / sizeof(_legal_words[0])); @@ -478,10 +120,13 @@ static uint32_t get_random(uint32_t max) { #endif } -static void display_letter(wordle_state_t *state) { +static void display_letter(wordle_state_t *state, bool display_dash) { char buf[1 + 1]; if (state->word_elements[state->position] >= _num_valid_letters) { - watch_display_string("-", state->position + 5); + 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]]); @@ -492,7 +137,7 @@ static void display_all_letters(wordle_state_t *state) { uint8_t prev_pos = state->position; for (size_t i = 0; i < WORDLE_LENGTH; i++) { state->position = i; - display_letter(state); + display_letter(state, false); } state->position = prev_pos; } @@ -512,8 +157,8 @@ static bool check_word(wordle_state_t *state) { // 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 (state->word_elements_result[j] != WORDLE_LETTER_WRONG) continue; if (_valid_letters[state->word_elements[i]] == _legal_words[state->curr_answer][j]) { printf("me: %c them: %c\r\n", _valid_letters[state->word_elements[i]], _legal_words[state->curr_answer][j]); state->word_elements_result[j] = WORDLE_LETTER_WRONG_LOC; @@ -550,12 +195,18 @@ static void reset_board(wordle_state_t *state) { watch_display_string(" ", 4); display_attempt(state->attempt); display_all_letters(state); + watch_display_string("-", 5); printf("rand: %s\r\n", _legal_words[state->curr_answer]); } static void display_title(wordle_state_t *state) { - char buf[11]; state->curr_screen = SCREEN_TITLE; + watch_display_string("WO WORDLE", 0); +} + +static void display_streak(wordle_state_t *state) { + char buf[12]; + state->curr_screen = SCREEN_STREAK; printf("streak %d \r\n", state->streak); sprintf(buf, "WO St%2ddy", state->streak); watch_display_string(buf, 0); @@ -563,13 +214,14 @@ static void display_title(wordle_state_t *state) { } static void display_lose(wordle_state_t *state, uint8_t subsecond) { - char buf[WORDLE_LENGTH + 5]; + char buf[WORDLE_LENGTH + 6]; sprintf(buf," L %s", subsecond % 2 ? _legal_words[state->curr_answer] : " "); watch_display_string(buf, 0); } static void display_win(wordle_state_t *state, uint8_t subsecond) { - char buf[11]; + (void) state; + char buf[13]; sprintf(buf," W %s ", subsecond % 2 ? "NICE" : "JOb "); watch_display_string(buf, 0); } @@ -625,7 +277,9 @@ static void display_result(wordle_state_t *state, uint8_t subsecond) { } static bool act_on_btn(wordle_state_t *state) { - if (state->curr_screen == SCREEN_RESULT) { + switch (state->curr_screen) + { + case SCREEN_RESULT: for (size_t i = 0; i < WORDLE_LENGTH; i++) { if (state->word_elements_result[i] != WORDLE_LETTER_CORRECT) state->word_elements[i] = _num_valid_letters; @@ -635,14 +289,18 @@ static bool act_on_btn(wordle_state_t *state) { state->position = get_first_pos(state->word_elements_result); state->curr_screen = SCREEN_PLAYING; return true; - } - if (state->curr_screen == SCREEN_TITLE) { + case SCREEN_TITLE: + display_streak(state); + return true; + case SCREEN_STREAK: reset_board(state); return true; - } - if (state->curr_screen >= SCREEN_WIN) { + case SCREEN_WIN: + case SCREEN_LOSE: display_title(state); - return true; + return true; + default: + return false; } return false; } @@ -681,7 +339,7 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi { case SCREEN_PLAYING: if (event.subsecond % 2) { - display_letter(state); + display_letter(state, true); } else { watch_display_string(" ", state->position + 5); } @@ -703,16 +361,16 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi if (act_on_btn(state)) break; if (state->word_elements[state->position] >= _num_valid_letters) state->word_elements[state->position] = 0; else state->word_elements[state->position] = (state->word_elements[state->position] + 1) % _num_valid_letters; - display_letter(state); + display_letter(state, true); break; case EVENT_LIGHT_LONG_PRESS: if (state->word_elements[state->position] >= _num_valid_letters) state->word_elements[state->position] = _num_valid_letters - 1; else state->word_elements[state->position] = (state->word_elements[state->position] + _num_valid_letters - 1) % _num_valid_letters; - display_letter(state); + display_letter(state, true); break; case EVENT_ALARM_BUTTON_UP: if (act_on_btn(state)) break; - display_letter(state); + display_letter(state, true); if (state->word_elements[state->position] == _num_valid_letters) break; state->position = get_next_pos(state->position, state->word_elements_result); if(WORDLE_LENGTH == (state->position)) { @@ -722,7 +380,7 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi state->streak++; break; } - if (state->attempt++ > WORDLE_MAX_ATTEMPTS) { + if (state->attempt++ >= WORDLE_MAX_ATTEMPTS) { state->curr_screen = SCREEN_LOSE; state->streak = 0; break; @@ -732,7 +390,7 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi } break; case EVENT_ALARM_LONG_PRESS: - display_letter(state); + display_letter(state, true); state->position = get_prev_pos(state->position, state->word_elements_result); break; case EVENT_LIGHT_BUTTON_DOWN: diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index 542a01b..b8e5d0f 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -49,6 +49,7 @@ typedef enum { SCREEN_PLAYING = 0, SCREEN_RESULT, SCREEN_TITLE, + SCREEN_STREAK, SCREEN_WIN, SCREEN_LOSE, SCREEN_COUNT diff --git a/utils/wordle_list.py b/utils/wordle_list.py new file mode 100644 index 0000000..30da4bd --- /dev/null +++ b/utils/wordle_list.py @@ -0,0 +1,697 @@ +# From: https://github.com/charlesreid1/five-letter-words/blob/master/sgb-words.txt +words = [ + "WHICH", "THERE", "THEIR", "ABOUT", "WOULD", "THESE", "OTHER", "WORDS", "COULD", + "WRITE", "FIRST", "WATER", "AFTER", "WHERE", "RIGHT", "THINK", "THREE", "YEARS", + "PLACE", "SOUND", "GREAT", "AGAIN", "STILL", "EVERY", "SMALL", "FOUND", "THOSE", + "NEVER", "UNDER", "MIGHT", "WHILE", "HOUSE", "WORLD", "BELOW", "ASKED", "GOING", + "LARGE", "UNTIL", "ALONG", "SHALL", "BEING", "OFTEN", "EARTH", "BEGAN", "SINCE", + "STUDY", "NIGHT", "LIGHT", "ABOVE", "PAPER", "PARTS", "YOUNG", "STORY", "POINT", + "TIMES", "HEARD", "WHOLE", "WHITE", "GIVEN", "MEANS", "MUSIC", "MILES", "THING", + "TODAY", "LATER", "USING", "MONEY", "LINES", "ORDER", "GROUP", "AMONG", "LEARN", + "KNOWN", "SPACE", "TABLE", "EARLY", "TREES", "SHORT", "HANDS", "STATE", "BLACK", + "SHOWN", "STOOD", "FRONT", "VOICE", "KINDS", "MAKES", "COMES", "CLOSE", "POWER", + "LIVED", "VOWEL", "TAKEN", "BUILT", "HEART", "READY", "QUITE", "CLASS", "BRING", + "ROUND", "HORSE", "SHOWS", "PIECE", "GREEN", "STAND", "BIRDS", "START", "RIVER", + "TRIED", "LEAST", "FIELD", "WHOSE", "GIRLS", "LEAVE", "ADDED", "COLOR", "THIRD", + "HOURS", "MOVED", "PLANT", "DOING", "NAMES", "FORMS", "HEAVY", "IDEAS", "CRIED", + "CHECK", "FLOOR", "BEGIN", "WOMAN", "ALONE", "PLANE", "SPELL", "WATCH", "CARRY", + "WROTE", "CLEAR", "NAMED", "BOOKS", "CHILD", "GLASS", "HUMAN", "TAKES", "PARTY", + "BUILD", "SEEMS", "BLOOD", "SIDES", "SEVEN", "MOUTH", "SOLVE", "NORTH", "VALUE", + "DEATH", "MAYBE", "HAPPY", "TELLS", "GIVES", "LOOKS", "SHAPE", "LIVES", "STEPS", + "AREAS", "SENSE", "SPEAK", "FORCE", "OCEAN", "SPEED", "WOMEN", "METAL", "SOUTH", + "GRASS", "SCALE", "CELLS", "LOWER", "SLEEP", "WRONG", "PAGES", "SHIPS", "NEEDS", + "ROCKS", "EIGHT", "MAJOR", "LEVEL", "TOTAL", "AHEAD", "REACH", "STARS", "STORE", + "SIGHT", "TERMS", "CATCH", "WORKS", "BOARD", "COVER", "SONGS", "EQUAL", "STONE", + "WAVES", "GUESS", "DANCE", "SPOKE", "BREAK", "CAUSE", "RADIO", "WEEKS", "LANDS", + "BASIC", "LIKED", "TRADE", "FRESH", "FINAL", "FIGHT", "MEANT", "DRIVE", "SPENT", + "LOCAL", "WAXES", "KNOWS", "TRAIN", "BREAD", "HOMES", "TEETH", "COAST", "THICK", + "BROWN", "CLEAN", "QUIET", "SUGAR", "FACTS", "STEEL", "FORTH", "RULES", "NOTES", + "UNITS", "PEACE", "MONTH", "VERBS", "SEEDS", "HELPS", "SHARP", "VISIT", "WOODS", + "CHIEF", "WALLS", "CROSS", "WINGS", "GROWN", "CASES", "FOODS", "CROPS", "FRUIT", + "STICK", "WANTS", "STAGE", "SHEEP", "NOUNS", "PLAIN", "DRINK", "BONES", "APART", + "TURNS", "MOVES", "TOUCH", "ANGLE", "BASED", "RANGE", "MARKS", "TIRED", "OLDER", + "FARMS", "SPEND", "SHOES", "GOODS", "CHAIR", "TWICE", "CENTS", "EMPTY", "ALIKE", + "STYLE", "BROKE", "PAIRS", "COUNT", "ENJOY", "SCORE", "SHORE", "ROOTS", "PAINT", + "HEADS", "SHOOK", "SERVE", "ANGRY", "CROWD", "WHEEL", "QUICK", "DRESS", "SHARE", + "ALIVE", "NOISE", "SOLID", "CLOTH", "SIGNS", "HILLS", "TYPES", "DRAWN", "WORTH", + "TRUCK", "PIANO", "UPPER", "LOVED", "USUAL", "FACES", "DROVE", "CABIN", "BOATS", + "TOWNS", "PROUD", "COURT", "MODEL", "PRIME", "FIFTY", "PLANS", "YARDS", "PROVE", + "TOOLS", "PRICE", "SHEET", "SMELL", "BOXES", "RAISE", "MATCH", "TRUTH", "ROADS", + "THREW", "ENEMY", "LUNCH", "CHART", "SCENE", "GRAPH", "DOUBT", "GUIDE", "WINDS", + "BLOCK", "GRAIN", "SMOKE", "MIXED", "GAMES", "WAGON", "SWEET", "TOPIC", "EXTRA", + "PLATE", "TITLE", "KNIFE", "FENCE", "FALLS", "CLOUD", "WHEAT", "PLAYS", "ENTER", + "BROAD", "STEAM", "ATOMS", "PRESS", "LYING", "BASIS", "CLOCK", "TASTE", "GROWS", + "THANK", "STORM", "AGREE", "BRAIN", "TRACK", "SMILE", "FUNNY", "BEACH", "STOCK", + "HURRY", "SAVED", "SORRY", "GIANT", "TRAIL", "OFFER", "OUGHT", "ROUGH", "DAILY", + "AVOID", "KEEPS", "THROW", "ALLOW", "CREAM", "LAUGH", "EDGES", "TEACH", "FRAME", + "BELLS", "DREAM", "MAGIC", "OCCUR", "ENDED", "CHORD", "FALSE", "SKILL", "HOLES", + "DOZEN", "BRAVE", "APPLE", "CLIMB", "OUTER", "PITCH", "RULER", "HOLDS", "FIXED", + "COSTS", "CALLS", "BLANK", "STAFF", "LABOR", "EATEN", "YOUTH", "TONES", "HONOR", + "GLOBE", "GASES", "DOORS", "POLES", "LOOSE", "APPLY", "TEARS", "EXACT", "BRUSH", + "CHEST", "LAYER", "WHALE", "MINOR", "FAITH", "TESTS", "JUDGE", "ITEMS", "WORRY", + "WASTE", "HOPED", "STRIP", "BEGUN", "ASIDE", "LAKES", "BOUND", "DEPTH", "CANDY", + "EVENT", "WORSE", "AWARE", "SHELL", "ROOMS", "RANCH", "IMAGE", "SNAKE", "ALOUD", + "DRIED", "LIKES", "MOTOR", "POUND", "KNEES", "REFER", "FULLY", "CHAIN", "SHIRT", + "FLOUR", "DROPS", "SPITE", "ORBIT", "BANKS", "SHOOT", "CURVE", "TRIBE", "TIGHT", + "BLIND", "SLEPT", "SHADE", "CLAIM", "FLIES", "THEME", "QUEEN", "FIFTH", "UNION", + "HENCE", "STRAW", "ENTRY", "ISSUE", "BIRTH", "FEELS", "ANGER", "BRIEF", "RHYME", + "GLORY", "GUARD", "FLOWS", "FLESH", "OWNED", "TRICK", "YOURS", "SIZES", "NOTED", + "WIDTH", "BURST", "ROUTE", "LUNGS", "UNCLE", "BEARS", "ROYAL", "KINGS", "FORTY", + "TRIAL", "CARDS", "BRASS", "OPERA", "CHOSE", "OWNER", "VAPOR", "BEATS", "MOUSE", + "TOUGH", "WIRES", "METER", "TOWER", "FINDS", "INNER", "STUCK", "ARROW", "POEMS", + "LABEL", "SWING", "SOLAR", "TRULY", "TENSE", "BEANS", "SPLIT", "RISES", "WEIGH", + "HOTEL", "STEMS", "PRIDE", "SWUNG", "GRADE", "DIGIT", "BADLY", "BOOTS", "PILOT", + "SALES", "SWEPT", "LUCKY", "PRIZE", "STOVE", "TUBES", "ACRES", "WOUND", "STEEP", + "SLIDE", "TRUNK", "ERROR", "PORCH", "SLAVE", "EXIST", "FACED", "MINES", "MARRY", + "JUICE", "RACED", "WAVED", "GOOSE", "TRUST", "FEWER", "FAVOR", "MILLS", "VIEWS", + "JOINT", "EAGER", "SPOTS", "BLEND", "RINGS", "ADULT", "INDEX", "NAILS", "HORNS", + "BALLS", "FLAME", "RATES", "DRILL", "TRACE", "SKINS", "WAXED", "SEATS", "STUFF", + "RATIO", "MINDS", "DIRTY", "SILLY", "COINS", "HELLO", "TRIPS", "LEADS", "RIFLE", + "HOPES", "BASES", "SHINE", "BENCH", "MORAL", "FIRES", "MEALS", "SHAKE", "SHOPS", + "CYCLE", "MOVIE", "SLOPE", "CANOE", "TEAMS", "FOLKS", "FIRED", "BANDS", "THUMB", + "SHOUT", "CANAL", "HABIT", "REPLY", "RULED", "FEVER", "CRUST", "SHELF", "WALKS", + "MIDST", "CRACK", "PRINT", "TALES", "COACH", "STIFF", "FLOOD", "VERSE", "AWAKE", + "ROCKY", "MARCH", "FAULT", "SWIFT", "FAINT", "CIVIL", "GHOST", "FEAST", "BLADE", + "LIMIT", "GERMS", "READS", "DUCKS", "DAIRY", "WORST", "GIFTS", "LISTS", "STOPS", + "RAPID", "BRICK", "CLAWS", "BEADS", "BEAST", "SKIRT", "CAKES", "LIONS", "FROGS", + "TRIES", "NERVE", "GRAND", "ARMED", "TREAT", "HONEY", "MOIST", "LEGAL", "PENNY", + "CROWN", "SHOCK", "TAXES", "SIXTY", "ALTAR", "PULLS", "SPORT", "DRUMS", "TALKS", + "DYING", "DATES", "DRANK", "BLOWS", "LEVER", "WAGES", "PROOF", "DRUGS", "TANKS", + "SINGS", "TAILS", "PAUSE", "HERDS", "AROSE", "HATED", "CLUES", "NOVEL", "SHAME", + "BURNT", "RACES", "FLASH", "WEARY", "HEELS", "TOKEN", "COATS", "SPARE", "SHINY", + "ALARM", "DIMES", "SIXTH", "CLERK", "MERCY", "SUNNY", "GUEST", "FLOAT", "SHONE", + "PIPES", "WORMS", "BILLS", "SWEAT", "SUITS", "SMART", "UPSET", "RAINS", "SANDY", + "RAINY", "PARKS", "SADLY", "FANCY", "RIDER", "UNITY", "BUNCH", "ROLLS", "CRASH", + "CRAFT", "NEWLY", "GATES", "HATCH", "PATHS", "FUNDS", "WIDER", "GRACE", "GRAVE", + "TIDES", "ADMIT", "SHIFT", "SAILS", "PUPIL", "TIGER", "ANGEL", "CRUEL", "AGENT", + "DRAMA", "URGED", "PATCH", "NESTS", "VITAL", "SWORD", "BLAME", "WEEDS", "SCREW", + "VOCAL", "BACON", "CHALK", "CARGO", "CRAZY", "ACTED", "GOATS", "ARISE", "WITCH", + "LOVES", "QUEER", "DWELL", "BACKS", "ROPES", "SHOTS", "MERRY", "PHONE", "CHEEK", + "PEAKS", "IDEAL", "BEARD", "EAGLE", "CREEK", "CRIES", "ASHES", "STALL", "YIELD", + "MAYOR", "OPENS", "INPUT", "FLEET", "TOOTH", "CUBIC", "WIVES", "BURNS", "POETS", + "APRON", "SPEAR", "ORGAN", "CLIFF", "STAMP", "PASTE", "RURAL", "BAKED", "CHASE", + "SLICE", "SLANT", "KNOCK", "NOISY", "SORTS", "STAYS", "WIPED", "BLOWN", "PILED", + "CLUBS", "CHEER", "WIDOW", "TWIST", "TENTH", "HIDES", "COMMA", "SWEEP", "SPOON", + "STERN", "CREPT", "MAPLE", "DEEDS", "RIDES", "MUDDY", "CRIME", "JELLY", "RIDGE", + "DRIFT", "DUSTY", "DEVIL", "TEMPO", "HUMOR", "SENDS", "STEAL", "TENTS", "WAIST", + "ROSES", "REIGN", "NOBLE", "CHEAP", "DENSE", "LINEN", "GEESE", "WOVEN", "POSTS", + "HIRED", "WRATH", "SALAD", "BOWED", "TIRES", "SHARK", "BELTS", "GRASP", "BLAST", + "POLAR", "FUNGI", "TENDS", "PEARL", "LOADS", "JOKES", "VEINS", "FROST", "HEARS", + "LOSES", "HOSTS", "DIVER", "PHASE", "TOADS", "ALERT", "TASKS", "SEAMS", "CORAL", + "FOCUS", "NAKED", "PUPPY", "JUMPS", "SPOIL", "QUART", "MACRO", "FEARS", "FLUNG", + "SPARK", "VIVID", "BROOK", "STEER", "SPRAY", "DECAY", "PORTS", "SOCKS", "URBAN", + "GOALS", "GRANT", "MINUS", "FILMS", "TUNES", "SHAFT", "FIRMS", "SKIES", "BRIDE", + "WRECK", "FLOCK", "STARE", "HOBBY", "BONDS", "DARED", "FADED", "THIEF", "CRUDE", + "PANTS", "FLUTE", "VOTES", "TONAL", "RADAR", "WELLS", "SKULL", "HAIRS", "ARGUE", + "WEARS", "DOLLS", "VOTED", "CAVES", "CARED", "BROOM", "SCENT", "PANEL", "FAIRY", + "OLIVE", "BENDS", "PRISM", "LAMPS", "CABLE", "PEACH", "RUINS", "RALLY", "SCHWA", + "LAMBS", "SELLS", "COOLS", "DRAFT", "CHARM", "LIMBS", "BRAKE", "GAZED", "CUBES", + "DELAY", "BEAMS", "FETCH", "RANKS", "ARRAY", "HARSH", "CAMEL", "VINES", "PICKS", + "NAVAL", "PURSE", "RIGID", "CRAWL", "TOAST", "SOILS", "SAUCE", "BASIN", "PONDS", + "TWINS", "WRIST", "FLUID", "POOLS", "BRAND", "STALK", "ROBOT", "REEDS", "HOOFS", + "BUSES", "SHEER", "GRIEF", "BLOOM", "DWELT", "MELTS", "RISEN", "FLAGS", "KNELT", + "FIBER", "ROOFS", "FREED", "ARMOR", "PILES", "AIMED", "ALGAE", "TWIGS", "LEMON", + "DITCH", "DRUNK", "RESTS", "CHILL", "SLAIN", "PANIC", "CORDS", "TUNED", "CRISP", + "LEDGE", "DIVED", "SWAMP", "CLUNG", "STOLE", "MOLDS", "YARNS", "LIVER", "GAUGE", + "BREED", "STOOL", "GULLS", "AWOKE", "GROSS", "DIARY", "RAILS", "BELLY", "TREND", + "FLASK", "STAKE", "FRIED", "DRAWS", "ACTOR", "HANDY", "BOWLS", "HASTE", "SCOPE", + "DEALS", "KNOTS", "MOONS", "ESSAY", "THUMP", "HANGS", "BLISS", "DEALT", "GAINS", + "BOMBS", "CLOWN", "PALMS", "CONES", "ROAST", "TIDAL", "BORED", "CHANT", "ACIDS", + "DOUGH", "CAMPS", "SWORE", "LOVER", "HOOKS", "MALES", "COCOA", "PUNCH", "AWARD", + "REINS", "NINTH", "NOSES", "LINKS", "DRAIN", "FILLS", "NYLON", "LUNAR", "PULSE", + "FLOWN", "ELBOW", "FATAL", "SITES", "MOTHS", "MEATS", "FOXES", "MINED", "ATTIC", + "FIERY", "MOUNT", "USAGE", "SWEAR", "SNOWY", "RUSTY", "SCARE", "TRAPS", "RELAX", + "REACT", "VALID", "ROBIN", "CEASE", "GILLS", "PRIOR", "SAFER", "POLIO", "LOYAL", + "SWELL", "SALTY", "MARSH", "VAGUE", "WEAVE", "MOUND", "SEALS", "MULES", "VIRUS", + "SCOUT", "ACUTE", "WINDY", "STOUT", "FOLDS", "SEIZE", "HILLY", "JOINS", "PLUCK", + "STACK", "LORDS", "DUNES", "BURRO", "HAWKS", "TROUT", "FEEDS", "SCARF", "HALLS", + "COALS", "TOWEL", "SOULS", "ELECT", "BUGGY", "PUMPS", "LOANS", "SPINS", "FILES", + "OXIDE", "PAINS", "PHOTO", "RIVAL", "FLATS", "SYRUP", "RODEO", "SANDS", "MOOSE", + "PINTS", "CURLY", "COMIC", "CLOAK", "ONION", "CLAMS", "SCRAP", "DIDST", "COUCH", + "CODES", "FAILS", "OUNCE", "LODGE", "GREET", "GYPSY", "UTTER", "PAVED", "ZONES", + "FOURS", "ALLEY", "TILES", "BLESS", "CREST", "ELDER", "KILLS", "YEAST", "ERECT", + "BUGLE", "MEDAL", "ROLES", "HOUND", "SNAIL", "ALTER", "ANKLE", "RELAY", "LOOPS", + "ZEROS", "BITES", "MODES", "DEBTS", "REALM", "GLOVE", "RAYON", "SWIMS", "POKED", + "STRAY", "LIFTS", "MAKER", "LUMPS", "GRAZE", "DREAD", "BARNS", "DOCKS", "MASTS", + "POURS", "WHARF", "CURSE", "PLUMP", "ROBES", "SEEKS", "CEDAR", "CURLS", "JOLLY", + "MYTHS", "CAGES", "GLOOM", "LOCKS", "PEDAL", "BEETS", "CROWS", "ANODE", "SLASH", + "CREEP", "ROWED", "CHIPS", "FISTS", "WINES", "CARES", "VALVE", "NEWER", "MOTEL", + "IVORY", "NECKS", "CLAMP", "BARGE", "BLUES", "ALIEN", "FROWN", "STRAP", "CREWS", + "SHACK", "GONNA", "SAVES", "STUMP", "FERRY", "IDOLS", "COOKS", "JUICY", "GLARE", + "CARTS", "ALLOY", "BULBS", "LAWNS", "LASTS", "FUELS", "ODDLY", "CRANE", "FILED", + "WEIRD", "SHAWL", "SLIPS", "TROOP", "BOLTS", "SUITE", "SLEEK", "QUILT", "TRAMP", + "BLAZE", "ATLAS", "ODORS", "SCRUB", "CRABS", "PROBE", "LOGIC", "ADOBE", "EXILE", + "REBEL", "GRIND", "STING", "SPINE", "CLING", "DESKS", "GROVE", "LEAPS", "PROSE", + "LOFTY", "AGONY", "SNARE", "TUSKS", "BULLS", "MOODS", "HUMID", "FINER", "DIMLY", + "PLANK", "CHINA", "PINES", "GUILT", "SACKS", "BRACE", "QUOTE", "LATHE", "GAILY", + "FONTS", "SCALP", "ADOPT", "FOGGY", "FERNS", "GRAMS", "CLUMP", "PERCH", "TUMOR", + "TEENS", "CRANK", "FABLE", "HEDGE", "GENES", "SOBER", "BOAST", "TRACT", "CIGAR", + "UNITE", "OWING", "THIGH", "HAIKU", "SWISH", "DIKES", "WEDGE", "BOOTH", "EASED", + "FRAIL", "COUGH", "TOMBS", "DARTS", "FORTS", "CHOIR", "POUCH", "PINCH", "HAIRY", + "BUYER", "TORCH", "VIGOR", "WALTZ", "HEATS", "HERBS", "USERS", "FLINT", "CLICK", + "MADAM", "BLEAK", "BLUNT", "AIDED", "LACKS", "MASKS", "WADED", "RISKS", "NURSE", + "CHAOS", "SEWED", "CURED", "AMPLE", "LEASE", "STEAK", "SINKS", "MERIT", "BLUFF", + "BATHE", "GLEAM", "BONUS", "COLTS", "SHEAR", "GLAND", "SILKY", "SKATE", "BIRCH", + "ANVIL", "SLEDS", "GROAN", "MAIDS", "MEETS", "SPECK", "HYMNS", "HINTS", "DROWN", + "BOSOM", "SLICK", "QUEST", "COILS", "SPIED", "SNOWS", "STEAD", "SNACK", "PLOWS", + "BLOND", "TAMED", "THORN", "WAITS", "GLUED", "BANJO", "TEASE", "ARENA", "BULKY", + "CARVE", "STUNT", "WARMS", "SHADY", "RAZOR", "FOLLY", "LEAFY", "NOTCH", "FOOLS", + "OTTER", "PEARS", "FLUSH", "GENUS", "ACHED", "FIVES", "FLAPS", "SPOUT", "SMOTE", + "FUMES", "ADAPT", "CUFFS", "TASTY", "STOOP", "CLIPS", "DISKS", "SNIFF", "LANES", + "BRISK", "IMPLY", "DEMON", "SUPER", "FURRY", "RAGED", "GROWL", "TEXTS", "HARDY", + "STUNG", "TYPED", "HATES", "WISER", "TIMID", "SERUM", "BEAKS", "ROTOR", "CASTS", + "BATHS", "GLIDE", "PLOTS", "TRAIT", "RESIN", "SLUMS", "LYRIC", "PUFFS", "DECKS", + "BROOD", "MOURN", "ALOFT", "ABUSE", "WHIRL", "EDGED", "OVARY", "QUACK", "HEAPS", + "SLANG", "AWAIT", "CIVIC", "SAINT", "BEVEL", "SONAR", "AUNTS", "PACKS", "FROZE", + "TONIC", "CORPS", "SWARM", "FRANK", "REPAY", "GAUNT", "WIRED", "NIECE", "CELLO", + "NEEDY", "CHUCK", "STONY", "MEDIA", "SURGE", "HURTS", "REPEL", "HUSKY", "DATED", + "HUNTS", "MISTS", "EXERT", "DRIES", "MATES", "SWORN", "BAKER", "SPICE", "OASIS", + "BOILS", "SPURS", "DOVES", "SNEAK", "PACES", "COLON", "SIEGE", "STRUM", "DRIER", + "CACAO", "HUMUS", "BALES", "PIPED", "NASTY", "RINSE", "BOXER", "SHRUB", "AMUSE", + "TACKS", "CITED", "SLUNG", "DELTA", "LADEN", "LARVA", "RENTS", "YELLS", "SPOOL", + "SPILL", "CRUSH", "JEWEL", "SNAPS", "STAIN", "KICKS", "TYING", "SLITS", "RATED", + "EERIE", "SMASH", "PLUMS", "ZEBRA", "EARNS", "BUSHY", "SCARY", "SQUAD", "TUTOR", + "SILKS", "SLABS", "BUMPS", "EVILS", "FANGS", "SNOUT", "PERIL", "PIVOT", "YACHT", + "LOBBY", "JEANS", "GRINS", "VIOLA", "LINER", "COMET", "SCARS", "CHOPS", "RAIDS", + "EATER", "SLATE", "SKIPS", "SOLES", "MISTY", "URINE", "KNOBS", "SLEET", "HOLLY", + "PESTS", "FORKS", "GRILL", "TRAYS", "PAILS", "BORNE", "TENOR", "WARES", "CAROL", + "WOODY", "CANON", "WAKES", "KITTY", "MINER", "POLLS", "SHAKY", "NASAL", "SCORN", + "CHESS", "TAXIS", "CRATE", "SHYLY", "TULIP", "FORGE", "NYMPH", "BUDGE", "LOWLY", + "ABIDE", "DEPOT", "OASES", "ASSES", "SHEDS", "FUDGE", "PILLS", "RIVET", "THINE", + "GROOM", "LANKY", "BOOST", "BROTH", "HEAVE", "GRAVY", "BEECH", "TIMED", "QUAIL", + "INERT", "GEARS", "CHICK", "HINGE", "TRASH", "CLASH", "SIGHS", "RENEW", "BOUGH", + "DWARF", "SLOWS", "QUILL", "SHAVE", "SPORE", "SIXES", "CHUNK", "MADLY", "PACED", + "BRAID", "FUZZY", "MOTTO", "SPIES", "SLACK", "MUCUS", "MAGMA", "AWFUL", "DISCS", + "ERASE", "POSED", "ASSET", "CIDER", "TAPER", "THEFT", "CHURN", "SATIN", "SLOTS", + "TAXED", "BULLY", "SLOTH", "SHALE", "TREAD", "RAKED", "CURDS", "MANOR", "AISLE", + "BULGE", "LOINS", "STAIR", "TAPES", "LEANS", "BUNKS", "SQUAT", "TOWED", "LANCE", + "PANES", "SAKES", "HEIRS", "CASTE", "DUMMY", "PORES", "FAUNA", "CROOK", "POISE", + "EPOCH", "RISKY", "WARNS", "FLING", "BERRY", "GRAPE", "FLANK", "DRAGS", "SQUID", + "PELTS", "ICING", "IRONY", "IRONS", "BARKS", "WHOOP", "CHOKE", "DIETS", "WHIPS", + "TALLY", "DOZED", "TWINE", "KITES", "BIKES", "TICKS", "RIOTS", "ROARS", "VAULT", + "LOOMS", "SCOLD", "BLINK", "DANDY", "PUPAE", "SIEVE", "SPIKE", "DUCTS", "LENDS", + "PIZZA", "BRINK", "WIDEN", "PLUMB", "PAGAN", "FEATS", "BISON", "SOGGY", "SCOOP", + "ARGON", "NUDGE", "SKIFF", "AMBER", "SEXES", "ROUSE", "SALTS", "HITCH", "EXALT", + "LEASH", "DINED", "CHUTE", "SNORT", "GUSTS", "MELON", "CHEAT", "REEFS", "LLAMA", + "LASSO", "DEBUT", "QUOTA", "OATHS", "PRONE", "MIXES", "RAFTS", "DIVES", "STALE", + "INLET", "FLICK", "PINTO", "BROWS", "UNTIE", "BATCH", "GREED", "CHORE", "STIRS", + "BLUSH", "ONSET", "BARBS", "VOLTS", "BEIGE", "SWOOP", "PADDY", "LACED", "SHOVE", + "JERKY", "POPPY", "LEAKS", "FARES", "DODGE", "GODLY", "SQUAW", "AFFIX", "BRUTE", + "NICER", "UNDUE", "SNARL", "MERGE", "DOSES", "SHOWY", "DADDY", "ROOST", "VASES", + "SWIRL", "PETTY", "COLDS", "CURRY", "COBRA", "GENIE", "FLARE", "MESSY", "CORES", + "SOAKS", "RIPEN", "WHINE", "AMINO", "PLAID", "SPINY", "MOWED", "BATON", "PEERS", + "VOWED", "PIOUS", "SWANS", "EXITS", "AFOOT", "PLUGS", "IDIOM", "CHILI", "RITES", + "SERFS", "CLEFT", "BERTH", "GRUBS", "ANNEX", "DIZZY", "HASTY", "LATCH", "WASPS", + "MIRTH", "BARON", "PLEAD", "ALOOF", "AGING", "PIXEL", "BARED", "MUMMY", "HOTLY", + "AUGER", "BUDDY", "CHAPS", "BADGE", "STARK", "FAIRS", "GULLY", "MUMPS", "EMERY", + "FILLY", "OVENS", "DRONE", "GAUZE", "IDIOT", "FUSSY", "ANNOY", "SHANK", "GOUGE", + "BLEED", "ELVES", "ROPED", "UNFIT", "BAGGY", "MOWER", "SCANT", "GRABS", "FLEAS", + "LOUSY", "ALBUM", "SAWED", "COOKY", "MURKY", "INFER", "BURLY", "WAGED", "DINGY", + "BRINE", "KNEEL", "CREAK", "VANES", "SMOKY", "SPURT", "COMBS", "EASEL", "LACES", + "HUMPS", "RUMOR", "AROMA", "HORDE", "SWISS", "LEAPT", "OPIUM", "SLIME", "AFIRE", + "PANSY", "MARES", "SOAPS", "HUSKS", "SNIPS", "HAZEL", "LINED", "CAFES", "NAIVE", + "WRAPS", "SIZED", "PIERS", "BESET", "AGILE", "TONGS", "STEED", "FRAUD", "BOOTY", + "VALOR", "DOWNY", "WITTY", "MOSSY", "PSALM", "SCUBA", "TOURS", "POLKA", "MILKY", + "GAUDY", "SHRUG", "TUFTS", "WILDS", "LASER", "TRUSS", "HARES", "CREED", "LILAC", + "SIREN", "TARRY", "BRIBE", "SWINE", "MUTED", "FLIPS", "CURES", "SINEW", "BOXED", + "HOOPS", "GASPS", "HOODS", "NICHE", "YUCCA", "GLOWS", "SEWER", "WHACK", "FUSES", + "GOWNS", "DROOP", "BUCKS", "PANGS", "MAILS", "WHISK", "HAVEN", "CLASP", "SLING", + "STINT", "URGES", "CHAMP", "PIETY", "CHIRP", "PLEAT", "POSSE", "SUNUP", "MENUS", + "HOWLS", "QUAKE", "KNACK", "PLAZA", "FIEND", "CAKED", "BANGS", "ERUPT", "POKER", + "OLDEN", "CRAMP", "VOTER", "POSES", "MANLY", "SLUMP", "FINED", "GRIPS", "GAPED", + "PURGE", "HIKED", "MAIZE", "FLUFF", "STRUT", "SLOOP", "PROWL", "ROACH", "COCKS", + "BLAND", "DIALS", "PLUME", "SLAPS", "SOUPS", "DULLY", "WILLS", "FOAMS", "SOLOS", + "SKIER", "EAVES", "TOTEM", "FUSED", "LATEX", "VEILS", "MUSED", "MAINS", "MYRRH", + "RACKS", "GALLS", "GNATS", "BOUTS", "SISAL", "SHUTS", "HOSES", "DRYLY", "HOVER", + "GLOSS", "SEEPS", "DENIM", "PUTTY", "GUPPY", "LEAKY", "DUSKY", "FILTH", "OBOES", + "SPANS", "FOWLS", "ADORN", "GLAZE", "HAUNT", "DARES", "OBEYS", "BAKES", "ABYSS", + "SMELT", "GANGS", "ACHES", "TRAWL", "CLAPS", "UNDID", "SPICY", "HOIST", "FADES", + "VICAR", "ACORN", "PUSSY", "GRUFF", "MUSTY", "TARTS", "SNUFF", "HUNCH", "TRUCE", + "TWEED", "DRYER", "LOSER", "SHEAF", "MOLES", "LAPSE", "TAWNY", "VEXED", "AUTOS", + "WAGER", "DOMES", "SHEEN", "CLANG", "SPADE", "SOWED", "BROIL", "SLYLY", "STUDS", + "GRUNT", "DONOR", "SLUGS", "ASPEN", "HOMER", "CROAK", "TITHE", "HALTS", "AVERT", + "HAVOC", "HOGAN", "GLINT", "RUDDY", "JEEPS", "FLAKY", "LADLE", "TAUNT", "SNORE", + "FINES", "PROPS", "PRUNE", "PESOS", "RADII", "POKES", "TILED", "DAISY", "HERON", + "VILLA", "FARCE", "BINDS", "CITES", "FIXES", "JERKS", "LIVID", "WAKED", "INKED", + "BOOMS", "CHEWS", "LICKS", "HYENA", "SCOFF", "LUSTY", "SONIC", "SMITH", "USHER", + "TUCKS", "VIGIL", "MOLTS", "SECTS", "SPARS", "DUMPS", "SCALY", "WISPS", "SORES", + "MINCE", "PANDA", "FLIER", "AXLES", "PLIED", "BOOBY", "PATIO", "RABBI", "PETAL", + "POLYP", "TINTS", "GRATE", "TROLL", "TOLLS", "RELIC", "PHONY", "BLEAT", "FLAWS", + "FLAKE", "SNAGS", "APTLY", "DRAWL", "ULCER", "SOAPY", "BOSSY", "MONKS", "CRAGS", + "CAGED", "TWANG", "DINER", "TAPED", "CADET", "GRIDS", "SPAWN", "GUILE", "NOOSE", + "MORES", "GIRTH", "SLIMY", "AIDES", "SPASM", "BURRS", "ALIBI", "LYMPH", "SAUCY", + "MUGGY", "LITER", "JOKED", "GOOFY", "EXAMS", "ENACT", "STORK", "LURED", "TOXIC", + "OMENS", "NEARS", "COVET", "WRUNG", "FORUM", "VENOM", "MOODY", "ALDER", "SASSY", + "FLAIR", "GUILD", "PRAYS", "WRENS", "HAULS", "STAVE", "TILTS", "PECKS", "STOMP", + "GALES", "TEMPT", "CAPES", "MESAS", "OMITS", "TEPEE", "HARRY", "WRING", "EVOKE", + "LIMES", "CLUCK", "LUNGE", "HIGHS", "CANES", "GIDDY", "LITHE", "VERGE", "KHAKI", + "QUEUE", "LOATH", "FOYER", "OUTDO", "FARED", "DETER", "CRUMB", "ASTIR", "SPIRE", + "JUMPY", "EXTOL", "BUOYS", "STUBS", "LUCID", "THONG", "AFORE", "WHIFF", "MAXIM", + "HULLS", "CLOGS", "SLATS", "JIFFY", "ARBOR", "CINCH", "IGLOO", "GOODY", "GAZES", + "DOWEL", "CALMS", "BITCH", "SCOWL", "GULPS", "CODED", "WAVER", "MASON", "LOBES", + "EBONY", "FLAIL", "ISLES", "CLODS", "DAZED", "ADEPT", "OOZED", "SEDAN", "CLAYS", + "WARTS", "KETCH", "SKUNK", "MANES", "ADORE", "SNEER", "MANGO", "FIORD", "FLORA", + "ROOMY", "MINKS", "THAWS", "WATTS", "FREER", "EXULT", "PLUSH", "PALED", "TWAIN", + "CLINK", "SCAMP", "PAWED", "GROPE", "BRAVO", "GABLE", "STINK", "SEVER", "WANED", + "RARER", "REGAL", "WARDS", "FAWNS", "BABES", "UNIFY", "AMEND", "OAKEN", "GLADE", + "VISOR", "HEFTY", "NINES", "THROB", "PECAN", "BUTTS", "PENCE", "SILLS", "JAILS", + "FLYER", "SABER", "NOMAD", "MITER", "BEEPS", "DOMED", "GULFS", "CURBS", "HEATH", + "MOORS", "AORTA", "LARKS", "TANGY", "WRYLY", "CHEEP", "RAGES", "EVADE", "LURES", + "FREAK", "VOGUE", "TUNIC", "SLAMS", "KNITS", "DUMPY", "MANIA", "SPITS", "FIRTH", + "HIKES", "TROTS", "NOSED", "CLANK", "DOGMA", "BLOAT", "BALSA", "GRAFT", "MIDDY", + "STILE", "KEYED", "FINCH", "SPERM", "CHAFF", "WILES", "AMIGO", "COPRA", "AMISS", + "EYING", "TWIRL", "LURCH", "POPES", "CHINS", "SMOCK", "TINES", "GUISE", "GRITS", + "JUNKS", "SHOAL", "CACHE", "TAPIR", "ATOLL", "DEITY", "TOILS", "SPREE", "MOCKS", + "SCANS", "SHORN", "REVEL", "RAVEN", "HOARY", "REELS", "SCUFF", "MIMIC", "WEEDY", + "CORNY", "TRUER", "ROUGE", "EMBER", "FLOES", "TORSO", "WIPES", "EDICT", "SULKY", + "RECUR", "GROIN", "BASTE", "KINKS", "SURER", "PIGGY", "MOLDY", "FRANC", "LIARS", + "INEPT", "GUSTY", "FACET", "JETTY", "EQUIP", "LEPER", "SLINK", "SOARS", "CATER", + "DOWRY", "SIDED", "YEARN", "DECOY", "TABOO", "OVALS", "HEALS", "PLEAS", "BERET", + "SPILT", "GAYLY", "ROVER", "ENDOW", "PYGMY", "CARAT", "ABBEY", "VENTS", "WAKEN", + "CHIMP", "FUMED", "SODAS", "VINYL", "CLOUT", "WADES", "MITES", "SMIRK", "BORES", + "BUNNY", "SURLY", "FROCK", "FORAY", "PURER", "MILKS", "QUERY", "MIRED", "BLARE", + "FROTH", "GRUEL", "NAVEL", "PALER", "PUFFY", "CASKS", "GRIME", "DERBY", "MAMMA", + "GAVEL", "TEDDY", "VOMIT", "MOANS", "ALLOT", "DEFER", "WIELD", "VIPER", "LOUSE", + "ERRED", "HEWED", "ABHOR", "WREST", "WAXEN", "ADAGE", "ARDOR", "STABS", "PORED", + "RONDO", "LOPED", "FISHY", "BIBLE", "HIRES", "FOALS", "FEUDS", "JAMBS", "THUDS", + "JEERS", "KNEAD", "QUIRK", "RUGBY", "EXPEL", "GREYS", "RIGOR", "ESTER", "LYRES", + "ABACK", "GLUES", "LOTUS", "LURID", "RUNGS", "HUTCH", "THYME", "VALET", "TOMMY", + "YOKES", "EPICS", "TRILL", "PIKES", "OZONE", "CAPER", "CHIME", "FREES", "FAMED", + "LEECH", "SMITE", "NEIGH", "ERODE", "ROBED", "HOARD", "SALVE", "CONIC", "GAWKY", + "CRAZE", "JACKS", "GLOAT", "MUSHY", "RUMPS", "FETUS", "WINCE", "PINKS", "SHALT", + "TOOTS", "GLENS", "COOED", "RUSTS", "STEWS", "SHRED", "PARKA", "CHUGS", "WINKS", + "CLOTS", "SHREW", "BOOED", "FILMY", "JUROR", "DENTS", "GUMMY", "GRAYS", "HOOKY", + "BUTTE", "DOGIE", "POLED", "REAMS", "FIFES", "SPANK", "GAYER", "TEPID", "SPOOK", + "TAINT", "FLIRT", "ROGUE", "SPIKY", "OPALS", "MISER", "COCKY", "COYLY", "BALMY", + "SLOSH", "BRAWL", "APHID", "FAKED", "HYDRA", "BRAGS", "CHIDE", "YANKS", "ALLAY", + "VIDEO", "ALTOS", "EASES", "METED", "CHASM", "LONGS", "EXCEL", "TAFFY", "IMPEL", + "SAVOR", "KOALA", "QUAYS", "DAWNS", "PROXY", "CLOVE", "DUETS", "DREGS", "TARDY", + "BRIAR", "GRIMY", "ULTRA", "MEATY", "HALVE", "WAILS", "SUEDE", "MAUVE", "ENVOY", + "ARSON", "COVES", "GOOEY", "BREWS", "SOFAS", "CHUMS", "AMAZE", "ZOOMS", "ABBOT", + "HALOS", "SCOUR", "SUING", "CRIBS", "SAGAS", "ENEMA", "WORDY", "HARPS", "COUPE", + "MOLAR", "FLOPS", "WEEPS", "MINTS", "ASHEN", "FELTS", "ASKEW", "MUNCH", "MEWED", + "DIVAN", "VICES", "JUMBO", "BLOBS", "BLOTS", "SPUNK", "ACRID", "TOPAZ", "CUBED", + "CLANS", "FLEES", "SLURS", "GNAWS", "WELDS", "FORDS", "EMITS", "AGATE", "PUMAS", + "MENDS", "DARKS", "DUKES", "PLIES", "CANNY", "HOOTS", "OOZES", "LAMED", "FOULS", + "CLEFS", "NICKS", "MATED", "SKIMS", "BRUNT", "TUBER", "TINGE", "FATES", "DITTY", + "THINS", "FRETS", "EIDER", "BAYOU", "MULCH", "FASTS", "AMASS", "DAMPS", "MORNS", + "FRIAR", "PALSY", "VISTA", "CROON", "CONCH", "UDDER", "TACOS", "SKITS", "MIKES", + "QUITS", "PREEN", "ASTER", "ADDER", "ELEGY", "PULPY", "SCOWS", "BALED", "HOVEL", + "LAVAS", "CRAVE", "OPTIC", "WELTS", "BUSTS", "KNAVE", "RAZED", "SHINS", "TOTES", + "SCOOT", "DEARS", "CROCK", "MUTES", "TRIMS", "SKEIN", "DOTED", "SHUNS", "VEERS", + "FAKES", "YOKED", "WOOED", "HACKS", "SPRIG", "WANDS", "LULLS", "SEERS", "SNOBS", + "NOOKS", "PINED", "PERKY", "MOOED", "FRILL", "DINES", "BOOZE", "TRIPE", "PRONG", + "DRIPS", "ODDER", "LEVEE", "ANTIC", "SIDLE", "PITHY", "CORKS", "YELPS", "JOKER", + "FLECK", "BUFFS", "SCRAM", "TIERS", "BOGEY", "DOLED", "IRATE", "VALES", "COPED", + "HAILS", "ELUDE", "BULKS", "AIRED", "VYING", "STAGS", "STREW", "COCCI", "PACTS", + "SCABS", "SILOS", "DUSTS", "YODEL", "TERSE", "JADED", "BASER", "JIBES", "FOILS", + "SWAYS", "FORGO", "SLAYS", "PREYS", "TREKS", "QUELL", "PEEKS", "ASSAY", "LURKS", + "EJECT", "BOARS", "TRITE", "BELCH", "GNASH", "WANES", "LUTES", "WHIMS", "DOSED", + "CHEWY", "SNIPE", "UMBRA", "TEEMS", "DOZES", "KELPS", "UPPED", "BRAWN", "DOPED", + "SHUSH", "RINDS", "SLUSH", "MORON", "VOILE", "WOKEN", "FJORD", "SHEIK", "JESTS", + "KAYAK", "SLEWS", "TOTED", "SANER", "DRAPE", "PATTY", "RAVES", "SULFA", "GRIST", + "SKIED", "VIXEN", "CIVET", "VOUCH", "TIARA", "HOMEY", "MOPED", "RUNTS", "SERGE", + "KINKY", "RILLS", "CORNS", "BRATS", "PRIES", "AMBLE", "FRIES", "LOONS", "TSARS", + "DATUM", "MUSKY", "PIGMY", "GNOME", "RAVEL", "OVULE", "ICILY", "LIKEN", "LEMUR", + "FRAYS", "SILTS", "SIFTS", "PLODS", "RAMPS", "TRESS", "EARLS", "DUDES", "WAIVE", + "KARAT", "JOLTS", "PEONS", "BEERS", "HORNY", "PALES", "WREAK", "LAIRS", "LYNCH", + "STANK", "SWOON", "IDLER", "ABORT", "BLITZ", "ENSUE", "ATONE", "BINGO", "ROVES", + "KILTS", "SCALD", "ADIOS", "CYNIC", "DULLS", "MEMOS", "ELFIN", "DALES", "PEELS", + "PEALS", "BARES", "SINUS", "CRONE", "SABLE", "HINDS", "SHIRK", "ENROL", "WILTS", + "ROAMS", "DUPED", "CYSTS", "MITTS", "SAFES", "SPATS", "COOPS", "FILET", "KNELL", + "REFIT", "COVEY", "PUNKS", "KILNS", "FITLY", "ABATE", "TALCS", "HEEDS", "DUELS", + "WANLY", "RUFFS", "GAUSS", "LAPEL", "JAUNT", "WHELP", "CLEAT", "GAUZY", "DIRGE", + "EDITS", "WORMY", "MOATS", "SMEAR", "PRODS", "BOWEL", "FRISK", "VESTS", "BAYED", + "RASPS", "TAMES", "DELVE", "EMBED", "BEFIT", "WAFER", "CEDED", "NOVAS", "FEIGN", + "SPEWS", "LARCH", "HUFFS", "DOLES", "MAMAS", "HULKS", "PRIED", "BRIMS", "IRKED", + "ASPIC", "SWIPE", "MEALY", "SKIMP", "BLUER", "SLAKE", "DOWDY", "PENIS", "BRAYS", + "PUPAS", "EGRET", "FLUNK", "PHLOX", "GRIPE", "PEONY", "DOUSE", "BLURS", "DARNS", + "SLUNK", "LEFTS", "CHATS", "INANE", "VIALS", "STILT", "RINKS", "WOOFS", "WOWED", + "BONGS", "FROND", "INGOT", "EVICT", "SINGE", "SHYER", "FLIED", "SLOPS", "DOLTS", + "DROOL", "DELLS", "WHELK", "HIPPY", "FETED", "ETHER", "COCOS", "HIVES", "JIBED", + "MAZES", "TRIOS", "SIRUP", "SQUAB", "LATHS", "LEERS", "PASTA", "RIFTS", "LOPES", + "ALIAS", "WHIRS", "DICED", "SLAGS", "LODES", "FOXED", "IDLED", "PROWS", "PLAIT", + "MALTS", "CHAFE", "COWER", "TOYED", "CHEFS", "KEELS", "STIES", "RACER", "ETUDE", + "SUCKS", "SULKS", "MICAS", "CZARS", "COPSE", "AILED", "ABLER", "RABID", "GOLDS", + "CROUP", "SNAKY", "VISAS", "PALLS", "MOPES", "BONED", "WISPY", "RAVED", "SWAPS", + "JUNKY", "DOILY", "PAWNS", "TAMER", "POACH", "BAITS", "DAMNS", "GUMBO", "DAUNT", + "PRANK", "HUNKS", "BUXOM", "HERES", "HONKS", "STOWS", "UNBAR", "IDLES", "ROUTS", + "SAGES", "GOADS", "REMIT", "COPES", "DEIGN", "CULLS", "GIRDS", "HAVES", "LUCKS", + "STUNK", "DODOS", "SHAMS", "SNUBS", "ICONS", "USURP", "DOOMS", "HELLS", "SOLED", + "COMAS", "PAVES", "MATHS", "PERKS", "LIMPS", "WOMBS", "BLURB", "DAUBS", "COKES", + "SOURS", "STUNS", "CASED", "MUSTS", "COEDS", "COWED", "APING", "ZONED", "RUMMY", + "FETES", "SKULK", "QUAFF", "RAJAH", "DEANS", "REAPS", "GALAS", "TILLS", "ROVED", + "KUDOS", "TONED", "PARED", "SCULL", "VEXES", "PUNTS", "SNOOP", "BAILS", "DAMES", + "HAZES", "LORES", "MARTS", "VOIDS", "AMEBA", "RAKES", "ADZES", "HARMS", "REARS", + "SATYR", "SWILL", "HEXES", "COLIC", "LEEKS", "HURLS", "YOWLS", "IVIES", "PLOPS", + "MUSKS", "PAPAW", "JELLS", "BUSED", "CRUET", "BIDED", "FILCH", "ZESTS", "ROOKS", + "LAXLY", "RENDS", "LOAMS", "BASKS", "SIRES", "CARPS", "POKEY", "FLITS", "MUSES", + "BAWLS", "SHUCK", "VILER", "LISPS", "PEEPS", "SORER", "LOLLS", "PRUDE", "DIKED", + "FLOSS", "FLOGS", "SCUMS", "DOPES", "BOGIE", "PINKY", "LEAFS", "TUBAS", "SCADS", + "LOWED", "YESES", "BIKED", "QUALM", "EVENS", "CANED", "GAWKS", "WHITS", "WOOLY", + "GLUTS", "ROMPS", "BESTS", "DUNCE", "CRONY", "JOIST", "TUNAS", "BONER", "MALLS", + "PARCH", "AVERS", "CRAMS", "PARES", "DALLY", "BIGOT", "KALES", "FLAYS", "LEACH", + "GUSHY", "POOCH", "HUGER", "SLYER", "GOLFS", "MIRES", "FLUES", "LOAFS", "ARCED", + "ACNES", "NEONS", "FIEFS", "DINTS", "DAZES", "POUTS", "CORED", "YULES", "LILTS", + "BEEFS", "MUTTS", "FELLS", "COWLS", "SPUDS", "LAMES", "JAWED", "DUPES", "DEADS", + "BYLAW", "NOONS", "NIFTY", "CLUED", "VIREO", "GAPES", "METES", "CUTER", "MAIMS", + "DROLL", "CUPID", "MAULS", "SEDGE", "PAPAS", "WHEYS", "EKING", "LOOTS", "HILTS", + "MEOWS", "BEAUS", "DICES", "PEPPY", "RIPER", "FOGEY", "GISTS", "YOGAS", "GILTS", + "SKEWS", "CEDES", "ZEALS", "ALUMS", "OKAYS", "ELOPE", "GRUMP", "WAFTS", "SOOTS", + "BLIMP", "HEFTS", "MULLS", "HOSED", "CRESS", "DOFFS", "RUDER", "PIXIE", "WAIFS", + "OUSTS", "PUCKS", "BIERS", "GULCH", "SUETS", "HOBOS", "LINTS", "BRANS", "TEALS", + "GARBS", "PEWEE", "HELMS", "TURFS", "QUIPS", "WENDS", "BANES", "NAPES", "ICIER", + "SWATS", "BAGEL", "HEXED", "OGRES", "GONER", "GILDS", "PYRES", "LARDS", "BIDES", + "PAGED", "TALON", "FLOUT", "MEDIC", "VEALS", "PUTTS", "DIRKS", "DOTES", "TIPPY", + "BLURT", "PITHS", "ACING", "BARER", "WHETS", "GAITS", "WOOLS", "DUNKS", "HEROS", + "SWABS", "DIRTS", "JUTES", "HEMPS", "SURFS", "OKAPI", "CHOWS", "SHOOS", "DUSKS", + "PARRY", "DECAL", "FURLS", "CILIA", "SEARS", "NOVAE", "MURKS", "WARPS", "SLUES", + "LAMER", "SARIS", "WEANS", "PURRS", "DILLS", "TOGAS", "NEWTS", "MEANY", "BUNTS", + "RAZES", "GOONS", "WICKS", "RUSES", "VENDS", "GEODE", "DRAKE", "JUDOS", "LOFTS", + "PULPS", "LAUDS", "MUCKS", "VISES", "MOCHA", "OILED", "ROMAN", "ETHYL", "GOTTA", + "FUGUE", "SMACK", "GOURD", "BUMPY", "RADIX", "FATTY", "BORAX", "CUBIT", "CACTI", + "GAMMA", "FOCAL", "AVAIL", "PAPAL", "GOLLY", "ELITE", "VERSA", "BILLY", "ADIEU", + "ANNUM", "HOWDY", "RHINO", "NORMS", "BOBBY", "AXIOM", "SETUP", "YOLKS", "TERNS", + "MIXER", "GENRE", "KNOLL", "ABODE", "JUNTA", "GORGE", "COMBO", "ALPHA", "OVERT", + "KINDA", "SPELT", "PRICK", "NOBLY", "EPHOD", "AUDIO", "MODAL", "VELDT", "WARTY", + "FLUKE", "BONNY", "BREAM", "ROSIN", "BOLLS", "DOERS", "DOWNS", "BEADY", "MOTIF", + "HUMPH", "FELLA", "MOULD", "CREPE", "KERNS", "ALOHA", "GLYPH", "AZURE", "RISER", + "BLEST", "LOCUS", "LUMPY", "BERYL", "WANNA", "BRIER", "TUNER", "ROWDY", "MURAL", + "TIMER", "CANST", "KRILL", "QUOTH", "LEMME", "TRIAD", "TENON", "AMPLY", "DEEPS", + "PADRE", "LEANT", "PACER", "OCTAL", "DOLLY", "TRANS", "SUMAC", "FOAMY", "LOLLY", + "GIVER", "QUIPU", "CODEX", "MANNA", "UNWED", "VODKA", "FERNY", "SALON", "DUPLE", + "BORON", "REVUE", "CRIER", "ALACK", "INTER", "DILLY", "WHIST", "CULTS", "SPAKE", + "RESET", "LOESS", "DECOR", "MOVER", "VERVE", "ETHIC", "GAMUT", "LINGO", "DUNNO", + "ALIGN", "SISSY", "INCUR", "REEDY", "AVANT", "PIPER", "WAXER", "CALYX", "BASIL", + "COONS", "SEINE", "PINEY", "LEMMA", "TRAMS", "WINCH", "WHIRR", "SAITH", "IONIC", + "HEADY", "HAREM", "TUMMY", "SALLY", "SHIED", "DROSS", "FARAD", "SAVER", "TILDE", + "JINGO", "BOWER", "SERIF", "FACTO", "BELLE", "INSET", "BOGUS", "CAVED", "FORTE", + "SOOTY", "BONGO", "TOVES", "CREDO", "BASAL", "YELLA", "AGLOW", "GLEAN", "GUSTO", + "HYMEN", "ETHOS", "TERRA", "BRASH", "SCRIP", "SWASH", "ALEPH", "TINNY", "ITCHY", + "WANTA", "TRICE", "JOWLS", "GONGS", "GARDE", "BORIC", "TWILL", "SOWER", "HENRY", + "AWASH", "LIBEL", "SPURN", "SABRE", "REBUT", "PENAL", "OBESE", "SONNY", "QUIRT", + "MEBBE", "TACIT", "GREEK", "XENON", "HULLO", "PIQUE", "ROGER", "NEGRO", "HADST", + "GECKO", "BEGET", "UNCUT", "ALOES", "LOUIS", "QUINT", "CLUNK", "RAPED", "SALVO", + "DIODE", "MATEY", "HERTZ", "XYLEM", "KIOSK", "APACE", "CAWED", "PETER", "WENCH", + "COHOS", "SORTA", "GAMBA", "BYTES", "TANGO", "NUTTY", "AXIAL", "ALECK", "NATAL", + "CLOMP", "GORED", "SIREE", "BANDY", "GUNNY", "RUNIC", "WHIZZ", "RUPEE", "FATED", + "WIPER", "BARDS", "BRINY", "STAID", "HOCKS", "OCHRE", "YUMMY", "GENTS", "SOUPY", + "ROPER", "SWATH", "CAMEO", "EDGER", "SPATE", "GIMME", "EBBED", "BREVE", "THETA", + "DEEMS", "DYKES", "SERVO", "TELLY", "TABBY", "TARES", "BLOCS", "WELCH", "GHOUL", + "VITAE", "CUMIN", "DINKY", "BRONC", "TABOR", "TEENY", "COMER", "BORER", "SIRED", + "PRIVY", "MAMMY", "DEARY", "GYROS", "SPRIT", "CONGA", "QUIRE", "THUGS", "FUROR", + "BLOKE", "RUNES", "BAWDY", "CADRE", "TOXIN", "ANNUL", "EGGED", "ANION", "NODES", + "PICKY", "STEIN", "JELLO", "AUDIT", "ECHOS", "FAGOT", "LETUP", "EYRIE", "FOUNT", + "CAPED", "AXONS", "AMUCK", "BANAL", "RILED", "PETIT", "UMBER", "MILER", "FIBRE", + "AGAVE", "BATED", "BILGE", "VITRO", "FEINT", "PUDGY", "MATER", "MANIC", "UMPED", + "PESKY", "STREP", "SLURP", "PYLON", "PUREE", "CARET", "TEMPS", "NEWEL", "YAWNS", + "SEEDY", "TREED", "COUPS", "RANGY", "BRADS", "MANGY", "LONER", "CIRCA", "TIBIA", + "AFOUL", "MOMMY", "TITER", "CARNE", "KOOKY", "MOTES", "AMITY", "SUAVE", "HIPPO", + "CURVY", "SAMBA", "NEWSY", "ANISE", "IMAMS", "TULLE", "AWAYS", "LIVEN", "HALLO", + "WALES", "OPTED", "CANTO", "IDYLL", "BODES", "CURIO", "WRACK", "HIKER", "CHIVE", + "YOKEL", "DOTTY", "DEMUR", "CUSPS", "SPECS", "QUADS", "LAITY", "TONER", "DECRY", + "WRITS", "SAUTE", "CLACK", "AUGHT", "LOGOS", "TIPSY", "NATTY", "DUCAL", "BIDET", + "BULGY", "METRE", "LUSTS", "UNARY", "GOETH", "BALER", "SITED", "SHIES", "HASPS", + "BRUNG", "HOLED", "SWANK", "LOOKY", "MELEE", "HUFFY", "LOAMY", "PIMPS", "TITAN", + "BINGE", "SHUNT", "FEMUR", "LIBRA", "SEDER", "HONED", "ANNAS", "COYPU", "SHIMS", + "ZOWIE", "JIHAD", "SAVVY", "NADIR", "BASSO", "MONIC", "MANED", "MOUSY", "OMEGA", + "LAVER", "PRIMA", "PICAS", "FOLIO", "MECCA", "REALS", "TROTH", "TESTY", "BALKY", + "CRIMP", "CHINK", "ABETS", "SPLAT", "ABACI", "VAUNT", "CUTIE", "PASTY", "MORAY", + "LEVIS", "RATTY", "ISLET", "JOUST", "MOTET", "VIRAL", "NUKES", "GRADS", "COMFY", + "VOILA", "WOOZY", "BLUED", "WHOMP", "SWARD", "METRO", "SKEET", "CHINE", "AERIE", + "BOWIE", "TUBBY", "EMIRS", "COATI", "UNZIP", "SLOBS", "TRIKE", "FUNKY", "DUCAT", + "DEWEY", "SKOAL", "WADIS", "OOMPH", "TAKER", "MINIM", "GETUP", "STOIC", "SYNOD", + "RUNTY", "FLYBY", "BRAZE", "INLAY", "VENUE", "LOUTS", "PEATY", "ORLON", "HUMPY", + "RADON", "BEAUT", "RASPY", "UNFED", "CRICK", "NAPPY", "VIZOR", "YIPES", "REBUS", + "DIVOT", "KIWIS", "VETCH", "SQUIB", "SITAR", "KIDDO", "DYERS", "COTTA", "MATZO", + "LAGER", "ZEBUS", "CRASS", "DACHA", "KNEED", "DICTA", "FAKIR", "KNURL", "RUNNY", + "UNPIN", "JULEP", "GLOBS", "NUDES", "SUSHI", "TACKY", "STOKE", "KAPUT", "BUTCH", + "HULAS", "CROFT", "ACHOO", "GENII", "NODAL", "OUTGO", "SPIEL", "VIOLS", "FETID", + "CAGEY", "FUDGY", "EPOXY", "LEGGY", "HANKY", "LAPIS", "FELON", "BEEFY", "COOTS", + "MELBA", "CADDY", "SEGUE", "BETEL", "FRIZZ", "DREAR", "KOOKS", "TURBO", "HOAGY", + "MOULT", "HELIX", "ZONAL", "ARIAS", "NOSEY", "PAEAN", "LACEY", "BANNS", "SWAIN", + "FRYER", "RETCH", "TENET", "GIGAS", "WHINY", "OGLED", "RUMEN", "BEGOT", "CRUSE", + "ABUTS", "RIVEN", "BALKS", "SINES", "SIGMA", "ABASE", "ENNUI", "GORES", "UNSET", + "AUGUR", "SATED", "ODIUM", "LATIN", "DINGS", "MOIRE", "SCION", "HENNA", "KRAUT", + "DICKS", "LIFER", "PRIGS", "BEBOP", "GAGES", "GAZER", "FANNY", "GIBES", "AURAL", + "TEMPI", "HOOCH", "RAPES", "SNUCK", "HARTS", "TECHS", "EMEND", "NINNY", "GUAVA", + "SCARP", "LIEGE", "TUFTY", "SEPIA", "TOMES", "CAROB", "EMCEE", "PRAMS", "POSER", + "VERSO", "HUBBA", "JOULE", "BAIZE", "BLIPS", "SCRIM", "CUBBY", "CLAVE", "WINOS", + "REARM", "LIENS", "LUMEN", "CHUMP", "NANNY", "TRUMP", "FICHU", "CHOMP", "HOMOS", + "PURTY", "MASER", "WOOSH", "PATSY", "SHILL", "RUSKS", "AVAST", "SWAMI", "BODED", + "AHHHH", "LOBED", "NATCH", "SHISH", "TANSY", "SNOOT", "PAYER", "ALTHO", "SAPPY", + "LAXER", "HUBBY", "AEGIS", "RILES", "DITTO", "JAZZY", "DINGO", "QUASI", "SEPTA", + "PEAKY", "LORRY", "HEERD", "BITTY", "PAYEE", "SEAMY", "APSES", "IMBUE", "BELIE", + "CHARY", "SPOOF", "PHYLA", "CLIME", "BABEL", "WACKY", "SUMPS", "SKIDS", "KHANS", + "CRYPT", "INURE", "NONCE", "OUTEN", "FAIRE", "HOOEY", "ANOLE", "KAZOO", "CALVE", + "LIMBO", "ARGOT", "DUCKY", "FAKER", "VIBES", "GASSY", "UNLIT", "NERVY", "FEMME", + "BITER", "FICHE", "BOORS", "GAFFE", "SAXES", "RECAP", "SYNCH", "FACIE", "DICEY", + "OUIJA", "HEWER", "LEGIT", "GURUS", "EDIFY", "TWEAK", "CARON", "TYPOS", "RERUN", + "POLLY", "SURDS", "HAMZA", "NULLS", "HATER", "LEFTY", "MOGUL", "MAFIA", "DEBUG", + "PATES", "BLABS", "SPLAY", "TALUS", "PORNO", "MOOLA", "NIXED", "KILOS", "SNIDE", + "HORSY", "GESSO", "JAGGY", "TROVE", "NIXES", "CREEL", "PATER", "IOTAS", "CADGE", + "SKYED", "HOKUM", "FURZE", "ANKHS", "CURIE", "NUTSY", "HILUM", "REMIX", "ANGST", + "BURLS", "JIMMY", "VEINY", "TRYST", "CODON", "BEFOG", "GAMED", "FLUME", "AXMAN", + "DOOZY", "LUBES", "RHEAS", "BOZOS", "BUTYL", "KELLY", "MYNAH", "JOCKS", "DONUT", + "AVIAN", "WURST", "CHOCK", "QUASH", "QUALS", "HAYED", "BOMBE", "CUSHY", "SPACY", + "PUKED", "LEERY", "THEWS", "PRINK", "AMENS", "TESLA", "INTRO", "FIVER", "FRUMP", + "CAPOS", "OPINE", "CODER", "NAMER", "JOWLY", "PUKES", "HALED", "CHARD", "DUFFS", + "BRUIN", "REUSE", "WHANG", "TOONS", "FRATS", "SILTY", "TELEX", "CUTUP", "NISEI", + "NEATO", "DECAF", "SOFTY", "BIMBO", "ADLIB", "LOONY", "SHOED", "AGUES", "PEEVE", + "NOWAY", "GAMEY", "SARGE", "RERAN", "EPACT", "POTTY", "CONED", "UPEND", "NARCO", + "IKATS", "WHORL", "JINKS", "TIZZY", "WEEPY", "POSIT", "MARGE", "VEGAN", "CLOPS", + "NUMBS", "REEKS", "RUBES", "ROWER", "BIPED", "TIFFS", "HOCUS", "HAMMY", "BUNCO", + "FIXIT", "TYKES", "CHAWS", "YUCKY", "HOKEY", "RESEW", "MAVEN", "ADMAN", "SCUZZ", + "SLOGS", "SOUSE", "NACHO", "MIMED", "MELDS", "BOFFO", "DEBIT", "PINUP", "VAGUS", + "GULAG", "RANDY", "BOSUN", "EDUCE", "FAXES", "AURAS", "PESTO", "ANTSY", "BETAS", + "FIZZY", "DORKY", "SNITS", "MOXIE", "THANE", "MYLAR", "NOBBY", "GAMIN", "GOUTY", + "ESSES", "GOYIM", "PANED", "DRUID", "JADES", "REHAB", "GOFER", "TZARS", "OCTET", + "HOMED", "SOCKO", "DORKS", "EARED", "ANTED", "ELIDE", "FAZES", "OXBOW", "DOWSE", + "SITUS", "MACAW", "SCONE", "DRILY", "HYPER", "SALSA", "MOOCH", "GATED", "UNJAM", + "LIPID", "MITRE", "VENAL", "KNISH", "RITZY", "DIVAS", "TORUS", "MANGE", "DIMER", + "RECUT", "MESON", "WINED", "FENDS", "PHAGE", "FIATS", "CAULK", "CAVIL", "PANTY", + "ROANS", "BILKS", "HONES", "BOTCH", "ESTOP", "SULLY", "SOOTH", "GELDS", "AHOLD", + "RAPER", "PAGER", "FIXER", "INFIX", "HICKS", "TUXES", "PLEBE", "TWITS", "ABASH", + "TWIXT", "WACKO", "PRIMP", "NABLA", "GIRTS", "MIFFS", "EMOTE", "XEROX", "REBID", + "SHAHS", "RUTTY", "GROUT", "GRIFT", "DEIFY", "BIDDY", "KOPEK", "SEMIS", "BRIES", + "ACMES", "PITON", "HUSSY", "TORTS", "DISCO", "WHORE", "BOOZY", "GIBED", "VAMPS", + "AMOUR", "SOPPY", "GONZO", "DURST", "WADER", "TUTUS", "PERMS", "CATTY", "GLITZ", + "BRIGS", "NERDS", "BARMY", "GIZMO", "OWLET", "SAYER", "MOLLS", "SHARD", "WHOPS", + "COMPS", "CORER", "COLAS", "MATTE", "DROID", "PLOYS", "VAPID", "CAIRN", "DEISM", + "MIXUP", "YIKES", "PROSY", "RAKER", "FLUBS", "WHISH", "REIFY", "CRAPS", "SHAGS", + "CLONE", "HAZED", "MACHO", "RECTO", "REFIX", "DRAMS", "BIKER", "AQUAS", "PORKY", + "DOYEN", "EXUDE", "GOOFS", "DIVVY", "NOELS", "JIVED", "HULKY", "CAGER", "HARPY", + "OLDIE", "VIVAS", "ADMIX", "CODAS", "ZILCH", "DEIST", "ORCAS", "RETRO", "PILAF", + "PARSE", "RANTS", "ZINGY", "TODDY", "CHIFF", "MICRO", "VEEPS", "GIRLY", "NEXUS", + "DEMOS", "BIBBS", "ANTES", "LULUS", "GNARL", "ZIPPY", "IVIED", "EPEES", "WIMPS", + "TROMP", "GRAIL", "YOYOS", "POUFS", "HALES", "ROUST", "CABAL", "RAWER", "PAMPA", + "MOSEY", "KEFIR", "BURGS", "UNMET", "CUSPY", "BOOBS", "BOONS", "HYPES", "DYNES", + "NARDS", "LANAI", "YOGIS", "SEPAL", "QUARK", "TOKED", "PRATE", "AYINS", "HAWED", + "SWIGS", "VITAS", "TOKER", "DOPER", "BOSSA", "LINTY", "FOIST", "MONDO", "STASH", + "KAYOS", "TWERP", "ZESTY", "CAPON", "WIMPY", "REWED", "FUNGO", "TAROT", "FROSH", + "KABOB", "PINKO", "REDID", "MIMEO", "HEIST", "TARPS", "LAMAS", "SUTRA", "DINAR", + "WHAMS", "BUSTY", "SPAYS", "MAMBO", "NABOB", "PREPS", "ODOUR", "CABBY", "CONKS", + "SLUFF", "DADOS", "HOURI", "SWART", "BALMS", "GUTSY", "FAXED", "EGADS", "PUSHY", + "RETRY", "AGORA", "DRUBS", "DAFFY", "CHITS", "MUFTI", "KARMA", "LOTTO", "TOFFS", + "BURPS", "DEUCE", "ZINGS", "KAPPA", "CLADS", "DOGGY", "DUPER", "SCAMS", "OGLER", + "MIMES", "THROE", "ZETAS", "WALED", "PROMO", "BLATS", "MUFFS", "OINKS", "VIAND", + "COSET", "FINKS", "FADDY", "MINIS", "SNAFU", "SAUNA", "USURY", "MUXES", "CRAWS", + "STATS", "CONDO", "COXES", "LOOPY", "DORMS", "ASCOT", "DIPPY", "EXECS", "DOPEY", + "ENVOI", "UMPTY", "GISMO", "FAZED", "STROP", "JIVES", "SLIMS", "BATIK", "PINGS", + "SONLY", "LEGGO", "PEKOE", "PRAWN", "LUAUS", "CAMPY", "OODLE", "PREXY", "PROMS", + "TOUTS", "OGLES", "TWEET", "TOADY", "NAIAD", "HIDER", "NUKED", "FATSO", "SLUTS", + "OBITS", "NARCS", "TYROS", "DELIS", "WOOER", "HYPED", "POSET", "BYWAY", "TEXAS", + "SCROD", "AVOWS", "FUTON", "TORTE", "TUPLE", "CAROM", "KEBAB", "TAMPS", "JILTS", + "DUALS", "ARTSY", "REPRO", "MODEM", "TOPED", "PSYCH", "SICKO", "KLUTZ", "TARNS", + "COXED", "DRAYS", "CLOYS", "ANDED", "PIKER", "AIMER", "SURAS", "LIMOS", "FLACK", + "HAPAX", "DUTCH", "MUCKY", "SHIRE", "KLIEG", "STAPH", "LAYUP", "TOKES", "AXING", + "TOPER", "DUVET", "COWRY", "PROFS", "BLAHS", "ADDLE", "SUDSY", "BATTY", "COIFS", + "SUETY", "GABBY", "HAFTA", "PITAS", "GOUDA", "DEICE", "TAUPE", "TOPES", "DUCHY", + "NITRO", "CARNY", "LIMEY", "ORALS", "HIRER", "TAXER", "ROILS", "RUBLE", "ELATE", + "DOLOR", "WRYER", "SNOTS", "QUAIS", "COKED", "GIMEL", "GORSE", "MINAS", "GOEST", + "AGAPE", "MANTA", "JINGS", "ILIAC", "ADMEN", "OFFEN", "CILLS", "OFFAL", "LOTTA", + "BOLAS", "THWAP", "ALWAY", "BOGGY", "DONNA", "LOCOS", "BELAY", "GLUEY", "BITSY", + "MIMSY", "HILAR", "OUTTA", "VROOM", "FETAL", "RATHS", "RENAL", "DYADS", "CROCS", + "VIRES", "CULPA", "KIVAS", "FEIST", "TEATS", "THATS", "YAWLS", "WHENS", "ABACA", + "OHHHH", "APHIS", "FUSTY", "ECLAT", "PERDU", "MAYST", "EXEAT", "MOLLY", "SUPRA", + "WETLY", "PLASM", "BUFFA", "SEMEN", "PUKKA", "TAGUA", "PARAS", "STOAT", "SECCO", + "CARTE", "HAUTE", "MOLAL", "SHADS", "FORMA", "OVOID", "PIONS", "MODUS", "BUENO", + "RHEUM", "SCURF", "PARER", "EPHAH", "DOEST", "SPRUE", "FLAMS", "MOLTO", "DIETH", + "CHOOS", "MIKED", "BRONX", "GOOPY", "BALLY", "PLUMY", "MOONY", "MORTS", "YOURN", + "BIPOD", "SPUME", "ALGAL", "AMBIT", "MUCHO", "SPUED", "DOZER", "HARUM", "GROAT", + "SKINT", "LAUDE", "THRUM", "PAPPY", "ONCET", "RIMED", "GIGUE", "LIMED", "PLEIN", + "REDLY", "HUMPF", "LITES", "SEEST", "GREBE", "ABSIT", "THANX", "PSHAW", "YAWPS", + "PLATS", "PAYED", "AREAL", "TILTH", "YOUSE", "GWINE", "THEES", "WATSA", "LENTO", + "SPITZ", "YAWED", "GIPSY", "SPRAT", "CORNU", "AMAHS", "BLOWY", "WAHOO", "LUBRA", + "MECUM", "WHOOO", "COQUI", "SABRA", "EDEMA", "MRADS", "DICOT", "ASTRO", "KITED", + "OUZEL", "DIDOS", "GRATA", "BONNE", "AXMEN", "KLUNK", "SUMMA", "LAVES", "PURLS", + "YAWNY", "TEARY", "MASSE", "LARGO", "BAZAR", "PSSST", "SYLPH", "LULAB", "TOQUE", + "FUGIT", "PLUNK", "ORTHO", "LUCRE", "COOCH", "WHIPT", "FOLKY", "TYRES", "WHEEE", + "CORKY", "INJUN", "SOLON", "DIDOT", "KERFS", "RAYED", "WASSA", "CHILE", "BEGAT", + "NIPPY", "LITRE", "MAGNA", "REBOX", "HYDRO", "MILCH", "BRENT", "GYVES", "LAZED", + "FEUED", "MAVIS", "INAPT", "BAULK", "CASUS", "SCRUM", "WISED", "FOSSA", "DOWER", + "KYRIE", "BHOYS", "SCUSE", "FEUAR", "OHMIC", "JUSTE", "UKASE", "BEAUX", "TUSKY", + "ORATE", "MUSTA", "LARDY", "INTRA", "QUIFF", "EPSOM", "NEATH", "OCHER", "TARED", + "HOMME", "MEZZO", "CORMS", "PSOAS", "BEAKY", "TERRY", "INFRA", "SPIVS", "TUANS", + "BELLI", "BERGS", "ANIMA", "WEIRS", "MAHUA", "SCOPS", "MANSE", "TITRE", "CURIA", + "KEBOB", "CYCAD", "TALKY", "FUCKS", "TAPIS", "AMIDE", "DOLCE", "SLOES", "JAKES", + "RUSSE", "BLASH", "TUTTI", "PRUTA", "PANGA", "BLEBS", "TENCH", "SWARF", "HEREM", + "MISSY", "MERSE", "PAWKY", "LIMEN", "VIVRE", "CHERT", "UNSEE", "TIROS", "BRACK", + "FOOTS", "WELSH", "FOSSE", "KNOPS", "ILEUM", "NOIRE", "FIRMA", "PODGY", "LAIRD", + "THUNK", "SHUTE", "ROWAN", "SHOJI", "POESY", "UNCAP", "FAMES", "GLEES", "COSTA", + "TURPS", "FORES", "SOLUM", "IMAGO", "BYRES", "FONDU", "CONEY", "POLIS", "DICTU", + "KRAAL", "SHERD", "MUMBO", "WROTH", "CHARS", "UNBOX", "VACUO", "SLUED", "WEEST", + "HADES", "WILED", "SYNCS", "MUSER", "EXCON", "HOARS", "SIBYL", "PASSE", "JOEYS", + "LOTSA", "LEPTA", "SHAYS", "BOCKS", "ENDUE", "DARER", "NONES", "ILEUS", "PLASH", + "BUSBY", "WHEAL", "BUFFO", "YOBBO", "BILES", "POXES", "ROOTY", "LICIT", "TERCE", + "BROMO", "HAYEY", "DWEEB", "IMBED", "SARAN", "BRUIT", "PUNKY", "SOFTS", "BIFFS", + "LOPPY", "AGARS", "AQUAE", "LIVRE", "BIOME", "BUNDS", "SHEWS", "DIEMS", "GINNY", + "DEGUM", "POLOS", "DESEX", "UNMAN", "DUNGY", "VITAM", "WEDGY", "GLEBE", "APERS", + "RIDGY", "ROIDS", "WIFEY", "VAPES", "WHOAS", "BUNKO", "YOLKY", "ULNAS", "REEKY", + "BODGE", "BRANT", "DAVIT", "DEQUE", "LIKER", "JENNY", "TACTS", "FULLS", "TREAP", + "LIGNE", "ACKED", "REFRY", "VOWER", "AARGH", "CHURL", "MOMMA", "GAOLS", "WHUMP", + "ARRAS", "MARLS", "TILER", "GROGS", "MEMES", "MIDIS", "TIDED", "HALER", "DUCES", + "TWINY", "POSTE", "UNRIG", "PRISE", "DRABS", "QUIDS", "FACER", "SPIER", "BARIC", + "GEOID", "REMAP", "TRIER", "GUNKS", "STENO", "STOMA", "AIRER", "OVATE", "TORAH", + "APIAN", "SMUTS", "POCKS", "YURTS", "EXURB", "DEFOG", "NUDER", "BOSKY", "NIMBI", + "MOTHY", "JOYED", "LABIA", "PARDS", "JAMMY", "BIGLY", "FAXER", "HOPPY", "NURBS", + "COTES", "DISHY", "VISED", "CELEB", "PISMO", "CASAS", "WITHS", "DODGY", "SCUDI", + "MUNGS", "MUONS", "UREAS", "IOCTL", "UNHIP", "KRONE", "SAGER", "VERST", "EXPAT", + "GRONK", "UVULA", "SHAWM", "BILGY", "BRAES", "CENTO", "WEBBY", "LIPPY", "GAMIC", + "LORDY", "MAZED", "TINGS", "SHOAT", "FAERY", "WIRER", "DIAZO", "CARER", "RATER", + "GREPS", "RENTE", "ZLOTY", "VIERS", "UNAPT", "POOPS", "FECAL", "KEPIS", "TAXON", + "EYERS", "WONTS", "SPINA", "STOAE", "YENTA", "POOEY", "BURET", "JAPAN", "BEDEW", + "HAFTS", "SELFS", "OARED", "HERBY", "PRYER", "OAKUM", "DINKS", "TITTY", "SEPOY", + "PENES", "FUSEE", "WINEY", "GIMPS", "NIHIL", "RILLE", "GIBER", "OUSEL", "UMIAK", + "CUPPY", "HAMES", "SHITS", "AZINE", "GLADS", "TACET", "BUMPH", "COYER", "HONKY", + "GAMER", "GOOKY", "WASPY", "SEDGY", "BENTS", "VARIA", "DJINN", "JUNCO", "PUBIC", + "WILCO", "LAZES", "IDYLS", "LUPUS", "RIVES", "SNOOD", "SCHMO", "SPAZZ", "FINIS", + "NOTER", "PAVAN", "ORBED", "BATES", "PIPET", "BADDY", "GOERS", "SHAKO", "STETS", + "SEBUM", "SEETH", "LOBAR", "RAVER", "AJUGA", "RICED", "VELDS", "DRIBS", "VILLE", + "DHOWS", "UNSEW", "HALMA", "KRONA", "LIMBY", "JIFFS", "TREYS", "BAUDS", "PFFFT", + "MIMER", "PLEBS", "CANER", "JIBER", "CUPPA", "WASHY", "CHUFF", "UNARM", "YUKKY", + "STYES", "WAKER", "FLAKS", "MACES", "RIMES", "GIMPY", "GUANO", "LIRAS", "KAPOK", + "SCUDS", "BWANA", "ORING", "AIDER", "PRIER", "KLUGY", "MONTE", "GOLEM", "VELAR", + "FIRER", "PIETA", "UMBEL", "CAMPO", "UNPEG", "FOVEA", "ABEAM", "BOSON", "ASKER", + "GOTHS", "VOCAB", "VINED", "TROWS", "TIKIS", "LOPER", "INDIE", "BOFFS", "SPANG", + "GRAPY", "TATER", "ICHOR", "KILTY", "LOCHS", "SUPES", "DEGAS", "FLICS", "TORSI", + "BETHS", "WEBER", "RESAW", "LAWNY", "COVEN", "MUJIK", "RELET", "THERM", "HEIGH", + "SHNOR", "TRUED", "ZAYIN", "LIEST", "BARFS", "BASSI", "QOPHS", "ROILY", "FLABS", + "PUNNY", "OKRAS", "HANKS", "DIPSO", "NERFS", "FAUNS", "CALLA", "PSEUD", "LURER", + "MAGUS", "OBEAH", "ATRIA", "TWINK", "PALMY", "POCKY", "PENDS", "RECTA", "PLONK", + "SLAWS", "KEENS", "NICAD", "PONES", "INKER", "WHEWS", "GROKS", "MOSTS", "TREWS", + "ULNAR", "GYPPY", "COCAS", "EXPOS", "ERUCT", "OILER", "VACUA", "DRECK", "DATER", + "ARUMS", "TUBAL", "VOXEL", "DIXIT", "BEERY", "ASSAI", "LADES", "ACTIN", "GHOTI", + "BUZZY", "MEADS", "GRODY", "RIBBY", "CLEWS", "CREME", "EMAIL", "PYXIE", "KULAK", + "BOCCI", "RIVED", "DUDDY", "HOPER", "LAPIN", "WONKS", "PETRI", "PHIAL", "FUGAL", + "HOLON", "BOOMY", "DUOMO", "MUSOS", "SHIER", "HAYER", "PORGY", "HIVED", "LITHO", + "FISTY", "STAGY", "LUVYA", "MARIA", "SMOGS", "ASANA", "YOGIC", "SLOMO", "FAWNY", + "AMINE", "WEFTS", "GONAD", "TWIRP", "BRAVA", "PLYER", "FERMI", "LOGES", "NITER", + "REVET", "UNATE", "GYVED", "TOTTY", "ZAPPY", "HONER", "GIROS", "DICER", "CALKS", + "LUXES", "MONAD", "CRUFT", "QUOIN", "FUMER", "AMPED", "SHLEP", "VINCA", "YAHOO", + "VULVA", "ZOOEY", "DRYAD", "NIXIE", "MOPER", "IAMBS", "LUNES", "NUDIE", "LIMNS", + "WEALS", "NOHOW", "MIAOW", "GOUTS", "MYNAS", "MAZER", "KIKES", "OXEYE", "STOUP", + "JUJUS", "DEBAR", "PUBES", "TAELS", "DEFUN", "RANDS", "BLEAR", "PAVER", "GOOSY", + "SPROG", "OLEOS", "TOFFY", "PAWER", "MACED", "CRITS", "KLUGE", "TUBED", "SAHIB", + "GANEF", "SCATS", "SPUTA", "VANED", "ACNED", "TAXOL", "PLINK", "OWETH", "TRIBS", + "RESAY", "BOULE", "THOUS", "HAPLY", "GLANS", "MAXIS", "BEZEL", "ANTIS", "PORKS", + "QUOIT", "ALKYD", "GLARY", "BEAMY", "HEXAD", "BONKS", "TECUM", "KERBS", "FILAR", + "FRIER", "REDUX", "ABUZZ", "FADER", "SHOER", "COUTH", "TRUES", "GUYED", "GOONY", + "BOOKY", "FUZES", "HURLY", "GENET", "HODAD", "CALIX", "FILER", "PAWLS", "IODIC", + "UTERO", "HENGE", "UNSAY", "LIERS", "PIING", "WEALD", "SEXED", "FOLIC", "POXED", + "CUNTS", "ANILE", "KITHS", "BECKS", "TATTY", "PLENA", "REBAR", "ABLED", "TOYER", + "ATTAR", "TEAKS", "AIOLI", "AWING", "ANENT", "FECES", "REDIP", "WISTS", "PRATS", + "MESNE", "MUTER", "SMURF", "OWEST", "BAHTS", "LOSSY", "FTPED", "HUNKY", "HOERS", + "SLIER", "SICKS", "FATLY", "DELFT", "HIVER", "HIMBO", "PENGO", "BUSKS", "LOXES", + "ZONKS", "ILIUM", "APORT", "IKONS", "MULCT", "REEVE", "CIVVY", "CANNA", "BARFY", + "KAIAK", "SCUDO", "KNOUT", "GAPER", "BHANG", "PEASE", "UTERI", "LASES", "PATEN", + "RASAE", "AXELS", "STOAS", "OMBRE", "STYLI", "GUNKY", "HAZER", "KENAF", "AHOYS", + "AMMOS", "WEENY", "URGER", "KUDZU", "PAREN", "BOLOS", "FETOR", "NITTY", "TECHY", + "LIETH", "SOMAS", "DARKY", "VILLI", "GLUON", "JANES", "CANTS", "FARTS", "SOCLE", + "JINNS", "RUING", "SLILY", "RICER", "HADDA", "WOWEE", "RICES", "NERTS", "CAULS", + "SWIVE", "LILTY", "MICKS", "ARITY", "PASHA", "FINIF", "OINKY", "GUTTY", "TETRA", + "WISES", "WOLDS", "BALDS", "PICOT", "WHATS", "SHIKI", "BUNGS", "SNARF", "LEGOS", + "DUNGS", "STOGY", "BERMS", "TANGS", "VAILS", "ROODS", "MOREL", "SWARE", "ELANS", + "LATUS", "GULES", "RAZER", "DOXIE", "BUENA", "OVERS", "GUTTA", "ZINCS", "NATES", + "KIRKS", "TIKES", "DONEE", "JERRY", "MOHEL", "CEDER", "DOGES", "UNMAP", "FOLIA", + "RAWLY", "SNARK", "TOPOI", "CEILS", "IMMIX", "YORES", "DIEST", "BUBBA", "POMPS", + "FORKY", "TURDY", "LAWZY", "POOHS", "WORTS", "GLOMS", "BEANO", "MULEY", "BARKY", + "TUNNY", "AURIC", "FUNKS", "GAFFS", "CORDY", "CURDY", "LISLE", "TORIC", "SOYAS", + "REMAN", "MUNGY", "CARPY", "APISH", "OATEN", "GAPPY", "AURAE", "BRACT", "ROOKY", + "AXLED", "BURRY", "SIZER", "PROEM", "TURFY", "IMPRO", "MASHY", "MIENS", "NONNY", + "OLIOS", "GROOK", "SATES", "AGLEY", "CORGI", "DASHY", "DOSER", "DILDO", "APSOS", + "XORED", "LAKER", "PLAYA", "SELAH", "MALTY", "DULSE", "FRIGS", "DEMIT", "WHOSO", + "RIALS", "SAWER", "BEDIM", "SNUGS", "FANIN", "AZOIC", "ICERS", "SUERS", "WIZEN", + "KOINE", "TOPOS", "SHIRR", "RIFER", "FERAL", "LADED", "LASED", "TURDS", "SWEDE", + "EASTS", "COZEN", "UNHIT", "PALLY", "AITCH", "SEDUM", "COPER", "RUCHE", "GEEKS", + "SWAGS", "ETEXT", "ALGIN", "OFFED", "NINJA", "HOLER", "DOTER", "TOTER", "BESOT", + "DICUT", "MACER", "PEENS", "PEWIT", "REDOX", "POLER", "YECCH", "FLUKY", "DOETH", + "TWATS", "CRUDS", "BEBUG", "BIDER", "STELE", "HEXER", "WESTS", "GLUER", "PILAU", + "ABAFT", "WHELM", "LACER", "INODE", "TABUS", "GATOR", "CUING", "REFLY", "LUTED", + "CUKES", "BAIRN", "BIGHT", "ARSES", "CRUMP", "LOGGY", "BLINI", "SPOOR", "TOYON", + "HARKS", "WAZOO", "FENNY", "NAVES", "KEYER", "TUFAS", "MORPH", "RAJAS", "TYPAL", + "SPIFF", "OXLIP", "UNBAN", "MUSSY", "FINNY", "RIMER", "LOGIN", "MOLAS", "CIRRI", + "HUZZA", "AGONE", "UNSEX", "UNWON", "PEATS", "TOILE", "ZOMBI", "DEWED", "NOOKY", + "ALKYL", "IXNAY", "DOVEY", "HOLEY", "CUBER", "AMYLS", "PODIA", "CHINO", "APNEA", + "PRIMS", "LYCRA", "JOHNS", "PRIMO", "FATWA", "EGGER", "HEMPY", "SNOOK", "HYING", + "FUZED", "BARMS", "CRINK", "MOOTS", "YERBA", "RHUMB", "UNARC", "DIRER", "MUNGE", + "ELAND", "NARES", "WRIER", "NODDY", "ATILT", "JUKES", "ENDER", "THENS", "UNFIX", + "DOGGO", "ZOOKS", "DIDDY", "SHMOO", "BRUSK", "PREST", "CURER", "PASTS", "KELPY", + "BOCCE", "KICKY", "TAROS", "LINGS", "DICKY", "NERDY", "ABEND", "STELA", "BIGGY", + "LAVED", "BALDY", "PUBIS", "GOOKS", "WONKY", "STIED", "HYPOS", "ASSED", "SPUMY", + "OSIER", "ROBLE", "RUMBA", "BIFFY", "PUPAL" +] + +alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] + +def most_used_letters(): + dicto = {} + for i in alphabet: + count = 0 + for word in words: + for letter in word: + if i.upper() == letter.upper(): + count+=1 + break + dicto[i] = count + dicto = dict(sorted(dicto.items(), key=lambda item: item[1], reverse=True)) + print("Letter | Usage") + print("--------------") + for k in dicto: + print(f"{k.upper()} | {dicto[k]}") + + +def list_of_valid_words(): + letters = ['s', 'e', 'a', 'r', 'o', 'i', 'L', 'n', 'p', 'c'] + for i, letter in enumerate(letters): # Force all letters to be capitalized + letters[i] = letter.upper() + + legal_words = [] + for word in words: + valid_word = True + for letter in word: + if letter.upper() not in letters: + valid_word = False + break + if valid_word and word not in legal_words: + legal_words.append(word) + + for i,word in enumerate(legal_words): + legal_words[i] = word.upper().replace("D","d") + + print(f"Number of words found: {len(legal_words)}") + items_per_row = 9 + i = 0 + print("static const char _legal_words[][WORDLE_LENGTH + 1] = {") + while i < len(legal_words): + print(" ", end='') + for j in range(min(items_per_row, len(legal_words)-i)): + print(f'"{legal_words[i]}", ', end='') + i+=1 + print('') + print("};") + +if __name__ == "__main__": + #most_used_letters() + list_of_valid_words() + From ee53e83ae7d9d920add1af6d0faad01ce1b017ef Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Wed, 14 Aug 2024 23:31:00 -0400 Subject: [PATCH 049/161] Changed dict to a smaller and simpler one --- .../watch_faces/complication/wordle_face.c | 115 +-- utils/wordle_list.py | 969 ++++++------------ 2 files changed, 374 insertions(+), 710 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index f6f63fd..4052ef7 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -36,77 +36,60 @@ TODO: */ -// From: https://github.com/charlesreid1/five-letter-words/blob/master/sgb-words.txt +// From: https://gist.github.com/shmookey/b28e342e1b1756c4700f42f17102c2ff /* -Letter | Usage in sgb-words.txt -S | 2674 -E | 2658 -A | 2181 -R | 1799 -O | 1683 -I | 1539 -T | 1462 But looks bad across all positions -L | 1434 -N | 1219 -D | 1100 lowercase d looks like a in certain positions -U | 1068 C has more words with the other letters here (457 total vs 390) -C | 920 -P | 895 +Letter | Usage +E | 1519 +S | 1490 +A | 1213 +R | 1026 +O | 852 +L | 850 +I | 843 +T | 819 But looks bad across all positions +N | 681 +D | 619 lowercase d looks like a in certain positions +C | 525 +U | 514 P has more words with the other letters here (281 vs 198) +P | 448 */ -static const char _valid_letters[] = {'A', 'C', 'E', 'I', 'L', 'N', 'O', 'P', 'R', 'S'}; +static const char _valid_letters[] = {'E', 'S', 'A', 'R', 'O', 'L', 'I', 'N', 'C', 'P'}; +// Number of words found: 281 static const char _legal_words[][WORDLE_LENGTH + 1] = { - "PLACE", "SINCE", "PAPER", "LINES", "LEARN", "SPACE", "CLOSE", "CLASS", "PIECE", - "COLOR", "ALONE", "PLANE", "SPELL", "CLEAR", "AREAS", "SENSE", "OCEAN", "SCALE", - "CELLS", "SLEEP", "LOCAL", "CLEAN", "PEACE", "CROSS", "CASES", "CROPS", "PLAIN", - "PAIRS", "SCORE", "NOISE", "PIANO", "PLANS", "PRICE", "RAISE", "SCENE", "PRESS", - "APPLE", "CALLS", "POLES", "LOOSE", "OPERA", "INNER", "SOLAR", "RISES", "SALES", - "ACRES", "ERROR", "NAILS", "COINS", "SLOPE", "CANOE", "CANAL", "LIONS", "AROSE", - "RACES", "SPARE", "PIPES", "RAINS", "ROLLS", "SAILS", "ARISE", "ROPES", "CRIES", - "OPENS", "APRON", "SPEAR", "SLICE", "SPOON", "ROSES", "LINEN", "POLAR", "PEARL", - "LOSES", "CORAL", "SPOIL", "PANEL", "SELLS", "COOLS", "SOILS", "POOLS", "RISEN", - "PILES", "SLAIN", "PANIC", "CRISP", "RAILS", "SCOPE", "CONES", "COCOA", "REINS", - "NOSES", "SCARE", "CEASE", "PRIOR", "POLIO", "SEALS", "COALS", "LOANS", "SPINS", - "PAINS", "ONION", "SCRAP", "ROLES", "SNAIL", "LOOPS", "CREEP", "CARES", "ALIEN", - "CRANE", "SLIPS", "SPINE", "LEAPS", "PROSE", "SNARE", "PINES", "SCALP", "LEASE", - "COILS", "ARENA", "PEARS", "CLIPS", "LANES", "RESIN", "SONAR", "CORPS", "NIECE", - "CELLO", "REPEL", "SPICE", "OASIS", "PACES", "COLON", "CACAO", "RINSE", "SPOOL", - "SPILL", "SNAPS", "EERIE", "EARNS", "PERIL", "LINER", "SCARS", "SOLES", "PAILS", - "CAROL", "CANON", "POLLS", "NASAL", "SCORN", "OASES", "ASSES", "PILLS", "SPORE", - "SPIES", "ERASE", "AISLE", "LOINS", "LEANS", "LANCE", "PANES", "PORES", "POISE", - "IRONS", "ROARS", "SCOOP", "LASSO", "PRONE", "NICER", "SNARL", "CORES", "RIPEN", - "PEERS", "EASEL", "LACES", "SOAPS", "SNIPS", "PIERS", "LASER", "LILAC", "SIREN", - "CLASP", "POSSE", "POSES", "SLOOP", "SLAPS", "SOLOS", "SISAL", "SEEPS", "SPANS", - "CLAPS", "ACORN", "LOSER", "LAPSE", "ASPEN", "SNORE", "PROPS", "PESOS", "SONIC", - "SPARS", "SORES", "RELIC", "NOOSE", "NEARS", "CAPES", "CANES", "SPIRE", "ISLES", - "SNEER", "RARER", "NINES", "PECAN", "PENCE", "SILLS", "COPRA", "POPES", "SPREE", - "SCANS", "REELS", "LIARS", "LEPER", "SOARS", "PLEAS", "PALER", "EPICS", "CAPER", - "CONIC", "OPALS", "EASES", "ARSON", "CLANS", "PLIES", "CROON", "PREEN", "SEERS", - "COCCI", "SILOS", "SNIPE", "SANER", "RILLS", "CORNS", "PRIES", "LOONS", "EARLS", - "PEONS", "PALES", "LAIRS", "PEELS", "PEALS", "CRONE", "ENROL", "COOPS", "LAPEL", - "RASPS", "ASPIC", "PENIS", "INANE", "SLOPS", "COCOS", "LEERS", "LOPES", "ALIAS", - "RACER", "COPSE", "PALLS", "COPES", "ICONS", "REAPS", "SNOOP", "LORES", "REARS", - "COLIC", "PLOPS", "SIRES", "CARPS", "LISPS", "PEEPS", "SORER", "LOLLS", "PARES", - "ACNES", "NEONS", "NOONS", "PAPAS", "RIPER", "ELOPE", "CRESS", "NAPES", "ICIER", - "CILIA", "SEARS", "SARIS", "PAPAL", "ROSIN", "CREPE", "RISER", "PACER", "SALON", - "CRIER", "LOESS", "PIPER", "COONS", "SEINE", "IONIC", "SCRIP", "PENAL", "ALOES", - "APACE", "SIREE", "ROPER", "ANION", "LONER", "CIRCA", "CARNE", "ANISE", "SPECS", - "ANNAS", "PICAS", "REALS", "AERIE", "ORLON", "CRASS", "SPIEL", "LAPIS", "ARIAS", - "PAEAN", "SINES", "SCION", "RAPES", "SCARP", "SEPIA", "POSER", "LIENS", "RILES", - "APSES", "NONCE", "ANOLE", "RECAP", "CARON", "PORNO", "CREEL", "CAPOS", "OPINE", - "NISEI", "RERAN", "NARCO", "CLOPS", "ESSES", "SCONE", "SALSA", "ROANS", "RAPER", - "CORER", "COLAS", "CAIRN", "CRAPS", "CLONE", "NOELS", "ORCAS", "PARSE", "EPEES", - "LANAI", "SEPAL", "CAPON", "PREPS", "NARCS", "REPRO", "ORALS", "ROILS", "ILIAC", - "CILLS", "LOCOS", "RENAL", "CROCS", "PARAS", "SECCO", "PIONS", "PARER", "PLEIN", - "AREAL", "SOLON", "PSOAS", "SCOPS", "SLOES", "NOIRE", "POLIS", "PASSE", "NONES", - "SARAN", "POLOS", "APERS", "ARRAS", "PRISE", "SPIER", "AIRER", "APIAN", "CASAS", - "CARER", "POOPS", "SPINA", "PENES", "RILLE", "CANER", "LIRAS", "PRIER", "LOPER", - "CALLA", "PONES", "COCAS", "OILER", "ASSAI", "LAPIN", "ASANA", "OLEOS", "LIERS", - "ANILE", "PLENA", "AIOLI", "SLIER", "CANNA", "PEASE", "LASES", "RASAE", "PAREN", - "SOCLE", "RICER", "RICES", "ELANS", "CEILS", "LISLE", "OLIOS", "APSOS", "RIALS", - "ICERS", "COPER", "PEENS", "POLER", "LACER", "ARSES", "SPOOR", "CIRRI", "APNEA", - "NARES", "OSIER", + "SPIES", "SOLAR", "RAISE", "RARES", "PAEAN", "PLIES", "CRASS", "PEARS", "SNORE", + "POLES", "ROLLS", "ALOES", "LOSES", "SLICE", "PEACE", "POLLS", "POSES", "LANES", + "COPRA", "SPANS", "CANAL", "LOSER", "PAPER", "PILES", "CLASS", "RACER", "POOLS", + "PLAIN", "SPEAR", "SPARE", "INNER", "ALIEN", "NOSES", "EARLS", "SEALS", "LEARN", + "COLIC", "OPERA", "LOOSE", "SPOOR", "SCALE", "SOARS", "PAILS", "PRONE", "OPALS", + "PIPER", "RILLS", "CAIRN", "POISE", "LEAPS", "ELOPE", "NICER", "SLOOP", "PANES", + "SOLES", "CROSS", "NIECE", "LAIRS", "LEASE", "SALES", "SCENE", "SORES", "SNARL", + "SPIRE", "LASSO", "CLOSE", "OSIER", "SPOOL", "PRICE", "LOANS", "POSSE", "PENAL", + "SLAPS", "RELIC", "SINCE", "CIRCA", "LIARS", "RISES", "OPENS", "ROARS", "PACES", + "ARISE", "RISEN", "PENIS", "LAPEL", "CROPS", "CANON", "LAPSE", "SCION", "ARSON", + "AREAS", "SLAIN", "CANOE", "EERIE", "NOOSE", "PIANO", "PLANE", "CLASP", "SCARE", + "COCOA", "CRESS", "NASAL", "LOCAL", "RINSE", "SCARS", "PROPS", "OASES", "SLEEP", + "SNAPS", "SIRES", "CANES", "RAILS", "RESIN", "COLON", "PEASE", "POPES", "PENCE", + "AROSE", "REELS", "SALSA", "OCEAN", "PESOS", "OPINE", "RACES", "RAINS", "PRIES", + "CRIES", "CALLS", "PIERS", "CELLS", "SCRAP", "EARNS", "IRONS", "SPACE", "LOONS", + "SILLS", "COALS", "PIECE", "PALER", "REINS", "APACE", "SLOPE", "CREPE", "CONES", + "CAPER", "SEERS", "CAPES", "OASIS", "REAPS", "PALES", "CLAPS", "PLEAS", "INANE", + "COINS", "SNAIL", "CLEAR", "ROSIN", "LILAC", "SPARS", "SPINE", "NONCE", "CRISP", + "CRAPE", "AISLE", "CRONE", "SPOIL", "SPOON", "ARENA", "PARSE", "CASES", "SPICE", + "RIPER", "PILLS", "SOLOS", "SPINS", "PEERS", "RARER", "CONIC", "REARS", "CACAO", + "PAPAS", "ACRES", "ROPES", "CORAL", "CLEAN", "EASES", "SPILL", "SENSE", "PIPES", + "CLANS", "PRESS", "LOINS", "PAPAL", "APPLE", "PAIRS", "SCORN", "ALONE", "PEEPS", + "SPREE", "SNARE", "CLIPS", "EASEL", "CAROL", "ASPEN", "SALON", "LOOPS", "PEALS", + "SNEER", "PLACE", "SELLS", "LINEN", "CRIER", "ACORN", "SLIPS", "ERASE", "LIONS", + "NAILS", "REPEL", "CORES", "LEPER", "APPAL", "ROSES", "SCORE", "RISER", "CREEP", + "CAPON", "ERROR", "NOISE", "CARES", "APRON", "SOILS", "SLOPS", "PAINS", "EPICS", + "SANER", "SAILS", "PRIOR", "ASSES", "COILS", "SCOOP", "LACES", "SCALP", "CRANE", + "PLANS", "ISLES", "SPORE", "PANIC", "COOLS", "SPELL", "ALIAS", "PORES", "SCRIP", + "PEARL", "PANEL", "ENROL", "LANCE", "CORPS", "LINES", "COPSE", "ONION", "NEARS", + "RIPEN", "LINER", "SCOPE", "SCANS", "SNIPE", "CEASE", "LEANS", "AEONS", "PINES", + "POPPA", "ROLES", "REALS", "PERIL", "POSER", "PROSE", "POLAR", "CORNS", "LIENS", + "SIREN", "PEONS", }; static const uint32_t _num_words = (sizeof(_legal_words) / sizeof(_legal_words[0])); diff --git a/utils/wordle_list.py b/utils/wordle_list.py index 30da4bd..87e6066 100644 --- a/utils/wordle_list.py +++ b/utils/wordle_list.py @@ -1,645 +1,318 @@ -# From: https://github.com/charlesreid1/five-letter-words/blob/master/sgb-words.txt +import random + +# From: https://gist.github.com/shmookey/b28e342e1b1756c4700f42f17102c2ff words = [ - "WHICH", "THERE", "THEIR", "ABOUT", "WOULD", "THESE", "OTHER", "WORDS", "COULD", - "WRITE", "FIRST", "WATER", "AFTER", "WHERE", "RIGHT", "THINK", "THREE", "YEARS", - "PLACE", "SOUND", "GREAT", "AGAIN", "STILL", "EVERY", "SMALL", "FOUND", "THOSE", - "NEVER", "UNDER", "MIGHT", "WHILE", "HOUSE", "WORLD", "BELOW", "ASKED", "GOING", - "LARGE", "UNTIL", "ALONG", "SHALL", "BEING", "OFTEN", "EARTH", "BEGAN", "SINCE", - "STUDY", "NIGHT", "LIGHT", "ABOVE", "PAPER", "PARTS", "YOUNG", "STORY", "POINT", - "TIMES", "HEARD", "WHOLE", "WHITE", "GIVEN", "MEANS", "MUSIC", "MILES", "THING", - "TODAY", "LATER", "USING", "MONEY", "LINES", "ORDER", "GROUP", "AMONG", "LEARN", - "KNOWN", "SPACE", "TABLE", "EARLY", "TREES", "SHORT", "HANDS", "STATE", "BLACK", - "SHOWN", "STOOD", "FRONT", "VOICE", "KINDS", "MAKES", "COMES", "CLOSE", "POWER", - "LIVED", "VOWEL", "TAKEN", "BUILT", "HEART", "READY", "QUITE", "CLASS", "BRING", - "ROUND", "HORSE", "SHOWS", "PIECE", "GREEN", "STAND", "BIRDS", "START", "RIVER", - "TRIED", "LEAST", "FIELD", "WHOSE", "GIRLS", "LEAVE", "ADDED", "COLOR", "THIRD", - "HOURS", "MOVED", "PLANT", "DOING", "NAMES", "FORMS", "HEAVY", "IDEAS", "CRIED", - "CHECK", "FLOOR", "BEGIN", "WOMAN", "ALONE", "PLANE", "SPELL", "WATCH", "CARRY", - "WROTE", "CLEAR", "NAMED", "BOOKS", "CHILD", "GLASS", "HUMAN", "TAKES", "PARTY", - "BUILD", "SEEMS", "BLOOD", "SIDES", "SEVEN", "MOUTH", "SOLVE", "NORTH", "VALUE", - "DEATH", "MAYBE", "HAPPY", "TELLS", "GIVES", "LOOKS", "SHAPE", "LIVES", "STEPS", - "AREAS", "SENSE", "SPEAK", "FORCE", "OCEAN", "SPEED", "WOMEN", "METAL", "SOUTH", - "GRASS", "SCALE", "CELLS", "LOWER", "SLEEP", "WRONG", "PAGES", "SHIPS", "NEEDS", - "ROCKS", "EIGHT", "MAJOR", "LEVEL", "TOTAL", "AHEAD", "REACH", "STARS", "STORE", - "SIGHT", "TERMS", "CATCH", "WORKS", "BOARD", "COVER", "SONGS", "EQUAL", "STONE", - "WAVES", "GUESS", "DANCE", "SPOKE", "BREAK", "CAUSE", "RADIO", "WEEKS", "LANDS", - "BASIC", "LIKED", "TRADE", "FRESH", "FINAL", "FIGHT", "MEANT", "DRIVE", "SPENT", - "LOCAL", "WAXES", "KNOWS", "TRAIN", "BREAD", "HOMES", "TEETH", "COAST", "THICK", - "BROWN", "CLEAN", "QUIET", "SUGAR", "FACTS", "STEEL", "FORTH", "RULES", "NOTES", - "UNITS", "PEACE", "MONTH", "VERBS", "SEEDS", "HELPS", "SHARP", "VISIT", "WOODS", - "CHIEF", "WALLS", "CROSS", "WINGS", "GROWN", "CASES", "FOODS", "CROPS", "FRUIT", - "STICK", "WANTS", "STAGE", "SHEEP", "NOUNS", "PLAIN", "DRINK", "BONES", "APART", - "TURNS", "MOVES", "TOUCH", "ANGLE", "BASED", "RANGE", "MARKS", "TIRED", "OLDER", - "FARMS", "SPEND", "SHOES", "GOODS", "CHAIR", "TWICE", "CENTS", "EMPTY", "ALIKE", - "STYLE", "BROKE", "PAIRS", "COUNT", "ENJOY", "SCORE", "SHORE", "ROOTS", "PAINT", - "HEADS", "SHOOK", "SERVE", "ANGRY", "CROWD", "WHEEL", "QUICK", "DRESS", "SHARE", - "ALIVE", "NOISE", "SOLID", "CLOTH", "SIGNS", "HILLS", "TYPES", "DRAWN", "WORTH", - "TRUCK", "PIANO", "UPPER", "LOVED", "USUAL", "FACES", "DROVE", "CABIN", "BOATS", - "TOWNS", "PROUD", "COURT", "MODEL", "PRIME", "FIFTY", "PLANS", "YARDS", "PROVE", - "TOOLS", "PRICE", "SHEET", "SMELL", "BOXES", "RAISE", "MATCH", "TRUTH", "ROADS", - "THREW", "ENEMY", "LUNCH", "CHART", "SCENE", "GRAPH", "DOUBT", "GUIDE", "WINDS", - "BLOCK", "GRAIN", "SMOKE", "MIXED", "GAMES", "WAGON", "SWEET", "TOPIC", "EXTRA", - "PLATE", "TITLE", "KNIFE", "FENCE", "FALLS", "CLOUD", "WHEAT", "PLAYS", "ENTER", - "BROAD", "STEAM", "ATOMS", "PRESS", "LYING", "BASIS", "CLOCK", "TASTE", "GROWS", - "THANK", "STORM", "AGREE", "BRAIN", "TRACK", "SMILE", "FUNNY", "BEACH", "STOCK", - "HURRY", "SAVED", "SORRY", "GIANT", "TRAIL", "OFFER", "OUGHT", "ROUGH", "DAILY", - "AVOID", "KEEPS", "THROW", "ALLOW", "CREAM", "LAUGH", "EDGES", "TEACH", "FRAME", - "BELLS", "DREAM", "MAGIC", "OCCUR", "ENDED", "CHORD", "FALSE", "SKILL", "HOLES", - "DOZEN", "BRAVE", "APPLE", "CLIMB", "OUTER", "PITCH", "RULER", "HOLDS", "FIXED", - "COSTS", "CALLS", "BLANK", "STAFF", "LABOR", "EATEN", "YOUTH", "TONES", "HONOR", - "GLOBE", "GASES", "DOORS", "POLES", "LOOSE", "APPLY", "TEARS", "EXACT", "BRUSH", - "CHEST", "LAYER", "WHALE", "MINOR", "FAITH", "TESTS", "JUDGE", "ITEMS", "WORRY", - "WASTE", "HOPED", "STRIP", "BEGUN", "ASIDE", "LAKES", "BOUND", "DEPTH", "CANDY", - "EVENT", "WORSE", "AWARE", "SHELL", "ROOMS", "RANCH", "IMAGE", "SNAKE", "ALOUD", - "DRIED", "LIKES", "MOTOR", "POUND", "KNEES", "REFER", "FULLY", "CHAIN", "SHIRT", - "FLOUR", "DROPS", "SPITE", "ORBIT", "BANKS", "SHOOT", "CURVE", "TRIBE", "TIGHT", - "BLIND", "SLEPT", "SHADE", "CLAIM", "FLIES", "THEME", "QUEEN", "FIFTH", "UNION", - "HENCE", "STRAW", "ENTRY", "ISSUE", "BIRTH", "FEELS", "ANGER", "BRIEF", "RHYME", - "GLORY", "GUARD", "FLOWS", "FLESH", "OWNED", "TRICK", "YOURS", "SIZES", "NOTED", - "WIDTH", "BURST", "ROUTE", "LUNGS", "UNCLE", "BEARS", "ROYAL", "KINGS", "FORTY", - "TRIAL", "CARDS", "BRASS", "OPERA", "CHOSE", "OWNER", "VAPOR", "BEATS", "MOUSE", - "TOUGH", "WIRES", "METER", "TOWER", "FINDS", "INNER", "STUCK", "ARROW", "POEMS", - "LABEL", "SWING", "SOLAR", "TRULY", "TENSE", "BEANS", "SPLIT", "RISES", "WEIGH", - "HOTEL", "STEMS", "PRIDE", "SWUNG", "GRADE", "DIGIT", "BADLY", "BOOTS", "PILOT", - "SALES", "SWEPT", "LUCKY", "PRIZE", "STOVE", "TUBES", "ACRES", "WOUND", "STEEP", - "SLIDE", "TRUNK", "ERROR", "PORCH", "SLAVE", "EXIST", "FACED", "MINES", "MARRY", - "JUICE", "RACED", "WAVED", "GOOSE", "TRUST", "FEWER", "FAVOR", "MILLS", "VIEWS", - "JOINT", "EAGER", "SPOTS", "BLEND", "RINGS", "ADULT", "INDEX", "NAILS", "HORNS", - "BALLS", "FLAME", "RATES", "DRILL", "TRACE", "SKINS", "WAXED", "SEATS", "STUFF", - "RATIO", "MINDS", "DIRTY", "SILLY", "COINS", "HELLO", "TRIPS", "LEADS", "RIFLE", - "HOPES", "BASES", "SHINE", "BENCH", "MORAL", "FIRES", "MEALS", "SHAKE", "SHOPS", - "CYCLE", "MOVIE", "SLOPE", "CANOE", "TEAMS", "FOLKS", "FIRED", "BANDS", "THUMB", - "SHOUT", "CANAL", "HABIT", "REPLY", "RULED", "FEVER", "CRUST", "SHELF", "WALKS", - "MIDST", "CRACK", "PRINT", "TALES", "COACH", "STIFF", "FLOOD", "VERSE", "AWAKE", - "ROCKY", "MARCH", "FAULT", "SWIFT", "FAINT", "CIVIL", "GHOST", "FEAST", "BLADE", - "LIMIT", "GERMS", "READS", "DUCKS", "DAIRY", "WORST", "GIFTS", "LISTS", "STOPS", - "RAPID", "BRICK", "CLAWS", "BEADS", "BEAST", "SKIRT", "CAKES", "LIONS", "FROGS", - "TRIES", "NERVE", "GRAND", "ARMED", "TREAT", "HONEY", "MOIST", "LEGAL", "PENNY", - "CROWN", "SHOCK", "TAXES", "SIXTY", "ALTAR", "PULLS", "SPORT", "DRUMS", "TALKS", - "DYING", "DATES", "DRANK", "BLOWS", "LEVER", "WAGES", "PROOF", "DRUGS", "TANKS", - "SINGS", "TAILS", "PAUSE", "HERDS", "AROSE", "HATED", "CLUES", "NOVEL", "SHAME", - "BURNT", "RACES", "FLASH", "WEARY", "HEELS", "TOKEN", "COATS", "SPARE", "SHINY", - "ALARM", "DIMES", "SIXTH", "CLERK", "MERCY", "SUNNY", "GUEST", "FLOAT", "SHONE", - "PIPES", "WORMS", "BILLS", "SWEAT", "SUITS", "SMART", "UPSET", "RAINS", "SANDY", - "RAINY", "PARKS", "SADLY", "FANCY", "RIDER", "UNITY", "BUNCH", "ROLLS", "CRASH", - "CRAFT", "NEWLY", "GATES", "HATCH", "PATHS", "FUNDS", "WIDER", "GRACE", "GRAVE", - "TIDES", "ADMIT", "SHIFT", "SAILS", "PUPIL", "TIGER", "ANGEL", "CRUEL", "AGENT", - "DRAMA", "URGED", "PATCH", "NESTS", "VITAL", "SWORD", "BLAME", "WEEDS", "SCREW", - "VOCAL", "BACON", "CHALK", "CARGO", "CRAZY", "ACTED", "GOATS", "ARISE", "WITCH", - "LOVES", "QUEER", "DWELL", "BACKS", "ROPES", "SHOTS", "MERRY", "PHONE", "CHEEK", - "PEAKS", "IDEAL", "BEARD", "EAGLE", "CREEK", "CRIES", "ASHES", "STALL", "YIELD", - "MAYOR", "OPENS", "INPUT", "FLEET", "TOOTH", "CUBIC", "WIVES", "BURNS", "POETS", - "APRON", "SPEAR", "ORGAN", "CLIFF", "STAMP", "PASTE", "RURAL", "BAKED", "CHASE", - "SLICE", "SLANT", "KNOCK", "NOISY", "SORTS", "STAYS", "WIPED", "BLOWN", "PILED", - "CLUBS", "CHEER", "WIDOW", "TWIST", "TENTH", "HIDES", "COMMA", "SWEEP", "SPOON", - "STERN", "CREPT", "MAPLE", "DEEDS", "RIDES", "MUDDY", "CRIME", "JELLY", "RIDGE", - "DRIFT", "DUSTY", "DEVIL", "TEMPO", "HUMOR", "SENDS", "STEAL", "TENTS", "WAIST", - "ROSES", "REIGN", "NOBLE", "CHEAP", "DENSE", "LINEN", "GEESE", "WOVEN", "POSTS", - "HIRED", "WRATH", "SALAD", "BOWED", "TIRES", "SHARK", "BELTS", "GRASP", "BLAST", - "POLAR", "FUNGI", "TENDS", "PEARL", "LOADS", "JOKES", "VEINS", "FROST", "HEARS", - "LOSES", "HOSTS", "DIVER", "PHASE", "TOADS", "ALERT", "TASKS", "SEAMS", "CORAL", - "FOCUS", "NAKED", "PUPPY", "JUMPS", "SPOIL", "QUART", "MACRO", "FEARS", "FLUNG", - "SPARK", "VIVID", "BROOK", "STEER", "SPRAY", "DECAY", "PORTS", "SOCKS", "URBAN", - "GOALS", "GRANT", "MINUS", "FILMS", "TUNES", "SHAFT", "FIRMS", "SKIES", "BRIDE", - "WRECK", "FLOCK", "STARE", "HOBBY", "BONDS", "DARED", "FADED", "THIEF", "CRUDE", - "PANTS", "FLUTE", "VOTES", "TONAL", "RADAR", "WELLS", "SKULL", "HAIRS", "ARGUE", - "WEARS", "DOLLS", "VOTED", "CAVES", "CARED", "BROOM", "SCENT", "PANEL", "FAIRY", - "OLIVE", "BENDS", "PRISM", "LAMPS", "CABLE", "PEACH", "RUINS", "RALLY", "SCHWA", - "LAMBS", "SELLS", "COOLS", "DRAFT", "CHARM", "LIMBS", "BRAKE", "GAZED", "CUBES", - "DELAY", "BEAMS", "FETCH", "RANKS", "ARRAY", "HARSH", "CAMEL", "VINES", "PICKS", - "NAVAL", "PURSE", "RIGID", "CRAWL", "TOAST", "SOILS", "SAUCE", "BASIN", "PONDS", - "TWINS", "WRIST", "FLUID", "POOLS", "BRAND", "STALK", "ROBOT", "REEDS", "HOOFS", - "BUSES", "SHEER", "GRIEF", "BLOOM", "DWELT", "MELTS", "RISEN", "FLAGS", "KNELT", - "FIBER", "ROOFS", "FREED", "ARMOR", "PILES", "AIMED", "ALGAE", "TWIGS", "LEMON", - "DITCH", "DRUNK", "RESTS", "CHILL", "SLAIN", "PANIC", "CORDS", "TUNED", "CRISP", - "LEDGE", "DIVED", "SWAMP", "CLUNG", "STOLE", "MOLDS", "YARNS", "LIVER", "GAUGE", - "BREED", "STOOL", "GULLS", "AWOKE", "GROSS", "DIARY", "RAILS", "BELLY", "TREND", - "FLASK", "STAKE", "FRIED", "DRAWS", "ACTOR", "HANDY", "BOWLS", "HASTE", "SCOPE", - "DEALS", "KNOTS", "MOONS", "ESSAY", "THUMP", "HANGS", "BLISS", "DEALT", "GAINS", - "BOMBS", "CLOWN", "PALMS", "CONES", "ROAST", "TIDAL", "BORED", "CHANT", "ACIDS", - "DOUGH", "CAMPS", "SWORE", "LOVER", "HOOKS", "MALES", "COCOA", "PUNCH", "AWARD", - "REINS", "NINTH", "NOSES", "LINKS", "DRAIN", "FILLS", "NYLON", "LUNAR", "PULSE", - "FLOWN", "ELBOW", "FATAL", "SITES", "MOTHS", "MEATS", "FOXES", "MINED", "ATTIC", - "FIERY", "MOUNT", "USAGE", "SWEAR", "SNOWY", "RUSTY", "SCARE", "TRAPS", "RELAX", - "REACT", "VALID", "ROBIN", "CEASE", "GILLS", "PRIOR", "SAFER", "POLIO", "LOYAL", - "SWELL", "SALTY", "MARSH", "VAGUE", "WEAVE", "MOUND", "SEALS", "MULES", "VIRUS", - "SCOUT", "ACUTE", "WINDY", "STOUT", "FOLDS", "SEIZE", "HILLY", "JOINS", "PLUCK", - "STACK", "LORDS", "DUNES", "BURRO", "HAWKS", "TROUT", "FEEDS", "SCARF", "HALLS", - "COALS", "TOWEL", "SOULS", "ELECT", "BUGGY", "PUMPS", "LOANS", "SPINS", "FILES", - "OXIDE", "PAINS", "PHOTO", "RIVAL", "FLATS", "SYRUP", "RODEO", "SANDS", "MOOSE", - "PINTS", "CURLY", "COMIC", "CLOAK", "ONION", "CLAMS", "SCRAP", "DIDST", "COUCH", - "CODES", "FAILS", "OUNCE", "LODGE", "GREET", "GYPSY", "UTTER", "PAVED", "ZONES", - "FOURS", "ALLEY", "TILES", "BLESS", "CREST", "ELDER", "KILLS", "YEAST", "ERECT", - "BUGLE", "MEDAL", "ROLES", "HOUND", "SNAIL", "ALTER", "ANKLE", "RELAY", "LOOPS", - "ZEROS", "BITES", "MODES", "DEBTS", "REALM", "GLOVE", "RAYON", "SWIMS", "POKED", - "STRAY", "LIFTS", "MAKER", "LUMPS", "GRAZE", "DREAD", "BARNS", "DOCKS", "MASTS", - "POURS", "WHARF", "CURSE", "PLUMP", "ROBES", "SEEKS", "CEDAR", "CURLS", "JOLLY", - "MYTHS", "CAGES", "GLOOM", "LOCKS", "PEDAL", "BEETS", "CROWS", "ANODE", "SLASH", - "CREEP", "ROWED", "CHIPS", "FISTS", "WINES", "CARES", "VALVE", "NEWER", "MOTEL", - "IVORY", "NECKS", "CLAMP", "BARGE", "BLUES", "ALIEN", "FROWN", "STRAP", "CREWS", - "SHACK", "GONNA", "SAVES", "STUMP", "FERRY", "IDOLS", "COOKS", "JUICY", "GLARE", - "CARTS", "ALLOY", "BULBS", "LAWNS", "LASTS", "FUELS", "ODDLY", "CRANE", "FILED", - "WEIRD", "SHAWL", "SLIPS", "TROOP", "BOLTS", "SUITE", "SLEEK", "QUILT", "TRAMP", - "BLAZE", "ATLAS", "ODORS", "SCRUB", "CRABS", "PROBE", "LOGIC", "ADOBE", "EXILE", - "REBEL", "GRIND", "STING", "SPINE", "CLING", "DESKS", "GROVE", "LEAPS", "PROSE", - "LOFTY", "AGONY", "SNARE", "TUSKS", "BULLS", "MOODS", "HUMID", "FINER", "DIMLY", - "PLANK", "CHINA", "PINES", "GUILT", "SACKS", "BRACE", "QUOTE", "LATHE", "GAILY", - "FONTS", "SCALP", "ADOPT", "FOGGY", "FERNS", "GRAMS", "CLUMP", "PERCH", "TUMOR", - "TEENS", "CRANK", "FABLE", "HEDGE", "GENES", "SOBER", "BOAST", "TRACT", "CIGAR", - "UNITE", "OWING", "THIGH", "HAIKU", "SWISH", "DIKES", "WEDGE", "BOOTH", "EASED", - "FRAIL", "COUGH", "TOMBS", "DARTS", "FORTS", "CHOIR", "POUCH", "PINCH", "HAIRY", - "BUYER", "TORCH", "VIGOR", "WALTZ", "HEATS", "HERBS", "USERS", "FLINT", "CLICK", - "MADAM", "BLEAK", "BLUNT", "AIDED", "LACKS", "MASKS", "WADED", "RISKS", "NURSE", - "CHAOS", "SEWED", "CURED", "AMPLE", "LEASE", "STEAK", "SINKS", "MERIT", "BLUFF", - "BATHE", "GLEAM", "BONUS", "COLTS", "SHEAR", "GLAND", "SILKY", "SKATE", "BIRCH", - "ANVIL", "SLEDS", "GROAN", "MAIDS", "MEETS", "SPECK", "HYMNS", "HINTS", "DROWN", - "BOSOM", "SLICK", "QUEST", "COILS", "SPIED", "SNOWS", "STEAD", "SNACK", "PLOWS", - "BLOND", "TAMED", "THORN", "WAITS", "GLUED", "BANJO", "TEASE", "ARENA", "BULKY", - "CARVE", "STUNT", "WARMS", "SHADY", "RAZOR", "FOLLY", "LEAFY", "NOTCH", "FOOLS", - "OTTER", "PEARS", "FLUSH", "GENUS", "ACHED", "FIVES", "FLAPS", "SPOUT", "SMOTE", - "FUMES", "ADAPT", "CUFFS", "TASTY", "STOOP", "CLIPS", "DISKS", "SNIFF", "LANES", - "BRISK", "IMPLY", "DEMON", "SUPER", "FURRY", "RAGED", "GROWL", "TEXTS", "HARDY", - "STUNG", "TYPED", "HATES", "WISER", "TIMID", "SERUM", "BEAKS", "ROTOR", "CASTS", - "BATHS", "GLIDE", "PLOTS", "TRAIT", "RESIN", "SLUMS", "LYRIC", "PUFFS", "DECKS", - "BROOD", "MOURN", "ALOFT", "ABUSE", "WHIRL", "EDGED", "OVARY", "QUACK", "HEAPS", - "SLANG", "AWAIT", "CIVIC", "SAINT", "BEVEL", "SONAR", "AUNTS", "PACKS", "FROZE", - "TONIC", "CORPS", "SWARM", "FRANK", "REPAY", "GAUNT", "WIRED", "NIECE", "CELLO", - "NEEDY", "CHUCK", "STONY", "MEDIA", "SURGE", "HURTS", "REPEL", "HUSKY", "DATED", - "HUNTS", "MISTS", "EXERT", "DRIES", "MATES", "SWORN", "BAKER", "SPICE", "OASIS", - "BOILS", "SPURS", "DOVES", "SNEAK", "PACES", "COLON", "SIEGE", "STRUM", "DRIER", - "CACAO", "HUMUS", "BALES", "PIPED", "NASTY", "RINSE", "BOXER", "SHRUB", "AMUSE", - "TACKS", "CITED", "SLUNG", "DELTA", "LADEN", "LARVA", "RENTS", "YELLS", "SPOOL", - "SPILL", "CRUSH", "JEWEL", "SNAPS", "STAIN", "KICKS", "TYING", "SLITS", "RATED", - "EERIE", "SMASH", "PLUMS", "ZEBRA", "EARNS", "BUSHY", "SCARY", "SQUAD", "TUTOR", - "SILKS", "SLABS", "BUMPS", "EVILS", "FANGS", "SNOUT", "PERIL", "PIVOT", "YACHT", - "LOBBY", "JEANS", "GRINS", "VIOLA", "LINER", "COMET", "SCARS", "CHOPS", "RAIDS", - "EATER", "SLATE", "SKIPS", "SOLES", "MISTY", "URINE", "KNOBS", "SLEET", "HOLLY", - "PESTS", "FORKS", "GRILL", "TRAYS", "PAILS", "BORNE", "TENOR", "WARES", "CAROL", - "WOODY", "CANON", "WAKES", "KITTY", "MINER", "POLLS", "SHAKY", "NASAL", "SCORN", - "CHESS", "TAXIS", "CRATE", "SHYLY", "TULIP", "FORGE", "NYMPH", "BUDGE", "LOWLY", - "ABIDE", "DEPOT", "OASES", "ASSES", "SHEDS", "FUDGE", "PILLS", "RIVET", "THINE", - "GROOM", "LANKY", "BOOST", "BROTH", "HEAVE", "GRAVY", "BEECH", "TIMED", "QUAIL", - "INERT", "GEARS", "CHICK", "HINGE", "TRASH", "CLASH", "SIGHS", "RENEW", "BOUGH", - "DWARF", "SLOWS", "QUILL", "SHAVE", "SPORE", "SIXES", "CHUNK", "MADLY", "PACED", - "BRAID", "FUZZY", "MOTTO", "SPIES", "SLACK", "MUCUS", "MAGMA", "AWFUL", "DISCS", - "ERASE", "POSED", "ASSET", "CIDER", "TAPER", "THEFT", "CHURN", "SATIN", "SLOTS", - "TAXED", "BULLY", "SLOTH", "SHALE", "TREAD", "RAKED", "CURDS", "MANOR", "AISLE", - "BULGE", "LOINS", "STAIR", "TAPES", "LEANS", "BUNKS", "SQUAT", "TOWED", "LANCE", - "PANES", "SAKES", "HEIRS", "CASTE", "DUMMY", "PORES", "FAUNA", "CROOK", "POISE", - "EPOCH", "RISKY", "WARNS", "FLING", "BERRY", "GRAPE", "FLANK", "DRAGS", "SQUID", - "PELTS", "ICING", "IRONY", "IRONS", "BARKS", "WHOOP", "CHOKE", "DIETS", "WHIPS", - "TALLY", "DOZED", "TWINE", "KITES", "BIKES", "TICKS", "RIOTS", "ROARS", "VAULT", - "LOOMS", "SCOLD", "BLINK", "DANDY", "PUPAE", "SIEVE", "SPIKE", "DUCTS", "LENDS", - "PIZZA", "BRINK", "WIDEN", "PLUMB", "PAGAN", "FEATS", "BISON", "SOGGY", "SCOOP", - "ARGON", "NUDGE", "SKIFF", "AMBER", "SEXES", "ROUSE", "SALTS", "HITCH", "EXALT", - "LEASH", "DINED", "CHUTE", "SNORT", "GUSTS", "MELON", "CHEAT", "REEFS", "LLAMA", - "LASSO", "DEBUT", "QUOTA", "OATHS", "PRONE", "MIXES", "RAFTS", "DIVES", "STALE", - "INLET", "FLICK", "PINTO", "BROWS", "UNTIE", "BATCH", "GREED", "CHORE", "STIRS", - "BLUSH", "ONSET", "BARBS", "VOLTS", "BEIGE", "SWOOP", "PADDY", "LACED", "SHOVE", - "JERKY", "POPPY", "LEAKS", "FARES", "DODGE", "GODLY", "SQUAW", "AFFIX", "BRUTE", - "NICER", "UNDUE", "SNARL", "MERGE", "DOSES", "SHOWY", "DADDY", "ROOST", "VASES", - "SWIRL", "PETTY", "COLDS", "CURRY", "COBRA", "GENIE", "FLARE", "MESSY", "CORES", - "SOAKS", "RIPEN", "WHINE", "AMINO", "PLAID", "SPINY", "MOWED", "BATON", "PEERS", - "VOWED", "PIOUS", "SWANS", "EXITS", "AFOOT", "PLUGS", "IDIOM", "CHILI", "RITES", - "SERFS", "CLEFT", "BERTH", "GRUBS", "ANNEX", "DIZZY", "HASTY", "LATCH", "WASPS", - "MIRTH", "BARON", "PLEAD", "ALOOF", "AGING", "PIXEL", "BARED", "MUMMY", "HOTLY", - "AUGER", "BUDDY", "CHAPS", "BADGE", "STARK", "FAIRS", "GULLY", "MUMPS", "EMERY", - "FILLY", "OVENS", "DRONE", "GAUZE", "IDIOT", "FUSSY", "ANNOY", "SHANK", "GOUGE", - "BLEED", "ELVES", "ROPED", "UNFIT", "BAGGY", "MOWER", "SCANT", "GRABS", "FLEAS", - "LOUSY", "ALBUM", "SAWED", "COOKY", "MURKY", "INFER", "BURLY", "WAGED", "DINGY", - "BRINE", "KNEEL", "CREAK", "VANES", "SMOKY", "SPURT", "COMBS", "EASEL", "LACES", - "HUMPS", "RUMOR", "AROMA", "HORDE", "SWISS", "LEAPT", "OPIUM", "SLIME", "AFIRE", - "PANSY", "MARES", "SOAPS", "HUSKS", "SNIPS", "HAZEL", "LINED", "CAFES", "NAIVE", - "WRAPS", "SIZED", "PIERS", "BESET", "AGILE", "TONGS", "STEED", "FRAUD", "BOOTY", - "VALOR", "DOWNY", "WITTY", "MOSSY", "PSALM", "SCUBA", "TOURS", "POLKA", "MILKY", - "GAUDY", "SHRUG", "TUFTS", "WILDS", "LASER", "TRUSS", "HARES", "CREED", "LILAC", - "SIREN", "TARRY", "BRIBE", "SWINE", "MUTED", "FLIPS", "CURES", "SINEW", "BOXED", - "HOOPS", "GASPS", "HOODS", "NICHE", "YUCCA", "GLOWS", "SEWER", "WHACK", "FUSES", - "GOWNS", "DROOP", "BUCKS", "PANGS", "MAILS", "WHISK", "HAVEN", "CLASP", "SLING", - "STINT", "URGES", "CHAMP", "PIETY", "CHIRP", "PLEAT", "POSSE", "SUNUP", "MENUS", - "HOWLS", "QUAKE", "KNACK", "PLAZA", "FIEND", "CAKED", "BANGS", "ERUPT", "POKER", - "OLDEN", "CRAMP", "VOTER", "POSES", "MANLY", "SLUMP", "FINED", "GRIPS", "GAPED", - "PURGE", "HIKED", "MAIZE", "FLUFF", "STRUT", "SLOOP", "PROWL", "ROACH", "COCKS", - "BLAND", "DIALS", "PLUME", "SLAPS", "SOUPS", "DULLY", "WILLS", "FOAMS", "SOLOS", - "SKIER", "EAVES", "TOTEM", "FUSED", "LATEX", "VEILS", "MUSED", "MAINS", "MYRRH", - "RACKS", "GALLS", "GNATS", "BOUTS", "SISAL", "SHUTS", "HOSES", "DRYLY", "HOVER", - "GLOSS", "SEEPS", "DENIM", "PUTTY", "GUPPY", "LEAKY", "DUSKY", "FILTH", "OBOES", - "SPANS", "FOWLS", "ADORN", "GLAZE", "HAUNT", "DARES", "OBEYS", "BAKES", "ABYSS", - "SMELT", "GANGS", "ACHES", "TRAWL", "CLAPS", "UNDID", "SPICY", "HOIST", "FADES", - "VICAR", "ACORN", "PUSSY", "GRUFF", "MUSTY", "TARTS", "SNUFF", "HUNCH", "TRUCE", - "TWEED", "DRYER", "LOSER", "SHEAF", "MOLES", "LAPSE", "TAWNY", "VEXED", "AUTOS", - "WAGER", "DOMES", "SHEEN", "CLANG", "SPADE", "SOWED", "BROIL", "SLYLY", "STUDS", - "GRUNT", "DONOR", "SLUGS", "ASPEN", "HOMER", "CROAK", "TITHE", "HALTS", "AVERT", - "HAVOC", "HOGAN", "GLINT", "RUDDY", "JEEPS", "FLAKY", "LADLE", "TAUNT", "SNORE", - "FINES", "PROPS", "PRUNE", "PESOS", "RADII", "POKES", "TILED", "DAISY", "HERON", - "VILLA", "FARCE", "BINDS", "CITES", "FIXES", "JERKS", "LIVID", "WAKED", "INKED", - "BOOMS", "CHEWS", "LICKS", "HYENA", "SCOFF", "LUSTY", "SONIC", "SMITH", "USHER", - "TUCKS", "VIGIL", "MOLTS", "SECTS", "SPARS", "DUMPS", "SCALY", "WISPS", "SORES", - "MINCE", "PANDA", "FLIER", "AXLES", "PLIED", "BOOBY", "PATIO", "RABBI", "PETAL", - "POLYP", "TINTS", "GRATE", "TROLL", "TOLLS", "RELIC", "PHONY", "BLEAT", "FLAWS", - "FLAKE", "SNAGS", "APTLY", "DRAWL", "ULCER", "SOAPY", "BOSSY", "MONKS", "CRAGS", - "CAGED", "TWANG", "DINER", "TAPED", "CADET", "GRIDS", "SPAWN", "GUILE", "NOOSE", - "MORES", "GIRTH", "SLIMY", "AIDES", "SPASM", "BURRS", "ALIBI", "LYMPH", "SAUCY", - "MUGGY", "LITER", "JOKED", "GOOFY", "EXAMS", "ENACT", "STORK", "LURED", "TOXIC", - "OMENS", "NEARS", "COVET", "WRUNG", "FORUM", "VENOM", "MOODY", "ALDER", "SASSY", - "FLAIR", "GUILD", "PRAYS", "WRENS", "HAULS", "STAVE", "TILTS", "PECKS", "STOMP", - "GALES", "TEMPT", "CAPES", "MESAS", "OMITS", "TEPEE", "HARRY", "WRING", "EVOKE", - "LIMES", "CLUCK", "LUNGE", "HIGHS", "CANES", "GIDDY", "LITHE", "VERGE", "KHAKI", - "QUEUE", "LOATH", "FOYER", "OUTDO", "FARED", "DETER", "CRUMB", "ASTIR", "SPIRE", - "JUMPY", "EXTOL", "BUOYS", "STUBS", "LUCID", "THONG", "AFORE", "WHIFF", "MAXIM", - "HULLS", "CLOGS", "SLATS", "JIFFY", "ARBOR", "CINCH", "IGLOO", "GOODY", "GAZES", - "DOWEL", "CALMS", "BITCH", "SCOWL", "GULPS", "CODED", "WAVER", "MASON", "LOBES", - "EBONY", "FLAIL", "ISLES", "CLODS", "DAZED", "ADEPT", "OOZED", "SEDAN", "CLAYS", - "WARTS", "KETCH", "SKUNK", "MANES", "ADORE", "SNEER", "MANGO", "FIORD", "FLORA", - "ROOMY", "MINKS", "THAWS", "WATTS", "FREER", "EXULT", "PLUSH", "PALED", "TWAIN", - "CLINK", "SCAMP", "PAWED", "GROPE", "BRAVO", "GABLE", "STINK", "SEVER", "WANED", - "RARER", "REGAL", "WARDS", "FAWNS", "BABES", "UNIFY", "AMEND", "OAKEN", "GLADE", - "VISOR", "HEFTY", "NINES", "THROB", "PECAN", "BUTTS", "PENCE", "SILLS", "JAILS", - "FLYER", "SABER", "NOMAD", "MITER", "BEEPS", "DOMED", "GULFS", "CURBS", "HEATH", - "MOORS", "AORTA", "LARKS", "TANGY", "WRYLY", "CHEEP", "RAGES", "EVADE", "LURES", - "FREAK", "VOGUE", "TUNIC", "SLAMS", "KNITS", "DUMPY", "MANIA", "SPITS", "FIRTH", - "HIKES", "TROTS", "NOSED", "CLANK", "DOGMA", "BLOAT", "BALSA", "GRAFT", "MIDDY", - "STILE", "KEYED", "FINCH", "SPERM", "CHAFF", "WILES", "AMIGO", "COPRA", "AMISS", - "EYING", "TWIRL", "LURCH", "POPES", "CHINS", "SMOCK", "TINES", "GUISE", "GRITS", - "JUNKS", "SHOAL", "CACHE", "TAPIR", "ATOLL", "DEITY", "TOILS", "SPREE", "MOCKS", - "SCANS", "SHORN", "REVEL", "RAVEN", "HOARY", "REELS", "SCUFF", "MIMIC", "WEEDY", - "CORNY", "TRUER", "ROUGE", "EMBER", "FLOES", "TORSO", "WIPES", "EDICT", "SULKY", - "RECUR", "GROIN", "BASTE", "KINKS", "SURER", "PIGGY", "MOLDY", "FRANC", "LIARS", - "INEPT", "GUSTY", "FACET", "JETTY", "EQUIP", "LEPER", "SLINK", "SOARS", "CATER", - "DOWRY", "SIDED", "YEARN", "DECOY", "TABOO", "OVALS", "HEALS", "PLEAS", "BERET", - "SPILT", "GAYLY", "ROVER", "ENDOW", "PYGMY", "CARAT", "ABBEY", "VENTS", "WAKEN", - "CHIMP", "FUMED", "SODAS", "VINYL", "CLOUT", "WADES", "MITES", "SMIRK", "BORES", - "BUNNY", "SURLY", "FROCK", "FORAY", "PURER", "MILKS", "QUERY", "MIRED", "BLARE", - "FROTH", "GRUEL", "NAVEL", "PALER", "PUFFY", "CASKS", "GRIME", "DERBY", "MAMMA", - "GAVEL", "TEDDY", "VOMIT", "MOANS", "ALLOT", "DEFER", "WIELD", "VIPER", "LOUSE", - "ERRED", "HEWED", "ABHOR", "WREST", "WAXEN", "ADAGE", "ARDOR", "STABS", "PORED", - "RONDO", "LOPED", "FISHY", "BIBLE", "HIRES", "FOALS", "FEUDS", "JAMBS", "THUDS", - "JEERS", "KNEAD", "QUIRK", "RUGBY", "EXPEL", "GREYS", "RIGOR", "ESTER", "LYRES", - "ABACK", "GLUES", "LOTUS", "LURID", "RUNGS", "HUTCH", "THYME", "VALET", "TOMMY", - "YOKES", "EPICS", "TRILL", "PIKES", "OZONE", "CAPER", "CHIME", "FREES", "FAMED", - "LEECH", "SMITE", "NEIGH", "ERODE", "ROBED", "HOARD", "SALVE", "CONIC", "GAWKY", - "CRAZE", "JACKS", "GLOAT", "MUSHY", "RUMPS", "FETUS", "WINCE", "PINKS", "SHALT", - "TOOTS", "GLENS", "COOED", "RUSTS", "STEWS", "SHRED", "PARKA", "CHUGS", "WINKS", - "CLOTS", "SHREW", "BOOED", "FILMY", "JUROR", "DENTS", "GUMMY", "GRAYS", "HOOKY", - "BUTTE", "DOGIE", "POLED", "REAMS", "FIFES", "SPANK", "GAYER", "TEPID", "SPOOK", - "TAINT", "FLIRT", "ROGUE", "SPIKY", "OPALS", "MISER", "COCKY", "COYLY", "BALMY", - "SLOSH", "BRAWL", "APHID", "FAKED", "HYDRA", "BRAGS", "CHIDE", "YANKS", "ALLAY", - "VIDEO", "ALTOS", "EASES", "METED", "CHASM", "LONGS", "EXCEL", "TAFFY", "IMPEL", - "SAVOR", "KOALA", "QUAYS", "DAWNS", "PROXY", "CLOVE", "DUETS", "DREGS", "TARDY", - "BRIAR", "GRIMY", "ULTRA", "MEATY", "HALVE", "WAILS", "SUEDE", "MAUVE", "ENVOY", - "ARSON", "COVES", "GOOEY", "BREWS", "SOFAS", "CHUMS", "AMAZE", "ZOOMS", "ABBOT", - "HALOS", "SCOUR", "SUING", "CRIBS", "SAGAS", "ENEMA", "WORDY", "HARPS", "COUPE", - "MOLAR", "FLOPS", "WEEPS", "MINTS", "ASHEN", "FELTS", "ASKEW", "MUNCH", "MEWED", - "DIVAN", "VICES", "JUMBO", "BLOBS", "BLOTS", "SPUNK", "ACRID", "TOPAZ", "CUBED", - "CLANS", "FLEES", "SLURS", "GNAWS", "WELDS", "FORDS", "EMITS", "AGATE", "PUMAS", - "MENDS", "DARKS", "DUKES", "PLIES", "CANNY", "HOOTS", "OOZES", "LAMED", "FOULS", - "CLEFS", "NICKS", "MATED", "SKIMS", "BRUNT", "TUBER", "TINGE", "FATES", "DITTY", - "THINS", "FRETS", "EIDER", "BAYOU", "MULCH", "FASTS", "AMASS", "DAMPS", "MORNS", - "FRIAR", "PALSY", "VISTA", "CROON", "CONCH", "UDDER", "TACOS", "SKITS", "MIKES", - "QUITS", "PREEN", "ASTER", "ADDER", "ELEGY", "PULPY", "SCOWS", "BALED", "HOVEL", - "LAVAS", "CRAVE", "OPTIC", "WELTS", "BUSTS", "KNAVE", "RAZED", "SHINS", "TOTES", - "SCOOT", "DEARS", "CROCK", "MUTES", "TRIMS", "SKEIN", "DOTED", "SHUNS", "VEERS", - "FAKES", "YOKED", "WOOED", "HACKS", "SPRIG", "WANDS", "LULLS", "SEERS", "SNOBS", - "NOOKS", "PINED", "PERKY", "MOOED", "FRILL", "DINES", "BOOZE", "TRIPE", "PRONG", - "DRIPS", "ODDER", "LEVEE", "ANTIC", "SIDLE", "PITHY", "CORKS", "YELPS", "JOKER", - "FLECK", "BUFFS", "SCRAM", "TIERS", "BOGEY", "DOLED", "IRATE", "VALES", "COPED", - "HAILS", "ELUDE", "BULKS", "AIRED", "VYING", "STAGS", "STREW", "COCCI", "PACTS", - "SCABS", "SILOS", "DUSTS", "YODEL", "TERSE", "JADED", "BASER", "JIBES", "FOILS", - "SWAYS", "FORGO", "SLAYS", "PREYS", "TREKS", "QUELL", "PEEKS", "ASSAY", "LURKS", - "EJECT", "BOARS", "TRITE", "BELCH", "GNASH", "WANES", "LUTES", "WHIMS", "DOSED", - "CHEWY", "SNIPE", "UMBRA", "TEEMS", "DOZES", "KELPS", "UPPED", "BRAWN", "DOPED", - "SHUSH", "RINDS", "SLUSH", "MORON", "VOILE", "WOKEN", "FJORD", "SHEIK", "JESTS", - "KAYAK", "SLEWS", "TOTED", "SANER", "DRAPE", "PATTY", "RAVES", "SULFA", "GRIST", - "SKIED", "VIXEN", "CIVET", "VOUCH", "TIARA", "HOMEY", "MOPED", "RUNTS", "SERGE", - "KINKY", "RILLS", "CORNS", "BRATS", "PRIES", "AMBLE", "FRIES", "LOONS", "TSARS", - "DATUM", "MUSKY", "PIGMY", "GNOME", "RAVEL", "OVULE", "ICILY", "LIKEN", "LEMUR", - "FRAYS", "SILTS", "SIFTS", "PLODS", "RAMPS", "TRESS", "EARLS", "DUDES", "WAIVE", - "KARAT", "JOLTS", "PEONS", "BEERS", "HORNY", "PALES", "WREAK", "LAIRS", "LYNCH", - "STANK", "SWOON", "IDLER", "ABORT", "BLITZ", "ENSUE", "ATONE", "BINGO", "ROVES", - "KILTS", "SCALD", "ADIOS", "CYNIC", "DULLS", "MEMOS", "ELFIN", "DALES", "PEELS", - "PEALS", "BARES", "SINUS", "CRONE", "SABLE", "HINDS", "SHIRK", "ENROL", "WILTS", - "ROAMS", "DUPED", "CYSTS", "MITTS", "SAFES", "SPATS", "COOPS", "FILET", "KNELL", - "REFIT", "COVEY", "PUNKS", "KILNS", "FITLY", "ABATE", "TALCS", "HEEDS", "DUELS", - "WANLY", "RUFFS", "GAUSS", "LAPEL", "JAUNT", "WHELP", "CLEAT", "GAUZY", "DIRGE", - "EDITS", "WORMY", "MOATS", "SMEAR", "PRODS", "BOWEL", "FRISK", "VESTS", "BAYED", - "RASPS", "TAMES", "DELVE", "EMBED", "BEFIT", "WAFER", "CEDED", "NOVAS", "FEIGN", - "SPEWS", "LARCH", "HUFFS", "DOLES", "MAMAS", "HULKS", "PRIED", "BRIMS", "IRKED", - "ASPIC", "SWIPE", "MEALY", "SKIMP", "BLUER", "SLAKE", "DOWDY", "PENIS", "BRAYS", - "PUPAS", "EGRET", "FLUNK", "PHLOX", "GRIPE", "PEONY", "DOUSE", "BLURS", "DARNS", - "SLUNK", "LEFTS", "CHATS", "INANE", "VIALS", "STILT", "RINKS", "WOOFS", "WOWED", - "BONGS", "FROND", "INGOT", "EVICT", "SINGE", "SHYER", "FLIED", "SLOPS", "DOLTS", - "DROOL", "DELLS", "WHELK", "HIPPY", "FETED", "ETHER", "COCOS", "HIVES", "JIBED", - "MAZES", "TRIOS", "SIRUP", "SQUAB", "LATHS", "LEERS", "PASTA", "RIFTS", "LOPES", - "ALIAS", "WHIRS", "DICED", "SLAGS", "LODES", "FOXED", "IDLED", "PROWS", "PLAIT", - "MALTS", "CHAFE", "COWER", "TOYED", "CHEFS", "KEELS", "STIES", "RACER", "ETUDE", - "SUCKS", "SULKS", "MICAS", "CZARS", "COPSE", "AILED", "ABLER", "RABID", "GOLDS", - "CROUP", "SNAKY", "VISAS", "PALLS", "MOPES", "BONED", "WISPY", "RAVED", "SWAPS", - "JUNKY", "DOILY", "PAWNS", "TAMER", "POACH", "BAITS", "DAMNS", "GUMBO", "DAUNT", - "PRANK", "HUNKS", "BUXOM", "HERES", "HONKS", "STOWS", "UNBAR", "IDLES", "ROUTS", - "SAGES", "GOADS", "REMIT", "COPES", "DEIGN", "CULLS", "GIRDS", "HAVES", "LUCKS", - "STUNK", "DODOS", "SHAMS", "SNUBS", "ICONS", "USURP", "DOOMS", "HELLS", "SOLED", - "COMAS", "PAVES", "MATHS", "PERKS", "LIMPS", "WOMBS", "BLURB", "DAUBS", "COKES", - "SOURS", "STUNS", "CASED", "MUSTS", "COEDS", "COWED", "APING", "ZONED", "RUMMY", - "FETES", "SKULK", "QUAFF", "RAJAH", "DEANS", "REAPS", "GALAS", "TILLS", "ROVED", - "KUDOS", "TONED", "PARED", "SCULL", "VEXES", "PUNTS", "SNOOP", "BAILS", "DAMES", - "HAZES", "LORES", "MARTS", "VOIDS", "AMEBA", "RAKES", "ADZES", "HARMS", "REARS", - "SATYR", "SWILL", "HEXES", "COLIC", "LEEKS", "HURLS", "YOWLS", "IVIES", "PLOPS", - "MUSKS", "PAPAW", "JELLS", "BUSED", "CRUET", "BIDED", "FILCH", "ZESTS", "ROOKS", - "LAXLY", "RENDS", "LOAMS", "BASKS", "SIRES", "CARPS", "POKEY", "FLITS", "MUSES", - "BAWLS", "SHUCK", "VILER", "LISPS", "PEEPS", "SORER", "LOLLS", "PRUDE", "DIKED", - "FLOSS", "FLOGS", "SCUMS", "DOPES", "BOGIE", "PINKY", "LEAFS", "TUBAS", "SCADS", - "LOWED", "YESES", "BIKED", "QUALM", "EVENS", "CANED", "GAWKS", "WHITS", "WOOLY", - "GLUTS", "ROMPS", "BESTS", "DUNCE", "CRONY", "JOIST", "TUNAS", "BONER", "MALLS", - "PARCH", "AVERS", "CRAMS", "PARES", "DALLY", "BIGOT", "KALES", "FLAYS", "LEACH", - "GUSHY", "POOCH", "HUGER", "SLYER", "GOLFS", "MIRES", "FLUES", "LOAFS", "ARCED", - "ACNES", "NEONS", "FIEFS", "DINTS", "DAZES", "POUTS", "CORED", "YULES", "LILTS", - "BEEFS", "MUTTS", "FELLS", "COWLS", "SPUDS", "LAMES", "JAWED", "DUPES", "DEADS", - "BYLAW", "NOONS", "NIFTY", "CLUED", "VIREO", "GAPES", "METES", "CUTER", "MAIMS", - "DROLL", "CUPID", "MAULS", "SEDGE", "PAPAS", "WHEYS", "EKING", "LOOTS", "HILTS", - "MEOWS", "BEAUS", "DICES", "PEPPY", "RIPER", "FOGEY", "GISTS", "YOGAS", "GILTS", - "SKEWS", "CEDES", "ZEALS", "ALUMS", "OKAYS", "ELOPE", "GRUMP", "WAFTS", "SOOTS", - "BLIMP", "HEFTS", "MULLS", "HOSED", "CRESS", "DOFFS", "RUDER", "PIXIE", "WAIFS", - "OUSTS", "PUCKS", "BIERS", "GULCH", "SUETS", "HOBOS", "LINTS", "BRANS", "TEALS", - "GARBS", "PEWEE", "HELMS", "TURFS", "QUIPS", "WENDS", "BANES", "NAPES", "ICIER", - "SWATS", "BAGEL", "HEXED", "OGRES", "GONER", "GILDS", "PYRES", "LARDS", "BIDES", - "PAGED", "TALON", "FLOUT", "MEDIC", "VEALS", "PUTTS", "DIRKS", "DOTES", "TIPPY", - "BLURT", "PITHS", "ACING", "BARER", "WHETS", "GAITS", "WOOLS", "DUNKS", "HEROS", - "SWABS", "DIRTS", "JUTES", "HEMPS", "SURFS", "OKAPI", "CHOWS", "SHOOS", "DUSKS", - "PARRY", "DECAL", "FURLS", "CILIA", "SEARS", "NOVAE", "MURKS", "WARPS", "SLUES", - "LAMER", "SARIS", "WEANS", "PURRS", "DILLS", "TOGAS", "NEWTS", "MEANY", "BUNTS", - "RAZES", "GOONS", "WICKS", "RUSES", "VENDS", "GEODE", "DRAKE", "JUDOS", "LOFTS", - "PULPS", "LAUDS", "MUCKS", "VISES", "MOCHA", "OILED", "ROMAN", "ETHYL", "GOTTA", - "FUGUE", "SMACK", "GOURD", "BUMPY", "RADIX", "FATTY", "BORAX", "CUBIT", "CACTI", - "GAMMA", "FOCAL", "AVAIL", "PAPAL", "GOLLY", "ELITE", "VERSA", "BILLY", "ADIEU", - "ANNUM", "HOWDY", "RHINO", "NORMS", "BOBBY", "AXIOM", "SETUP", "YOLKS", "TERNS", - "MIXER", "GENRE", "KNOLL", "ABODE", "JUNTA", "GORGE", "COMBO", "ALPHA", "OVERT", - "KINDA", "SPELT", "PRICK", "NOBLY", "EPHOD", "AUDIO", "MODAL", "VELDT", "WARTY", - "FLUKE", "BONNY", "BREAM", "ROSIN", "BOLLS", "DOERS", "DOWNS", "BEADY", "MOTIF", - "HUMPH", "FELLA", "MOULD", "CREPE", "KERNS", "ALOHA", "GLYPH", "AZURE", "RISER", - "BLEST", "LOCUS", "LUMPY", "BERYL", "WANNA", "BRIER", "TUNER", "ROWDY", "MURAL", - "TIMER", "CANST", "KRILL", "QUOTH", "LEMME", "TRIAD", "TENON", "AMPLY", "DEEPS", - "PADRE", "LEANT", "PACER", "OCTAL", "DOLLY", "TRANS", "SUMAC", "FOAMY", "LOLLY", - "GIVER", "QUIPU", "CODEX", "MANNA", "UNWED", "VODKA", "FERNY", "SALON", "DUPLE", - "BORON", "REVUE", "CRIER", "ALACK", "INTER", "DILLY", "WHIST", "CULTS", "SPAKE", - "RESET", "LOESS", "DECOR", "MOVER", "VERVE", "ETHIC", "GAMUT", "LINGO", "DUNNO", - "ALIGN", "SISSY", "INCUR", "REEDY", "AVANT", "PIPER", "WAXER", "CALYX", "BASIL", - "COONS", "SEINE", "PINEY", "LEMMA", "TRAMS", "WINCH", "WHIRR", "SAITH", "IONIC", - "HEADY", "HAREM", "TUMMY", "SALLY", "SHIED", "DROSS", "FARAD", "SAVER", "TILDE", - "JINGO", "BOWER", "SERIF", "FACTO", "BELLE", "INSET", "BOGUS", "CAVED", "FORTE", - "SOOTY", "BONGO", "TOVES", "CREDO", "BASAL", "YELLA", "AGLOW", "GLEAN", "GUSTO", - "HYMEN", "ETHOS", "TERRA", "BRASH", "SCRIP", "SWASH", "ALEPH", "TINNY", "ITCHY", - "WANTA", "TRICE", "JOWLS", "GONGS", "GARDE", "BORIC", "TWILL", "SOWER", "HENRY", - "AWASH", "LIBEL", "SPURN", "SABRE", "REBUT", "PENAL", "OBESE", "SONNY", "QUIRT", - "MEBBE", "TACIT", "GREEK", "XENON", "HULLO", "PIQUE", "ROGER", "NEGRO", "HADST", - "GECKO", "BEGET", "UNCUT", "ALOES", "LOUIS", "QUINT", "CLUNK", "RAPED", "SALVO", - "DIODE", "MATEY", "HERTZ", "XYLEM", "KIOSK", "APACE", "CAWED", "PETER", "WENCH", - "COHOS", "SORTA", "GAMBA", "BYTES", "TANGO", "NUTTY", "AXIAL", "ALECK", "NATAL", - "CLOMP", "GORED", "SIREE", "BANDY", "GUNNY", "RUNIC", "WHIZZ", "RUPEE", "FATED", - "WIPER", "BARDS", "BRINY", "STAID", "HOCKS", "OCHRE", "YUMMY", "GENTS", "SOUPY", - "ROPER", "SWATH", "CAMEO", "EDGER", "SPATE", "GIMME", "EBBED", "BREVE", "THETA", - "DEEMS", "DYKES", "SERVO", "TELLY", "TABBY", "TARES", "BLOCS", "WELCH", "GHOUL", - "VITAE", "CUMIN", "DINKY", "BRONC", "TABOR", "TEENY", "COMER", "BORER", "SIRED", - "PRIVY", "MAMMY", "DEARY", "GYROS", "SPRIT", "CONGA", "QUIRE", "THUGS", "FUROR", - "BLOKE", "RUNES", "BAWDY", "CADRE", "TOXIN", "ANNUL", "EGGED", "ANION", "NODES", - "PICKY", "STEIN", "JELLO", "AUDIT", "ECHOS", "FAGOT", "LETUP", "EYRIE", "FOUNT", - "CAPED", "AXONS", "AMUCK", "BANAL", "RILED", "PETIT", "UMBER", "MILER", "FIBRE", - "AGAVE", "BATED", "BILGE", "VITRO", "FEINT", "PUDGY", "MATER", "MANIC", "UMPED", - "PESKY", "STREP", "SLURP", "PYLON", "PUREE", "CARET", "TEMPS", "NEWEL", "YAWNS", - "SEEDY", "TREED", "COUPS", "RANGY", "BRADS", "MANGY", "LONER", "CIRCA", "TIBIA", - "AFOUL", "MOMMY", "TITER", "CARNE", "KOOKY", "MOTES", "AMITY", "SUAVE", "HIPPO", - "CURVY", "SAMBA", "NEWSY", "ANISE", "IMAMS", "TULLE", "AWAYS", "LIVEN", "HALLO", - "WALES", "OPTED", "CANTO", "IDYLL", "BODES", "CURIO", "WRACK", "HIKER", "CHIVE", - "YOKEL", "DOTTY", "DEMUR", "CUSPS", "SPECS", "QUADS", "LAITY", "TONER", "DECRY", - "WRITS", "SAUTE", "CLACK", "AUGHT", "LOGOS", "TIPSY", "NATTY", "DUCAL", "BIDET", - "BULGY", "METRE", "LUSTS", "UNARY", "GOETH", "BALER", "SITED", "SHIES", "HASPS", - "BRUNG", "HOLED", "SWANK", "LOOKY", "MELEE", "HUFFY", "LOAMY", "PIMPS", "TITAN", - "BINGE", "SHUNT", "FEMUR", "LIBRA", "SEDER", "HONED", "ANNAS", "COYPU", "SHIMS", - "ZOWIE", "JIHAD", "SAVVY", "NADIR", "BASSO", "MONIC", "MANED", "MOUSY", "OMEGA", - "LAVER", "PRIMA", "PICAS", "FOLIO", "MECCA", "REALS", "TROTH", "TESTY", "BALKY", - "CRIMP", "CHINK", "ABETS", "SPLAT", "ABACI", "VAUNT", "CUTIE", "PASTY", "MORAY", - "LEVIS", "RATTY", "ISLET", "JOUST", "MOTET", "VIRAL", "NUKES", "GRADS", "COMFY", - "VOILA", "WOOZY", "BLUED", "WHOMP", "SWARD", "METRO", "SKEET", "CHINE", "AERIE", - "BOWIE", "TUBBY", "EMIRS", "COATI", "UNZIP", "SLOBS", "TRIKE", "FUNKY", "DUCAT", - "DEWEY", "SKOAL", "WADIS", "OOMPH", "TAKER", "MINIM", "GETUP", "STOIC", "SYNOD", - "RUNTY", "FLYBY", "BRAZE", "INLAY", "VENUE", "LOUTS", "PEATY", "ORLON", "HUMPY", - "RADON", "BEAUT", "RASPY", "UNFED", "CRICK", "NAPPY", "VIZOR", "YIPES", "REBUS", - "DIVOT", "KIWIS", "VETCH", "SQUIB", "SITAR", "KIDDO", "DYERS", "COTTA", "MATZO", - "LAGER", "ZEBUS", "CRASS", "DACHA", "KNEED", "DICTA", "FAKIR", "KNURL", "RUNNY", - "UNPIN", "JULEP", "GLOBS", "NUDES", "SUSHI", "TACKY", "STOKE", "KAPUT", "BUTCH", - "HULAS", "CROFT", "ACHOO", "GENII", "NODAL", "OUTGO", "SPIEL", "VIOLS", "FETID", - "CAGEY", "FUDGY", "EPOXY", "LEGGY", "HANKY", "LAPIS", "FELON", "BEEFY", "COOTS", - "MELBA", "CADDY", "SEGUE", "BETEL", "FRIZZ", "DREAR", "KOOKS", "TURBO", "HOAGY", - "MOULT", "HELIX", "ZONAL", "ARIAS", "NOSEY", "PAEAN", "LACEY", "BANNS", "SWAIN", - "FRYER", "RETCH", "TENET", "GIGAS", "WHINY", "OGLED", "RUMEN", "BEGOT", "CRUSE", - "ABUTS", "RIVEN", "BALKS", "SINES", "SIGMA", "ABASE", "ENNUI", "GORES", "UNSET", - "AUGUR", "SATED", "ODIUM", "LATIN", "DINGS", "MOIRE", "SCION", "HENNA", "KRAUT", - "DICKS", "LIFER", "PRIGS", "BEBOP", "GAGES", "GAZER", "FANNY", "GIBES", "AURAL", - "TEMPI", "HOOCH", "RAPES", "SNUCK", "HARTS", "TECHS", "EMEND", "NINNY", "GUAVA", - "SCARP", "LIEGE", "TUFTY", "SEPIA", "TOMES", "CAROB", "EMCEE", "PRAMS", "POSER", - "VERSO", "HUBBA", "JOULE", "BAIZE", "BLIPS", "SCRIM", "CUBBY", "CLAVE", "WINOS", - "REARM", "LIENS", "LUMEN", "CHUMP", "NANNY", "TRUMP", "FICHU", "CHOMP", "HOMOS", - "PURTY", "MASER", "WOOSH", "PATSY", "SHILL", "RUSKS", "AVAST", "SWAMI", "BODED", - "AHHHH", "LOBED", "NATCH", "SHISH", "TANSY", "SNOOT", "PAYER", "ALTHO", "SAPPY", - "LAXER", "HUBBY", "AEGIS", "RILES", "DITTO", "JAZZY", "DINGO", "QUASI", "SEPTA", - "PEAKY", "LORRY", "HEERD", "BITTY", "PAYEE", "SEAMY", "APSES", "IMBUE", "BELIE", - "CHARY", "SPOOF", "PHYLA", "CLIME", "BABEL", "WACKY", "SUMPS", "SKIDS", "KHANS", - "CRYPT", "INURE", "NONCE", "OUTEN", "FAIRE", "HOOEY", "ANOLE", "KAZOO", "CALVE", - "LIMBO", "ARGOT", "DUCKY", "FAKER", "VIBES", "GASSY", "UNLIT", "NERVY", "FEMME", - "BITER", "FICHE", "BOORS", "GAFFE", "SAXES", "RECAP", "SYNCH", "FACIE", "DICEY", - "OUIJA", "HEWER", "LEGIT", "GURUS", "EDIFY", "TWEAK", "CARON", "TYPOS", "RERUN", - "POLLY", "SURDS", "HAMZA", "NULLS", "HATER", "LEFTY", "MOGUL", "MAFIA", "DEBUG", - "PATES", "BLABS", "SPLAY", "TALUS", "PORNO", "MOOLA", "NIXED", "KILOS", "SNIDE", - "HORSY", "GESSO", "JAGGY", "TROVE", "NIXES", "CREEL", "PATER", "IOTAS", "CADGE", - "SKYED", "HOKUM", "FURZE", "ANKHS", "CURIE", "NUTSY", "HILUM", "REMIX", "ANGST", - "BURLS", "JIMMY", "VEINY", "TRYST", "CODON", "BEFOG", "GAMED", "FLUME", "AXMAN", - "DOOZY", "LUBES", "RHEAS", "BOZOS", "BUTYL", "KELLY", "MYNAH", "JOCKS", "DONUT", - "AVIAN", "WURST", "CHOCK", "QUASH", "QUALS", "HAYED", "BOMBE", "CUSHY", "SPACY", - "PUKED", "LEERY", "THEWS", "PRINK", "AMENS", "TESLA", "INTRO", "FIVER", "FRUMP", - "CAPOS", "OPINE", "CODER", "NAMER", "JOWLY", "PUKES", "HALED", "CHARD", "DUFFS", - "BRUIN", "REUSE", "WHANG", "TOONS", "FRATS", "SILTY", "TELEX", "CUTUP", "NISEI", - "NEATO", "DECAF", "SOFTY", "BIMBO", "ADLIB", "LOONY", "SHOED", "AGUES", "PEEVE", - "NOWAY", "GAMEY", "SARGE", "RERAN", "EPACT", "POTTY", "CONED", "UPEND", "NARCO", - "IKATS", "WHORL", "JINKS", "TIZZY", "WEEPY", "POSIT", "MARGE", "VEGAN", "CLOPS", - "NUMBS", "REEKS", "RUBES", "ROWER", "BIPED", "TIFFS", "HOCUS", "HAMMY", "BUNCO", - "FIXIT", "TYKES", "CHAWS", "YUCKY", "HOKEY", "RESEW", "MAVEN", "ADMAN", "SCUZZ", - "SLOGS", "SOUSE", "NACHO", "MIMED", "MELDS", "BOFFO", "DEBIT", "PINUP", "VAGUS", - "GULAG", "RANDY", "BOSUN", "EDUCE", "FAXES", "AURAS", "PESTO", "ANTSY", "BETAS", - "FIZZY", "DORKY", "SNITS", "MOXIE", "THANE", "MYLAR", "NOBBY", "GAMIN", "GOUTY", - "ESSES", "GOYIM", "PANED", "DRUID", "JADES", "REHAB", "GOFER", "TZARS", "OCTET", - "HOMED", "SOCKO", "DORKS", "EARED", "ANTED", "ELIDE", "FAZES", "OXBOW", "DOWSE", - "SITUS", "MACAW", "SCONE", "DRILY", "HYPER", "SALSA", "MOOCH", "GATED", "UNJAM", - "LIPID", "MITRE", "VENAL", "KNISH", "RITZY", "DIVAS", "TORUS", "MANGE", "DIMER", - "RECUT", "MESON", "WINED", "FENDS", "PHAGE", "FIATS", "CAULK", "CAVIL", "PANTY", - "ROANS", "BILKS", "HONES", "BOTCH", "ESTOP", "SULLY", "SOOTH", "GELDS", "AHOLD", - "RAPER", "PAGER", "FIXER", "INFIX", "HICKS", "TUXES", "PLEBE", "TWITS", "ABASH", - "TWIXT", "WACKO", "PRIMP", "NABLA", "GIRTS", "MIFFS", "EMOTE", "XEROX", "REBID", - "SHAHS", "RUTTY", "GROUT", "GRIFT", "DEIFY", "BIDDY", "KOPEK", "SEMIS", "BRIES", - "ACMES", "PITON", "HUSSY", "TORTS", "DISCO", "WHORE", "BOOZY", "GIBED", "VAMPS", - "AMOUR", "SOPPY", "GONZO", "DURST", "WADER", "TUTUS", "PERMS", "CATTY", "GLITZ", - "BRIGS", "NERDS", "BARMY", "GIZMO", "OWLET", "SAYER", "MOLLS", "SHARD", "WHOPS", - "COMPS", "CORER", "COLAS", "MATTE", "DROID", "PLOYS", "VAPID", "CAIRN", "DEISM", - "MIXUP", "YIKES", "PROSY", "RAKER", "FLUBS", "WHISH", "REIFY", "CRAPS", "SHAGS", - "CLONE", "HAZED", "MACHO", "RECTO", "REFIX", "DRAMS", "BIKER", "AQUAS", "PORKY", - "DOYEN", "EXUDE", "GOOFS", "DIVVY", "NOELS", "JIVED", "HULKY", "CAGER", "HARPY", - "OLDIE", "VIVAS", "ADMIX", "CODAS", "ZILCH", "DEIST", "ORCAS", "RETRO", "PILAF", - "PARSE", "RANTS", "ZINGY", "TODDY", "CHIFF", "MICRO", "VEEPS", "GIRLY", "NEXUS", - "DEMOS", "BIBBS", "ANTES", "LULUS", "GNARL", "ZIPPY", "IVIED", "EPEES", "WIMPS", - "TROMP", "GRAIL", "YOYOS", "POUFS", "HALES", "ROUST", "CABAL", "RAWER", "PAMPA", - "MOSEY", "KEFIR", "BURGS", "UNMET", "CUSPY", "BOOBS", "BOONS", "HYPES", "DYNES", - "NARDS", "LANAI", "YOGIS", "SEPAL", "QUARK", "TOKED", "PRATE", "AYINS", "HAWED", - "SWIGS", "VITAS", "TOKER", "DOPER", "BOSSA", "LINTY", "FOIST", "MONDO", "STASH", - "KAYOS", "TWERP", "ZESTY", "CAPON", "WIMPY", "REWED", "FUNGO", "TAROT", "FROSH", - "KABOB", "PINKO", "REDID", "MIMEO", "HEIST", "TARPS", "LAMAS", "SUTRA", "DINAR", - "WHAMS", "BUSTY", "SPAYS", "MAMBO", "NABOB", "PREPS", "ODOUR", "CABBY", "CONKS", - "SLUFF", "DADOS", "HOURI", "SWART", "BALMS", "GUTSY", "FAXED", "EGADS", "PUSHY", - "RETRY", "AGORA", "DRUBS", "DAFFY", "CHITS", "MUFTI", "KARMA", "LOTTO", "TOFFS", - "BURPS", "DEUCE", "ZINGS", "KAPPA", "CLADS", "DOGGY", "DUPER", "SCAMS", "OGLER", - "MIMES", "THROE", "ZETAS", "WALED", "PROMO", "BLATS", "MUFFS", "OINKS", "VIAND", - "COSET", "FINKS", "FADDY", "MINIS", "SNAFU", "SAUNA", "USURY", "MUXES", "CRAWS", - "STATS", "CONDO", "COXES", "LOOPY", "DORMS", "ASCOT", "DIPPY", "EXECS", "DOPEY", - "ENVOI", "UMPTY", "GISMO", "FAZED", "STROP", "JIVES", "SLIMS", "BATIK", "PINGS", - "SONLY", "LEGGO", "PEKOE", "PRAWN", "LUAUS", "CAMPY", "OODLE", "PREXY", "PROMS", - "TOUTS", "OGLES", "TWEET", "TOADY", "NAIAD", "HIDER", "NUKED", "FATSO", "SLUTS", - "OBITS", "NARCS", "TYROS", "DELIS", "WOOER", "HYPED", "POSET", "BYWAY", "TEXAS", - "SCROD", "AVOWS", "FUTON", "TORTE", "TUPLE", "CAROM", "KEBAB", "TAMPS", "JILTS", - "DUALS", "ARTSY", "REPRO", "MODEM", "TOPED", "PSYCH", "SICKO", "KLUTZ", "TARNS", - "COXED", "DRAYS", "CLOYS", "ANDED", "PIKER", "AIMER", "SURAS", "LIMOS", "FLACK", - "HAPAX", "DUTCH", "MUCKY", "SHIRE", "KLIEG", "STAPH", "LAYUP", "TOKES", "AXING", - "TOPER", "DUVET", "COWRY", "PROFS", "BLAHS", "ADDLE", "SUDSY", "BATTY", "COIFS", - "SUETY", "GABBY", "HAFTA", "PITAS", "GOUDA", "DEICE", "TAUPE", "TOPES", "DUCHY", - "NITRO", "CARNY", "LIMEY", "ORALS", "HIRER", "TAXER", "ROILS", "RUBLE", "ELATE", - "DOLOR", "WRYER", "SNOTS", "QUAIS", "COKED", "GIMEL", "GORSE", "MINAS", "GOEST", - "AGAPE", "MANTA", "JINGS", "ILIAC", "ADMEN", "OFFEN", "CILLS", "OFFAL", "LOTTA", - "BOLAS", "THWAP", "ALWAY", "BOGGY", "DONNA", "LOCOS", "BELAY", "GLUEY", "BITSY", - "MIMSY", "HILAR", "OUTTA", "VROOM", "FETAL", "RATHS", "RENAL", "DYADS", "CROCS", - "VIRES", "CULPA", "KIVAS", "FEIST", "TEATS", "THATS", "YAWLS", "WHENS", "ABACA", - "OHHHH", "APHIS", "FUSTY", "ECLAT", "PERDU", "MAYST", "EXEAT", "MOLLY", "SUPRA", - "WETLY", "PLASM", "BUFFA", "SEMEN", "PUKKA", "TAGUA", "PARAS", "STOAT", "SECCO", - "CARTE", "HAUTE", "MOLAL", "SHADS", "FORMA", "OVOID", "PIONS", "MODUS", "BUENO", - "RHEUM", "SCURF", "PARER", "EPHAH", "DOEST", "SPRUE", "FLAMS", "MOLTO", "DIETH", - "CHOOS", "MIKED", "BRONX", "GOOPY", "BALLY", "PLUMY", "MOONY", "MORTS", "YOURN", - "BIPOD", "SPUME", "ALGAL", "AMBIT", "MUCHO", "SPUED", "DOZER", "HARUM", "GROAT", - "SKINT", "LAUDE", "THRUM", "PAPPY", "ONCET", "RIMED", "GIGUE", "LIMED", "PLEIN", - "REDLY", "HUMPF", "LITES", "SEEST", "GREBE", "ABSIT", "THANX", "PSHAW", "YAWPS", - "PLATS", "PAYED", "AREAL", "TILTH", "YOUSE", "GWINE", "THEES", "WATSA", "LENTO", - "SPITZ", "YAWED", "GIPSY", "SPRAT", "CORNU", "AMAHS", "BLOWY", "WAHOO", "LUBRA", - "MECUM", "WHOOO", "COQUI", "SABRA", "EDEMA", "MRADS", "DICOT", "ASTRO", "KITED", - "OUZEL", "DIDOS", "GRATA", "BONNE", "AXMEN", "KLUNK", "SUMMA", "LAVES", "PURLS", - "YAWNY", "TEARY", "MASSE", "LARGO", "BAZAR", "PSSST", "SYLPH", "LULAB", "TOQUE", - "FUGIT", "PLUNK", "ORTHO", "LUCRE", "COOCH", "WHIPT", "FOLKY", "TYRES", "WHEEE", - "CORKY", "INJUN", "SOLON", "DIDOT", "KERFS", "RAYED", "WASSA", "CHILE", "BEGAT", - "NIPPY", "LITRE", "MAGNA", "REBOX", "HYDRO", "MILCH", "BRENT", "GYVES", "LAZED", - "FEUED", "MAVIS", "INAPT", "BAULK", "CASUS", "SCRUM", "WISED", "FOSSA", "DOWER", - "KYRIE", "BHOYS", "SCUSE", "FEUAR", "OHMIC", "JUSTE", "UKASE", "BEAUX", "TUSKY", - "ORATE", "MUSTA", "LARDY", "INTRA", "QUIFF", "EPSOM", "NEATH", "OCHER", "TARED", - "HOMME", "MEZZO", "CORMS", "PSOAS", "BEAKY", "TERRY", "INFRA", "SPIVS", "TUANS", - "BELLI", "BERGS", "ANIMA", "WEIRS", "MAHUA", "SCOPS", "MANSE", "TITRE", "CURIA", - "KEBOB", "CYCAD", "TALKY", "FUCKS", "TAPIS", "AMIDE", "DOLCE", "SLOES", "JAKES", - "RUSSE", "BLASH", "TUTTI", "PRUTA", "PANGA", "BLEBS", "TENCH", "SWARF", "HEREM", - "MISSY", "MERSE", "PAWKY", "LIMEN", "VIVRE", "CHERT", "UNSEE", "TIROS", "BRACK", - "FOOTS", "WELSH", "FOSSE", "KNOPS", "ILEUM", "NOIRE", "FIRMA", "PODGY", "LAIRD", - "THUNK", "SHUTE", "ROWAN", "SHOJI", "POESY", "UNCAP", "FAMES", "GLEES", "COSTA", - "TURPS", "FORES", "SOLUM", "IMAGO", "BYRES", "FONDU", "CONEY", "POLIS", "DICTU", - "KRAAL", "SHERD", "MUMBO", "WROTH", "CHARS", "UNBOX", "VACUO", "SLUED", "WEEST", - "HADES", "WILED", "SYNCS", "MUSER", "EXCON", "HOARS", "SIBYL", "PASSE", "JOEYS", - "LOTSA", "LEPTA", "SHAYS", "BOCKS", "ENDUE", "DARER", "NONES", "ILEUS", "PLASH", - "BUSBY", "WHEAL", "BUFFO", "YOBBO", "BILES", "POXES", "ROOTY", "LICIT", "TERCE", - "BROMO", "HAYEY", "DWEEB", "IMBED", "SARAN", "BRUIT", "PUNKY", "SOFTS", "BIFFS", - "LOPPY", "AGARS", "AQUAE", "LIVRE", "BIOME", "BUNDS", "SHEWS", "DIEMS", "GINNY", - "DEGUM", "POLOS", "DESEX", "UNMAN", "DUNGY", "VITAM", "WEDGY", "GLEBE", "APERS", - "RIDGY", "ROIDS", "WIFEY", "VAPES", "WHOAS", "BUNKO", "YOLKY", "ULNAS", "REEKY", - "BODGE", "BRANT", "DAVIT", "DEQUE", "LIKER", "JENNY", "TACTS", "FULLS", "TREAP", - "LIGNE", "ACKED", "REFRY", "VOWER", "AARGH", "CHURL", "MOMMA", "GAOLS", "WHUMP", - "ARRAS", "MARLS", "TILER", "GROGS", "MEMES", "MIDIS", "TIDED", "HALER", "DUCES", - "TWINY", "POSTE", "UNRIG", "PRISE", "DRABS", "QUIDS", "FACER", "SPIER", "BARIC", - "GEOID", "REMAP", "TRIER", "GUNKS", "STENO", "STOMA", "AIRER", "OVATE", "TORAH", - "APIAN", "SMUTS", "POCKS", "YURTS", "EXURB", "DEFOG", "NUDER", "BOSKY", "NIMBI", - "MOTHY", "JOYED", "LABIA", "PARDS", "JAMMY", "BIGLY", "FAXER", "HOPPY", "NURBS", - "COTES", "DISHY", "VISED", "CELEB", "PISMO", "CASAS", "WITHS", "DODGY", "SCUDI", - "MUNGS", "MUONS", "UREAS", "IOCTL", "UNHIP", "KRONE", "SAGER", "VERST", "EXPAT", - "GRONK", "UVULA", "SHAWM", "BILGY", "BRAES", "CENTO", "WEBBY", "LIPPY", "GAMIC", - "LORDY", "MAZED", "TINGS", "SHOAT", "FAERY", "WIRER", "DIAZO", "CARER", "RATER", - "GREPS", "RENTE", "ZLOTY", "VIERS", "UNAPT", "POOPS", "FECAL", "KEPIS", "TAXON", - "EYERS", "WONTS", "SPINA", "STOAE", "YENTA", "POOEY", "BURET", "JAPAN", "BEDEW", - "HAFTS", "SELFS", "OARED", "HERBY", "PRYER", "OAKUM", "DINKS", "TITTY", "SEPOY", - "PENES", "FUSEE", "WINEY", "GIMPS", "NIHIL", "RILLE", "GIBER", "OUSEL", "UMIAK", - "CUPPY", "HAMES", "SHITS", "AZINE", "GLADS", "TACET", "BUMPH", "COYER", "HONKY", - "GAMER", "GOOKY", "WASPY", "SEDGY", "BENTS", "VARIA", "DJINN", "JUNCO", "PUBIC", - "WILCO", "LAZES", "IDYLS", "LUPUS", "RIVES", "SNOOD", "SCHMO", "SPAZZ", "FINIS", - "NOTER", "PAVAN", "ORBED", "BATES", "PIPET", "BADDY", "GOERS", "SHAKO", "STETS", - "SEBUM", "SEETH", "LOBAR", "RAVER", "AJUGA", "RICED", "VELDS", "DRIBS", "VILLE", - "DHOWS", "UNSEW", "HALMA", "KRONA", "LIMBY", "JIFFS", "TREYS", "BAUDS", "PFFFT", - "MIMER", "PLEBS", "CANER", "JIBER", "CUPPA", "WASHY", "CHUFF", "UNARM", "YUKKY", - "STYES", "WAKER", "FLAKS", "MACES", "RIMES", "GIMPY", "GUANO", "LIRAS", "KAPOK", - "SCUDS", "BWANA", "ORING", "AIDER", "PRIER", "KLUGY", "MONTE", "GOLEM", "VELAR", - "FIRER", "PIETA", "UMBEL", "CAMPO", "UNPEG", "FOVEA", "ABEAM", "BOSON", "ASKER", - "GOTHS", "VOCAB", "VINED", "TROWS", "TIKIS", "LOPER", "INDIE", "BOFFS", "SPANG", - "GRAPY", "TATER", "ICHOR", "KILTY", "LOCHS", "SUPES", "DEGAS", "FLICS", "TORSI", - "BETHS", "WEBER", "RESAW", "LAWNY", "COVEN", "MUJIK", "RELET", "THERM", "HEIGH", - "SHNOR", "TRUED", "ZAYIN", "LIEST", "BARFS", "BASSI", "QOPHS", "ROILY", "FLABS", - "PUNNY", "OKRAS", "HANKS", "DIPSO", "NERFS", "FAUNS", "CALLA", "PSEUD", "LURER", - "MAGUS", "OBEAH", "ATRIA", "TWINK", "PALMY", "POCKY", "PENDS", "RECTA", "PLONK", - "SLAWS", "KEENS", "NICAD", "PONES", "INKER", "WHEWS", "GROKS", "MOSTS", "TREWS", - "ULNAR", "GYPPY", "COCAS", "EXPOS", "ERUCT", "OILER", "VACUA", "DRECK", "DATER", - "ARUMS", "TUBAL", "VOXEL", "DIXIT", "BEERY", "ASSAI", "LADES", "ACTIN", "GHOTI", - "BUZZY", "MEADS", "GRODY", "RIBBY", "CLEWS", "CREME", "EMAIL", "PYXIE", "KULAK", - "BOCCI", "RIVED", "DUDDY", "HOPER", "LAPIN", "WONKS", "PETRI", "PHIAL", "FUGAL", - "HOLON", "BOOMY", "DUOMO", "MUSOS", "SHIER", "HAYER", "PORGY", "HIVED", "LITHO", - "FISTY", "STAGY", "LUVYA", "MARIA", "SMOGS", "ASANA", "YOGIC", "SLOMO", "FAWNY", - "AMINE", "WEFTS", "GONAD", "TWIRP", "BRAVA", "PLYER", "FERMI", "LOGES", "NITER", - "REVET", "UNATE", "GYVED", "TOTTY", "ZAPPY", "HONER", "GIROS", "DICER", "CALKS", - "LUXES", "MONAD", "CRUFT", "QUOIN", "FUMER", "AMPED", "SHLEP", "VINCA", "YAHOO", - "VULVA", "ZOOEY", "DRYAD", "NIXIE", "MOPER", "IAMBS", "LUNES", "NUDIE", "LIMNS", - "WEALS", "NOHOW", "MIAOW", "GOUTS", "MYNAS", "MAZER", "KIKES", "OXEYE", "STOUP", - "JUJUS", "DEBAR", "PUBES", "TAELS", "DEFUN", "RANDS", "BLEAR", "PAVER", "GOOSY", - "SPROG", "OLEOS", "TOFFY", "PAWER", "MACED", "CRITS", "KLUGE", "TUBED", "SAHIB", - "GANEF", "SCATS", "SPUTA", "VANED", "ACNED", "TAXOL", "PLINK", "OWETH", "TRIBS", - "RESAY", "BOULE", "THOUS", "HAPLY", "GLANS", "MAXIS", "BEZEL", "ANTIS", "PORKS", - "QUOIT", "ALKYD", "GLARY", "BEAMY", "HEXAD", "BONKS", "TECUM", "KERBS", "FILAR", - "FRIER", "REDUX", "ABUZZ", "FADER", "SHOER", "COUTH", "TRUES", "GUYED", "GOONY", - "BOOKY", "FUZES", "HURLY", "GENET", "HODAD", "CALIX", "FILER", "PAWLS", "IODIC", - "UTERO", "HENGE", "UNSAY", "LIERS", "PIING", "WEALD", "SEXED", "FOLIC", "POXED", - "CUNTS", "ANILE", "KITHS", "BECKS", "TATTY", "PLENA", "REBAR", "ABLED", "TOYER", - "ATTAR", "TEAKS", "AIOLI", "AWING", "ANENT", "FECES", "REDIP", "WISTS", "PRATS", - "MESNE", "MUTER", "SMURF", "OWEST", "BAHTS", "LOSSY", "FTPED", "HUNKY", "HOERS", - "SLIER", "SICKS", "FATLY", "DELFT", "HIVER", "HIMBO", "PENGO", "BUSKS", "LOXES", - "ZONKS", "ILIUM", "APORT", "IKONS", "MULCT", "REEVE", "CIVVY", "CANNA", "BARFY", - "KAIAK", "SCUDO", "KNOUT", "GAPER", "BHANG", "PEASE", "UTERI", "LASES", "PATEN", - "RASAE", "AXELS", "STOAS", "OMBRE", "STYLI", "GUNKY", "HAZER", "KENAF", "AHOYS", - "AMMOS", "WEENY", "URGER", "KUDZU", "PAREN", "BOLOS", "FETOR", "NITTY", "TECHY", - "LIETH", "SOMAS", "DARKY", "VILLI", "GLUON", "JANES", "CANTS", "FARTS", "SOCLE", - "JINNS", "RUING", "SLILY", "RICER", "HADDA", "WOWEE", "RICES", "NERTS", "CAULS", - "SWIVE", "LILTY", "MICKS", "ARITY", "PASHA", "FINIF", "OINKY", "GUTTY", "TETRA", - "WISES", "WOLDS", "BALDS", "PICOT", "WHATS", "SHIKI", "BUNGS", "SNARF", "LEGOS", - "DUNGS", "STOGY", "BERMS", "TANGS", "VAILS", "ROODS", "MOREL", "SWARE", "ELANS", - "LATUS", "GULES", "RAZER", "DOXIE", "BUENA", "OVERS", "GUTTA", "ZINCS", "NATES", - "KIRKS", "TIKES", "DONEE", "JERRY", "MOHEL", "CEDER", "DOGES", "UNMAP", "FOLIA", - "RAWLY", "SNARK", "TOPOI", "CEILS", "IMMIX", "YORES", "DIEST", "BUBBA", "POMPS", - "FORKY", "TURDY", "LAWZY", "POOHS", "WORTS", "GLOMS", "BEANO", "MULEY", "BARKY", - "TUNNY", "AURIC", "FUNKS", "GAFFS", "CORDY", "CURDY", "LISLE", "TORIC", "SOYAS", - "REMAN", "MUNGY", "CARPY", "APISH", "OATEN", "GAPPY", "AURAE", "BRACT", "ROOKY", - "AXLED", "BURRY", "SIZER", "PROEM", "TURFY", "IMPRO", "MASHY", "MIENS", "NONNY", - "OLIOS", "GROOK", "SATES", "AGLEY", "CORGI", "DASHY", "DOSER", "DILDO", "APSOS", - "XORED", "LAKER", "PLAYA", "SELAH", "MALTY", "DULSE", "FRIGS", "DEMIT", "WHOSO", - "RIALS", "SAWER", "BEDIM", "SNUGS", "FANIN", "AZOIC", "ICERS", "SUERS", "WIZEN", - "KOINE", "TOPOS", "SHIRR", "RIFER", "FERAL", "LADED", "LASED", "TURDS", "SWEDE", - "EASTS", "COZEN", "UNHIT", "PALLY", "AITCH", "SEDUM", "COPER", "RUCHE", "GEEKS", - "SWAGS", "ETEXT", "ALGIN", "OFFED", "NINJA", "HOLER", "DOTER", "TOTER", "BESOT", - "DICUT", "MACER", "PEENS", "PEWIT", "REDOX", "POLER", "YECCH", "FLUKY", "DOETH", - "TWATS", "CRUDS", "BEBUG", "BIDER", "STELE", "HEXER", "WESTS", "GLUER", "PILAU", - "ABAFT", "WHELM", "LACER", "INODE", "TABUS", "GATOR", "CUING", "REFLY", "LUTED", - "CUKES", "BAIRN", "BIGHT", "ARSES", "CRUMP", "LOGGY", "BLINI", "SPOOR", "TOYON", - "HARKS", "WAZOO", "FENNY", "NAVES", "KEYER", "TUFAS", "MORPH", "RAJAS", "TYPAL", - "SPIFF", "OXLIP", "UNBAN", "MUSSY", "FINNY", "RIMER", "LOGIN", "MOLAS", "CIRRI", - "HUZZA", "AGONE", "UNSEX", "UNWON", "PEATS", "TOILE", "ZOMBI", "DEWED", "NOOKY", - "ALKYL", "IXNAY", "DOVEY", "HOLEY", "CUBER", "AMYLS", "PODIA", "CHINO", "APNEA", - "PRIMS", "LYCRA", "JOHNS", "PRIMO", "FATWA", "EGGER", "HEMPY", "SNOOK", "HYING", - "FUZED", "BARMS", "CRINK", "MOOTS", "YERBA", "RHUMB", "UNARC", "DIRER", "MUNGE", - "ELAND", "NARES", "WRIER", "NODDY", "ATILT", "JUKES", "ENDER", "THENS", "UNFIX", - "DOGGO", "ZOOKS", "DIDDY", "SHMOO", "BRUSK", "PREST", "CURER", "PASTS", "KELPY", - "BOCCE", "KICKY", "TAROS", "LINGS", "DICKY", "NERDY", "ABEND", "STELA", "BIGGY", - "LAVED", "BALDY", "PUBIS", "GOOKS", "WONKY", "STIED", "HYPOS", "ASSED", "SPUMY", - "OSIER", "ROBLE", "RUMBA", "BIFFY", "PUPAL" + "ABACK", "ABAFT", "ABASE", "ABATE", "ABBEY", "ABBOT", "ABHOR", "ABIDE", "ABLER", "ABODE", + "ABOUT", "ABOVE", "ABUSE", "ABYSS", "ACHED", "ACHES", "ACIDS", "ACORN", "ACRES", "ACRID", + "ACTED", "ACTOR", "ACUTE", "ADAGE", "ADAPT", "ADDED", "ADDER", "ADEPT", "ADIEU", "ADMIT", + "ADOBE", "ADOPT", "ADORE", "ADORN", "ADULT", "AEGIS", "AEONS", "AFFIX", "AFIRE", "AFOOT", + "AFTER", "AGAIN", "AGAPE", "AGATE", "AGENT", "AGILE", "AGING", "AGLOW", "AGONY", "AGREE", + "AHEAD", "AIDED", "AIDES", "AILED", "AIMED", "AIRED", "AISLE", "ALARM", "ALBUM", "ALDER", + "ALERT", "ALIAS", "ALIBI", "ALIEN", "ALIKE", "ALIVE", "ALLAY", "ALLEY", "ALLOT", "ALLOW", + "ALLOY", "ALOES", "ALOFT", "ALONE", "ALONG", "ALOOF", "ALOUD", "ALPHA", "ALTAR", "ALTER", + "ALTOS", "AMASS", "AMAZE", "AMBER", "AMBLE", "AMEND", "AMIGO", "AMISS", "AMITY", "AMONG", + "AMOUR", "AMPLE", "AMPLY", "AMUSE", "ANGEL", "ANGER", "ANGLE", "ANGRY", "ANGST", "ANIME", + "ANKLE", "ANNEX", "ANNOY", "ANNUL", "ANTES", "ANTIC", "ANVIL", "APACE", "APART", "APING", + "APPAL", "APPLE", "APPLY", "APRON", "APTLY", "AREAS", "ARENA", "ARGUE", "ARISE", "ARMED", + "AROMA", "AROSE", "ARRAY", "ARROW", "ARSON", "ASHEN", "ASHES", "ASIDE", "ASKED", "ASKEW", + "ASPEN", "ASSAY", "ASSES", "ASSET", "ASTER", "ASTIR", "ATLAS", "ATOLL", "ATOMS", "ATONE", + "ATTAR", "ATTIC", "AUDIO", "AUDIT", "AUGER", "AUGHT", "AUGUR", "AUNTS", "AURAS", "AUTOS", + "AVAIL", "AVERS", "AVERT", "AVOID", "AVOWS", "AWAIT", "AWAKE", "AWARD", "AWARE", "AWFUL", + "AWOKE", "AXIOM", "AXLES", "AZURE", "BABEL", "BABES", "BACKS", "BACON", "BADGE", "BADLY", + "BAGGY", "BAITS", "BAIZE", "BAKED", "BAKER", "BALES", "BALLS", "BALMY", "BANAL", "BANDS", + "BANDY", "BANGS", "BANJO", "BANKS", "BANNS", "BARBS", "BARDS", "BARED", "BARGE", "BARKS", + "BARNS", "BARON", "BASAL", "BASED", "BASER", "BASES", "BASIC", "BASIL", "BASIN", "BASIS", + "BASSO", "BASTE", "BATCH", "BATED", "BATHE", "BATHS", "BATON", "BAYOU", "BEACH", "BEADS", + "BEADY", "BEAKS", "BEAMS", "BEANS", "BEARD", "BEARS", "BEAST", "BEAUX", "BEECH", "BEETS", + "BEFIT", "BEGAN", "BEGAT", "BEGET", "BEGIN", "BEGOT", "BEGUN", "BEING", "BELIE", "BELLE", + "BELLS", "BELLY", "BELOW", "BELTS", "BENCH", "BENDS", "BERGS", "BERRY", "BERTH", "BERYL", + "BESET", "BESOM", "BEVEL", "BIBLE", "BIDED", "BIDES", "BIGHT", "BIGOT", "BILGE", "BILLS", + "BILLY", "BINDS", "BIPED", "BIRCH", "BIRDS", "BIRTH", "BISON", "BITCH", "BITES", "BLACK", + "BLADE", "BLAME", "BLAND", "BLANK", "BLARE", "BLAST", "BLAZE", "BLEAK", "BLEAT", "BLEED", + "BLEND", "BLENT", "BLESS", "BLEST", "BLIND", "BLINK", "BLISS", "BLOCK", "BLOCS", "BLOND", + "BLOOD", "BLOOM", "BLOTS", "BLOWN", "BLOWS", "BLUER", "BLUES", "BLUFF", "BLUNT", "BLURT", + "BLUSH", "BOARD", "BOARS", "BOAST", "BOATS", "BODED", "BODES", "BOGGY", "BOGUS", "BOILS", + "BOLES", "BOLTS", "BOMBS", "BONDS", "BONED", "BONES", "BONNY", "BONUS", "BOOBY", "BOOKS", + "BOOMS", "BOONS", "BOORS", "BOOST", "BOOTH", "BOOTS", "BOOTY", "BOOZE", "BORAX", "BORED", + "BORES", "BORNE", "BOSOM", "BOUGH", "BOUND", "BOUTS", "BOWED", "BOWEL", "BOWER", "BOWLS", + "BOXED", "BOXER", "BOXES", "BRACE", "BRAGS", "BRAID", "BRAIN", "BRAKE", "BRAND", "BRASS", + "BRATS", "BRAVE", "BRAVO", "BRAWL", "BRAWN", "BREAD", "BREAK", "BREED", "BRIAR", "BRIBE", + "BRICK", "BRIDE", "BRIEF", "BRIER", "BRIGS", "BRIMS", "BRINE", "BRING", "BRINK", "BRINY", + "BRISK", "BROAD", "BROIL", "BROKE", "BROOD", "BROOK", "BROOM", "BROTH", "BROWN", "BROWS", + "BRUIN", "BRUNT", "BRUSH", "BRUTE", "BUCKS", "BUDGE", "BUGGY", "BUGLE", "BUILD", "BUILT", + "BULBS", "BULGE", "BULKS", "BULKY", "BULLS", "BULLY", "BUMPS", "BUNCH", "BUNKS", "BUOYS", + "BURLY", "BURNS", "BURNT", "BURRO", "BURRS", "BURST", "BUSHY", "BUSTS", "BUTTE", "BUTTS", + "BUXOM", "BUYER", "CABAL", "CABBY", "CABIN", "CABLE", "CACAO", "CACHE", "CADET", "CADRE", + "CAGED", "CAGES", "CAIRN", "CAKED", "CAKES", "CALLS", "CALMS", "CALYX", "CAMEL", "CAMEO", + "CAMPS", "CANAL", "CANDY", "CANES", "CANNY", "CANOE", "CANON", "CANTO", "CAPER", "CAPES", + "CAPON", "CARDS", "CARED", "CARES", "CARGO", "CAROL", "CARRY", "CARTS", "CARVE", "CASED", + "CASES", "CASKS", "CASTE", "CASTS", "CATCH", "CATER", "CAUSE", "CAVED", "CAVES", "CAVIL", + "CEASE", "CEDAR", "CEDED", "CELLS", "CENTS", "CHAFE", "CHAFF", "CHAIN", "CHAIR", "CHALK", + "CHAMP", "CHANT", "CHAOS", "CHAPS", "CHARM", "CHART", "CHARY", "CHASE", "CHASM", "CHATS", + "CHEAP", "CHEAT", "CHECK", "CHEEK", "CHEER", "CHEFS", "CHESS", "CHEST", "CHICK", "CHIDE", + "CHIEF", "CHILD", "CHILL", "CHIME", "CHINA", "CHINK", "CHINS", "CHIPS", "CHIRP", "CHOIR", + "CHOKE", "CHOPS", "CHORD", "CHOSE", "CHUCK", "CHUMP", "CHUMS", "CHUNK", "CHURL", "CHURN", + "CHUTE", "CIDER", "CIGAR", "CINCH", "CIRCA", "CITED", "CITES", "CIVET", "CIVIC", "CIVIL", + "CLACK", "CLAIM", "CLAMP", "CLAMS", "CLANG", "CLANK", "CLANS", "CLAPS", "CLASH", "CLASP", + "CLASS", "CLAWS", "CLEAN", "CLEAR", "CLEFS", "CLEFT", "CLERK", "CLEWS", "CLICK", "CLIFF", + "CLIMB", "CLIME", "CLING", "CLINK", "CLIPS", "CLOAK", "CLOCK", "CLODS", "CLOGS", "CLOSE", + "CLOTH", "CLOUD", "CLOUT", "CLOVE", "CLOWN", "CLUBS", "CLUCK", "CLUES", "CLUMP", "CLUNG", + "COACH", "COALS", "COAST", "COATS", "COBRA", "COCKS", "COCOA", "CODES", "COILS", "COINS", + "COLDS", "COLIC", "COLON", "COLTS", "COMBS", "COMER", "COMES", "COMET", "COMIC", "COMMA", + "CONCH", "CONES", "CONIC", "COOED", "COOKS", "COOLS", "COPRA", "COPSE", "CORAL", "CORDS", + "CORES", "CORKS", "CORNS", "CORPS", "COSTS", "COTES", "COUCH", "COUGH", "COULD", "COUNT", + "COUPE", "COUPS", "COURT", "COVER", "COVES", "COVET", "COVEY", "COWED", "COWER", "COYLY", + "COZEN", "CRABS", "CRACK", "CRAFT", "CRAGS", "CRAMP", "CRANE", "CRANK", "CRAPE", "CRASH", + "CRASS", "CRATE", "CRAVE", "CRAWL", "CRAZE", "CRAZY", "CREAK", "CREAM", "CREDO", "CREED", + "CREEK", "CREEP", "CREPE", "CREPT", "CRESS", "CREST", "CREWS", "CRIBS", "CRICK", "CRIED", + "CRIER", "CRIES", "CRIME", "CRIMP", "CRISP", "CROAK", "CROCK", "CRONE", "CRONY", "CROOK", + "CROPS", "CROSS", "CROUP", "CROWD", "CROWN", "CROWS", "CRUDE", "CRUEL", "CRUMB", "CRUSH", + "CRUST", "CRYPT", "CUBES", "CUBIC", "CUBIT", "CUFFS", "CULTS", "CURDS", "CURED", "CURES", + "CURLS", "CURLY", "CURRY", "CURSE", "CURST", "CURVE", "CYCLE", "CYNIC", "DADDY", "DAILY", + "DAIRY", "DAISY", "DALES", "DALLY", "DAMES", "DAMPS", "DANCE", "DANDY", "DARED", "DARES", + "DARTS", "DATED", "DATES", "DATUM", "DAUBS", "DAUNT", "DAWNS", "DAZED", "DEALS", "DEALT", + "DEANS", "DEARS", "DEATH", "DEBAR", "DEBIT", "DEBTS", "DEBUT", "DECAY", "DECKS", "DECOY", + "DECRY", "DEEDS", "DEEMS", "DEEPS", "DEFER", "DEIGN", "DEITY", "DELAY", "DELLS", "DELTA", + "DELVE", "DEMON", "DEMUR", "DENSE", "DENTS", "DEPOT", "DEPTH", "DERBY", "DESKS", "DETER", + "DEUCE", "DEVIL", "DIARY", "DICED", "DICES", "DICTA", "DIETS", "DIGIT", "DIKES", "DIMES", + "DIMLY", "DINED", "DINER", "DINES", "DINGY", "DIRGE", "DIRTY", "DISCS", "DISKS", "DITCH", + "DITTO", "DITTY", "DIVAN", "DIVED", "DIVER", "DIVES", "DIZZY", "DOCKS", "DODGE", "DOERS", + "DOGMA", "DOING", "DOLED", "DOLLS", "DOMED", "DOMES", "DONOR", "DOOMS", "DOORS", "DOSED", + "DOSES", "DOTED", "DOTES", "DOUBT", "DOUGH", "DOVES", "DOWDY", "DOWNS", "DOWNY", "DOWRY", + "DOZED", "DOZEN", "DRAFT", "DRAGS", "DRAIN", "DRAKE", "DRAMA", "DRAMS", "DRANK", "DRAPE", + "DRAWL", "DRAWN", "DRAWS", "DRAYS", "DREAD", "DREAM", "DREGS", "DRESS", "DRIED", "DRIER", + "DRIES", "DRIFT", "DRILL", "DRILY", "DRINK", "DRIPS", "DRIVE", "DROLL", "DRONE", "DROOP", + "DROPS", "DROSS", "DROVE", "DROWN", "DRUGS", "DRUMS", "DRUNK", "DRYLY", "DUCAL", "DUCAT", + "DUCHY", "DUCKS", "DUCTS", "DUELS", "DUETS", "DUKES", "DULLY", "DUMMY", "DUMPS", "DUMPY", + "DUNCE", "DUNES", "DUNNO", "DUPED", "DUPES", "DUSKY", "DUSTY", "DWARF", "DWELL", "DWELT", + "DYING", "DYKES", "EAGER", "EAGLE", "EARLS", "EARLY", "EARNS", "EARTH", "EASED", "EASEL", + "EASES", "EATEN", "EATER", "EAVES", "EBBED", "EBONY", "EDGED", "EDGES", "EDICT", "EDIFY", + "EERIE", "EGGED", "EIGHT", "EJECT", "ELATE", "ELBOW", "ELDER", "ELECT", "ELEGY", "ELFIN", + "ELITE", "ELOPE", "ELUDE", "ELVES", "EMAIL", "EMITS", "EMPTY", "ENACT", "ENDED", "ENDOW", + "ENEMY", "ENJOY", "ENNUI", "ENROL", "ENSUE", "ENTER", "ENTRY", "ENVOY", "EPICS", "EPOCH", + "EQUAL", "EQUIP", "ERASE", "ERECT", "ERRED", "ERROR", "ESSAY", "ETHER", "ETHIC", "EVADE", + "EVENT", "EVERY", "EVILS", "EVOKE", "EXACT", "EXALT", "EXCEL", "EXERT", "EXILE", "EXIST", + "EXITS", "EXPEL", "EXTOL", "EXTRA", "EXULT", "EYING", "EYRIE", "FABLE", "FACED", "FACES", + "FACTS", "FADED", "FADES", "FAILS", "FAINT", "FAIRS", "FAIRY", "FAITH", "FAKIR", "FALLS", + "FALSE", "FAMED", "FANCY", "FANGS", "FARCE", "FARED", "FARES", "FARMS", "FASTS", "FATAL", + "FATED", "FATES", "FATTY", "FAULT", "FAUNA", "FAUNS", "FAWNS", "FEARS", "FEAST", "FEATS", + "FEEDS", "FEELS", "FEIGN", "FEINT", "FELLS", "FELON", "FENCE", "FERAL", "FERNS", "FERRY", + "FETCH", "FETED", "FETID", "FETUS", "FEUDS", "FEVER", "FEWER", "FICHE", "FIEFS", "FIELD", + "FIEND", "FIERY", "FIFES", "FIFTH", "FIFTY", "FIGHT", "FILCH", "FILED", "FILES", "FILET", + "FILLS", "FILLY", "FILMS", "FILMY", "FILTH", "FINAL", "FINCH", "FINDS", "FINED", "FINER", + "FINES", "FINIS", "FINNY", "FIORD", "FIRED", "FIRES", "FIRMS", "FIRST", "FISHY", "FISTS", + "FITLY", "FIVES", "FIXED", "FIXER", "FIXES", "FJORD", "FLAGS", "FLAIL", "FLAIR", "FLAKE", + "FLAKY", "FLAME", "FLANK", "FLAPS", "FLARE", "FLASH", "FLASK", "FLATS", "FLAWS", "FLEAS", + "FLECK", "FLEES", "FLEET", "FLESH", "FLICK", "FLIER", "FLIES", "FLING", "FLINT", "FLIRT", + "FLITS", "FLOAT", "FLOCK", "FLOES", "FLOOD", "FLOOR", "FLORA", "FLOSS", "FLOUR", "FLOUT", + "FLOWN", "FLOWS", "FLUES", "FLUFF", "FLUID", "FLUKE", "FLUME", "FLUNG", "FLUSH", "FLUTE", + "FLYER", "FOAMS", "FOAMY", "FOCAL", "FOCUS", "FOGGY", "FOILS", "FOIST", "FOLDS", "FOLIO", + "FOLKS", "FOLLY", "FOODS", "FOOLS", "FORAY", "FORCE", "FORDS", "FORGE", "FORGO", "FORKS", + "FORMS", "FORTE", "FORTH", "FORTS", "FORTY", "FORUM", "FOUND", "FOUNT", "FOURS", "FOWLS", + "FOXES", "FOYER", "FRAIL", "FRAME", "FRANC", "FRANK", "FRAUD", "FREAK", "FREED", "FREER", + "FREES", "FRESH", "FRETS", "FRIAR", "FRIED", "FRILL", "FRISK", "FROCK", "FROGS", "FROND", + "FRONT", "FROST", "FROTH", "FROWN", "FROZE", "FRUIT", "FUDGE", "FUELS", "FUGUE", "FULLY", + "FUMED", "FUMES", "FUNDS", "FUNGI", "FUNNY", "FURRY", "FURZE", "FUSED", "FUSES", "FUSSY", + "FUZZY", "GABLE", "GAILY", "GAINS", "GALES", "GALLS", "GAMES", "GAMIN", "GAMMA", "GAMUT", + "GANGS", "GAPED", "GAPES", "GASES", "GASPS", "GATES", "GAUDY", "GAUGE", "GAUNT", "GAUZE", + "GAUZY", "GAVEL", "GAWKY", "GAYER", "GAYLY", "GAZED", "GAZER", "GAZES", "GEARS", "GEESE", + "GENIE", "GENII", "GENRE", "GENTS", "GENUS", "GERMS", "GHOST", "GIANT", "GIBES", "GIDDY", + "GIFTS", "GILDS", "GILLS", "GIMME", "GIPSY", "GIRDS", "GIRLS", "GIRTH", "GIVEN", "GIVES", + "GLADE", "GLAND", "GLARE", "GLASS", "GLAZE", "GLEAM", "GLEAN", "GLENS", "GLIDE", "GLINT", + "GLOAT", "GLOBE", "GLOOM", "GLORY", "GLOSS", "GLOVE", "GLOWS", "GLUED", "GNASH", "GNATS", + "GNAWS", "GNOME", "GOADS", "GOALS", "GOATS", "GODLY", "GOING", "GOLLY", "GONGS", "GONNA", + "GOODS", "GOODY", "GOOSE", "GORED", "GORGE", "GORSE", "GOTTA", "GOUGE", "GOURD", "GOUTY", + "GOWNS", "GRABS", "GRACE", "GRADE", "GRAFT", "GRAIN", "GRAMS", "GRAND", "GRANT", "GRAPE", + "GRAPH", "GRASP", "GRASS", "GRATE", "GRAVE", "GRAVY", "GRAZE", "GREAT", "GREED", "GREEN", + "GREET", "GREYS", "GRIEF", "GRILL", "GRIME", "GRIMY", "GRIND", "GRINS", "GRIPE", "GRIPS", + "GRIST", "GROAN", "GROIN", "GROOM", "GROPE", "GROSS", "GROUP", "GROVE", "GROWL", "GROWN", + "GROWS", "GRUBS", "GRUEL", "GRUFF", "GRUNT", "GUANO", "GUARD", "GUESS", "GUEST", "GUIDE", + "GUILD", "GUILE", "GUILT", "GUISE", "GULCH", "GULFS", "GULLS", "GULLY", "GUMMY", "GUSTO", + "GUSTS", "GUSTY", "GYPSY", "HABIT", "HACKS", "HAILS", "HAIRS", "HAIRY", "HALED", "HALLS", + "HALTS", "HALVE", "HANDS", "HANDY", "HANGS", "HAPPY", "HARDY", "HAREM", "HARES", "HARMS", + "HARPS", "HARPY", "HARRY", "HARSH", "HARTS", "HASTE", "HASTY", "HATCH", "HATED", "HATER", + "HAULS", "HAVEN", "HAVOC", "HAWKS", "HAZEL", "HEADS", "HEADY", "HEALS", "HEAPS", "HEARD", + "HEARS", "HEART", "HEATH", "HEATS", "HEAVE", "HEAVY", "HEDGE", "HEEDS", "HEELS", "HEIRS", + "HELIX", "HELLO", "HELMS", "HELPS", "HENCE", "HERBS", "HERDS", "HERON", "HEROS", "HEWED", + "HIDES", "HILLS", "HILLY", "HILTS", "HINDS", "HINGE", "HINTS", "HIRED", "HIRES", "HITCH", + "HIVES", "HOARD", "HOARY", "HOBBY", "HOIST", "HOLDS", "HOLES", "HOLLY", "HOMES", "HONEY", + "HOODS", "HOOFS", "HOOKS", "HOOPS", "HOOTS", "HOPED", "HOPES", "HORDE", "HORNS", "HORNY", + "HORSE", "HOSTS", "HOTEL", "HOTLY", "HOUND", "HOURS", "HOUSE", "HOVEL", "HOVER", "HOWLS", + "HULKS", "HULLS", "HUMAN", "HUMID", "HUMPS", "HUMUS", "HUNCH", "HUNTS", "HURLS", "HURRY", + "HURTS", "HUSKS", "HUSKY", "HUSSY", "HYDRA", "HYENA", "HYMNS", "ICILY", "ICING", "IDEAL", + "IDEAS", "IDIOM", "IDIOT", "IDLED", "IDLER", "IDOLS", "IDYLL", "IGLOO", "IMAGE", "IMBUE", + "IMPEL", "IMPLY", "INANE", "INCUR", "INDEX", "INEPT", "INERT", "INFER", "INGOT", "INLET", + "INNER", "INTER", "INURE", "IRATE", "IRKED", "IRONS", "IRONY", "ISLES", "ISLET", "ISSUE", + "ITEMS", "IVORY", "JACKS", "JADED", "JAILS", "JAUNT", "JEANS", "JEERS", "JELLY", "JERKS", + "JERKY", "JESTS", "JETTY", "JEWEL", "JIFFY", "JOINS", "JOINT", "JOKED", "JOKER", "JOKES", + "JOLLY", "JOUST", "JOYED", "JUDGE", "JUICE", "JUICY", "JUMPS", "JUNKS", "JUNTA", "JUROR", + "KARMA", "KEELS", "KEEPS", "KETCH", "KEYED", "KHAKI", "KICKS", "KILLS", "KINDA", "KINDS", + "KINGS", "KIOSK", "KITES", "KNACK", "KNAVE", "KNEAD", "KNEEL", "KNEES", "KNELL", "KNELT", + "KNIFE", "KNITS", "KNOBS", "KNOCK", "KNOLL", "KNOTS", "KNOWN", "KNOWS", "LABEL", "LACED", + "LACES", "LACKS", "LADEN", "LADLE", "LAGER", "LAIRS", "LAITY", "LAKES", "LAMBS", "LAMED", + "LAMES", "LAMPS", "LANCE", "LANDS", "LANES", "LANKY", "LAPEL", "LAPSE", "LARCH", "LARGE", + "LARGO", "LARKS", "LARVA", "LASSO", "LASTS", "LATCH", "LATER", "LATHE", "LATHS", "LAUGH", + "LAWNS", "LAYER", "LEADS", "LEAFY", "LEAKS", "LEAKY", "LEANS", "LEAPS", "LEAPT", "LEARN", + "LEASE", "LEASH", "LEAST", "LEAVE", "LEDGE", "LEECH", "LEEKS", "LEGAL", "LEMME", "LEMON", + "LENDS", "LEPER", "LEVEE", "LEVEL", "LEVER", "LIARS", "LIBEL", "LICKS", "LIEGE", "LIENS", + "LIFTS", "LIGHT", "LIKED", "LIKEN", "LIKER", "LIKES", "LILAC", "LIMBO", "LIMBS", "LIMES", + "LIMIT", "LINED", "LINEN", "LINER", "LINES", "LINGO", "LINKS", "LIONS", "LISTS", "LITHE", + "LIVED", "LIVER", "LIVES", "LIVID", "LLAMA", "LOADS", "LOAMY", "LOANS", "LOATH", "LOBBY", + "LOBES", "LOCAL", "LOCKS", "LOCUS", "LODGE", "LOFTY", "LOGES", "LOGIC", "LOGIN", "LOINS", + "LONGS", "LOOKS", "LOOMS", "LOONS", "LOOPS", "LOOSE", "LORDS", "LOSER", "LOSES", "LOTUS", + "LOUSE", "LOUSY", "LOVED", "LOVER", "LOVES", "LOWED", "LOWER", "LOWLY", "LOYAL", "LUCID", + "LUCKY", "LULLS", "LUMPS", "LUMPY", "LUNAR", "LUNCH", "LUNGE", "LUNGS", "LURCH", "LURED", + "LURES", "LURID", "LURKS", "LUSTS", "LUSTY", "LUTES", "LYING", "LYMPH", "LYNCH", "LYRIC", + "MACES", "MADAM", "MADLY", "MAGIC", "MAIDS", "MAILS", "MAINS", "MAIZE", "MAJOR", "MAKER", + "MAKES", "MALES", "MAMMA", "MANES", "MANGA", "MANGE", "MANGO", "MANGY", "MANIA", "MANLY", + "MANNA", "MANOR", "MANSE", "MAPLE", "MARCH", "MARES", "MARKS", "MARRY", "MARSH", "MARTS", + "MASKS", "MASON", "MASTS", "MATCH", "MATED", "MATES", "MAUVE", "MAXIM", "MAYBE", "MAYOR", + "MAZES", "MEALS", "MEALY", "MEANS", "MEANT", "MEATS", "MEDAL", "MEDIA", "MEETS", "MELON", + "MELTS", "MEMES", "MENDS", "MENUS", "MERCY", "MERES", "MERGE", "MERIT", "MERRY", "MESAS", + "METAL", "METED", "METER", "MEWED", "MIDST", "MIENS", "MIGHT", "MILCH", "MILES", "MILKY", + "MILLS", "MIMES", "MIMIC", "MINCE", "MINDS", "MINED", "MINER", "MINES", "MINOR", "MINTS", + "MINUS", "MIRTH", "MISER", "MISTS", "MITES", "MIXED", "MIXES", "MOANS", "MOATS", "MOCKS", + "MODEL", "MODEM", "MODES", "MOIST", "MOLAR", "MOLES", "MOMMA", "MONEY", "MONKS", "MONTH", + "MOODS", "MOODY", "MOONS", "MOORS", "MOOSE", "MOPED", "MORAL", "MORES", "MOSSY", "MOTES", + "MOTHS", "MOTIF", "MOTOR", "MOTTO", "MOUND", "MOUNT", "MOURN", "MOUSE", "MOUTH", "MOVED", + "MOVER", "MOVES", "MOVIE", "MOWED", "MOWER", "MUCUS", "MUDDY", "MULES", "MULTI", "MUMMY", + "MUMPS", "MUNCH", "MURAL", "MURKY", "MUSED", "MUSES", "MUSIC", "MUSKY", "MUSTY", "MUTED", + "MUTES", "MYRRH", "MYTHS", "NABOB", "NAILS", "NAIVE", "NAKED", "NAMED", "NAMES", "NASAL", + "NASTY", "NATAL", "NATTY", "NAVAL", "NAVEL", "NAVES", "NEARS", "NECKS", "NEEDS", "NEEDY", + "NEIGH", "NERVE", "NESTS", "NEVER", "NEWER", "NEWLY", "NICER", "NICHE", "NIECE", "NIGHT", + "NINNY", "NOBLE", "NOBLY", "NOISE", "NOISY", "NOMAD", "NONCE", "NOOKS", "NOOSE", "NORTH", + "NOSED", "NOSES", "NOTCH", "NOTED", "NOTES", "NOUNS", "NOVEL", "NUDGE", "NURSE", "NYMPH", + "OAKEN", "OAKUM", "OASES", "OASIS", "OATEN", "OATHS", "OBESE", "OBEYS", "OCCUR", "OCEAN", + "OCHRE", "ODDER", "ODDLY", "ODIUM", "OFFAL", "OFFER", "OFTEN", "OILED", "OLDEN", "OLDER", + "OMENS", "OMITS", "ONION", "ONSET", "OOZED", "OOZES", "OPALS", "OPENS", "OPERA", "OPINE", + "OPIUM", "OPTIC", "ORBIT", "ORDER", "ORGAN", "OSIER", "OTHER", "OTTER", "OUGHT", "OUNCE", + "OUTDO", "OUTER", "OVALS", "OVARY", "OVENS", "OVERT", "OWING", "OWNED", "OWNER", "OXIDE", + "OZONE", "PACES", "PACKS", "PADDY", "PADRE", "PAEAN", "PAGAN", "PAGES", "PAILS", "PAINS", + "PAINT", "PAIRS", "PALED", "PALER", "PALES", "PALMS", "PALMY", "PALSY", "PANEL", "PANES", + "PANGS", "PANIC", "PANSY", "PANTS", "PAPAL", "PAPAS", "PAPER", "PARED", "PARKA", "PARKS", + "PARRY", "PARSE", "PARTS", "PARTY", "PASHA", "PASTE", "PASTY", "PATCH", "PATES", "PATHS", + "PATIO", "PAUSE", "PAVED", "PAWED", "PAWNS", "PAYED", "PAYER", "PEACE", "PEACH", "PEAKS", + "PEALS", "PEARL", "PEARS", "PEASE", "PECKS", "PEDAL", "PEEPS", "PEERS", "PELTS", "PENAL", + "PENCE", "PENIS", "PENNY", "PEONS", "PERCH", "PERIL", "PESKY", "PESOS", "PESTS", "PETAL", + "PETTY", "PHASE", "PHIAL", "PHONE", "PHOTO", "PIANO", "PICKS", "PIECE", "PIERS", "PIETY", + "PIGMY", "PIKES", "PILED", "PILES", "PILLS", "PILOT", "PINCH", "PINED", "PINES", "PINKS", + "PINTO", "PINTS", "PIOUS", "PIPED", "PIPER", "PIPES", "PIQUE", "PITCH", "PITHY", "PIVOT", + "PLACE", "PLAID", "PLAIN", "PLAIT", "PLANE", "PLANK", "PLANS", "PLANT", "PLATE", "PLAYS", + "PLAZA", "PLEAD", "PLEAS", "PLIED", "PLIES", "PLOTS", "PLUCK", "PLUGS", "PLUMB", "PLUME", + "PLUMS", "PLUSH", "PODIA", "POEMS", "POESY", "POETS", "POINT", "POISE", "POKED", "POKER", + "POKES", "POLAR", "POLES", "POLKA", "POLLS", "PONDS", "POOLS", "POPES", "POPPA", "POPPY", + "PORCH", "PORED", "PORES", "PORTS", "POSED", "POSER", "POSES", "POSSE", "POSTS", "POUCH", + "POUND", "POURS", "POWER", "PRANK", "PRATE", "PRAYS", "PRESS", "PREYS", "PRICE", "PRICK", + "PRIDE", "PRIED", "PRIES", "PRIME", "PRINT", "PRIOR", "PRISM", "PRIVY", "PRIZE", "PROBE", + "PRONE", "PROOF", "PROPS", "PROSE", "PROSY", "PROUD", "PROVE", "PROWL", "PROWS", "PROXY", + "PRUDE", "PRUNE", "PSALM", "PSHAW", "PUDGY", "PUFFS", "PUFFY", "PULLS", "PULPY", "PULSE", + "PUMPS", "PUNCH", "PUPIL", "PUPPY", "PUREE", "PURER", "PURGE", "PURSE", "PUSSY", "PUTTY", + "QUACK", "QUAFF", "QUAIL", "QUAKE", "QUALM", "QUART", "QUASI", "QUAYS", "QUEEN", "QUEER", + "QUELL", "QUERY", "QUEST", "QUEUE", "QUICK", "QUIET", "QUILL", "QUILT", "QUIPS", "QUIRE", + "QUITE", "QUITS", "QUOTA", "QUOTE", "QUOTH", "RABBI", "RABID", "RACED", "RACER", "RACES", + "RACKS", "RADII", "RADIO", "RAFTS", "RAGED", "RAGES", "RAIDS", "RAILS", "RAINS", "RAINY", + "RAISE", "RAJAH", "RAKED", "RAKES", "RALLY", "RANCH", "RANGE", "RANKS", "RAPID", "RARER", + "RARES", "RATED", "RATES", "RATIO", "RAVED", "RAVEN", "RAVES", "RAYON", "RAZED", "RAZOR", + "REACH", "REACT", "READS", "READY", "REALM", "REALS", "REAMS", "REAPS", "REARS", "REBEL", + "REBUS", "REBUT", "RECUR", "REEDS", "REEDY", "REEFS", "REEKS", "REELS", "REEVE", "REFER", + "REFIT", "REGAL", "REIGN", "REINS", "RELAX", "RELAY", "RELIC", "REMIT", "RENDS", "RENEW", + "RENTS", "REPAY", "REPEL", "REPLY", "RESET", "RESIN", "RESTS", "REVEL", "REVUE", "RHEUM", + "RHYME", "RICKS", "RIDER", "RIDES", "RIDGE", "RIFLE", "RIFTS", "RIGHT", "RIGID", "RILED", + "RILLS", "RIMES", "RINGS", "RINSE", "RIOTS", "RIPEN", "RIPER", "RISEN", "RISER", "RISES", + "RISKS", "RISKY", "RITES", "RIVAL", "RIVEN", "RIVER", "RIVET", "ROADS", "ROAMS", "ROARS", + "ROAST", "ROBED", "ROBES", "ROBIN", "ROCKS", "ROCKY", "ROGUE", "ROLES", "ROLLS", "ROMAN", + "ROOFS", "ROOKS", "ROOMS", "ROOMY", "ROOST", "ROOTS", "ROPED", "ROPES", "ROSES", "ROSIN", + "ROUGE", "ROUGH", "ROUND", "ROUSE", "ROUTE", "ROUTS", "ROVED", "ROVER", "ROWDY", "ROWED", + "ROYAL", "RUDER", "RUFFS", "RUINS", "RULED", "RULER", "RULES", "RUNES", "RUNGS", "RUPEE", + "RURAL", "RUSES", "SABLE", "SABRE", "SACKS", "SADLY", "SAFER", "SAGAS", "SAGES", "SAHIB", + "SAILS", "SAINT", "SAITH", "SALAD", "SALES", "SALLY", "SALON", "SALSA", "SALTS", "SALTY", + "SALVE", "SALVO", "SANDS", "SANDY", "SANER", "SATED", "SATIN", "SATYR", "SAUCE", "SAUCY", + "SAVED", "SAVES", "SAWED", "SCALD", "SCALE", "SCALP", "SCALY", "SCAMP", "SCANS", "SCANT", + "SCARE", "SCARF", "SCARS", "SCENE", "SCENT", "SCION", "SCOFF", "SCOLD", "SCOOP", "SCOPE", + "SCORE", "SCORN", "SCOUR", "SCOUT", "SCOWL", "SCRAP", "SCREW", "SCRIP", "SCRUB", "SCULL", + "SEALS", "SEAMS", "SEAMY", "SEATS", "SECTS", "SEDAN", "SEDGE", "SEEDS", "SEEDY", "SEEKS", + "SEEMS", "SEERS", "SEIZE", "SELLS", "SEMEN", "SENDS", "SENSE", "SERFS", "SERGE", "SERUM", + "SERVE", "SEVEN", "SEVER", "SEWED", "SEWER", "SEXES", "SHACK", "SHADE", "SHADY", "SHAFT", + "SHAKE", "SHAKY", "SHALE", "SHALL", "SHALT", "SHAME", "SHAMS", "SHANK", "SHAPE", "SHARE", + "SHARK", "SHARP", "SHAVE", "SHAWL", "SHEAF", "SHEAR", "SHEDS", "SHEEN", "SHEEP", "SHEER", + "SHEET", "SHEIK", "SHELF", "SHELL", "SHIED", "SHIFT", "SHINE", "SHINS", "SHINY", "SHIPS", + "SHIRE", "SHIRK", "SHIRT", "SHOAL", "SHOCK", "SHOES", "SHONE", "SHOOK", "SHOON", "SHOOT", + "SHOPS", "SHORE", "SHORN", "SHORT", "SHOTS", "SHOUT", "SHOVE", "SHOWN", "SHOWS", "SHOWY", + "SHRED", "SHREW", "SHRUB", "SHRUG", "SHUNS", "SHUTS", "SHYLY", "SIBYL", "SIDED", "SIDES", + "SIEGE", "SIEVE", "SIGHS", "SIGHT", "SIGMA", "SIGNS", "SILKS", "SILKY", "SILLS", "SILLY", + "SINCE", "SINEW", "SINGE", "SINGS", "SINKS", "SIREN", "SIRES", "SITES", "SIXES", "SIXTH", + "SIXTY", "SIZED", "SIZES", "SKATE", "SKEIN", "SKIES", "SKIFF", "SKILL", "SKIMS", "SKINS", + "SKIPS", "SKIRT", "SKULK", "SKULL", "SKUNK", "SLABS", "SLACK", "SLAGS", "SLAIN", "SLAKE", + "SLANG", "SLANT", "SLAPS", "SLASH", "SLATE", "SLATS", "SLAVE", "SLAYS", "SLEDS", "SLEEK", + "SLEEP", "SLEET", "SLEPT", "SLICE", "SLICK", "SLIDE", "SLILY", "SLIME", "SLIMY", "SLING", + "SLINK", "SLIPS", "SLITS", "SLOOP", "SLOPE", "SLOPS", "SLOTH", "SLUGS", "SLUMP", "SLUMS", + "SLUNG", "SLUNK", "SLUSH", "SLYLY", "SMACK", "SMALL", "SMART", "SMASH", "SMEAR", "SMELL", + "SMELT", "SMILE", "SMIRK", "SMITE", "SMITH", "SMOCK", "SMOKE", "SMOKY", "SMOTE", "SNACK", + "SNAGS", "SNAIL", "SNAKE", "SNAKY", "SNAPS", "SNARE", "SNARL", "SNEAK", "SNEER", "SNIFF", + "SNIPE", "SNOBS", "SNORE", "SNORT", "SNOUT", "SNOWS", "SNOWY", "SNUFF", "SOAPY", "SOARS", + "SOBER", "SOCKS", "SOFAS", "SOGGY", "SOILS", "SOLAR", "SOLES", "SOLID", "SOLOS", "SOLVE", + "SONGS", "SONNY", "SOOTH", "SOOTY", "SORES", "SORRY", "SORTS", "SOUGH", "SOULS", "SOUND", + "SOUPS", "SOUSE", "SOUTH", "SOWED", "SOWER", "SPACE", "SPADE", "SPAKE", "SPANK", "SPANS", + "SPARE", "SPARK", "SPARS", "SPASM", "SPAWN", "SPEAK", "SPEAR", "SPECK", "SPEED", "SPELL", + "SPELT", "SPEND", "SPENT", "SPERM", "SPICE", "SPICY", "SPIED", "SPIES", "SPIKE", "SPILL", + "SPILT", "SPINE", "SPINS", "SPINY", "SPIRE", "SPITE", "SPITS", "SPLIT", "SPOIL", "SPOKE", + "SPOOK", "SPOOL", "SPOON", "SPOOR", "SPORE", "SPORT", "SPOTS", "SPOUT", "SPRAY", "SPREE", + "SPRIG", "SPUNK", "SPURN", "SPURS", "SPURT", "SQUAD", "SQUAT", "SQUAW", "STABS", "STACK", + "STAFF", "STAGE", "STAGS", "STAID", "STAIN", "STAIR", "STAKE", "STALE", "STALK", "STALL", + "STAMP", "STAND", "STANK", "STARE", "STARK", "STARS", "START", "STATE", "STAVE", "STAYS", + "STEAD", "STEAK", "STEAL", "STEAM", "STEED", "STEEL", "STEEP", "STEER", "STEMS", "STEPS", + "STERN", "STEWS", "STICK", "STIFF", "STILE", "STILL", "STING", "STINK", "STINT", "STIRS", + "STOCK", "STOIC", "STOLE", "STONE", "STONY", "STOOD", "STOOL", "STOOP", "STOPS", "STORE", + "STORK", "STORM", "STORY", "STOUT", "STOVE", "STRAP", "STRAW", "STRAY", "STREW", "STRIP", + "STRUT", "STUCK", "STUDS", "STUDY", "STUFF", "STUMP", "STUNG", "STUNT", "STYLE", "SUAVE", + "SUCKS", "SUGAR", "SUING", "SUITE", "SUITS", "SULKS", "SULKY", "SULLY", "SUNNY", "SUPER", + "SURER", "SURGE", "SURLY", "SWAIN", "SWAMP", "SWANS", "SWARD", "SWARM", "SWAYS", "SWEAR", + "SWEAT", "SWEEP", "SWEET", "SWELL", "SWEPT", "SWIFT", "SWILL", "SWIMS", "SWINE", "SWING", + "SWIRL", "SWISH", "SWOON", "SWOOP", "SWORD", "SWORE", "SWORN", "SWUNG", "SYNOD", "SYRUP", + "TABBY", "TABLE", "TABOO", "TACIT", "TACKS", "TAILS", "TAINT", "TAKEN", "TAKES", "TALES", + "TALKS", "TALLY", "TALON", "TAMED", "TAMER", "TANKS", "TAPER", "TAPES", "TARDY", "TARES", + "TARRY", "TARTS", "TASKS", "TASTE", "TASTY", "TAUNT", "TAWNY", "TAXED", "TAXES", "TEACH", + "TEAMS", "TEARS", "TEASE", "TEEMS", "TEENS", "TEETH", "TELLS", "TEMPI", "TEMPO", "TEMPS", + "TENDS", "TENET", "TENOR", "TENSE", "TENTH", "TENTS", "TEPEE", "TEPID", "TERMS", "TERSE", + "TESTS", "TESTY", "TEXTS", "THANK", "THEFT", "THEIR", "THEME", "THERE", "THESE", "THICK", + "THIEF", "THIGH", "THINE", "THING", "THINK", "THIRD", "THONG", "THORN", "THOSE", "THREE", + "THREW", "THROB", "THROE", "THROW", "THUMB", "THUMP", "THYME", "TIARA", "TIBIA", "TICKS", + "TIDAL", "TIDES", "TIERS", "TIGER", "TIGHT", "TILDE", "TILED", "TILES", "TILLS", "TILTS", + "TIMED", "TIMES", "TIMID", "TINGE", "TINTS", "TIPSY", "TIRED", "TIRES", "TITHE", "TITLE", + "TOADS", "TOAST", "TODAY", "TODDY", "TOILS", "TOKEN", "TOLLS", "TOMBS", "TOMES", "TONED", + "TONES", "TONGS", "TONIC", "TOOLS", "TOOTH", "TOPAZ", "TOPIC", "TOQUE", "TORCH", "TORSO", + "TORTS", "TOTAL", "TOTEM", "TOUCH", "TOUGH", "TOURS", "TOWED", "TOWEL", "TOWER", "TOWNS", + "TOXIC", "TOYED", "TRACE", "TRACK", "TRACT", "TRADE", "TRAIL", "TRAIN", "TRAIT", "TRAMP", + "TRAMS", "TRAPS", "TRASH", "TRAYS", "TREAD", "TREAT", "TREED", "TREES", "TREND", "TRESS", + "TRIAD", "TRIAL", "TRIBE", "TRICE", "TRICK", "TRIED", "TRIES", "TRILL", "TRIPE", "TRIPS", + "TRITE", "TROLL", "TROOP", "TROTH", "TROTS", "TROUT", "TRUCE", "TRUCK", "TRUER", "TRULY", + "TRUMP", "TRUNK", "TRUSS", "TRUST", "TRUTH", "TRYST", "TUBES", "TUFTS", "TULIP", "TULLE", + "TUNED", "TUNES", "TUNIC", "TURNS", "TUSKS", "TUTOR", "TWAIN", "TWANG", "TWEED", "TWICE", + "TWIGS", "TWINE", "TWINS", "TWIRL", "TWIST", "TYING", "TYPED", "TYPES", "UDDER", "ULCER", + "ULTRA", "UNCLE", "UNCUT", "UNDER", "UNDID", "UNDUE", "UNFIT", "UNION", "UNITE", "UNITS", + "UNITY", "UNSAY", "UNTIE", "UNTIL", "UPPER", "UPSET", "URBAN", "URGED", "URGES", "URINE", + "USAGE", "USERS", "USHER", "USING", "USUAL", "USURP", "USURY", "UTTER", "VAGUE", "VALES", + "VALET", "VALID", "VALUE", "VALVE", "VANES", "VAPID", "VASES", "VAULT", "VAUNT", "VEILS", + "VEINS", "VELDT", "VENAL", "VENOM", "VENTS", "VENUE", "VERBS", "VERGE", "VERSE", "VERVE", + "VESTS", "VEXED", "VEXES", "VIALS", "VICAR", "VICES", "VIDEO", "VIEWS", "VIGIL", "VILER", + "VILLA", "VINES", "VIOLA", "VIPER", "VIRUS", "VISIT", "VISOR", "VISTA", "VITAL", "VIVID", + "VIXEN", "VIZOR", "VOCAL", "VODKA", "VOGUE", "VOICE", "VOILE", "VOLTS", "VOMIT", "VOTED", + "VOTER", "VOTES", "VOUCH", "VOWED", "VOWEL", "VYING", "WADED", "WAFER", "WAFTS", "WAGED", + "WAGER", "WAGES", "WAGON", "WAIFS", "WAILS", "WAIST", "WAITS", "WAIVE", "WAKED", "WAKEN", + "WAKES", "WALKS", "WALLS", "WALTZ", "WANDS", "WANED", "WANES", "WANTS", "WARDS", "WARES", + "WARMS", "WARNS", "WARTS", "WASPS", "WASTE", "WATCH", "WATER", "WAVED", "WAVER", "WAVES", + "WAXED", "WAXEN", "WAXES", "WEARS", "WEARY", "WEAVE", "WEDGE", "WEEDS", "WEEDY", "WEEKS", + "WEEPS", "WEIGH", "WEIRD", "WELCH", "WELLS", "WENCH", "WHACK", "WHALE", "WHARF", "WHEAT", + "WHEEL", "WHELP", "WHERE", "WHICH", "WHIFF", "WHILE", "WHIMS", "WHINE", "WHIPS", "WHIRL", + "WHIRR", "WHISK", "WHIST", "WHITE", "WHOLE", "WHOOP", "WHORE", "WHOSE", "WICKS", "WIDEN", + "WIDER", "WIDOW", "WIDTH", "WIELD", "WIGHT", "WILDS", "WILES", "WILLS", "WINCE", "WINCH", + "WINDS", "WINDY", "WINES", "WINGS", "WINKS", "WIPED", "WIPES", "WIRED", "WIRES", "WISER", + "WISPS", "WITCH", "WITTY", "WIVES", "WOMAN", "WOMEN", "WOODS", "WOODY", "WOOED", "WOOER", + "WORDS", "WORDY", "WORKS", "WORLD", "WORMS", "WORRY", "WORSE", "WORST", "WORTH", "WOULD", + "WOUND", "WRACK", "WRAPS", "WRAPT", "WRATH", "WREAK", "WRECK", "WREST", "WRING", "WRIST", + "WRITE", "WRITS", "WRONG", "WROTE", "WROTH", "YACHT", "YARDS", "YARNS", "YAWNS", "YEARN", + "YEARS", "YEAST", "YELLS", "YELPS", "YIELD", "YOKED", "YOKES", "YOLKS", "YOUNG", "YOURS", + "YOUTH", "ZEBRA", "ZONES", ] alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] @@ -662,7 +335,7 @@ def most_used_letters(): def list_of_valid_words(): - letters = ['s', 'e', 'a', 'r', 'o', 'i', 'L', 'n', 'p', 'c'] + letters = ['e', 's', 'a', 'r', 'o', 'l', 'i', 'n', 'c', 'p'] for i, letter in enumerate(letters): # Force all letters to be capitalized letters[i] = letter.upper() @@ -678,8 +351,16 @@ def list_of_valid_words(): for i,word in enumerate(legal_words): legal_words[i] = word.upper().replace("D","d") + random.shuffle(legal_words) + # Just in case the watch's random function is too pseudo, better to shuffle th elist so it's less likely to always have the same starting letter - print(f"Number of words found: {len(legal_words)}") + print("static const char _valid_letters[] = {", end='') + for letter in letters[:-1]: + print(f"'{letter}', ", end='') + print(f"'{letters[-1]}'" + "};") + print("") + + print(f"// Number of words found: {len(legal_words)}") items_per_row = 9 i = 0 print("static const char _legal_words[][WORDLE_LENGTH + 1] = {") @@ -692,6 +373,6 @@ def list_of_valid_words(): print("};") if __name__ == "__main__": - #most_used_letters() + most_used_letters() list_of_valid_words() From 3a24ede3de5467f3418704a88b7a32ef2c64b2e1 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Thu, 15 Aug 2024 08:17:10 -0400 Subject: [PATCH 050/161] day streak and further wordle dev --- .../watch_faces/complication/wordle_face.c | 92 ++++++++++++++----- .../watch_faces/complication/wordle_face.h | 5 +- utils/wordle_list.py | 1 + 3 files changed, 72 insertions(+), 26 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 4052ef7..62141cb 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -25,14 +25,15 @@ #include #include #include "wordle_face.h" +#if USE_DAILY_STREAK +#include "watch_utility.h" +#endif /* TODO: * Add quick iteration (8x freq to get to the letter we want) * Fix the word matching (if answer is AAAAA and we put in AACAA, the C blinks) -* Verify pressing back always work when the board is G_G_G -* Add daily streak and wait for next day -* Add a way tpo recount previous attempts +* Add a way to recount previous attempts */ @@ -54,7 +55,7 @@ C | 525 U | 514 P has more words with the other letters here (281 vs 198) P | 448 */ -static const char _valid_letters[] = {'E', 'S', 'A', 'R', 'O', 'L', 'I', 'N', 'C', 'P'}; +static const char _valid_letters[] = {'A', 'C', 'E', 'I', 'L', 'N', 'O', 'P', 'R', 'S'}; // Number of words found: 281 static const char _legal_words[][WORDLE_LENGTH + 1] = { @@ -126,12 +127,13 @@ static void display_all_letters(wordle_state_t *state) { } static bool check_word(wordle_state_t *state) { - + WordleLetterResult checked_letter_in_answer[WORDLE_LENGTH] = {WORDLE_LETTER_WRONG}; // Exact bool is_exact_match = true; for (size_t i = 0; i < WORDLE_LENGTH; i++) { - if (_valid_letters[state->word_elements[i]] == _legal_words[state->curr_answer][i]) - state->word_elements_result[i] = WORDLE_LETTER_CORRECT; + if (_valid_letters[state->word_elements[i]] == _legal_words[state->curr_answer][i]) { + state->word_elements_result[i] = checked_letter_in_answer[i] = WORDLE_LETTER_CORRECT; + } else { state->word_elements_result[i] = WORDLE_LETTER_WRONG; is_exact_match = false; @@ -141,10 +143,10 @@ static bool check_word(wordle_state_t *state) { // Wrong Location for (size_t i = 0; i < WORDLE_LENGTH; i++) { for (size_t j = 0; j < WORDLE_LENGTH; j++) { - if (state->word_elements_result[j] != WORDLE_LETTER_WRONG) continue; + if (checked_letter_in_answer[j] != WORDLE_LETTER_WRONG) continue; if (_valid_letters[state->word_elements[i]] == _legal_words[state->curr_answer][j]) { printf("me: %c them: %c\r\n", _valid_letters[state->word_elements[i]], _legal_words[state->curr_answer][j]); - state->word_elements_result[j] = WORDLE_LETTER_WRONG_LOC; + state->word_elements_result[i] = checked_letter_in_answer[j] = WORDLE_LETTER_WRONG_LOC; break; } } @@ -190,12 +192,29 @@ static void display_title(wordle_state_t *state) { static void display_streak(wordle_state_t *state) { char buf[12]; state->curr_screen = SCREEN_STREAK; - printf("streak %d \r\n", state->streak); +#if USE_DAILY_STREAK sprintf(buf, "WO St%2ddy", state->streak); +#else + sprintf(buf, "WO St%4d", state->streak); +#endif watch_display_string(buf, 0); watch_set_colon(); } +#if USE_DAILY_STREAK +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); + } +} +#endif + static void display_lose(wordle_state_t *state, uint8_t subsecond) { char buf[WORDLE_LENGTH + 6]; sprintf(buf," L %s", subsecond % 2 ? _legal_words[state->curr_answer] : " "); @@ -218,23 +237,30 @@ static uint8_t get_first_pos(WordleLetterResult *word_elements_result) { } static uint8_t get_next_pos(uint8_t curr_pos, WordleLetterResult *word_elements_result) { - uint8_t pos = curr_pos; - do { - pos++; - if (pos > WORDLE_LENGTH) return WORDLE_LENGTH + 1; - } while (word_elements_result[pos] == WORDLE_LETTER_CORRECT); - return pos; + for (size_t pos = curr_pos+1; pos < WORDLE_LENGTH; pos++) { + 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) { - int8_t pos = curr_pos; - do { - pos--; - if (pos < 0) return curr_pos; - } while (word_elements_result[pos] == WORDLE_LETTER_CORRECT); - return pos; + if (curr_pos == 0) return 0; + for (int8_t pos = curr_pos-1; pos >= 0; pos--) { + if (word_elements_result[pos] != WORDLE_LETTER_CORRECT) + return pos; + } + return curr_pos; } +#if USE_DAILY_STREAK +static uint32_t get_day_unix_time(void) { + watch_date_time now = watch_rtc_get_date_time(); + now.unit.hour = now.unit.minute = now.unit.second = 0; + return watch_utility_date_time_to_unix_time(now, 0); +} +#endif + 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++) @@ -273,6 +299,12 @@ static bool act_on_btn(wordle_state_t *state) { state->curr_screen = SCREEN_PLAYING; return true; case SCREEN_TITLE: +#if USE_DAILY_STREAK + if (state->prev_day == get_day_unix_time()) { + display_wait(state); + return true; + } +#endif display_streak(state); return true; case SCREEN_STREAK: @@ -281,7 +313,12 @@ static bool act_on_btn(wordle_state_t *state) { case SCREEN_WIN: case SCREEN_LOSE: display_title(state); - return true; + return true; +#if USE_DAILY_STREAK + case SCREEN_WAIT: + display_title(state); + return true; +#endif default: return false; } @@ -296,7 +333,6 @@ void wordle_face_setup(movement_settings_t *settings, uint8_t watch_face_index, memset(*context_ptr, 0, sizeof(wordle_state_t)); wordle_state_t *state = (wordle_state_t *)*context_ptr; state->curr_screen = SCREEN_TITLE; - } // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep. } @@ -305,6 +341,9 @@ void wordle_face_activate(movement_settings_t *settings, void *context) { (void) settings; wordle_state_t *state = (wordle_state_t *)context; movement_request_tick_frequency(2); +#if USE_DAILY_STREAK + if (state->prev_day <= (get_day_unix_time() + (60 *60 * 24))) state->streak = 0; +#endif if (state->curr_screen == SCREEN_TITLE) display_title(state); } @@ -356,11 +395,14 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi display_letter(state, true); if (state->word_elements[state->position] == _num_valid_letters) break; state->position = get_next_pos(state->position, state->word_elements_result); - if(WORDLE_LENGTH == (state->position)) { + if (state->position >= WORDLE_LENGTH) { bool exact_match = check_word(state); if (exact_match) { state->curr_screen = SCREEN_WIN; state->streak++; +#if USE_DAILY_STREAK + state->prev_day = get_day_unix_time(); +#endif break; } if (state->attempt++ >= WORDLE_MAX_ATTEMPTS) { diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index b8e5d0f..15b2a94 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -50,6 +50,9 @@ typedef enum { SCREEN_RESULT, SCREEN_TITLE, SCREEN_STREAK, +#if USE_DAILY_STREAK + SCREEN_WAIT, +#endif SCREEN_WIN, SCREEN_LOSE, SCREEN_COUNT @@ -66,7 +69,7 @@ typedef struct { uint8_t streak; WordleScreen curr_screen; #if USE_DAILY_STREAK - // For the day info + uint32_t prev_day; #endif } wordle_state_t; diff --git a/utils/wordle_list.py b/utils/wordle_list.py index 87e6066..1b5baf0 100644 --- a/utils/wordle_list.py +++ b/utils/wordle_list.py @@ -336,6 +336,7 @@ def most_used_letters(): def list_of_valid_words(): letters = ['e', 's', 'a', 'r', 'o', 'l', 'i', 'n', 'c', 'p'] + letters = sorted(letters) for i, letter in enumerate(letters): # Force all letters to be capitalized letters[i] = letter.upper() From 676f50d1943f15fbb0c7b2544a32a21492d32fd5 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Thu, 15 Aug 2024 08:56:49 -0400 Subject: [PATCH 051/161] Added fast cycle --- .../watch_faces/complication/wordle_face.c | 77 +++++++++++++------ 1 file changed, 55 insertions(+), 22 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 62141cb..4fa6720 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -29,6 +29,9 @@ #include "watch_utility.h" #endif +#define FREQ_FAST 4 +#define FREQ 2 + /* TODO: * Add quick iteration (8x freq to get to the letter we want) @@ -104,6 +107,17 @@ static uint32_t get_random(uint32_t max) { #endif } +static bool _quick_ticks_running; +static void start_quick_cyc(void){ + _quick_ticks_running = true; + movement_request_tick_frequency(FREQ_FAST); +} + +static void stop_quick_cyc(void){ + _quick_ticks_running = false; + movement_request_tick_frequency(FREQ); +} + static void display_letter(wordle_state_t *state, bool display_dash) { char buf[1 + 1]; if (state->word_elements[state->position] >= _num_valid_letters) { @@ -145,19 +159,11 @@ static bool check_word(wordle_state_t *state) { for (size_t j = 0; j < WORDLE_LENGTH; j++) { if (checked_letter_in_answer[j] != WORDLE_LETTER_WRONG) continue; if (_valid_letters[state->word_elements[i]] == _legal_words[state->curr_answer][j]) { - printf("me: %c them: %c\r\n", _valid_letters[state->word_elements[i]], _legal_words[state->curr_answer][j]); state->word_elements_result[i] = checked_letter_in_answer[j] = WORDLE_LETTER_WRONG_LOC; break; } } } - - for (size_t i = 0; i < WORDLE_LENGTH; i++) { - printf("%d : %d\r\n", i, state->word_elements_result[i]); - } - - - return is_exact_match; } @@ -181,7 +187,9 @@ static void reset_board(wordle_state_t *state) { display_attempt(state->attempt); display_all_letters(state); watch_display_string("-", 5); - printf("rand: %s\r\n", _legal_words[state->curr_answer]); +#if __EMSCRIPTEN__ + printf("ANSWER: %s\r\n", _legal_words[state->curr_answer]); +#endif } static void display_title(wordle_state_t *state) { @@ -253,6 +261,16 @@ static uint8_t get_prev_pos(uint8_t curr_pos, WordleLetterResult *word_elements_ return curr_pos; } +static void get_next_letter(uint8_t curr_pos, uint8_t *word_elements) { + if (word_elements[curr_pos] >= _num_valid_letters) word_elements[curr_pos] = 0; + else word_elements[curr_pos] = (word_elements[curr_pos] + 1) % _num_valid_letters; +} + +static void get_prev_letter(uint8_t curr_pos, uint8_t *word_elements) { + if (word_elements[curr_pos] >= _num_valid_letters) word_elements[curr_pos] = _num_valid_letters - 1; + else word_elements[curr_pos] = (word_elements[curr_pos] + _num_valid_letters - 1) % _num_valid_letters; +} + #if USE_DAILY_STREAK static uint32_t get_day_unix_time(void) { watch_date_time now = watch_rtc_get_date_time(); @@ -340,7 +358,7 @@ void wordle_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void wordle_face_activate(movement_settings_t *settings, void *context) { (void) settings; wordle_state_t *state = (wordle_state_t *)context; - movement_request_tick_frequency(2); + movement_request_tick_frequency(FREQ); #if USE_DAILY_STREAK if (state->prev_day <= (get_day_unix_time() + (60 *60 * 24))) state->streak = 0; #endif @@ -360,10 +378,19 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi switch (state->curr_screen) { case SCREEN_PLAYING: - if (event.subsecond % 2) { - display_letter(state, true); - } else { - watch_display_string(" ", state->position + 5); + if (_quick_ticks_running) { + if (watch_get_pin_level(BTN_ALARM)){ + get_next_letter(state->position, state->word_elements); + display_letter(state, true); + } + else stop_quick_cyc(); + } + else { + if (event.subsecond % 2) { + display_letter(state, true); + } else { + watch_display_string(" ", state->position + 5); + } } break; case SCREEN_RESULT: @@ -379,18 +406,23 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi break; } break; - case EVENT_LIGHT_BUTTON_UP: + case EVENT_ALARM_BUTTON_UP: if (act_on_btn(state)) break; - if (state->word_elements[state->position] >= _num_valid_letters) state->word_elements[state->position] = 0; - else state->word_elements[state->position] = (state->word_elements[state->position] + 1) % _num_valid_letters; + get_next_letter(state->position, state->word_elements); display_letter(state, true); break; - case EVENT_LIGHT_LONG_PRESS: - if (state->word_elements[state->position] >= _num_valid_letters) state->word_elements[state->position] = _num_valid_letters - 1; - else state->word_elements[state->position] = (state->word_elements[state->position] + _num_valid_letters - 1) % _num_valid_letters; + case EVENT_ALARM_LONG_PRESS: + if (state->curr_screen != SCREEN_PLAYING) break; + get_prev_letter(state->position, state->word_elements); display_letter(state, true); + break; + case EVENT_ALARM_LONGER_PRESS: + if (state->curr_screen != SCREEN_PLAYING) break; + get_next_letter(state->position, state->word_elements); + display_letter(state, true); + start_quick_cyc(); break; - case EVENT_ALARM_BUTTON_UP: + case EVENT_LIGHT_BUTTON_UP: if (act_on_btn(state)) break; display_letter(state, true); if (state->word_elements[state->position] == _num_valid_letters) break; @@ -414,7 +446,8 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi break; } break; - case EVENT_ALARM_LONG_PRESS: + case EVENT_LIGHT_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; From 0c86be4a40ef338ee7641dc4e405af9525ae54b1 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Thu, 15 Aug 2024 08:59:22 -0400 Subject: [PATCH 052/161] Swapped button mapping and removed fast iteration --- .../watch_faces/complication/wordle_face.c | 43 ++++--------------- 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 4fa6720..f701fc4 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -29,7 +29,6 @@ #include "watch_utility.h" #endif -#define FREQ_FAST 4 #define FREQ 2 /* @@ -107,17 +106,6 @@ static uint32_t get_random(uint32_t max) { #endif } -static bool _quick_ticks_running; -static void start_quick_cyc(void){ - _quick_ticks_running = true; - movement_request_tick_frequency(FREQ_FAST); -} - -static void stop_quick_cyc(void){ - _quick_ticks_running = false; - movement_request_tick_frequency(FREQ); -} - static void display_letter(wordle_state_t *state, bool display_dash) { char buf[1 + 1]; if (state->word_elements[state->position] >= _num_valid_letters) { @@ -378,19 +366,10 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi switch (state->curr_screen) { case SCREEN_PLAYING: - if (_quick_ticks_running) { - if (watch_get_pin_level(BTN_ALARM)){ - get_next_letter(state->position, state->word_elements); - display_letter(state, true); - } - else stop_quick_cyc(); - } - else { - if (event.subsecond % 2) { - display_letter(state, true); - } else { - watch_display_string(" ", state->position + 5); - } + if (event.subsecond % 2) { + display_letter(state, true); + } else { + watch_display_string(" ", state->position + 5); } break; case SCREEN_RESULT: @@ -406,23 +385,17 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi break; } break; - case EVENT_ALARM_BUTTON_UP: + case EVENT_LIGHT_BUTTON_UP: if (act_on_btn(state)) break; get_next_letter(state->position, state->word_elements); display_letter(state, true); break; - case EVENT_ALARM_LONG_PRESS: + case EVENT_LIGHT_LONG_PRESS: if (state->curr_screen != SCREEN_PLAYING) break; get_prev_letter(state->position, state->word_elements); display_letter(state, true); - break; - case EVENT_ALARM_LONGER_PRESS: - if (state->curr_screen != SCREEN_PLAYING) break; - get_next_letter(state->position, state->word_elements); - display_letter(state, true); - start_quick_cyc(); break; - case EVENT_LIGHT_BUTTON_UP: + case EVENT_ALARM_BUTTON_UP: if (act_on_btn(state)) break; display_letter(state, true); if (state->word_elements[state->position] == _num_valid_letters) break; @@ -446,7 +419,7 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi break; } break; - case EVENT_LIGHT_LONG_PRESS: + 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); From 8ea779874f0405fbe5dd99e13208833ecae4eb42 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Thu, 15 Aug 2024 17:02:14 -0400 Subject: [PATCH 053/161] Face compares the values correctly now and does a dict lookup first --- .../watch_faces/complication/wordle_face.c | 177 +++++++++++------- .../watch_faces/complication/wordle_face.h | 7 +- 2 files changed, 113 insertions(+), 71 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index f701fc4..13b4b6f 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -33,9 +33,8 @@ /* TODO: -* Add quick iteration (8x freq to get to the letter we want) -* Fix the word matching (if answer is AAAAA and we put in AACAA, the C blinks) * Add a way to recount previous attempts +* Only allow dictionary attempts - Show "nodict" otherwise */ @@ -106,6 +105,41 @@ static uint32_t get_random(uint32_t 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+1; pos < WORDLE_LENGTH; pos++) { + 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 (size_t pos = curr_pos-1; pos > 0; pos--) { + if (word_elements_result[pos] != WORDLE_LETTER_CORRECT) + return pos; + } + return curr_pos; +} + +static void get_next_letter(uint8_t curr_pos, uint8_t *word_elements) { + if (word_elements[curr_pos] >= _num_valid_letters) word_elements[curr_pos] = 0; + else word_elements[curr_pos] = (word_elements[curr_pos] + 1) % _num_valid_letters; +} + +static void get_prev_letter(uint8_t curr_pos, uint8_t *word_elements) { + if (word_elements[curr_pos] >= _num_valid_letters) word_elements[curr_pos] = _num_valid_letters - 1; + else word_elements[curr_pos] = (word_elements[curr_pos] + _num_valid_letters - 1) % _num_valid_letters; +} + static void display_letter(wordle_state_t *state, bool display_dash) { char buf[1 + 1]; if (state->word_elements[state->position] >= _num_valid_letters) { @@ -121,6 +155,7 @@ static void display_letter(wordle_state_t *state, bool display_dash) { 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); @@ -128,31 +163,47 @@ static void display_all_letters(wordle_state_t *state) { state->position = prev_pos; } +static bool check_word_in_dict(uint8_t *word_elements) { + bool is_exact_match; + for (uint16_t i = 0; i < _num_words; i++) { + is_exact_match = true; + for (size_t j = 0; j < WORDLE_LENGTH; j++) { + if (_valid_letters[word_elements[j]] != _legal_words[i][j]) { + is_exact_match = false; + break; + } + } + if (is_exact_match) return true; + } + return false; +} + static bool check_word(wordle_state_t *state) { - WordleLetterResult checked_letter_in_answer[WORDLE_LENGTH] = {WORDLE_LETTER_WRONG}; // Exact bool is_exact_match = true; for (size_t i = 0; i < WORDLE_LENGTH; i++) { - if (_valid_letters[state->word_elements[i]] == _legal_words[state->curr_answer][i]) { - state->word_elements_result[i] = checked_letter_in_answer[i] = WORDLE_LETTER_CORRECT; - } + if (_valid_letters[state->word_elements[i]] == _legal_words[state->curr_answer][i]) + state->word_elements_result[i] = WORDLE_LETTER_CORRECT; 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++) { + bool answer_found_wrong_loc[WORDLE_LENGTH] = { false }; + 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 (checked_letter_in_answer[j] != WORDLE_LETTER_WRONG) continue; + if (answer_found_wrong_loc[j]) continue; if (_valid_letters[state->word_elements[i]] == _legal_words[state->curr_answer][j]) { - state->word_elements_result[i] = checked_letter_in_answer[j] = WORDLE_LETTER_WRONG_LOC; + state->word_elements_result[i] = WORDLE_LETTER_WRONG_LOC; + answer_found_wrong_loc[j] = true; break; } } } - return is_exact_match; + return false; } static void display_attempt(uint8_t attempt) { @@ -161,19 +212,27 @@ static void display_attempt(uint8_t attempt) { watch_display_string(buf, 3); } +static void show_start_of_attempt(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] = _num_valid_letters; + } + display_attempt(state->attempt); + display_all_letters(state); + state->position = get_first_pos(state->word_elements_result); + state->curr_screen = SCREEN_PLAYING; +} + static void reset_board(wordle_state_t *state) { for (size_t i = 0; i < WORDLE_LENGTH; i++) { state->word_elements[i] = _num_valid_letters; state->word_elements_result[i] = WORDLE_LETTER_WRONG; } state->curr_answer = get_random(_num_words); - state->position = 0; state->attempt = 1; - state->curr_screen = SCREEN_PLAYING; watch_clear_colon(); watch_display_string(" ", 4); - display_attempt(state->attempt); - display_all_letters(state); + show_start_of_attempt(state); watch_display_string("-", 5); #if __EMSCRIPTEN__ printf("ANSWER: %s\r\n", _legal_words[state->curr_answer]); @@ -182,7 +241,7 @@ static void reset_board(wordle_state_t *state) { static void display_title(wordle_state_t *state) { state->curr_screen = SCREEN_TITLE; - watch_display_string("WO WORDLE", 0); + watch_display_string("WO WordLE", 0); } static void display_streak(wordle_state_t *state) { @@ -211,6 +270,11 @@ static void display_wait(wordle_state_t *state) { } #endif +static void display_not_in_dict(wordle_state_t *state) { + state->curr_screen = SCREEN_NO_DICT; + watch_display_string("WO nodict", 0); +} + static void display_lose(wordle_state_t *state, uint8_t subsecond) { char buf[WORDLE_LENGTH + 6]; sprintf(buf," L %s", subsecond % 2 ? _legal_words[state->curr_answer] : " "); @@ -224,41 +288,6 @@ static void display_win(wordle_state_t *state, uint8_t subsecond) { watch_display_string(buf, 0); } -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+1; pos < WORDLE_LENGTH; pos++) { - 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-1; pos >= 0; pos--) { - if (word_elements_result[pos] != WORDLE_LETTER_CORRECT) - return pos; - } - return curr_pos; -} - -static void get_next_letter(uint8_t curr_pos, uint8_t *word_elements) { - if (word_elements[curr_pos] >= _num_valid_letters) word_elements[curr_pos] = 0; - else word_elements[curr_pos] = (word_elements[curr_pos] + 1) % _num_valid_letters; -} - -static void get_prev_letter(uint8_t curr_pos, uint8_t *word_elements) { - if (word_elements[curr_pos] >= _num_valid_letters) word_elements[curr_pos] = _num_valid_letters - 1; - else word_elements[curr_pos] = (word_elements[curr_pos] + _num_valid_letters - 1) % _num_valid_letters; -} - #if USE_DAILY_STREAK static uint32_t get_day_unix_time(void) { watch_date_time now = watch_rtc_get_date_time(); @@ -295,14 +324,7 @@ static bool act_on_btn(wordle_state_t *state) { switch (state->curr_screen) { case SCREEN_RESULT: - for (size_t i = 0; i < WORDLE_LENGTH; i++) { - if (state->word_elements_result[i] != WORDLE_LETTER_CORRECT) - state->word_elements[i] = _num_valid_letters; - } - display_attempt(state->attempt); - display_all_letters(state); - state->position = get_first_pos(state->word_elements_result); - state->curr_screen = SCREEN_PLAYING; + show_start_of_attempt(state); return true; case SCREEN_TITLE: #if USE_DAILY_STREAK @@ -311,15 +333,24 @@ static bool act_on_btn(wordle_state_t *state) { return true; } #endif - display_streak(state); + if (state->playing) + show_start_of_attempt(state); + else + display_streak(state); return true; case SCREEN_STREAK: +#if USE_DAILY_STREAK + state->curr_day = get_day_unix_time(); +#endif reset_board(state); return true; case SCREEN_WIN: case SCREEN_LOSE: display_title(state); return true; + case SCREEN_NO_DICT: + show_start_of_attempt(state); + return true; #if USE_DAILY_STREAK case SCREEN_WAIT: display_title(state); @@ -346,22 +377,19 @@ void wordle_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void wordle_face_activate(movement_settings_t *settings, void *context) { (void) settings; wordle_state_t *state = (wordle_state_t *)context; - movement_request_tick_frequency(FREQ); #if USE_DAILY_STREAK - if (state->prev_day <= (get_day_unix_time() + (60 *60 * 24))) state->streak = 0; + uint32_t now = get_day_unix_time() ; + if (state->prev_day <= (now + (60 *60 * 24))) state->streak = 0; + if (state->curr_day != now) state->playing = false; #endif - if (state->curr_screen == SCREEN_TITLE) - display_title(state); + movement_request_tick_frequency(FREQ); + display_title(state); } bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { wordle_state_t *state = (wordle_state_t *)context; switch (event.event_type) { - case EVENT_ACTIVATE: - if (state->curr_screen == SCREEN_PLAYING) - display_all_letters(state); - break; case EVENT_TICK: switch (state->curr_screen) { @@ -399,10 +427,17 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi if (act_on_btn(state)) break; display_letter(state, true); if (state->word_elements[state->position] == _num_valid_letters) break; + state->playing = true; state->position = get_next_pos(state->position, state->word_elements_result); if (state->position >= WORDLE_LENGTH) { + bool in_dict = check_word_in_dict(state->word_elements); + if (!in_dict) { + display_not_in_dict(state); + break; + } bool exact_match = check_word(state); if (exact_match) { + state->playing = false; state->curr_screen = SCREEN_WIN; state->streak++; #if USE_DAILY_STREAK @@ -411,6 +446,7 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi break; } if (state->attempt++ >= WORDLE_MAX_ATTEMPTS) { + state->playing = false; state->curr_screen = SCREEN_LOSE; state->streak = 0; break; @@ -425,9 +461,12 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi state->position = get_prev_pos(state->position, state->word_elements_result); break; case EVENT_LIGHT_BUTTON_DOWN: - break; + case EVENT_ACTIVATE: case EVENT_TIMEOUT: + break; case EVENT_LOW_ENERGY_UPDATE: + if (state->curr_screen == SCREEN_TITLE) + display_title(state); break; default: return movement_default_loop_handler(event, settings); diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index 15b2a94..633d3a2 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -35,7 +35,7 @@ */ #define WORDLE_LENGTH 5 -#define WORDLE_MAX_ATTEMPTS 5 +#define WORDLE_MAX_ATTEMPTS 6 #define USE_DAILY_STREAK true typedef enum { @@ -55,6 +55,7 @@ typedef enum { #endif SCREEN_WIN, SCREEN_LOSE, + SCREEN_NO_DICT, SCREEN_COUNT } WordleScreen; @@ -64,12 +65,14 @@ typedef struct { WordleLetterResult word_elements_result[WORDLE_LENGTH]; uint8_t attempt : 3; uint8_t position : 3; - uint8_t unused : 2; + bool playing : 1; + bool unused : 1; uint16_t curr_answer; uint8_t streak; WordleScreen curr_screen; #if USE_DAILY_STREAK uint32_t prev_day; + uint32_t curr_day; #endif } wordle_state_t; From cef0d8836a9f4b2e3907b0e243c7dcfa8037c960 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Thu, 15 Aug 2024 17:40:04 -0400 Subject: [PATCH 054/161] Don't allow readding already guessed items --- .../watch_faces/complication/wordle_face.c | 108 +++++++++++------- .../watch_faces/complication/wordle_face.h | 2 + 2 files changed, 67 insertions(+), 43 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 13b4b6f..08ad478 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2024 <#author_name#> + * Copyright (c) 2024 * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -34,7 +34,6 @@ /* TODO: * Add a way to recount previous attempts -* Only allow dictionary attempts - Show "nodict" otherwise */ @@ -42,6 +41,7 @@ TODO: /* Letter | Usage +_______|______ E | 1519 S | 1490 A | 1213 @@ -60,7 +60,7 @@ static const char _valid_letters[] = {'A', 'C', 'E', 'I', 'L', 'N', 'O', 'P', 'R // Number of words found: 281 static const char _legal_words[][WORDLE_LENGTH + 1] = { - "SPIES", "SOLAR", "RAISE", "RARES", "PAEAN", "PLIES", "CRASS", "PEARS", "SNORE", + "AAAAA","SPIES", "SOLAR", "RAISE", "RARES", "PAEAN", "PLIES", "CRASS", "PEARS", "SNORE", "POLES", "ROLLS", "ALOES", "LOSES", "SLICE", "PEACE", "POLLS", "POSES", "LANES", "COPRA", "SPANS", "CANAL", "LOSER", "PAPER", "PILES", "CLASS", "RACER", "POOLS", "PLAIN", "SPEAR", "SPARE", "INNER", "ALIEN", "NOSES", "EARLS", "SEALS", "LEARN", @@ -94,7 +94,7 @@ static const char _legal_words[][WORDLE_LENGTH + 1] = { "SIREN", "PEONS", }; -static const uint32_t _num_words = (sizeof(_legal_words) / sizeof(_legal_words[0])); +static const uint16_t _num_words = (sizeof(_legal_words) / sizeof(_legal_words[0])); static const uint8_t _num_valid_letters = (sizeof(_valid_letters) / sizeof(_valid_letters[0])); static uint32_t get_random(uint32_t max) { @@ -163,7 +163,7 @@ static void display_all_letters(wordle_state_t *state) { state->position = prev_pos; } -static bool check_word_in_dict(uint8_t *word_elements) { +static uint32_t check_word_in_dict(uint8_t *word_elements) { bool is_exact_match; for (uint16_t i = 0; i < _num_words; i++) { is_exact_match = true; @@ -173,9 +173,9 @@ static bool check_word_in_dict(uint8_t *word_elements) { break; } } - if (is_exact_match) return true; + if (is_exact_match) return i; } - return false; + return _num_words; } static bool check_word(wordle_state_t *state) { @@ -208,7 +208,7 @@ static bool check_word(wordle_state_t *state) { static void display_attempt(uint8_t attempt) { char buf[2]; - sprintf(buf, "%d", attempt); + sprintf(buf, "%d", attempt+1); watch_display_string(buf, 3); } @@ -228,8 +228,11 @@ static void reset_board(wordle_state_t *state) { state->word_elements[i] = _num_valid_letters; state->word_elements_result[i] = WORDLE_LETTER_WRONG; } + for (size_t i = 0; i < WORDLE_MAX_ATTEMPTS; i++) { + state->guessed_words[i] = _num_words; + } state->curr_answer = get_random(_num_words); - state->attempt = 1; + state->attempt = 0; watch_clear_colon(); watch_display_string(" ", 4); show_start_of_attempt(state); @@ -268,11 +271,22 @@ static void display_wait(wordle_state_t *state) { watch_display_string("WO WaIt ", 0); } } + +static uint32_t get_day_unix_time(void) { + watch_date_time now = watch_rtc_get_date_time(); + now.unit.hour = now.unit.minute = now.unit.second = 0; + return watch_utility_date_time_to_unix_time(now, 0); +} #endif static void display_not_in_dict(wordle_state_t *state) { state->curr_screen = SCREEN_NO_DICT; - watch_display_string("WO nodict", 0); + 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 void display_lose(wordle_state_t *state, uint8_t subsecond) { @@ -288,14 +302,6 @@ static void display_win(wordle_state_t *state, uint8_t subsecond) { watch_display_string(buf, 0); } -#if USE_DAILY_STREAK -static uint32_t get_day_unix_time(void) { - watch_date_time now = watch_rtc_get_date_time(); - now.unit.hour = now.unit.minute = now.unit.second = 0; - return watch_utility_date_time_to_unix_time(now, 0); -} -#endif - 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++) @@ -349,6 +355,7 @@ static bool act_on_btn(wordle_state_t *state) { display_title(state); return true; case SCREEN_NO_DICT: + case SCREEN_ALREADY_GUESSED: show_start_of_attempt(state); return true; #if USE_DAILY_STREAK @@ -362,6 +369,44 @@ static bool act_on_btn(wordle_state_t *state) { return false; } +static void get_result(wordle_state_t *state) { + // Check if it's in the dict + uint16_t in_dict = check_word_in_dict(state->word_elements); + if (in_dict == _num_words) { + display_not_in_dict(state); + return; + } + + // Check if already guessed + for (size_t i = 0; i < WORDLE_MAX_ATTEMPTS; i++) { + printf("%d %d \r\n",state->guessed_words[state->attempt], state->guessed_words[i]); + if(in_dict == state->guessed_words[i]) { + display_already_guessed(state); + return; + } + } + + state->guessed_words[state->attempt] = in_dict; + bool exact_match = check_word(state); + if (exact_match) { + state->playing = false; + state->curr_screen = SCREEN_WIN; + state->streak++; +#if USE_DAILY_STREAK + state->prev_day = get_day_unix_time(); +#endif + return; + } + if (state->attempt++ > WORDLE_MAX_ATTEMPTS) { + state->playing = false; + state->curr_screen = SCREEN_LOSE; + state->streak = 0; + return; + } + state->curr_screen = SCREEN_RESULT; + return; +} + void wordle_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { (void) settings; (void) watch_face_index; @@ -429,31 +474,8 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi if (state->word_elements[state->position] == _num_valid_letters) break; state->playing = true; state->position = get_next_pos(state->position, state->word_elements_result); - if (state->position >= WORDLE_LENGTH) { - bool in_dict = check_word_in_dict(state->word_elements); - if (!in_dict) { - display_not_in_dict(state); - break; - } - bool exact_match = check_word(state); - if (exact_match) { - state->playing = false; - state->curr_screen = SCREEN_WIN; - state->streak++; -#if USE_DAILY_STREAK - state->prev_day = get_day_unix_time(); -#endif - break; - } - if (state->attempt++ >= WORDLE_MAX_ATTEMPTS) { - state->playing = false; - state->curr_screen = SCREEN_LOSE; - state->streak = 0; - break; - } - state->curr_screen = SCREEN_RESULT; - break; - } + if (state->position >= WORDLE_LENGTH) + get_result(state); break; case EVENT_ALARM_LONG_PRESS: if (state->curr_screen != SCREEN_PLAYING) break; diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index 633d3a2..9117334 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -56,6 +56,7 @@ typedef enum { SCREEN_WIN, SCREEN_LOSE, SCREEN_NO_DICT, + SCREEN_ALREADY_GUESSED, SCREEN_COUNT } WordleScreen; @@ -63,6 +64,7 @@ typedef struct { // Anything you need to keep track of, put it here! uint8_t word_elements[WORDLE_LENGTH]; WordleLetterResult word_elements_result[WORDLE_LENGTH]; + uint16_t guessed_words[WORDLE_MAX_ATTEMPTS]; uint8_t attempt : 3; uint8_t position : 3; bool playing : 1; From 3e327eb7fdc7afef9f3c9afd7d20f44d43625c40 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Thu, 15 Aug 2024 18:12:54 -0400 Subject: [PATCH 055/161] Another fix on the word_check --- movement/watch_faces/complication/wordle_face.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 08ad478..3cf4722 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -60,7 +60,7 @@ static const char _valid_letters[] = {'A', 'C', 'E', 'I', 'L', 'N', 'O', 'P', 'R // Number of words found: 281 static const char _legal_words[][WORDLE_LENGTH + 1] = { - "AAAAA","SPIES", "SOLAR", "RAISE", "RARES", "PAEAN", "PLIES", "CRASS", "PEARS", "SNORE", + "SPIES", "SOLAR", "RAISE", "RARES", "PAEAN", "PLIES", "CRASS", "PEARS", "SNORE", "POLES", "ROLLS", "ALOES", "LOSES", "SLICE", "PEACE", "POLLS", "POSES", "LANES", "COPRA", "SPANS", "CANAL", "LOSER", "PAPER", "PILES", "CLASS", "RACER", "POOLS", "PLAIN", "SPEAR", "SPARE", "INNER", "ALIEN", "NOSES", "EARLS", "SEALS", "LEARN", @@ -181,9 +181,12 @@ static uint32_t check_word_in_dict(uint8_t *word_elements) { 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]] == _legal_words[state->curr_answer][i]) + if (_valid_letters[state->word_elements[i]] == _legal_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; @@ -191,14 +194,13 @@ static bool check_word(wordle_state_t *state) { } if (is_exact_match) return true; // Wrong Location - bool answer_found_wrong_loc[WORDLE_LENGTH] = { false }; 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_found_wrong_loc[j]) continue; + if (answer_already_accounted[j]) continue; if (_valid_letters[state->word_elements[i]] == _legal_words[state->curr_answer][j]) { state->word_elements_result[i] = WORDLE_LETTER_WRONG_LOC; - answer_found_wrong_loc[j] = true; + answer_already_accounted[j] = true; break; } } @@ -379,7 +381,6 @@ static void get_result(wordle_state_t *state) { // Check if already guessed for (size_t i = 0; i < WORDLE_MAX_ATTEMPTS; i++) { - printf("%d %d \r\n",state->guessed_words[state->attempt], state->guessed_words[i]); if(in_dict == state->guessed_words[i]) { display_already_guessed(state); return; From 02f6a3256cf0a243bf54c0b365e5894feef5af25 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sat, 17 Aug 2024 02:13:26 -0400 Subject: [PATCH 056/161] Added documentation for Wordle face --- .../watch_faces/complication/wordle_face.c | 38 +++----- .../watch_faces/complication/wordle_face.h | 32 +++++-- utils/{ => wordle_face}/wordle_list.py | 89 +++++++++++++++++-- 3 files changed, 119 insertions(+), 40 deletions(-) rename utils/{ => wordle_face}/wordle_list.py (88%) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 3cf4722..1d40252 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -29,33 +29,8 @@ #include "watch_utility.h" #endif -#define FREQ 2 - -/* -TODO: -* Add a way to recount previous attempts -*/ - // From: https://gist.github.com/shmookey/b28e342e1b1756c4700f42f17102c2ff - -/* -Letter | Usage -_______|______ -E | 1519 -S | 1490 -A | 1213 -R | 1026 -O | 852 -L | 850 -I | 843 -T | 819 But looks bad across all positions -N | 681 -D | 619 lowercase d looks like a in certain positions -C | 525 -U | 514 P has more words with the other letters here (281 vs 198) -P | 448 -*/ static const char _valid_letters[] = {'A', 'C', 'E', 'I', 'L', 'N', 'O', 'P', 'R', 'S'}; // Number of words found: 281 @@ -253,7 +228,10 @@ static void display_streak(wordle_state_t *state) { char buf[12]; state->curr_screen = SCREEN_STREAK; #if USE_DAILY_STREAK - sprintf(buf, "WO St%2ddy", state->streak); + 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 @@ -392,7 +370,8 @@ static void get_result(wordle_state_t *state) { if (exact_match) { state->playing = false; state->curr_screen = SCREEN_WIN; - state->streak++; + if (state->streak < 0x7F) + state->streak++; #if USE_DAILY_STREAK state->prev_day = get_day_unix_time(); #endif @@ -428,7 +407,7 @@ void wordle_face_activate(movement_settings_t *settings, void *context) { if (state->prev_day <= (now + (60 *60 * 24))) state->streak = 0; if (state->curr_day != now) state->playing = false; #endif - movement_request_tick_frequency(FREQ); + movement_request_tick_frequency(2); display_title(state); } @@ -485,7 +464,10 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi break; case EVENT_LIGHT_BUTTON_DOWN: case EVENT_ACTIVATE: + break; case EVENT_TIMEOUT: + if (state->curr_screen >= SCREEN_WIN) + display_title(state); break; case EVENT_LOW_ENERGY_UPDATE: if (state->curr_screen == SCREEN_TITLE) diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index 9117334..b137512 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2024 <#author_name#> + * Copyright (c) 2024 * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,15 +28,37 @@ #include "movement.h" /* - * A DESCRIPTION OF YOUR WATCH FACE - * - * and a description of how use it + * 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 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: None * + * Alarm Press + * If Playing: Next position + * Else: Next screen + * Alarm Hold + * If Playing: Previous position + * Else: None */ #define WORDLE_LENGTH 5 #define WORDLE_MAX_ATTEMPTS 6 -#define USE_DAILY_STREAK true +#define USE_DAILY_STREAK false typedef enum { WORDLE_LETTER_WRONG = 0, diff --git a/utils/wordle_list.py b/utils/wordle_face/wordle_list.py similarity index 88% rename from utils/wordle_list.py rename to utils/wordle_face/wordle_list.py index 1b5baf0..f0c894b 100644 --- a/utils/wordle_list.py +++ b/utils/wordle_face/wordle_list.py @@ -1,4 +1,4 @@ -import random +import random, itertools, time, ast # From: https://gist.github.com/shmookey/b28e342e1b1756c4700f42f17102c2ff words = [ @@ -315,9 +315,12 @@ words = [ "YOUTH", "ZEBRA", "ZONES", ] -alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] +alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] def most_used_letters(): + ''' + Outputs how many times each letter is used in the words array. + ''' dicto = {} for i in alphabet: count = 0 @@ -334,12 +337,13 @@ def most_used_letters(): print(f"{k.upper()} | {dicto[k]}") -def list_of_valid_words(): - letters = ['e', 's', 'a', 'r', 'o', 'l', 'i', 'n', 'c', 'p'] +def list_of_valid_words(letters): + ''' + Outputs the array of valid words that can be made with the combination of letters. + ''' letters = sorted(letters) for i, letter in enumerate(letters): # Force all letters to be capitalized letters[i] = letter.upper() - legal_words = [] for word in words: valid_word = True @@ -349,7 +353,14 @@ def list_of_valid_words(): break if valid_word and word not in legal_words: legal_words.append(word) + return legal_words + +def print_valid_words(letters): + ''' + Prints the array of valid words that the wordle_face.c can use + ''' + legal_words = list_of_valid_words(letters) for i,word in enumerate(legal_words): legal_words[i] = word.upper().replace("D","d") random.shuffle(legal_words) @@ -373,7 +384,71 @@ def list_of_valid_words(): print('') print("};") + +def get_sec_val_and_units(seconds): + if seconds < 1: + return f"{round(seconds * 1000)} ms" + hours = int(seconds // 3600) + minutes = int((seconds % 3600) // 60) + secs = int(seconds % 60) + if hours > 0: + return f"{hours} hr {minutes} min {secs} sec" + elif minutes > 0: + return f"{minutes} min {secs} sec" + else: + return f"{secs} sec" + + +def txt_of_all_letter_combos(num_letters_in_set): + ''' + Creates a txt file that shows every combination of letters and how many words + their combo can make. + num_letters_in_set - How many letters should be in each combination + ''' + num_status_prints = 100 + dict_combos_counts = {} + print_iter = 0 + prev = time.time() + start = prev + letters_to_ignore = ['D','T'] # Don't diplay well on the watch + legal_letters = [item for item in alphabet if item not in letters_to_ignore] + print(f"Finding all {num_letters_in_set} letter combinations with the following letters: {legal_letters}") + all_combos = list(itertools.combinations(legal_letters, num_letters_in_set)) + len_all_combos = len(all_combos) + to_print = max(1, int(len_all_combos/ num_status_prints)) + print(f"Amount of Combos: {len_all_combos}") + estimated_prints = round(len_all_combos / to_print) + for i, letters in enumerate(all_combos): + letters = sorted(letters) + dict_combos_counts[repr(letters)] = len(list_of_valid_words(letters)) + print_iter+=1 + if print_iter >= to_print: + curr = time.time() + delta = curr - prev + time_passed = curr - start + total_time_estimate = delta * estimated_prints + time_left_estimate = (delta * estimated_prints) - time_passed + output = f"Time Passed: {get_sec_val_and_units(time_passed)} | " + output+= f"Amount of time for {to_print} items: {get_sec_val_and_units(delta)} | " + output+= f"Estimate for total: {get_sec_val_and_units(total_time_estimate)} | " + output+= f"items Left {len_all_combos - i} | " + output+= f"Percent Complete {round((100 * i) / len_all_combos)}% | " + output+= f"Estimated Time Left : {get_sec_val_and_units(time_left_estimate)}" + print(output) + prev = curr + print_iter = 0 + dict_combos_counts = dict(sorted(dict_combos_counts.items(), key=lambda item: item[1], reverse=True)) + + most_common_key = next(iter(dict_combos_counts)) + print(f"The Most Common Combo is: {most_common_key}") + print_valid_words(ast.literal_eval(most_common_key)) + + with open('output.txt', 'w') as file: + for key, value in dict_combos_counts.items(): + file.write(f'{key}: {value}\n') + + if __name__ == "__main__": most_used_letters() - list_of_valid_words() - + print_valid_words(['A', 'C', 'E', 'I', 'L', 'N', 'O', 'P', 'R', 'S']) + #txt_of_all_letter_combos(10) \ No newline at end of file From 580f8bf8eece137f2262686df60f482f037f4dd0 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sat, 17 Aug 2024 06:33:14 -0400 Subject: [PATCH 057/161] bugfix on iterating to previous position --- movement/watch_faces/complication/wordle_face.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 1d40252..98da99c 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -98,7 +98,7 @@ static uint8_t get_next_pos(uint8_t curr_pos, WordleLetterResult *word_elements_ static uint8_t get_prev_pos(uint8_t curr_pos, WordleLetterResult *word_elements_result) { if (curr_pos == 0) return 0; - for (size_t pos = curr_pos-1; pos > 0; pos--) { + for (int8_t pos = curr_pos-1; pos >= 0; pos--) { if (word_elements_result[pos] != WORDLE_LETTER_CORRECT) return pos; } From 10eda8b2080d3a353a9d1a0d99c3d313a421dd61 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sat, 17 Aug 2024 06:19:31 -0400 Subject: [PATCH 058/161] Added expanded dictionary to check against --- .../watch_faces/complication/wordle_face.c | 144 +- utils/wordle_face/wordle_list.py | 1306 +++++++++++++---- 2 files changed, 1090 insertions(+), 360 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 98da99c..9b1db1b 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -30,46 +30,106 @@ #endif -// From: https://gist.github.com/shmookey/b28e342e1b1756c4700f42f17102c2ff static const char _valid_letters[] = {'A', 'C', 'E', 'I', 'L', 'N', 'O', 'P', 'R', 'S'}; -// Number of words found: 281 +// From: https://gist.github.com/shmookey/b28e342e1b1756c4700f42f17102c2ff +// Number of words found: 283 static const char _legal_words[][WORDLE_LENGTH + 1] = { - "SPIES", "SOLAR", "RAISE", "RARES", "PAEAN", "PLIES", "CRASS", "PEARS", "SNORE", - "POLES", "ROLLS", "ALOES", "LOSES", "SLICE", "PEACE", "POLLS", "POSES", "LANES", - "COPRA", "SPANS", "CANAL", "LOSER", "PAPER", "PILES", "CLASS", "RACER", "POOLS", - "PLAIN", "SPEAR", "SPARE", "INNER", "ALIEN", "NOSES", "EARLS", "SEALS", "LEARN", - "COLIC", "OPERA", "LOOSE", "SPOOR", "SCALE", "SOARS", "PAILS", "PRONE", "OPALS", - "PIPER", "RILLS", "CAIRN", "POISE", "LEAPS", "ELOPE", "NICER", "SLOOP", "PANES", - "SOLES", "CROSS", "NIECE", "LAIRS", "LEASE", "SALES", "SCENE", "SORES", "SNARL", - "SPIRE", "LASSO", "CLOSE", "OSIER", "SPOOL", "PRICE", "LOANS", "POSSE", "PENAL", - "SLAPS", "RELIC", "SINCE", "CIRCA", "LIARS", "RISES", "OPENS", "ROARS", "PACES", - "ARISE", "RISEN", "PENIS", "LAPEL", "CROPS", "CANON", "LAPSE", "SCION", "ARSON", - "AREAS", "SLAIN", "CANOE", "EERIE", "NOOSE", "PIANO", "PLANE", "CLASP", "SCARE", - "COCOA", "CRESS", "NASAL", "LOCAL", "RINSE", "SCARS", "PROPS", "OASES", "SLEEP", - "SNAPS", "SIRES", "CANES", "RAILS", "RESIN", "COLON", "PEASE", "POPES", "PENCE", - "AROSE", "REELS", "SALSA", "OCEAN", "PESOS", "OPINE", "RACES", "RAINS", "PRIES", - "CRIES", "CALLS", "PIERS", "CELLS", "SCRAP", "EARNS", "IRONS", "SPACE", "LOONS", - "SILLS", "COALS", "PIECE", "PALER", "REINS", "APACE", "SLOPE", "CREPE", "CONES", - "CAPER", "SEERS", "CAPES", "OASIS", "REAPS", "PALES", "CLAPS", "PLEAS", "INANE", - "COINS", "SNAIL", "CLEAR", "ROSIN", "LILAC", "SPARS", "SPINE", "NONCE", "CRISP", - "CRAPE", "AISLE", "CRONE", "SPOIL", "SPOON", "ARENA", "PARSE", "CASES", "SPICE", - "RIPER", "PILLS", "SOLOS", "SPINS", "PEERS", "RARER", "CONIC", "REARS", "CACAO", - "PAPAS", "ACRES", "ROPES", "CORAL", "CLEAN", "EASES", "SPILL", "SENSE", "PIPES", - "CLANS", "PRESS", "LOINS", "PAPAL", "APPLE", "PAIRS", "SCORN", "ALONE", "PEEPS", - "SPREE", "SNARE", "CLIPS", "EASEL", "CAROL", "ASPEN", "SALON", "LOOPS", "PEALS", - "SNEER", "PLACE", "SELLS", "LINEN", "CRIER", "ACORN", "SLIPS", "ERASE", "LIONS", - "NAILS", "REPEL", "CORES", "LEPER", "APPAL", "ROSES", "SCORE", "RISER", "CREEP", - "CAPON", "ERROR", "NOISE", "CARES", "APRON", "SOILS", "SLOPS", "PAINS", "EPICS", - "SANER", "SAILS", "PRIOR", "ASSES", "COILS", "SCOOP", "LACES", "SCALP", "CRANE", - "PLANS", "ISLES", "SPORE", "PANIC", "COOLS", "SPELL", "ALIAS", "PORES", "SCRIP", - "PEARL", "PANEL", "ENROL", "LANCE", "CORPS", "LINES", "COPSE", "ONION", "NEARS", - "RIPEN", "LINER", "SCOPE", "SCANS", "SNIPE", "CEASE", "LEANS", "AEONS", "PINES", - "POPPA", "ROLES", "REALS", "PERIL", "POSER", "PROSE", "POLAR", "CORNS", "LIENS", - "SIREN", "PEONS", + "PROSE", "SLOPE", "CLAPS", "CAIRN", "PLANE", "SPACE", "ARISE", "NEARS", "OPERA", + "EPICS", "LAIRS", "AISLE", "APRON", "SCRIP", "CARES", "PERIL", "PILES", "CLEAN", + "CLANS", "CANOE", "COALS", "RELIC", "CRANE", "SLICE", "RESIN", "NAILS", "LIARS", + "RISEN", "COILS", "RINSE", "PRIES", "PLEAS", "CAPON", "PARSE", "ROLES", "SPICE", + "CORAL", "LEARN", "SNIPE", "CAPER", "SIREN", "CORES", "CRAPE", "SPOIL", "EARNS", + "LANES", "PEALS", "RAINS", "ACRES", "SINCE", "LACES", "ROPES", "CLEAR", "PANIC", + "SCALE", "SANER", "CLOSE", "LINER", "CORNS", "SPINE", "SCARE", "ACORN", "PLANS", + "POSER", "SCORN", "AROSE", "LINES", "LIONS", "OCEAN", "SNAIL", "OSIER", "SCOPE", + "OPINE", "REINS", "COINS", "LOSER", "PANES", "RAISE", "RAILS", "ALIEN", "PLAIN", + "SNARE", "LOANS", "IRONS", "COPRA", "PLIES", "COPSE", "PEARS", "PALER", "SLAIN", + "LAPSE", "CONES", "ARSON", "POISE", "OPENS", "SPORE", "CAROL", "PACES", "LEAPS", + "ALONE", "ASPEN", "SNORE", "CRIES", "OPALS", "PENAL", "PAINS", "ENROL", "POLAR", + "SPEAR", "SCION", "POLES", "SCORE", "LEANS", "PEONS", "ROSIN", "REAPS", "SOLAR", + "EARLS", "PENIS", "PRICE", "REALS", "SCALP", "CLASP", "NOISE", "PANEL", "NICER", + "CRISP", "SPARE", "PEARL", "CORPS", "SALON", "RACES", "LOINS", "RIPEN", "PLACE", + "CRONE", "CANES", "LANCE", "LIENS", "ALOES", "PIANO", "PRONE", "PIERS", "SNARL", + "AEONS", "PINES", "SPIRE", "PAILS", "CAPES", "CLIPS", "PALES", "CROPS", "PAIRS", + "SCRAP", "PORES", "SALES", "COOLS", "SLOPS", "APACE", "CRIER", "ROLLS", "PIPES", + "SLIPS", "EASES", "PEACE", "CELLS", "POLLS", "POPPA", "SPREE", "RILLS", "SPILL", + "RIPER", "ONION", "ISLES", "CROSS", "PAPAS", "NOSES", "PAPAL", "SLEEP", "EERIE", + "SEALS", "PAPER", "COLOR", "SORES", "SPELL", "SNAPS", "SEERS", "SPARS", "PIPER", + "POSES", "LOOPS", "APPAL", "LOSES", "LINEN", "ROARS", "RARER", "NIECE", "LOCAL", + "NONCE", "CREEP", "SCARS", "SPOOR", "PEASE", "SIRES", "PRESS", "CLASS", "PAEAN", + "SPOOL", "LOOSE", "CRASS", "LILAC", "COLIC", "RACER", "CREPE", "NASAL", "SOLOS", + "ROSES", "COLON", "REPEL", "ASSES", "INANE", "SCENE", "SLAPS", "PEEPS", "LAPEL", + "PIECE", "LEPER", "REELS", "INNER", "PROPS", "SILLS", "LEASE", "SOLES", "CIRCA", + "CRESS", "SCANS", "SPOON", "REARS", "CACAO", "ERASE", "CANON", "SOARS", "SLOOP", + "AREAS", "SPINS", "OASIS", "OASES", "POPES", "ELOPE", "CEASE", "CINCO", "APPLE", + "NOOSE", "PEERS", "SENSE", "POOLS", "RISER", "PENCE", "POSSE", "ALIAS", "PESOS", + "SCOOP", "EASEL", "LOONS", "CONIC", "SPANS", "SPIES", "PRIOR", "SALSA", "SELLS", + "PILLS", "RISES", "RARES", "SNEER", "SOILS", "ARENA", "CASES", "CANAL", "SAILS", + "LASSO", "COCOA", "ERROR", "CALLS", +}; +// These are words that'll never be used, but still need to be in the dictionary for guesses. +// Top 100K most common words from Wiktionary https://gist.github.com/h3xx/1976236 +// Number of words found: 469 +static const char _expanded_words[][WORDLE_LENGTH + 1] = { + "PARIS", "APRIL", "SPAIN", "EINEN", "ASCII", "EINER", "SEINE", "AINSI", "ALICE", + "ALLES", "ALORS", "EINES", "ALLER", "PEINE", "PARCE", "CELLE", "CLARA", "ELLES", + "ELLEN", "OLISI", "ALLEN", "ISAAC", "APRES", "CROIS", "SANOI", "PASSE", "ELSIE", + "REINE", "ELLER", "AARON", "CLARE", "IRENE", "ANNIE", "ELLOS", "PARLE", "ALLAN", + "PELLE", "CAIRO", "SENOR", "PENSE", "CECIL", "SEELE", "ORION", "SELON", "COSAS", + "PASSA", "ELLIS", "CARLO", "ENNEN", "SILAS", "EENEN", "OSCAR", "ONCLE", "CESSE", + "SONNE", "ASSIS", "PRISE", "SERAI", "CELIA", "NOIRE", "NORSE", "SINNE", "LIESS", + "ELIAS", "REPOS", "COLIN", "NOIRS", "CLAIR", "CIELO", "PARLA", "SOINS", "LASSE", + "NELLA", "PAOLO", "SOLON", "REPAS", "NANCE", "PAINE", "SAISI", "ELISE", "CESAR", + "CANNA", "SALLE", "SINON", "SINAI", "LOIRE", "PENSA", "LEILA", "REISE", "ELLAS", + "POORE", "EARLE", "CERCA", "LEISE", "POOLE", "AILES", "SANOA", "LEONE", "LILLE", + "PROIE", "CARNE", "SPIEL", "CERES", "ENSIN", "ROLLO", "ARRAS", "SEIEN", "PRIER", + "ANNAN", "CALLE", "LIISA", "SALIR", "LESSE", "LESEN", "LIIAN", "NEERE", "ARIEL", + "PIENI", "PIERO", "ERANO", "ELENA", "SILLE", "NEALE", "SEENE", "ROLLE", "NELLE", + "SOLLE", "ESSER", "PASAR", "PREIS", "ASIAN", "SENCE", "ANSON", "SERRA", "CONAN", + "SERAS", "SIENA", "SOPRA", "RENEE", "ALINE", "CORSE", "ASSAI", "INSEL", "ROSIE", + "SONIA", "APPEL", "CRISE", "CIRCE", "LINIE", "RENAN", "CAIRE", "COLLA", "SANOO", + "EENER", "ANCOR", "NEPAL", "REINO", "LAINE", "SOONE", "ALAIN", "LAPSI", "INCAS", + "INNES", "CARON", "ROSEN", "CASAS", "NOLAN", "SERRE", "PESAR", "SEARS", "LEPIC", + "LISLE", "LOSSE", "CINNA", "SERIE", "RIRES", "CORSO", "SOIRS", "CREER", "POCOS", + "SIENS", "ARLES", "CROCE", "IONIC", "PONER", "ESSEN", "SANON", "CESSA", "SERIA", + "ALPES", "NINON", "LILLA", "AINOA", "CORPO", "LESER", "ILLIS", "ROPER", "ANNEE", + "PAIRE", "PEPIN", "ORIEL", "CANNE", "AIRES", "ARCIS", "EASIE", "ANNOS", "COLLE", + "SELLE", "EILEN", "CAPRI", "ERICA", "ROCCO", "ARIAN", "CLEON", "ALLIE", "PONCE", + "COPIE", "INNAN", "NOCES", "NAPPE", "CORNE", "ESIIN", "ENCOR", "LORNA", "SACRE", + "PAPEL", "SAILE", "SAEPE", "CREON", "LLENO", "ELISA", "PASSO", "ASILE", "LORCS", + "ASIAA", "SANIN", "ONNEN", "SONNA", "AILIE", "ALIIS", "ECOLE", "CREES", "PRESO", + "CLARO", "EARES", "ROSSI", "COREA", "SANAN", "AESOP", "SAPOR", "EISEN", "ACASO", + "PARAS", "NANON", "LAPIS", "ARRAN", "CLLIA", "SACRA", "PRINS", "CENCI", "CLAES", + "SLAAP", "ROLLA", "COLES", "LORNE", "OLELO", "CASSE", "NILES", "PASOS", "ESSAI", + "ROSAS", "LLENA", "LEERE", "CLASE", "CALOR", "ROSSE", "ALLEE", "SOREL", "SANAA", + "SLONE", "OLSEN", "OOREN", "PARER", "PASSI", "POSSA", "PLAIE", "OPERE", "SCAPE", + "POLEN", "RIPON", "SCALA", "AILLE", "PALOS", "CLAPP", "ESCAP", "ELLEI", "IONIA", + "NICOL", "PAESE", "PERON", "ORSON", "INNEN", "AISNE", "RANCE", "SLAAN", "PAOLI", + "COLLO", "ANNAS", "ERROL", "CLERC", "SAINE", "RAINA", "PRESE", "PARIA", "PERLE", + "RECAL", "SINAE", "PESER", "OISIN", "PLENA", "CARLE", "PERES", "SACAR", "ALPEN", + "CORRE", "ACCES", "RILLA", "ANNAL", "PERSE", "SAALE", "PERRO", "AILSA", "POCAS", + "SOLEN", "PLASE", "SOLIS", "PAPPI", "COPIA", "ARIES", "ROCCA", "ALIOS", "ROCAS", + "PELOS", "NEPOS", "COLPA", "ISORA", "LECON", "SOANE", "SNELL", "ILLOS", "PILAR", + "ECLAC", "PRESA", "SILLA", "NIELS", "LIPPO", "CROLL", "PONEN", "POSEE", "PIPPA", + "ILLAN", "CENIS", "SANNA", "RASSI", "CERRO", "SCENA", "CASOS", "COLPO", "POSSO", + "POSEN", "EINAR", "ISLAS", "IPSIS", "SALEN", "ASIEN", "CREAN", "LENIN", "LOCIS", + "NENNE", "ILION", "NARES", "ONNEA", "PALAA", "PIPPO", "POLIS", "RICOS", "ELSON", + "SNOOP", "ANNIS", "PROPE", "ELLIE", "ERNIE", "PLIER", "SERES", "REINA", "LIPPE", + "OLINE", "PARIE", "ARLEE", "NIAIS", "LEONI", "RAINE", "LARES", "SEINS", "CARRE", + "POILS", "ALENE", "LINEA", "NEARE", "PENSO", "PRISA", "CAPEL", "PAREA", "PEECE", + "SALIO", "COELO", "SCIRE", "NELLO", "LIENE", "ORICE", "EPAIS", "PERCE", "ALLIS", + "PEPLE", "LARNE", "NEILL", "OLLEN", "CASCA", "LAPIN", "OLLIE", "SALIC", "LIANE", + "REESE", "ELSLI", "SPION", "RIENS", "LILAS", "PAPPA", "ERRER", "SPISE", "CELIE", + "OLSON", "IRREN", "ARIAS", "ARION", "PASEO", "CAERE", "PISAN", "CARRO", "PAROI", + "NOONE", "SEPPI", "OPPIA", "SEALE", "LIPPI", "PELAS", "COCOS", "PLACA", "CONOR", + "LANCA", "OSASI", "ALOIS", "NAIRN", "PIENO", "SPASS", "SAONE", "ALNAR", "CARIA", + "PIENA", }; +static const uint16_t _num_unique_words = 155; // The _legal_words array begins with this many words where each letter is different. static const uint16_t _num_words = (sizeof(_legal_words) / sizeof(_legal_words[0])); +static const uint16_t _num_expanded_words = (sizeof(_expanded_words) / sizeof(_expanded_words[0])); static const uint8_t _num_valid_letters = (sizeof(_valid_letters) / sizeof(_valid_letters[0])); static uint32_t get_random(uint32_t max) { @@ -150,7 +210,17 @@ static uint32_t check_word_in_dict(uint8_t *word_elements) { } if (is_exact_match) return i; } - return _num_words; + for (uint16_t i = 0; i < _num_expanded_words; i++) { + is_exact_match = true; + for (size_t j = 0; j < WORDLE_LENGTH; j++) { + if (_valid_letters[word_elements[j]] != _expanded_words[i][j]) { + is_exact_match = false; + break; + } + } + if (is_exact_match) return _num_words + i; + } + return _num_words + _num_expanded_words; } static bool check_word(wordle_state_t *state) { @@ -206,7 +276,7 @@ static void reset_board(wordle_state_t *state) { state->word_elements_result[i] = WORDLE_LETTER_WRONG; } for (size_t i = 0; i < WORDLE_MAX_ATTEMPTS; i++) { - state->guessed_words[i] = _num_words; + state->guessed_words[i] = _num_words + _num_expanded_words; } state->curr_answer = get_random(_num_words); state->attempt = 0; @@ -352,7 +422,7 @@ static bool act_on_btn(wordle_state_t *state) { static void get_result(wordle_state_t *state) { // Check if it's in the dict uint16_t in_dict = check_word_in_dict(state->word_elements); - if (in_dict == _num_words) { + if (in_dict == _num_words + _num_expanded_words) { display_not_in_dict(state); return; } diff --git a/utils/wordle_face/wordle_list.py b/utils/wordle_face/wordle_list.py index f0c894b..d508a8b 100644 --- a/utils/wordle_face/wordle_list.py +++ b/utils/wordle_face/wordle_list.py @@ -1,323 +1,949 @@ import random, itertools, time, ast # From: https://gist.github.com/shmookey/b28e342e1b1756c4700f42f17102c2ff -words = [ - "ABACK", "ABAFT", "ABASE", "ABATE", "ABBEY", "ABBOT", "ABHOR", "ABIDE", "ABLER", "ABODE", - "ABOUT", "ABOVE", "ABUSE", "ABYSS", "ACHED", "ACHES", "ACIDS", "ACORN", "ACRES", "ACRID", - "ACTED", "ACTOR", "ACUTE", "ADAGE", "ADAPT", "ADDED", "ADDER", "ADEPT", "ADIEU", "ADMIT", - "ADOBE", "ADOPT", "ADORE", "ADORN", "ADULT", "AEGIS", "AEONS", "AFFIX", "AFIRE", "AFOOT", - "AFTER", "AGAIN", "AGAPE", "AGATE", "AGENT", "AGILE", "AGING", "AGLOW", "AGONY", "AGREE", - "AHEAD", "AIDED", "AIDES", "AILED", "AIMED", "AIRED", "AISLE", "ALARM", "ALBUM", "ALDER", - "ALERT", "ALIAS", "ALIBI", "ALIEN", "ALIKE", "ALIVE", "ALLAY", "ALLEY", "ALLOT", "ALLOW", - "ALLOY", "ALOES", "ALOFT", "ALONE", "ALONG", "ALOOF", "ALOUD", "ALPHA", "ALTAR", "ALTER", - "ALTOS", "AMASS", "AMAZE", "AMBER", "AMBLE", "AMEND", "AMIGO", "AMISS", "AMITY", "AMONG", - "AMOUR", "AMPLE", "AMPLY", "AMUSE", "ANGEL", "ANGER", "ANGLE", "ANGRY", "ANGST", "ANIME", - "ANKLE", "ANNEX", "ANNOY", "ANNUL", "ANTES", "ANTIC", "ANVIL", "APACE", "APART", "APING", - "APPAL", "APPLE", "APPLY", "APRON", "APTLY", "AREAS", "ARENA", "ARGUE", "ARISE", "ARMED", - "AROMA", "AROSE", "ARRAY", "ARROW", "ARSON", "ASHEN", "ASHES", "ASIDE", "ASKED", "ASKEW", - "ASPEN", "ASSAY", "ASSES", "ASSET", "ASTER", "ASTIR", "ATLAS", "ATOLL", "ATOMS", "ATONE", - "ATTAR", "ATTIC", "AUDIO", "AUDIT", "AUGER", "AUGHT", "AUGUR", "AUNTS", "AURAS", "AUTOS", - "AVAIL", "AVERS", "AVERT", "AVOID", "AVOWS", "AWAIT", "AWAKE", "AWARD", "AWARE", "AWFUL", - "AWOKE", "AXIOM", "AXLES", "AZURE", "BABEL", "BABES", "BACKS", "BACON", "BADGE", "BADLY", - "BAGGY", "BAITS", "BAIZE", "BAKED", "BAKER", "BALES", "BALLS", "BALMY", "BANAL", "BANDS", - "BANDY", "BANGS", "BANJO", "BANKS", "BANNS", "BARBS", "BARDS", "BARED", "BARGE", "BARKS", - "BARNS", "BARON", "BASAL", "BASED", "BASER", "BASES", "BASIC", "BASIL", "BASIN", "BASIS", - "BASSO", "BASTE", "BATCH", "BATED", "BATHE", "BATHS", "BATON", "BAYOU", "BEACH", "BEADS", - "BEADY", "BEAKS", "BEAMS", "BEANS", "BEARD", "BEARS", "BEAST", "BEAUX", "BEECH", "BEETS", - "BEFIT", "BEGAN", "BEGAT", "BEGET", "BEGIN", "BEGOT", "BEGUN", "BEING", "BELIE", "BELLE", - "BELLS", "BELLY", "BELOW", "BELTS", "BENCH", "BENDS", "BERGS", "BERRY", "BERTH", "BERYL", - "BESET", "BESOM", "BEVEL", "BIBLE", "BIDED", "BIDES", "BIGHT", "BIGOT", "BILGE", "BILLS", - "BILLY", "BINDS", "BIPED", "BIRCH", "BIRDS", "BIRTH", "BISON", "BITCH", "BITES", "BLACK", - "BLADE", "BLAME", "BLAND", "BLANK", "BLARE", "BLAST", "BLAZE", "BLEAK", "BLEAT", "BLEED", - "BLEND", "BLENT", "BLESS", "BLEST", "BLIND", "BLINK", "BLISS", "BLOCK", "BLOCS", "BLOND", - "BLOOD", "BLOOM", "BLOTS", "BLOWN", "BLOWS", "BLUER", "BLUES", "BLUFF", "BLUNT", "BLURT", - "BLUSH", "BOARD", "BOARS", "BOAST", "BOATS", "BODED", "BODES", "BOGGY", "BOGUS", "BOILS", - "BOLES", "BOLTS", "BOMBS", "BONDS", "BONED", "BONES", "BONNY", "BONUS", "BOOBY", "BOOKS", - "BOOMS", "BOONS", "BOORS", "BOOST", "BOOTH", "BOOTS", "BOOTY", "BOOZE", "BORAX", "BORED", - "BORES", "BORNE", "BOSOM", "BOUGH", "BOUND", "BOUTS", "BOWED", "BOWEL", "BOWER", "BOWLS", - "BOXED", "BOXER", "BOXES", "BRACE", "BRAGS", "BRAID", "BRAIN", "BRAKE", "BRAND", "BRASS", - "BRATS", "BRAVE", "BRAVO", "BRAWL", "BRAWN", "BREAD", "BREAK", "BREED", "BRIAR", "BRIBE", - "BRICK", "BRIDE", "BRIEF", "BRIER", "BRIGS", "BRIMS", "BRINE", "BRING", "BRINK", "BRINY", - "BRISK", "BROAD", "BROIL", "BROKE", "BROOD", "BROOK", "BROOM", "BROTH", "BROWN", "BROWS", - "BRUIN", "BRUNT", "BRUSH", "BRUTE", "BUCKS", "BUDGE", "BUGGY", "BUGLE", "BUILD", "BUILT", - "BULBS", "BULGE", "BULKS", "BULKY", "BULLS", "BULLY", "BUMPS", "BUNCH", "BUNKS", "BUOYS", - "BURLY", "BURNS", "BURNT", "BURRO", "BURRS", "BURST", "BUSHY", "BUSTS", "BUTTE", "BUTTS", - "BUXOM", "BUYER", "CABAL", "CABBY", "CABIN", "CABLE", "CACAO", "CACHE", "CADET", "CADRE", - "CAGED", "CAGES", "CAIRN", "CAKED", "CAKES", "CALLS", "CALMS", "CALYX", "CAMEL", "CAMEO", - "CAMPS", "CANAL", "CANDY", "CANES", "CANNY", "CANOE", "CANON", "CANTO", "CAPER", "CAPES", - "CAPON", "CARDS", "CARED", "CARES", "CARGO", "CAROL", "CARRY", "CARTS", "CARVE", "CASED", - "CASES", "CASKS", "CASTE", "CASTS", "CATCH", "CATER", "CAUSE", "CAVED", "CAVES", "CAVIL", - "CEASE", "CEDAR", "CEDED", "CELLS", "CENTS", "CHAFE", "CHAFF", "CHAIN", "CHAIR", "CHALK", - "CHAMP", "CHANT", "CHAOS", "CHAPS", "CHARM", "CHART", "CHARY", "CHASE", "CHASM", "CHATS", - "CHEAP", "CHEAT", "CHECK", "CHEEK", "CHEER", "CHEFS", "CHESS", "CHEST", "CHICK", "CHIDE", - "CHIEF", "CHILD", "CHILL", "CHIME", "CHINA", "CHINK", "CHINS", "CHIPS", "CHIRP", "CHOIR", - "CHOKE", "CHOPS", "CHORD", "CHOSE", "CHUCK", "CHUMP", "CHUMS", "CHUNK", "CHURL", "CHURN", - "CHUTE", "CIDER", "CIGAR", "CINCH", "CIRCA", "CITED", "CITES", "CIVET", "CIVIC", "CIVIL", - "CLACK", "CLAIM", "CLAMP", "CLAMS", "CLANG", "CLANK", "CLANS", "CLAPS", "CLASH", "CLASP", - "CLASS", "CLAWS", "CLEAN", "CLEAR", "CLEFS", "CLEFT", "CLERK", "CLEWS", "CLICK", "CLIFF", - "CLIMB", "CLIME", "CLING", "CLINK", "CLIPS", "CLOAK", "CLOCK", "CLODS", "CLOGS", "CLOSE", - "CLOTH", "CLOUD", "CLOUT", "CLOVE", "CLOWN", "CLUBS", "CLUCK", "CLUES", "CLUMP", "CLUNG", - "COACH", "COALS", "COAST", "COATS", "COBRA", "COCKS", "COCOA", "CODES", "COILS", "COINS", - "COLDS", "COLIC", "COLON", "COLTS", "COMBS", "COMER", "COMES", "COMET", "COMIC", "COMMA", - "CONCH", "CONES", "CONIC", "COOED", "COOKS", "COOLS", "COPRA", "COPSE", "CORAL", "CORDS", - "CORES", "CORKS", "CORNS", "CORPS", "COSTS", "COTES", "COUCH", "COUGH", "COULD", "COUNT", - "COUPE", "COUPS", "COURT", "COVER", "COVES", "COVET", "COVEY", "COWED", "COWER", "COYLY", - "COZEN", "CRABS", "CRACK", "CRAFT", "CRAGS", "CRAMP", "CRANE", "CRANK", "CRAPE", "CRASH", - "CRASS", "CRATE", "CRAVE", "CRAWL", "CRAZE", "CRAZY", "CREAK", "CREAM", "CREDO", "CREED", - "CREEK", "CREEP", "CREPE", "CREPT", "CRESS", "CREST", "CREWS", "CRIBS", "CRICK", "CRIED", - "CRIER", "CRIES", "CRIME", "CRIMP", "CRISP", "CROAK", "CROCK", "CRONE", "CRONY", "CROOK", - "CROPS", "CROSS", "CROUP", "CROWD", "CROWN", "CROWS", "CRUDE", "CRUEL", "CRUMB", "CRUSH", - "CRUST", "CRYPT", "CUBES", "CUBIC", "CUBIT", "CUFFS", "CULTS", "CURDS", "CURED", "CURES", - "CURLS", "CURLY", "CURRY", "CURSE", "CURST", "CURVE", "CYCLE", "CYNIC", "DADDY", "DAILY", - "DAIRY", "DAISY", "DALES", "DALLY", "DAMES", "DAMPS", "DANCE", "DANDY", "DARED", "DARES", - "DARTS", "DATED", "DATES", "DATUM", "DAUBS", "DAUNT", "DAWNS", "DAZED", "DEALS", "DEALT", - "DEANS", "DEARS", "DEATH", "DEBAR", "DEBIT", "DEBTS", "DEBUT", "DECAY", "DECKS", "DECOY", - "DECRY", "DEEDS", "DEEMS", "DEEPS", "DEFER", "DEIGN", "DEITY", "DELAY", "DELLS", "DELTA", - "DELVE", "DEMON", "DEMUR", "DENSE", "DENTS", "DEPOT", "DEPTH", "DERBY", "DESKS", "DETER", - "DEUCE", "DEVIL", "DIARY", "DICED", "DICES", "DICTA", "DIETS", "DIGIT", "DIKES", "DIMES", - "DIMLY", "DINED", "DINER", "DINES", "DINGY", "DIRGE", "DIRTY", "DISCS", "DISKS", "DITCH", - "DITTO", "DITTY", "DIVAN", "DIVED", "DIVER", "DIVES", "DIZZY", "DOCKS", "DODGE", "DOERS", - "DOGMA", "DOING", "DOLED", "DOLLS", "DOMED", "DOMES", "DONOR", "DOOMS", "DOORS", "DOSED", - "DOSES", "DOTED", "DOTES", "DOUBT", "DOUGH", "DOVES", "DOWDY", "DOWNS", "DOWNY", "DOWRY", - "DOZED", "DOZEN", "DRAFT", "DRAGS", "DRAIN", "DRAKE", "DRAMA", "DRAMS", "DRANK", "DRAPE", - "DRAWL", "DRAWN", "DRAWS", "DRAYS", "DREAD", "DREAM", "DREGS", "DRESS", "DRIED", "DRIER", - "DRIES", "DRIFT", "DRILL", "DRILY", "DRINK", "DRIPS", "DRIVE", "DROLL", "DRONE", "DROOP", - "DROPS", "DROSS", "DROVE", "DROWN", "DRUGS", "DRUMS", "DRUNK", "DRYLY", "DUCAL", "DUCAT", - "DUCHY", "DUCKS", "DUCTS", "DUELS", "DUETS", "DUKES", "DULLY", "DUMMY", "DUMPS", "DUMPY", - "DUNCE", "DUNES", "DUNNO", "DUPED", "DUPES", "DUSKY", "DUSTY", "DWARF", "DWELL", "DWELT", - "DYING", "DYKES", "EAGER", "EAGLE", "EARLS", "EARLY", "EARNS", "EARTH", "EASED", "EASEL", - "EASES", "EATEN", "EATER", "EAVES", "EBBED", "EBONY", "EDGED", "EDGES", "EDICT", "EDIFY", - "EERIE", "EGGED", "EIGHT", "EJECT", "ELATE", "ELBOW", "ELDER", "ELECT", "ELEGY", "ELFIN", - "ELITE", "ELOPE", "ELUDE", "ELVES", "EMAIL", "EMITS", "EMPTY", "ENACT", "ENDED", "ENDOW", - "ENEMY", "ENJOY", "ENNUI", "ENROL", "ENSUE", "ENTER", "ENTRY", "ENVOY", "EPICS", "EPOCH", - "EQUAL", "EQUIP", "ERASE", "ERECT", "ERRED", "ERROR", "ESSAY", "ETHER", "ETHIC", "EVADE", - "EVENT", "EVERY", "EVILS", "EVOKE", "EXACT", "EXALT", "EXCEL", "EXERT", "EXILE", "EXIST", - "EXITS", "EXPEL", "EXTOL", "EXTRA", "EXULT", "EYING", "EYRIE", "FABLE", "FACED", "FACES", - "FACTS", "FADED", "FADES", "FAILS", "FAINT", "FAIRS", "FAIRY", "FAITH", "FAKIR", "FALLS", - "FALSE", "FAMED", "FANCY", "FANGS", "FARCE", "FARED", "FARES", "FARMS", "FASTS", "FATAL", - "FATED", "FATES", "FATTY", "FAULT", "FAUNA", "FAUNS", "FAWNS", "FEARS", "FEAST", "FEATS", - "FEEDS", "FEELS", "FEIGN", "FEINT", "FELLS", "FELON", "FENCE", "FERAL", "FERNS", "FERRY", - "FETCH", "FETED", "FETID", "FETUS", "FEUDS", "FEVER", "FEWER", "FICHE", "FIEFS", "FIELD", - "FIEND", "FIERY", "FIFES", "FIFTH", "FIFTY", "FIGHT", "FILCH", "FILED", "FILES", "FILET", - "FILLS", "FILLY", "FILMS", "FILMY", "FILTH", "FINAL", "FINCH", "FINDS", "FINED", "FINER", - "FINES", "FINIS", "FINNY", "FIORD", "FIRED", "FIRES", "FIRMS", "FIRST", "FISHY", "FISTS", - "FITLY", "FIVES", "FIXED", "FIXER", "FIXES", "FJORD", "FLAGS", "FLAIL", "FLAIR", "FLAKE", - "FLAKY", "FLAME", "FLANK", "FLAPS", "FLARE", "FLASH", "FLASK", "FLATS", "FLAWS", "FLEAS", - "FLECK", "FLEES", "FLEET", "FLESH", "FLICK", "FLIER", "FLIES", "FLING", "FLINT", "FLIRT", - "FLITS", "FLOAT", "FLOCK", "FLOES", "FLOOD", "FLOOR", "FLORA", "FLOSS", "FLOUR", "FLOUT", - "FLOWN", "FLOWS", "FLUES", "FLUFF", "FLUID", "FLUKE", "FLUME", "FLUNG", "FLUSH", "FLUTE", - "FLYER", "FOAMS", "FOAMY", "FOCAL", "FOCUS", "FOGGY", "FOILS", "FOIST", "FOLDS", "FOLIO", - "FOLKS", "FOLLY", "FOODS", "FOOLS", "FORAY", "FORCE", "FORDS", "FORGE", "FORGO", "FORKS", - "FORMS", "FORTE", "FORTH", "FORTS", "FORTY", "FORUM", "FOUND", "FOUNT", "FOURS", "FOWLS", - "FOXES", "FOYER", "FRAIL", "FRAME", "FRANC", "FRANK", "FRAUD", "FREAK", "FREED", "FREER", - "FREES", "FRESH", "FRETS", "FRIAR", "FRIED", "FRILL", "FRISK", "FROCK", "FROGS", "FROND", - "FRONT", "FROST", "FROTH", "FROWN", "FROZE", "FRUIT", "FUDGE", "FUELS", "FUGUE", "FULLY", - "FUMED", "FUMES", "FUNDS", "FUNGI", "FUNNY", "FURRY", "FURZE", "FUSED", "FUSES", "FUSSY", - "FUZZY", "GABLE", "GAILY", "GAINS", "GALES", "GALLS", "GAMES", "GAMIN", "GAMMA", "GAMUT", - "GANGS", "GAPED", "GAPES", "GASES", "GASPS", "GATES", "GAUDY", "GAUGE", "GAUNT", "GAUZE", - "GAUZY", "GAVEL", "GAWKY", "GAYER", "GAYLY", "GAZED", "GAZER", "GAZES", "GEARS", "GEESE", - "GENIE", "GENII", "GENRE", "GENTS", "GENUS", "GERMS", "GHOST", "GIANT", "GIBES", "GIDDY", - "GIFTS", "GILDS", "GILLS", "GIMME", "GIPSY", "GIRDS", "GIRLS", "GIRTH", "GIVEN", "GIVES", - "GLADE", "GLAND", "GLARE", "GLASS", "GLAZE", "GLEAM", "GLEAN", "GLENS", "GLIDE", "GLINT", - "GLOAT", "GLOBE", "GLOOM", "GLORY", "GLOSS", "GLOVE", "GLOWS", "GLUED", "GNASH", "GNATS", - "GNAWS", "GNOME", "GOADS", "GOALS", "GOATS", "GODLY", "GOING", "GOLLY", "GONGS", "GONNA", - "GOODS", "GOODY", "GOOSE", "GORED", "GORGE", "GORSE", "GOTTA", "GOUGE", "GOURD", "GOUTY", - "GOWNS", "GRABS", "GRACE", "GRADE", "GRAFT", "GRAIN", "GRAMS", "GRAND", "GRANT", "GRAPE", - "GRAPH", "GRASP", "GRASS", "GRATE", "GRAVE", "GRAVY", "GRAZE", "GREAT", "GREED", "GREEN", - "GREET", "GREYS", "GRIEF", "GRILL", "GRIME", "GRIMY", "GRIND", "GRINS", "GRIPE", "GRIPS", - "GRIST", "GROAN", "GROIN", "GROOM", "GROPE", "GROSS", "GROUP", "GROVE", "GROWL", "GROWN", - "GROWS", "GRUBS", "GRUEL", "GRUFF", "GRUNT", "GUANO", "GUARD", "GUESS", "GUEST", "GUIDE", - "GUILD", "GUILE", "GUILT", "GUISE", "GULCH", "GULFS", "GULLS", "GULLY", "GUMMY", "GUSTO", - "GUSTS", "GUSTY", "GYPSY", "HABIT", "HACKS", "HAILS", "HAIRS", "HAIRY", "HALED", "HALLS", - "HALTS", "HALVE", "HANDS", "HANDY", "HANGS", "HAPPY", "HARDY", "HAREM", "HARES", "HARMS", - "HARPS", "HARPY", "HARRY", "HARSH", "HARTS", "HASTE", "HASTY", "HATCH", "HATED", "HATER", - "HAULS", "HAVEN", "HAVOC", "HAWKS", "HAZEL", "HEADS", "HEADY", "HEALS", "HEAPS", "HEARD", - "HEARS", "HEART", "HEATH", "HEATS", "HEAVE", "HEAVY", "HEDGE", "HEEDS", "HEELS", "HEIRS", - "HELIX", "HELLO", "HELMS", "HELPS", "HENCE", "HERBS", "HERDS", "HERON", "HEROS", "HEWED", - "HIDES", "HILLS", "HILLY", "HILTS", "HINDS", "HINGE", "HINTS", "HIRED", "HIRES", "HITCH", - "HIVES", "HOARD", "HOARY", "HOBBY", "HOIST", "HOLDS", "HOLES", "HOLLY", "HOMES", "HONEY", - "HOODS", "HOOFS", "HOOKS", "HOOPS", "HOOTS", "HOPED", "HOPES", "HORDE", "HORNS", "HORNY", - "HORSE", "HOSTS", "HOTEL", "HOTLY", "HOUND", "HOURS", "HOUSE", "HOVEL", "HOVER", "HOWLS", - "HULKS", "HULLS", "HUMAN", "HUMID", "HUMPS", "HUMUS", "HUNCH", "HUNTS", "HURLS", "HURRY", - "HURTS", "HUSKS", "HUSKY", "HUSSY", "HYDRA", "HYENA", "HYMNS", "ICILY", "ICING", "IDEAL", - "IDEAS", "IDIOM", "IDIOT", "IDLED", "IDLER", "IDOLS", "IDYLL", "IGLOO", "IMAGE", "IMBUE", - "IMPEL", "IMPLY", "INANE", "INCUR", "INDEX", "INEPT", "INERT", "INFER", "INGOT", "INLET", - "INNER", "INTER", "INURE", "IRATE", "IRKED", "IRONS", "IRONY", "ISLES", "ISLET", "ISSUE", - "ITEMS", "IVORY", "JACKS", "JADED", "JAILS", "JAUNT", "JEANS", "JEERS", "JELLY", "JERKS", - "JERKY", "JESTS", "JETTY", "JEWEL", "JIFFY", "JOINS", "JOINT", "JOKED", "JOKER", "JOKES", - "JOLLY", "JOUST", "JOYED", "JUDGE", "JUICE", "JUICY", "JUMPS", "JUNKS", "JUNTA", "JUROR", - "KARMA", "KEELS", "KEEPS", "KETCH", "KEYED", "KHAKI", "KICKS", "KILLS", "KINDA", "KINDS", - "KINGS", "KIOSK", "KITES", "KNACK", "KNAVE", "KNEAD", "KNEEL", "KNEES", "KNELL", "KNELT", - "KNIFE", "KNITS", "KNOBS", "KNOCK", "KNOLL", "KNOTS", "KNOWN", "KNOWS", "LABEL", "LACED", - "LACES", "LACKS", "LADEN", "LADLE", "LAGER", "LAIRS", "LAITY", "LAKES", "LAMBS", "LAMED", - "LAMES", "LAMPS", "LANCE", "LANDS", "LANES", "LANKY", "LAPEL", "LAPSE", "LARCH", "LARGE", - "LARGO", "LARKS", "LARVA", "LASSO", "LASTS", "LATCH", "LATER", "LATHE", "LATHS", "LAUGH", - "LAWNS", "LAYER", "LEADS", "LEAFY", "LEAKS", "LEAKY", "LEANS", "LEAPS", "LEAPT", "LEARN", - "LEASE", "LEASH", "LEAST", "LEAVE", "LEDGE", "LEECH", "LEEKS", "LEGAL", "LEMME", "LEMON", - "LENDS", "LEPER", "LEVEE", "LEVEL", "LEVER", "LIARS", "LIBEL", "LICKS", "LIEGE", "LIENS", - "LIFTS", "LIGHT", "LIKED", "LIKEN", "LIKER", "LIKES", "LILAC", "LIMBO", "LIMBS", "LIMES", - "LIMIT", "LINED", "LINEN", "LINER", "LINES", "LINGO", "LINKS", "LIONS", "LISTS", "LITHE", - "LIVED", "LIVER", "LIVES", "LIVID", "LLAMA", "LOADS", "LOAMY", "LOANS", "LOATH", "LOBBY", - "LOBES", "LOCAL", "LOCKS", "LOCUS", "LODGE", "LOFTY", "LOGES", "LOGIC", "LOGIN", "LOINS", - "LONGS", "LOOKS", "LOOMS", "LOONS", "LOOPS", "LOOSE", "LORDS", "LOSER", "LOSES", "LOTUS", - "LOUSE", "LOUSY", "LOVED", "LOVER", "LOVES", "LOWED", "LOWER", "LOWLY", "LOYAL", "LUCID", - "LUCKY", "LULLS", "LUMPS", "LUMPY", "LUNAR", "LUNCH", "LUNGE", "LUNGS", "LURCH", "LURED", - "LURES", "LURID", "LURKS", "LUSTS", "LUSTY", "LUTES", "LYING", "LYMPH", "LYNCH", "LYRIC", - "MACES", "MADAM", "MADLY", "MAGIC", "MAIDS", "MAILS", "MAINS", "MAIZE", "MAJOR", "MAKER", - "MAKES", "MALES", "MAMMA", "MANES", "MANGA", "MANGE", "MANGO", "MANGY", "MANIA", "MANLY", - "MANNA", "MANOR", "MANSE", "MAPLE", "MARCH", "MARES", "MARKS", "MARRY", "MARSH", "MARTS", - "MASKS", "MASON", "MASTS", "MATCH", "MATED", "MATES", "MAUVE", "MAXIM", "MAYBE", "MAYOR", - "MAZES", "MEALS", "MEALY", "MEANS", "MEANT", "MEATS", "MEDAL", "MEDIA", "MEETS", "MELON", - "MELTS", "MEMES", "MENDS", "MENUS", "MERCY", "MERES", "MERGE", "MERIT", "MERRY", "MESAS", - "METAL", "METED", "METER", "MEWED", "MIDST", "MIENS", "MIGHT", "MILCH", "MILES", "MILKY", - "MILLS", "MIMES", "MIMIC", "MINCE", "MINDS", "MINED", "MINER", "MINES", "MINOR", "MINTS", - "MINUS", "MIRTH", "MISER", "MISTS", "MITES", "MIXED", "MIXES", "MOANS", "MOATS", "MOCKS", - "MODEL", "MODEM", "MODES", "MOIST", "MOLAR", "MOLES", "MOMMA", "MONEY", "MONKS", "MONTH", - "MOODS", "MOODY", "MOONS", "MOORS", "MOOSE", "MOPED", "MORAL", "MORES", "MOSSY", "MOTES", - "MOTHS", "MOTIF", "MOTOR", "MOTTO", "MOUND", "MOUNT", "MOURN", "MOUSE", "MOUTH", "MOVED", - "MOVER", "MOVES", "MOVIE", "MOWED", "MOWER", "MUCUS", "MUDDY", "MULES", "MULTI", "MUMMY", - "MUMPS", "MUNCH", "MURAL", "MURKY", "MUSED", "MUSES", "MUSIC", "MUSKY", "MUSTY", "MUTED", - "MUTES", "MYRRH", "MYTHS", "NABOB", "NAILS", "NAIVE", "NAKED", "NAMED", "NAMES", "NASAL", - "NASTY", "NATAL", "NATTY", "NAVAL", "NAVEL", "NAVES", "NEARS", "NECKS", "NEEDS", "NEEDY", - "NEIGH", "NERVE", "NESTS", "NEVER", "NEWER", "NEWLY", "NICER", "NICHE", "NIECE", "NIGHT", - "NINNY", "NOBLE", "NOBLY", "NOISE", "NOISY", "NOMAD", "NONCE", "NOOKS", "NOOSE", "NORTH", - "NOSED", "NOSES", "NOTCH", "NOTED", "NOTES", "NOUNS", "NOVEL", "NUDGE", "NURSE", "NYMPH", - "OAKEN", "OAKUM", "OASES", "OASIS", "OATEN", "OATHS", "OBESE", "OBEYS", "OCCUR", "OCEAN", - "OCHRE", "ODDER", "ODDLY", "ODIUM", "OFFAL", "OFFER", "OFTEN", "OILED", "OLDEN", "OLDER", - "OMENS", "OMITS", "ONION", "ONSET", "OOZED", "OOZES", "OPALS", "OPENS", "OPERA", "OPINE", - "OPIUM", "OPTIC", "ORBIT", "ORDER", "ORGAN", "OSIER", "OTHER", "OTTER", "OUGHT", "OUNCE", - "OUTDO", "OUTER", "OVALS", "OVARY", "OVENS", "OVERT", "OWING", "OWNED", "OWNER", "OXIDE", - "OZONE", "PACES", "PACKS", "PADDY", "PADRE", "PAEAN", "PAGAN", "PAGES", "PAILS", "PAINS", - "PAINT", "PAIRS", "PALED", "PALER", "PALES", "PALMS", "PALMY", "PALSY", "PANEL", "PANES", - "PANGS", "PANIC", "PANSY", "PANTS", "PAPAL", "PAPAS", "PAPER", "PARED", "PARKA", "PARKS", - "PARRY", "PARSE", "PARTS", "PARTY", "PASHA", "PASTE", "PASTY", "PATCH", "PATES", "PATHS", - "PATIO", "PAUSE", "PAVED", "PAWED", "PAWNS", "PAYED", "PAYER", "PEACE", "PEACH", "PEAKS", - "PEALS", "PEARL", "PEARS", "PEASE", "PECKS", "PEDAL", "PEEPS", "PEERS", "PELTS", "PENAL", - "PENCE", "PENIS", "PENNY", "PEONS", "PERCH", "PERIL", "PESKY", "PESOS", "PESTS", "PETAL", - "PETTY", "PHASE", "PHIAL", "PHONE", "PHOTO", "PIANO", "PICKS", "PIECE", "PIERS", "PIETY", - "PIGMY", "PIKES", "PILED", "PILES", "PILLS", "PILOT", "PINCH", "PINED", "PINES", "PINKS", - "PINTO", "PINTS", "PIOUS", "PIPED", "PIPER", "PIPES", "PIQUE", "PITCH", "PITHY", "PIVOT", - "PLACE", "PLAID", "PLAIN", "PLAIT", "PLANE", "PLANK", "PLANS", "PLANT", "PLATE", "PLAYS", - "PLAZA", "PLEAD", "PLEAS", "PLIED", "PLIES", "PLOTS", "PLUCK", "PLUGS", "PLUMB", "PLUME", - "PLUMS", "PLUSH", "PODIA", "POEMS", "POESY", "POETS", "POINT", "POISE", "POKED", "POKER", - "POKES", "POLAR", "POLES", "POLKA", "POLLS", "PONDS", "POOLS", "POPES", "POPPA", "POPPY", - "PORCH", "PORED", "PORES", "PORTS", "POSED", "POSER", "POSES", "POSSE", "POSTS", "POUCH", - "POUND", "POURS", "POWER", "PRANK", "PRATE", "PRAYS", "PRESS", "PREYS", "PRICE", "PRICK", - "PRIDE", "PRIED", "PRIES", "PRIME", "PRINT", "PRIOR", "PRISM", "PRIVY", "PRIZE", "PROBE", - "PRONE", "PROOF", "PROPS", "PROSE", "PROSY", "PROUD", "PROVE", "PROWL", "PROWS", "PROXY", - "PRUDE", "PRUNE", "PSALM", "PSHAW", "PUDGY", "PUFFS", "PUFFY", "PULLS", "PULPY", "PULSE", - "PUMPS", "PUNCH", "PUPIL", "PUPPY", "PUREE", "PURER", "PURGE", "PURSE", "PUSSY", "PUTTY", - "QUACK", "QUAFF", "QUAIL", "QUAKE", "QUALM", "QUART", "QUASI", "QUAYS", "QUEEN", "QUEER", - "QUELL", "QUERY", "QUEST", "QUEUE", "QUICK", "QUIET", "QUILL", "QUILT", "QUIPS", "QUIRE", - "QUITE", "QUITS", "QUOTA", "QUOTE", "QUOTH", "RABBI", "RABID", "RACED", "RACER", "RACES", - "RACKS", "RADII", "RADIO", "RAFTS", "RAGED", "RAGES", "RAIDS", "RAILS", "RAINS", "RAINY", - "RAISE", "RAJAH", "RAKED", "RAKES", "RALLY", "RANCH", "RANGE", "RANKS", "RAPID", "RARER", - "RARES", "RATED", "RATES", "RATIO", "RAVED", "RAVEN", "RAVES", "RAYON", "RAZED", "RAZOR", - "REACH", "REACT", "READS", "READY", "REALM", "REALS", "REAMS", "REAPS", "REARS", "REBEL", - "REBUS", "REBUT", "RECUR", "REEDS", "REEDY", "REEFS", "REEKS", "REELS", "REEVE", "REFER", - "REFIT", "REGAL", "REIGN", "REINS", "RELAX", "RELAY", "RELIC", "REMIT", "RENDS", "RENEW", - "RENTS", "REPAY", "REPEL", "REPLY", "RESET", "RESIN", "RESTS", "REVEL", "REVUE", "RHEUM", - "RHYME", "RICKS", "RIDER", "RIDES", "RIDGE", "RIFLE", "RIFTS", "RIGHT", "RIGID", "RILED", - "RILLS", "RIMES", "RINGS", "RINSE", "RIOTS", "RIPEN", "RIPER", "RISEN", "RISER", "RISES", - "RISKS", "RISKY", "RITES", "RIVAL", "RIVEN", "RIVER", "RIVET", "ROADS", "ROAMS", "ROARS", - "ROAST", "ROBED", "ROBES", "ROBIN", "ROCKS", "ROCKY", "ROGUE", "ROLES", "ROLLS", "ROMAN", - "ROOFS", "ROOKS", "ROOMS", "ROOMY", "ROOST", "ROOTS", "ROPED", "ROPES", "ROSES", "ROSIN", - "ROUGE", "ROUGH", "ROUND", "ROUSE", "ROUTE", "ROUTS", "ROVED", "ROVER", "ROWDY", "ROWED", - "ROYAL", "RUDER", "RUFFS", "RUINS", "RULED", "RULER", "RULES", "RUNES", "RUNGS", "RUPEE", - "RURAL", "RUSES", "SABLE", "SABRE", "SACKS", "SADLY", "SAFER", "SAGAS", "SAGES", "SAHIB", - "SAILS", "SAINT", "SAITH", "SALAD", "SALES", "SALLY", "SALON", "SALSA", "SALTS", "SALTY", - "SALVE", "SALVO", "SANDS", "SANDY", "SANER", "SATED", "SATIN", "SATYR", "SAUCE", "SAUCY", - "SAVED", "SAVES", "SAWED", "SCALD", "SCALE", "SCALP", "SCALY", "SCAMP", "SCANS", "SCANT", - "SCARE", "SCARF", "SCARS", "SCENE", "SCENT", "SCION", "SCOFF", "SCOLD", "SCOOP", "SCOPE", - "SCORE", "SCORN", "SCOUR", "SCOUT", "SCOWL", "SCRAP", "SCREW", "SCRIP", "SCRUB", "SCULL", - "SEALS", "SEAMS", "SEAMY", "SEATS", "SECTS", "SEDAN", "SEDGE", "SEEDS", "SEEDY", "SEEKS", - "SEEMS", "SEERS", "SEIZE", "SELLS", "SEMEN", "SENDS", "SENSE", "SERFS", "SERGE", "SERUM", - "SERVE", "SEVEN", "SEVER", "SEWED", "SEWER", "SEXES", "SHACK", "SHADE", "SHADY", "SHAFT", - "SHAKE", "SHAKY", "SHALE", "SHALL", "SHALT", "SHAME", "SHAMS", "SHANK", "SHAPE", "SHARE", - "SHARK", "SHARP", "SHAVE", "SHAWL", "SHEAF", "SHEAR", "SHEDS", "SHEEN", "SHEEP", "SHEER", - "SHEET", "SHEIK", "SHELF", "SHELL", "SHIED", "SHIFT", "SHINE", "SHINS", "SHINY", "SHIPS", - "SHIRE", "SHIRK", "SHIRT", "SHOAL", "SHOCK", "SHOES", "SHONE", "SHOOK", "SHOON", "SHOOT", - "SHOPS", "SHORE", "SHORN", "SHORT", "SHOTS", "SHOUT", "SHOVE", "SHOWN", "SHOWS", "SHOWY", - "SHRED", "SHREW", "SHRUB", "SHRUG", "SHUNS", "SHUTS", "SHYLY", "SIBYL", "SIDED", "SIDES", - "SIEGE", "SIEVE", "SIGHS", "SIGHT", "SIGMA", "SIGNS", "SILKS", "SILKY", "SILLS", "SILLY", - "SINCE", "SINEW", "SINGE", "SINGS", "SINKS", "SIREN", "SIRES", "SITES", "SIXES", "SIXTH", - "SIXTY", "SIZED", "SIZES", "SKATE", "SKEIN", "SKIES", "SKIFF", "SKILL", "SKIMS", "SKINS", - "SKIPS", "SKIRT", "SKULK", "SKULL", "SKUNK", "SLABS", "SLACK", "SLAGS", "SLAIN", "SLAKE", - "SLANG", "SLANT", "SLAPS", "SLASH", "SLATE", "SLATS", "SLAVE", "SLAYS", "SLEDS", "SLEEK", - "SLEEP", "SLEET", "SLEPT", "SLICE", "SLICK", "SLIDE", "SLILY", "SLIME", "SLIMY", "SLING", - "SLINK", "SLIPS", "SLITS", "SLOOP", "SLOPE", "SLOPS", "SLOTH", "SLUGS", "SLUMP", "SLUMS", - "SLUNG", "SLUNK", "SLUSH", "SLYLY", "SMACK", "SMALL", "SMART", "SMASH", "SMEAR", "SMELL", - "SMELT", "SMILE", "SMIRK", "SMITE", "SMITH", "SMOCK", "SMOKE", "SMOKY", "SMOTE", "SNACK", - "SNAGS", "SNAIL", "SNAKE", "SNAKY", "SNAPS", "SNARE", "SNARL", "SNEAK", "SNEER", "SNIFF", - "SNIPE", "SNOBS", "SNORE", "SNORT", "SNOUT", "SNOWS", "SNOWY", "SNUFF", "SOAPY", "SOARS", - "SOBER", "SOCKS", "SOFAS", "SOGGY", "SOILS", "SOLAR", "SOLES", "SOLID", "SOLOS", "SOLVE", - "SONGS", "SONNY", "SOOTH", "SOOTY", "SORES", "SORRY", "SORTS", "SOUGH", "SOULS", "SOUND", - "SOUPS", "SOUSE", "SOUTH", "SOWED", "SOWER", "SPACE", "SPADE", "SPAKE", "SPANK", "SPANS", - "SPARE", "SPARK", "SPARS", "SPASM", "SPAWN", "SPEAK", "SPEAR", "SPECK", "SPEED", "SPELL", - "SPELT", "SPEND", "SPENT", "SPERM", "SPICE", "SPICY", "SPIED", "SPIES", "SPIKE", "SPILL", - "SPILT", "SPINE", "SPINS", "SPINY", "SPIRE", "SPITE", "SPITS", "SPLIT", "SPOIL", "SPOKE", - "SPOOK", "SPOOL", "SPOON", "SPOOR", "SPORE", "SPORT", "SPOTS", "SPOUT", "SPRAY", "SPREE", - "SPRIG", "SPUNK", "SPURN", "SPURS", "SPURT", "SQUAD", "SQUAT", "SQUAW", "STABS", "STACK", - "STAFF", "STAGE", "STAGS", "STAID", "STAIN", "STAIR", "STAKE", "STALE", "STALK", "STALL", - "STAMP", "STAND", "STANK", "STARE", "STARK", "STARS", "START", "STATE", "STAVE", "STAYS", - "STEAD", "STEAK", "STEAL", "STEAM", "STEED", "STEEL", "STEEP", "STEER", "STEMS", "STEPS", - "STERN", "STEWS", "STICK", "STIFF", "STILE", "STILL", "STING", "STINK", "STINT", "STIRS", - "STOCK", "STOIC", "STOLE", "STONE", "STONY", "STOOD", "STOOL", "STOOP", "STOPS", "STORE", - "STORK", "STORM", "STORY", "STOUT", "STOVE", "STRAP", "STRAW", "STRAY", "STREW", "STRIP", - "STRUT", "STUCK", "STUDS", "STUDY", "STUFF", "STUMP", "STUNG", "STUNT", "STYLE", "SUAVE", - "SUCKS", "SUGAR", "SUING", "SUITE", "SUITS", "SULKS", "SULKY", "SULLY", "SUNNY", "SUPER", - "SURER", "SURGE", "SURLY", "SWAIN", "SWAMP", "SWANS", "SWARD", "SWARM", "SWAYS", "SWEAR", - "SWEAT", "SWEEP", "SWEET", "SWELL", "SWEPT", "SWIFT", "SWILL", "SWIMS", "SWINE", "SWING", - "SWIRL", "SWISH", "SWOON", "SWOOP", "SWORD", "SWORE", "SWORN", "SWUNG", "SYNOD", "SYRUP", - "TABBY", "TABLE", "TABOO", "TACIT", "TACKS", "TAILS", "TAINT", "TAKEN", "TAKES", "TALES", - "TALKS", "TALLY", "TALON", "TAMED", "TAMER", "TANKS", "TAPER", "TAPES", "TARDY", "TARES", - "TARRY", "TARTS", "TASKS", "TASTE", "TASTY", "TAUNT", "TAWNY", "TAXED", "TAXES", "TEACH", - "TEAMS", "TEARS", "TEASE", "TEEMS", "TEENS", "TEETH", "TELLS", "TEMPI", "TEMPO", "TEMPS", - "TENDS", "TENET", "TENOR", "TENSE", "TENTH", "TENTS", "TEPEE", "TEPID", "TERMS", "TERSE", - "TESTS", "TESTY", "TEXTS", "THANK", "THEFT", "THEIR", "THEME", "THERE", "THESE", "THICK", - "THIEF", "THIGH", "THINE", "THING", "THINK", "THIRD", "THONG", "THORN", "THOSE", "THREE", - "THREW", "THROB", "THROE", "THROW", "THUMB", "THUMP", "THYME", "TIARA", "TIBIA", "TICKS", - "TIDAL", "TIDES", "TIERS", "TIGER", "TIGHT", "TILDE", "TILED", "TILES", "TILLS", "TILTS", - "TIMED", "TIMES", "TIMID", "TINGE", "TINTS", "TIPSY", "TIRED", "TIRES", "TITHE", "TITLE", - "TOADS", "TOAST", "TODAY", "TODDY", "TOILS", "TOKEN", "TOLLS", "TOMBS", "TOMES", "TONED", - "TONES", "TONGS", "TONIC", "TOOLS", "TOOTH", "TOPAZ", "TOPIC", "TOQUE", "TORCH", "TORSO", - "TORTS", "TOTAL", "TOTEM", "TOUCH", "TOUGH", "TOURS", "TOWED", "TOWEL", "TOWER", "TOWNS", - "TOXIC", "TOYED", "TRACE", "TRACK", "TRACT", "TRADE", "TRAIL", "TRAIN", "TRAIT", "TRAMP", - "TRAMS", "TRAPS", "TRASH", "TRAYS", "TREAD", "TREAT", "TREED", "TREES", "TREND", "TRESS", - "TRIAD", "TRIAL", "TRIBE", "TRICE", "TRICK", "TRIED", "TRIES", "TRILL", "TRIPE", "TRIPS", - "TRITE", "TROLL", "TROOP", "TROTH", "TROTS", "TROUT", "TRUCE", "TRUCK", "TRUER", "TRULY", - "TRUMP", "TRUNK", "TRUSS", "TRUST", "TRUTH", "TRYST", "TUBES", "TUFTS", "TULIP", "TULLE", - "TUNED", "TUNES", "TUNIC", "TURNS", "TUSKS", "TUTOR", "TWAIN", "TWANG", "TWEED", "TWICE", - "TWIGS", "TWINE", "TWINS", "TWIRL", "TWIST", "TYING", "TYPED", "TYPES", "UDDER", "ULCER", - "ULTRA", "UNCLE", "UNCUT", "UNDER", "UNDID", "UNDUE", "UNFIT", "UNION", "UNITE", "UNITS", - "UNITY", "UNSAY", "UNTIE", "UNTIL", "UPPER", "UPSET", "URBAN", "URGED", "URGES", "URINE", - "USAGE", "USERS", "USHER", "USING", "USUAL", "USURP", "USURY", "UTTER", "VAGUE", "VALES", - "VALET", "VALID", "VALUE", "VALVE", "VANES", "VAPID", "VASES", "VAULT", "VAUNT", "VEILS", - "VEINS", "VELDT", "VENAL", "VENOM", "VENTS", "VENUE", "VERBS", "VERGE", "VERSE", "VERVE", - "VESTS", "VEXED", "VEXES", "VIALS", "VICAR", "VICES", "VIDEO", "VIEWS", "VIGIL", "VILER", - "VILLA", "VINES", "VIOLA", "VIPER", "VIRUS", "VISIT", "VISOR", "VISTA", "VITAL", "VIVID", - "VIXEN", "VIZOR", "VOCAL", "VODKA", "VOGUE", "VOICE", "VOILE", "VOLTS", "VOMIT", "VOTED", - "VOTER", "VOTES", "VOUCH", "VOWED", "VOWEL", "VYING", "WADED", "WAFER", "WAFTS", "WAGED", - "WAGER", "WAGES", "WAGON", "WAIFS", "WAILS", "WAIST", "WAITS", "WAIVE", "WAKED", "WAKEN", - "WAKES", "WALKS", "WALLS", "WALTZ", "WANDS", "WANED", "WANES", "WANTS", "WARDS", "WARES", - "WARMS", "WARNS", "WARTS", "WASPS", "WASTE", "WATCH", "WATER", "WAVED", "WAVER", "WAVES", - "WAXED", "WAXEN", "WAXES", "WEARS", "WEARY", "WEAVE", "WEDGE", "WEEDS", "WEEDY", "WEEKS", - "WEEPS", "WEIGH", "WEIRD", "WELCH", "WELLS", "WENCH", "WHACK", "WHALE", "WHARF", "WHEAT", - "WHEEL", "WHELP", "WHERE", "WHICH", "WHIFF", "WHILE", "WHIMS", "WHINE", "WHIPS", "WHIRL", - "WHIRR", "WHISK", "WHIST", "WHITE", "WHOLE", "WHOOP", "WHORE", "WHOSE", "WICKS", "WIDEN", - "WIDER", "WIDOW", "WIDTH", "WIELD", "WIGHT", "WILDS", "WILES", "WILLS", "WINCE", "WINCH", - "WINDS", "WINDY", "WINES", "WINGS", "WINKS", "WIPED", "WIPES", "WIRED", "WIRES", "WISER", - "WISPS", "WITCH", "WITTY", "WIVES", "WOMAN", "WOMEN", "WOODS", "WOODY", "WOOED", "WOOER", - "WORDS", "WORDY", "WORKS", "WORLD", "WORMS", "WORRY", "WORSE", "WORST", "WORTH", "WOULD", - "WOUND", "WRACK", "WRAPS", "WRAPT", "WRATH", "WREAK", "WRECK", "WREST", "WRING", "WRIST", - "WRITE", "WRITS", "WRONG", "WROTE", "WROTH", "YACHT", "YARDS", "YARNS", "YAWNS", "YEARN", - "YEARS", "YEAST", "YELLS", "YELPS", "YIELD", "YOKED", "YOKES", "YOLKS", "YOUNG", "YOURS", - "YOUTH", "ZEBRA", "ZONES", +legal_list = [ + "ABACK", "ABAFT", "ABASE", "ABATE", "ABBEY", "ABBOT", "ABHOR", "ABIDE", "ABLER", "ABODE", "ABOUT", "ABOVE", + "ABUSE", "ABYSS", "ACHED", "ACHES", "ACIDS", "ACORN", "ACRES", "ACRID", "ACTED", "ACTOR", "ACUTE", "ADAGE", + "ADAPT", "ADDED", "ADDER", "ADEPT", "ADIEU", "ADMIT", "ADOBE", "ADOPT", "ADORE", "ADORN", "ADULT", "AEGIS", + "AEONS", "AFFIX", "AFIRE", "AFOOT", "AFTER", "AGAIN", "AGAPE", "AGATE", "AGENT", "AGILE", "AGING", "AGLOW", + "AGONY", "AGREE", "AHEAD", "AIDED", "AIDES", "AILED", "AIMED", "AIRED", "AISLE", "ALARM", "ALBUM", "ALDER", + "ALERT", "ALIAS", "ALIBI", "ALIEN", "ALIKE", "ALIVE", "ALLAY", "ALLEY", "ALLOT", "ALLOW", "ALLOY", "ALOES", + "ALOFT", "ALONE", "ALONG", "ALOOF", "ALOUD", "ALPHA", "ALTAR", "ALTER", "ALTOS", "AMASS", "AMAZE", "AMBER", + "AMBLE", "AMEND", "AMIGO", "AMISS", "AMITY", "AMONG", "AMOUR", "AMPLE", "AMPLY", "AMUSE", "ANGEL", "ANGER", + "ANGLE", "ANGRY", "ANGST", "ANIME", "ANKLE", "ANNEX", "ANNOY", "ANNUL", "ANTES", "ANTIC", "ANVIL", "APACE", + "APART", "APING", "APPAL", "APPLE", "APPLY", "APRON", "APTLY", "AREAS", "ARENA", "ARGUE", "ARISE", "ARMED", + "AROMA", "AROSE", "ARRAY", "ARROW", "ARSON", "ASHEN", "ASHES", "ASIDE", "ASKED", "ASKEW", "ASPEN", "ASSAY", + "ASSES", "ASSET", "ASTER", "ASTIR", "ATLAS", "ATOLL", "ATOMS", "ATONE", "ATTAR", "ATTIC", "AUDIO", "AUDIT", + "AUGER", "AUGHT", "AUGUR", "AUNTS", "AURAS", "AUTOS", "AVAIL", "AVERS", "AVERT", "AVOID", "AVOWS", "AWAIT", + "AWAKE", "AWARD", "AWARE", "AWFUL", "AWOKE", "AXIOM", "AXLES", "AZURE", "BABEL", "BABES", "BACKS", "BACON", + "BADGE", "BADLY", "BAGGY", "BAITS", "BAIZE", "BAKED", "BAKER", "BALES", "BALLS", "BALMY", "BANAL", "BANDS", + "BANDY", "BANGS", "BANJO", "BANKS", "BANNS", "BARBS", "BARDS", "BARED", "BARGE", "BARKS", "BARNS", "BARON", + "BASAL", "BASED", "BASER", "BASES", "BASIC", "BASIL", "BASIN", "BASIS", "BASSO", "BASTE", "BATCH", "BATED", + "BATHE", "BATHS", "BATON", "BAYOU", "BEACH", "BEADS", "BEADY", "BEAKS", "BEAMS", "BEANS", "BEARD", "BEARS", + "BEAST", "BEAUX", "BEECH", "BEETS", "BEFIT", "BEGAN", "BEGAT", "BEGET", "BEGIN", "BEGOT", "BEGUN", "BEING", + "BELIE", "BELLE", "BELLS", "BELLY", "BELOW", "BELTS", "BENCH", "BENDS", "BERGS", "BERRY", "BERTH", "BERYL", + "BESET", "BESOM", "BEVEL", "BIBLE", "BIDED", "BIDES", "BIGHT", "BIGOT", "BILGE", "BILLS", "BILLY", "BINDS", + "BIPED", "BIRCH", "BIRDS", "BIRTH", "BISON", "BITCH", "BITES", "BLACK", "BLADE", "BLAME", "BLAND", "BLANK", + "BLARE", "BLAST", "BLAZE", "BLEAK", "BLEAT", "BLEED", "BLEND", "BLENT", "BLESS", "BLEST", "BLIND", "BLINK", + "BLISS", "BLOCK", "BLOCS", "BLOND", "BLOOD", "BLOOM", "BLOTS", "BLOWN", "BLOWS", "BLUER", "BLUES", "BLUFF", + "BLUNT", "BLURT", "BLUSH", "BOARD", "BOARS", "BOAST", "BOATS", "BODED", "BODES", "BOGGY", "BOGUS", "BOILS", + "BOLES", "BOLTS", "BOMBS", "BONDS", "BONED", "BONES", "BONNY", "BONUS", "BOOBY", "BOOKS", "BOOMS", "BOONS", + "BOORS", "BOOST", "BOOTH", "BOOTS", "BOOTY", "BOOZE", "BORAX", "BORED", "BORES", "BORNE", "BOSOM", "BOUGH", + "BOUND", "BOUTS", "BOWED", "BOWEL", "BOWER", "BOWLS", "BOXED", "BOXER", "BOXES", "BRACE", "BRAGS", "BRAID", + "BRAIN", "BRAKE", "BRAND", "BRASS", "BRATS", "BRAVE", "BRAVO", "BRAWL", "BRAWN", "BREAD", "BREAK", "BREED", + "BRIAR", "BRIBE", "BRICK", "BRIDE", "BRIEF", "BRIER", "BRIGS", "BRIMS", "BRINE", "BRING", "BRINK", "BRINY", + "BRISK", "BROAD", "BROIL", "BROKE", "BROOD", "BROOK", "BROOM", "BROTH", "BROWN", "BROWS", "BRUIN", "BRUNT", + "BRUSH", "BRUTE", "BUCKS", "BUDGE", "BUGGY", "BUGLE", "BUILD", "BUILT", "BULBS", "BULGE", "BULKS", "BULKY", + "BULLS", "BULLY", "BUMPS", "BUNCH", "BUNKS", "BUOYS", "BURLY", "BURNS", "BURNT", "BURRO", "BURRS", "BURST", + "BUSHY", "BUSTS", "BUTTE", "BUTTS", "BUXOM", "BUYER", "CABAL", "CABBY", "CABIN", "CABLE", "CACAO", "CACHE", + "CADET", "CADRE", "CAGED", "CAGES", "CAIRN", "CAKED", "CAKES", "CALLS", "CALMS", "CALYX", "CAMEL", "CAMEO", + "CAMPS", "CANAL", "CANDY", "CANES", "CANNY", "CANOE", "CANON", "CANTO", "CAPER", "CAPES", "CAPON", "CARDS", + "CARED", "CARES", "CARGO", "CAROL", "CARRY", "CARTS", "CARVE", "CASED", "CASES", "CASKS", "CASTE", "CASTS", + "CATCH", "CATER", "CAUSE", "CAVED", "CAVES", "CAVIL", "CEASE", "CEDAR", "CEDED", "CELLS", "CENTS", "CHAFE", + "CHAFF", "CHAIN", "CHAIR", "CHALK", "CHAMP", "CHANT", "CHAOS", "CHAPS", "CHARM", "CHART", "CHARY", "CHASE", + "CHASM", "CHATS", "CHEAP", "CHEAT", "CHECK", "CHEEK", "CHEER", "CHEFS", "CHESS", "CHEST", "CHICK", "CHIDE", + "CHIEF", "CHILD", "CHILL", "CHIME", "CHINA", "CHINK", "CHINS", "CHIPS", "CHIRP", "CHOIR", "CHOKE", "CHOPS", + "CHORD", "CHOSE", "CHUCK", "CHUMP", "CHUMS", "CHUNK", "CHURL", "CHURN", "CHUTE", "CIDER", "CIGAR", "CINCH", + "CIRCA", "CITED", "CITES", "CIVET", "CIVIC", "CIVIL", "CLACK", "CLAIM", "CLAMP", "CLAMS", "CLANG", "CLANK", + "CLANS", "CLAPS", "CLASH", "CLASP", "CLASS", "CLAWS", "CLEAN", "CLEAR", "CLEFS", "CLEFT", "CLERK", "CLEWS", + "CLICK", "CLIFF", "CLIMB", "CLIME", "CLING", "CLINK", "CLIPS", "CLOAK", "CLOCK", "CLODS", "CLOGS", "CLOSE", + "CLOTH", "CLOUD", "CLOUT", "CLOVE", "CLOWN", "CLUBS", "CLUCK", "CLUES", "CLUMP", "CLUNG", "COACH", "COALS", + "COAST", "COATS", "COBRA", "COCKS", "COCOA", "CODES", "COILS", "COINS", "COLDS", "COLIC", "COLON", "COLTS", + "COMBS", "COMER", "COMES", "COMET", "COMIC", "COMMA", "CONCH", "CONES", "CONIC", "COOED", "COOKS", "COOLS", + "COPRA", "COPSE", "CORAL", "CORDS", "CORES", "CORKS", "CORNS", "CORPS", "COSTS", "COTES", "COUCH", "COUGH", + "COULD", "COUNT", "COUPE", "COUPS", "COURT", "COVER", "COVES", "COVET", "COVEY", "COWED", "COWER", "COYLY", + "COZEN", "CRABS", "CRACK", "CRAFT", "CRAGS", "CRAMP", "CRANE", "CRANK", "CRAPE", "CRASH", "CRASS", "CRATE", + "CRAVE", "CRAWL", "CRAZE", "CRAZY", "CREAK", "CREAM", "CREDO", "CREED", "CREEK", "CREEP", "CREPE", "CREPT", + "CRESS", "CREST", "CREWS", "CRIBS", "CRICK", "CRIED", "CRIER", "CRIES", "CRIME", "CRIMP", "CRISP", "CROAK", + "CROCK", "CRONE", "CRONY", "CROOK", "CROPS", "CROSS", "CROUP", "CROWD", "CROWN", "CROWS", "CRUDE", "CRUEL", + "CRUMB", "CRUSH", "CRUST", "CRYPT", "CUBES", "CUBIC", "CUBIT", "CUFFS", "CULTS", "CURDS", "CURED", "CURES", + "CURLS", "CURLY", "CURRY", "CURSE", "CURST", "CURVE", "CYCLE", "CYNIC", "DADDY", "DAILY", "DAIRY", "DAISY", + "DALES", "DALLY", "DAMES", "DAMPS", "DANCE", "DANDY", "DARED", "DARES", "DARTS", "DATED", "DATES", "DATUM", + "DAUBS", "DAUNT", "DAWNS", "DAZED", "DEALS", "DEALT", "DEANS", "DEARS", "DEATH", "DEBAR", "DEBIT", "DEBTS", + "DEBUT", "DECAY", "DECKS", "DECOY", "DECRY", "DEEDS", "DEEMS", "DEEPS", "DEFER", "DEIGN", "DEITY", "DELAY", + "DELLS", "DELTA", "DELVE", "DEMON", "DEMUR", "DENSE", "DENTS", "DEPOT", "DEPTH", "DERBY", "DESKS", "DETER", + "DEUCE", "DEVIL", "DIARY", "DICED", "DICES", "DICTA", "DIETS", "DIGIT", "DIKES", "DIMES", "DIMLY", "DINED", + "DINER", "DINES", "DINGY", "DIRGE", "DIRTY", "DISCS", "DISKS", "DITCH", "DITTO", "DITTY", "DIVAN", "DIVED", + "DIVER", "DIVES", "DIZZY", "DOCKS", "DODGE", "DOERS", "DOGMA", "DOING", "DOLED", "DOLLS", "DOMED", "DOMES", + "DONOR", "DOOMS", "DOORS", "DOSED", "DOSES", "DOTED", "DOTES", "DOUBT", "DOUGH", "DOVES", "DOWDY", "DOWNS", + "DOWNY", "DOWRY", "DOZED", "DOZEN", "DRAFT", "DRAGS", "DRAIN", "DRAKE", "DRAMA", "DRAMS", "DRANK", "DRAPE", + "DRAWL", "DRAWN", "DRAWS", "DRAYS", "DREAD", "DREAM", "DREGS", "DRESS", "DRIED", "DRIER", "DRIES", "DRIFT", + "DRILL", "DRILY", "DRINK", "DRIPS", "DRIVE", "DROLL", "DRONE", "DROOP", "DROPS", "DROSS", "DROVE", "DROWN", + "DRUGS", "DRUMS", "DRUNK", "DRYLY", "DUCAL", "DUCAT", "DUCHY", "DUCKS", "DUCTS", "DUELS", "DUETS", "DUKES", + "DULLY", "DUMMY", "DUMPS", "DUMPY", "DUNCE", "DUNES", "DUNNO", "DUPED", "DUPES", "DUSKY", "DUSTY", "DWARF", + "DWELL", "DWELT", "DYING", "DYKES", "EAGER", "EAGLE", "EARLS", "EARLY", "EARNS", "EARTH", "EASED", "EASEL", + "EASES", "EATEN", "EATER", "EAVES", "EBBED", "EBONY", "EDGED", "EDGES", "EDICT", "EDIFY", "EERIE", "EGGED", + "EIGHT", "EJECT", "ELATE", "ELBOW", "ELDER", "ELECT", "ELEGY", "ELFIN", "ELITE", "ELOPE", "ELUDE", "ELVES", + "EMAIL", "EMITS", "EMPTY", "ENACT", "ENDED", "ENDOW", "ENEMY", "ENJOY", "ENNUI", "ENROL", "ENSUE", "ENTER", + "ENTRY", "ENVOY", "EPICS", "EPOCH", "EQUAL", "EQUIP", "ERASE", "ERECT", "ERRED", "ERROR", "ESSAY", "ETHER", + "ETHIC", "EVADE", "EVENT", "EVERY", "EVILS", "EVOKE", "EXACT", "EXALT", "EXCEL", "EXERT", "EXILE", "EXIST", + "EXITS", "EXPEL", "EXTOL", "EXTRA", "EXULT", "EYING", "EYRIE", "FABLE", "FACED", "FACES", "FACTS", "FADED", + "FADES", "FAILS", "FAINT", "FAIRS", "FAIRY", "FAITH", "FAKIR", "FALLS", "FALSE", "FAMED", "FANCY", "FANGS", + "FARCE", "FARED", "FARES", "FARMS", "FASTS", "FATAL", "FATED", "FATES", "FATTY", "FAULT", "FAUNA", "FAUNS", + "FAWNS", "FEARS", "FEAST", "FEATS", "FEEDS", "FEELS", "FEIGN", "FEINT", "FELLS", "FELON", "FENCE", "FERAL", + "FERNS", "FERRY", "FETCH", "FETED", "FETID", "FETUS", "FEUDS", "FEVER", "FEWER", "FICHE", "FIEFS", "FIELD", + "FIEND", "FIERY", "FIFES", "FIFTH", "FIFTY", "FIGHT", "FILCH", "FILED", "FILES", "FILET", "FILLS", "FILLY", + "FILMS", "FILMY", "FILTH", "FINAL", "FINCH", "FINDS", "FINED", "FINER", "FINES", "FINIS", "FINNY", "FIORD", + "FIRED", "FIRES", "FIRMS", "FIRST", "FISHY", "FISTS", "FITLY", "FIVES", "FIXED", "FIXER", "FIXES", "FJORD", + "FLAGS", "FLAIL", "FLAIR", "FLAKE", "FLAKY", "FLAME", "FLANK", "FLAPS", "FLARE", "FLASH", "FLASK", "FLATS", + "FLAWS", "FLEAS", "FLECK", "FLEES", "FLEET", "FLESH", "FLICK", "FLIER", "FLIES", "FLING", "FLINT", "FLIRT", + "FLITS", "FLOAT", "FLOCK", "FLOES", "FLOOD", "FLOOR", "FLORA", "FLOSS", "FLOUR", "FLOUT", "FLOWN", "FLOWS", + "FLUES", "FLUFF", "FLUID", "FLUKE", "FLUME", "FLUNG", "FLUSH", "FLUTE", "FLYER", "FOAMS", "FOAMY", "FOCAL", + "FOCUS", "FOGGY", "FOILS", "FOIST", "FOLDS", "FOLIO", "FOLKS", "FOLLY", "FOODS", "FOOLS", "FORAY", "FORCE", + "FORDS", "FORGE", "FORGO", "FORKS", "FORMS", "FORTE", "FORTH", "FORTS", "FORTY", "FORUM", "FOUND", "FOUNT", + "FOURS", "FOWLS", "FOXES", "FOYER", "FRAIL", "FRAME", "FRANC", "FRANK", "FRAUD", "FREAK", "FREED", "FREER", + "FREES", "FRESH", "FRETS", "FRIAR", "FRIED", "FRILL", "FRISK", "FROCK", "FROGS", "FROND", "FRONT", "FROST", + "FROTH", "FROWN", "FROZE", "FRUIT", "FUDGE", "FUELS", "FUGUE", "FULLY", "FUMED", "FUMES", "FUNDS", "FUNGI", + "FUNNY", "FURRY", "FURZE", "FUSED", "FUSES", "FUSSY", "FUZZY", "GABLE", "GAILY", "GAINS", "GALES", "GALLS", + "GAMES", "GAMIN", "GAMMA", "GAMUT", "GANGS", "GAPED", "GAPES", "GASES", "GASPS", "GATES", "GAUDY", "GAUGE", + "GAUNT", "GAUZE", "GAUZY", "GAVEL", "GAWKY", "GAYER", "GAYLY", "GAZED", "GAZER", "GAZES", "GEARS", "GEESE", + "GENIE", "GENII", "GENRE", "GENTS", "GENUS", "GERMS", "GHOST", "GIANT", "GIBES", "GIDDY", "GIFTS", "GILDS", + "GILLS", "GIMME", "GIPSY", "GIRDS", "GIRLS", "GIRTH", "GIVEN", "GIVES", "GLADE", "GLAND", "GLARE", "GLASS", + "GLAZE", "GLEAM", "GLEAN", "GLENS", "GLIDE", "GLINT", "GLOAT", "GLOBE", "GLOOM", "GLORY", "GLOSS", "GLOVE", + "GLOWS", "GLUED", "GNASH", "GNATS", "GNAWS", "GNOME", "GOADS", "GOALS", "GOATS", "GODLY", "GOING", "GOLLY", + "GONGS", "GONNA", "GOODS", "GOODY", "GOOSE", "GORED", "GORGE", "GORSE", "GOTTA", "GOUGE", "GOURD", "GOUTY", + "GOWNS", "GRABS", "GRACE", "GRADE", "GRAFT", "GRAIN", "GRAMS", "GRAND", "GRANT", "GRAPE", "GRAPH", "GRASP", + "GRASS", "GRATE", "GRAVE", "GRAVY", "GRAZE", "GREAT", "GREED", "GREEN", "GREET", "GREYS", "GRIEF", "GRILL", + "GRIME", "GRIMY", "GRIND", "GRINS", "GRIPE", "GRIPS", "GRIST", "GROAN", "GROIN", "GROOM", "GROPE", "GROSS", + "GROUP", "GROVE", "GROWL", "GROWN", "GROWS", "GRUBS", "GRUEL", "GRUFF", "GRUNT", "GUANO", "GUARD", "GUESS", + "GUEST", "GUIDE", "GUILD", "GUILE", "GUILT", "GUISE", "GULCH", "GULFS", "GULLS", "GULLY", "GUMMY", "GUSTO", + "GUSTS", "GUSTY", "GYPSY", "HABIT", "HACKS", "HAILS", "HAIRS", "HAIRY", "HALED", "HALLS", "HALTS", "HALVE", + "HANDS", "HANDY", "HANGS", "HAPPY", "HARDY", "HAREM", "HARES", "HARMS", "HARPS", "HARPY", "HARRY", "HARSH", + "HARTS", "HASTE", "HASTY", "HATCH", "HATED", "HATER", "HAULS", "HAVEN", "HAVOC", "HAWKS", "HAZEL", "HEADS", + "HEADY", "HEALS", "HEAPS", "HEARD", "HEARS", "HEART", "HEATH", "HEATS", "HEAVE", "HEAVY", "HEDGE", "HEEDS", + "HEELS", "HEIRS", "HELIX", "HELLO", "HELMS", "HELPS", "HENCE", "HERBS", "HERDS", "HERON", "HEROS", "HEWED", + "HIDES", "HILLS", "HILLY", "HILTS", "HINDS", "HINGE", "HINTS", "HIRED", "HIRES", "HITCH", "HIVES", "HOARD", + "HOARY", "HOBBY", "HOIST", "HOLDS", "HOLES", "HOLLY", "HOMES", "HONEY", "HOODS", "HOOFS", "HOOKS", "HOOPS", + "HOOTS", "HOPED", "HOPES", "HORDE", "HORNS", "HORNY", "HORSE", "HOSTS", "HOTEL", "HOTLY", "HOUND", "HOURS", + "HOUSE", "HOVEL", "HOVER", "HOWLS", "HULKS", "HULLS", "HUMAN", "HUMID", "HUMPS", "HUMUS", "HUNCH", "HUNTS", + "HURLS", "HURRY", "HURTS", "HUSKS", "HUSKY", "HUSSY", "HYDRA", "HYENA", "HYMNS", "ICILY", "ICING", "IDEAL", + "IDEAS", "IDIOM", "IDIOT", "IDLED", "IDLER", "IDOLS", "IDYLL", "IGLOO", "IMAGE", "IMBUE", "IMPEL", "IMPLY", + "INANE", "INCUR", "INDEX", "INEPT", "INERT", "INFER", "INGOT", "INLET", "INNER", "INTER", "INURE", "IRATE", + "IRKED", "IRONS", "IRONY", "ISLES", "ISLET", "ISSUE", "ITEMS", "IVORY", "JACKS", "JADED", "JAILS", "JAUNT", + "JEANS", "JEERS", "JELLY", "JERKS", "JERKY", "JESTS", "JETTY", "JEWEL", "JIFFY", "JOINS", "JOINT", "JOKED", + "JOKER", "JOKES", "JOLLY", "JOUST", "JOYED", "JUDGE", "JUICE", "JUICY", "JUMPS", "JUNKS", "JUNTA", "JUROR", + "KARMA", "KEELS", "KEEPS", "KETCH", "KEYED", "KHAKI", "KICKS", "KILLS", "KINDA", "KINDS", "KINGS", "KIOSK", + "KITES", "KNACK", "KNAVE", "KNEAD", "KNEEL", "KNEES", "KNELL", "KNELT", "KNIFE", "KNITS", "KNOBS", "KNOCK", + "KNOLL", "KNOTS", "KNOWN", "KNOWS", "LABEL", "LACED", "LACES", "LACKS", "LADEN", "LADLE", "LAGER", "LAIRS", + "LAITY", "LAKES", "LAMBS", "LAMED", "LAMES", "LAMPS", "LANCE", "LANDS", "LANES", "LANKY", "LAPEL", "LAPSE", + "LARCH", "LARGE", "LARGO", "LARKS", "LARVA", "LASSO", "LASTS", "LATCH", "LATER", "LATHE", "LATHS", "LAUGH", + "LAWNS", "LAYER", "LEADS", "LEAFY", "LEAKS", "LEAKY", "LEANS", "LEAPS", "LEAPT", "LEARN", "LEASE", "LEASH", + "LEAST", "LEAVE", "LEDGE", "LEECH", "LEEKS", "LEGAL", "LEMME", "LEMON", "LENDS", "LEPER", "LEVEE", "LEVEL", + "LEVER", "LIARS", "LIBEL", "LICKS", "LIEGE", "LIENS", "LIFTS", "LIGHT", "LIKED", "LIKEN", "LIKER", "LIKES", + "LILAC", "LIMBO", "LIMBS", "LIMES", "LIMIT", "LINED", "LINEN", "LINER", "LINES", "LINGO", "LINKS", "LIONS", + "LISTS", "LITHE", "LIVED", "LIVER", "LIVES", "LIVID", "LLAMA", "LOADS", "LOAMY", "LOANS", "LOATH", "LOBBY", + "LOBES", "LOCAL", "LOCKS", "LOCUS", "LODGE", "LOFTY", "LOGES", "LOGIC", "LOGIN", "LOINS", "LONGS", "LOOKS", + "LOOMS", "LOONS", "LOOPS", "LOOSE", "LORDS", "LOSER", "LOSES", "LOTUS", "LOUSE", "LOUSY", "LOVED", "LOVER", + "LOVES", "LOWED", "LOWER", "LOWLY", "LOYAL", "LUCID", "LUCKY", "LULLS", "LUMPS", "LUMPY", "LUNAR", "LUNCH", + "LUNGE", "LUNGS", "LURCH", "LURED", "LURES", "LURID", "LURKS", "LUSTS", "LUSTY", "LUTES", "LYING", "LYMPH", + "LYNCH", "LYRIC", "MACES", "MADAM", "MADLY", "MAGIC", "MAIDS", "MAILS", "MAINS", "MAIZE", "MAJOR", "MAKER", + "MAKES", "MALES", "MAMMA", "MANES", "MANGA", "MANGE", "MANGO", "MANGY", "MANIA", "MANLY", "MANNA", "MANOR", + "MANSE", "MAPLE", "MARCH", "MARES", "MARKS", "MARRY", "MARSH", "MARTS", "MASKS", "MASON", "MASTS", "MATCH", + "MATED", "MATES", "MAUVE", "MAXIM", "MAYBE", "MAYOR", "MAZES", "MEALS", "MEALY", "MEANS", "MEANT", "MEATS", + "MEDAL", "MEDIA", "MEETS", "MELON", "MELTS", "MEMES", "MENDS", "MENUS", "MERCY", "MERES", "MERGE", "MERIT", + "MERRY", "MESAS", "METAL", "METED", "METER", "MEWED", "MIDST", "MIENS", "MIGHT", "MILCH", "MILES", "MILKY", + "MILLS", "MIMES", "MIMIC", "MINCE", "MINDS", "MINED", "MINER", "MINES", "MINOR", "MINTS", "MINUS", "MIRTH", + "MISER", "MISTS", "MITES", "MIXED", "MIXES", "MOANS", "MOATS", "MOCKS", "MODEL", "MODEM", "MODES", "MOIST", + "MOLAR", "MOLES", "MOMMA", "MONEY", "MONKS", "MONTH", "MOODS", "MOODY", "MOONS", "MOORS", "MOOSE", "MOPED", + "MORAL", "MORES", "MOSSY", "MOTES", "MOTHS", "MOTIF", "MOTOR", "MOTTO", "MOUND", "MOUNT", "MOURN", "MOUSE", + "MOUTH", "MOVED", "MOVER", "MOVES", "MOVIE", "MOWED", "MOWER", "MUCUS", "MUDDY", "MULES", "MULTI", "MUMMY", + "MUMPS", "MUNCH", "MURAL", "MURKY", "MUSED", "MUSES", "MUSIC", "MUSKY", "MUSTY", "MUTED", "MUTES", "MYRRH", + "MYTHS", "NABOB", "NAILS", "NAIVE", "NAKED", "NAMED", "NAMES", "NASAL", "NASTY", "NATAL", "NATTY", "NAVAL", + "NAVEL", "NAVES", "NEARS", "NECKS", "NEEDS", "NEEDY", "NEIGH", "NERVE", "NESTS", "NEVER", "NEWER", "NEWLY", + "NICER", "NICHE", "NIECE", "NIGHT", "NINNY", "NOBLE", "NOBLY", "NOISE", "NOISY", "NOMAD", "NONCE", "NOOKS", + "NOOSE", "NORTH", "NOSED", "NOSES", "NOTCH", "NOTED", "NOTES", "NOUNS", "NOVEL", "NUDGE", "NURSE", "NYMPH", + "OAKEN", "OAKUM", "OASES", "OASIS", "OATEN", "OATHS", "OBESE", "OBEYS", "OCCUR", "OCEAN", "OCHRE", "ODDER", + "ODDLY", "ODIUM", "OFFAL", "OFFER", "OFTEN", "OILED", "OLDEN", "OLDER", "OMENS", "OMITS", "ONION", "ONSET", + "OOZED", "OOZES", "OPALS", "OPENS", "OPERA", "OPINE", "OPIUM", "OPTIC", "ORBIT", "ORDER", "ORGAN", "OSIER", + "OTHER", "OTTER", "OUGHT", "OUNCE", "OUTDO", "OUTER", "OVALS", "OVARY", "OVENS", "OVERT", "OWING", "OWNED", + "OWNER", "OXIDE", "OZONE", "PACES", "PACKS", "PADDY", "PADRE", "PAEAN", "PAGAN", "PAGES", "PAILS", "PAINS", + "PAINT", "PAIRS", "PALED", "PALER", "PALES", "PALMS", "PALMY", "PALSY", "PANEL", "PANES", "PANGS", "PANIC", + "PANSY", "PANTS", "PAPAL", "PAPAS", "PAPER", "PARED", "PARKA", "PARKS", "PARRY", "PARSE", "PARTS", "PARTY", + "PASHA", "PASTE", "PASTY", "PATCH", "PATES", "PATHS", "PATIO", "PAUSE", "PAVED", "PAWED", "PAWNS", "PAYED", + "PAYER", "PEACE", "PEACH", "PEAKS", "PEALS", "PEARL", "PEARS", "PEASE", "PECKS", "PEDAL", "PEEPS", "PEERS", + "PELTS", "PENAL", "PENCE", "PENIS", "PENNY", "PEONS", "PERCH", "PERIL", "PESKY", "PESOS", "PESTS", "PETAL", + "PETTY", "PHASE", "PHIAL", "PHONE", "PHOTO", "PIANO", "PICKS", "PIECE", "PIERS", "PIETY", "PIGMY", "PIKES", + "PILED", "PILES", "PILLS", "PILOT", "PINCH", "PINED", "PINES", "PINKS", "PINTO", "PINTS", "PIOUS", "PIPED", + "PIPER", "PIPES", "PIQUE", "PITCH", "PITHY", "PIVOT", "PLACE", "PLAID", "PLAIN", "PLAIT", "PLANE", "PLANK", + "PLANS", "PLANT", "PLATE", "PLAYS", "PLAZA", "PLEAD", "PLEAS", "PLIED", "PLIES", "PLOTS", "PLUCK", "PLUGS", + "PLUMB", "PLUME", "PLUMS", "PLUSH", "PODIA", "POEMS", "POESY", "POETS", "POINT", "POISE", "POKED", "POKER", + "POKES", "POLAR", "POLES", "POLKA", "POLLS", "PONDS", "POOLS", "POPES", "POPPA", "POPPY", "PORCH", "PORED", + "PORES", "PORTS", "POSED", "POSER", "POSES", "POSSE", "POSTS", "POUCH", "POUND", "POURS", "POWER", "PRANK", + "PRATE", "PRAYS", "PRESS", "PREYS", "PRICE", "PRICK", "PRIDE", "PRIED", "PRIES", "PRIME", "PRINT", "PRIOR", + "PRISM", "PRIVY", "PRIZE", "PROBE", "PRONE", "PROOF", "PROPS", "PROSE", "PROSY", "PROUD", "PROVE", "PROWL", + "PROWS", "PROXY", "PRUDE", "PRUNE", "PSALM", "PSHAW", "PUDGY", "PUFFS", "PUFFY", "PULLS", "PULPY", "PULSE", + "PUMPS", "PUNCH", "PUPIL", "PUPPY", "PUREE", "PURER", "PURGE", "PURSE", "PUSSY", "PUTTY", "QUACK", "QUAFF", + "QUAIL", "QUAKE", "QUALM", "QUART", "QUASI", "QUAYS", "QUEEN", "QUEER", "QUELL", "QUERY", "QUEST", "QUEUE", + "QUICK", "QUIET", "QUILL", "QUILT", "QUIPS", "QUIRE", "QUITE", "QUITS", "QUOTA", "QUOTE", "QUOTH", "RABBI", + "RABID", "RACED", "RACER", "RACES", "RACKS", "RADII", "RADIO", "RAFTS", "RAGED", "RAGES", "RAIDS", "RAILS", + "RAINS", "RAINY", "RAISE", "RAJAH", "RAKED", "RAKES", "RALLY", "RANCH", "RANGE", "RANKS", "RAPID", "RARER", + "RARES", "RATED", "RATES", "RATIO", "RAVED", "RAVEN", "RAVES", "RAYON", "RAZED", "RAZOR", "REACH", "REACT", + "READS", "READY", "REALM", "REALS", "REAMS", "REAPS", "REARS", "REBEL", "REBUS", "REBUT", "RECUR", "REEDS", + "REEDY", "REEFS", "REEKS", "REELS", "REEVE", "REFER", "REFIT", "REGAL", "REIGN", "REINS", "RELAX", "RELAY", + "RELIC", "REMIT", "RENDS", "RENEW", "RENTS", "REPAY", "REPEL", "REPLY", "RESET", "RESIN", "RESTS", "REVEL", + "REVUE", "RHEUM", "RHYME", "RICKS", "RIDER", "RIDES", "RIDGE", "RIFLE", "RIFTS", "RIGHT", "RIGID", "RILED", + "RILLS", "RIMES", "RINGS", "RINSE", "RIOTS", "RIPEN", "RIPER", "RISEN", "RISER", "RISES", "RISKS", "RISKY", + "RITES", "RIVAL", "RIVEN", "RIVER", "RIVET", "ROADS", "ROAMS", "ROARS", "ROAST", "ROBED", "ROBES", "ROBIN", + "ROCKS", "ROCKY", "ROGUE", "ROLES", "ROLLS", "ROMAN", "ROOFS", "ROOKS", "ROOMS", "ROOMY", "ROOST", "ROOTS", + "ROPED", "ROPES", "ROSES", "ROSIN", "ROUGE", "ROUGH", "ROUND", "ROUSE", "ROUTE", "ROUTS", "ROVED", "ROVER", + "ROWDY", "ROWED", "ROYAL", "RUDER", "RUFFS", "RUINS", "RULED", "RULER", "RULES", "RUNES", "RUNGS", "RUPEE", + "RURAL", "RUSES", "SABLE", "SABRE", "SACKS", "SADLY", "SAFER", "SAGAS", "SAGES", "SAHIB", "SAILS", "SAINT", + "SAITH", "SALAD", "SALES", "SALLY", "SALON", "SALSA", "SALTS", "SALTY", "SALVE", "SALVO", "SANDS", "SANDY", + "SANER", "SATED", "SATIN", "SATYR", "SAUCE", "SAUCY", "SAVED", "SAVES", "SAWED", "SCALD", "SCALE", "SCALP", + "SCALY", "SCAMP", "SCANS", "SCANT", "SCARE", "SCARF", "SCARS", "SCENE", "SCENT", "SCION", "SCOFF", "SCOLD", + "SCOOP", "SCOPE", "SCORE", "SCORN", "SCOUR", "SCOUT", "SCOWL", "SCRAP", "SCREW", "SCRIP", "SCRUB", "SCULL", + "SEALS", "SEAMS", "SEAMY", "SEATS", "SECTS", "SEDAN", "SEDGE", "SEEDS", "SEEDY", "SEEKS", "SEEMS", "SEERS", + "SEIZE", "SELLS", "SEMEN", "SENDS", "SENSE", "SERFS", "SERGE", "SERUM", "SERVE", "SEVEN", "SEVER", "SEWED", + "SEWER", "SEXES", "SHACK", "SHADE", "SHADY", "SHAFT", "SHAKE", "SHAKY", "SHALE", "SHALL", "SHALT", "SHAME", + "SHAMS", "SHANK", "SHAPE", "SHARE", "SHARK", "SHARP", "SHAVE", "SHAWL", "SHEAF", "SHEAR", "SHEDS", "SHEEN", + "SHEEP", "SHEER", "SHEET", "SHEIK", "SHELF", "SHELL", "SHIED", "SHIFT", "SHINE", "SHINS", "SHINY", "SHIPS", + "SHIRE", "SHIRK", "SHIRT", "SHOAL", "SHOCK", "SHOES", "SHONE", "SHOOK", "SHOON", "SHOOT", "SHOPS", "SHORE", + "SHORN", "SHORT", "SHOTS", "SHOUT", "SHOVE", "SHOWN", "SHOWS", "SHOWY", "SHRED", "SHREW", "SHRUB", "SHRUG", + "SHUNS", "SHUTS", "SHYLY", "SIBYL", "SIDED", "SIDES", "SIEGE", "SIEVE", "SIGHS", "SIGHT", "SIGMA", "SIGNS", + "SILKS", "SILKY", "SILLS", "SILLY", "SINCE", "SINEW", "SINGE", "SINGS", "SINKS", "SIREN", "SIRES", "SITES", + "SIXES", "SIXTH", "SIXTY", "SIZED", "SIZES", "SKATE", "SKEIN", "SKIES", "SKIFF", "SKILL", "SKIMS", "SKINS", + "SKIPS", "SKIRT", "SKULK", "SKULL", "SKUNK", "SLABS", "SLACK", "SLAGS", "SLAIN", "SLAKE", "SLANG", "SLANT", + "SLAPS", "SLASH", "SLATE", "SLATS", "SLAVE", "SLAYS", "SLEDS", "SLEEK", "SLEEP", "SLEET", "SLEPT", "SLICE", + "SLICK", "SLIDE", "SLILY", "SLIME", "SLIMY", "SLING", "SLINK", "SLIPS", "SLITS", "SLOOP", "SLOPE", "SLOPS", + "SLOTH", "SLUGS", "SLUMP", "SLUMS", "SLUNG", "SLUNK", "SLUSH", "SLYLY", "SMACK", "SMALL", "SMART", "SMASH", + "SMEAR", "SMELL", "SMELT", "SMILE", "SMIRK", "SMITE", "SMITH", "SMOCK", "SMOKE", "SMOKY", "SMOTE", "SNACK", + "SNAGS", "SNAIL", "SNAKE", "SNAKY", "SNAPS", "SNARE", "SNARL", "SNEAK", "SNEER", "SNIFF", "SNIPE", "SNOBS", + "SNORE", "SNORT", "SNOUT", "SNOWS", "SNOWY", "SNUFF", "SOAPY", "SOARS", "SOBER", "SOCKS", "SOFAS", "SOGGY", + "SOILS", "SOLAR", "SOLES", "SOLID", "SOLOS", "SOLVE", "SONGS", "SONNY", "SOOTH", "SOOTY", "SORES", "SORRY", + "SORTS", "SOUGH", "SOULS", "SOUND", "SOUPS", "SOUSE", "SOUTH", "SOWED", "SOWER", "SPACE", "SPADE", "SPAKE", + "SPANK", "SPANS", "SPARE", "SPARK", "SPARS", "SPASM", "SPAWN", "SPEAK", "SPEAR", "SPECK", "SPEED", "SPELL", + "SPELT", "SPEND", "SPENT", "SPERM", "SPICE", "SPICY", "SPIED", "SPIES", "SPIKE", "SPILL", "SPILT", "SPINE", + "SPINS", "SPINY", "SPIRE", "SPITE", "SPITS", "SPLIT", "SPOIL", "SPOKE", "SPOOK", "SPOOL", "SPOON", "SPOOR", + "SPORE", "SPORT", "SPOTS", "SPOUT", "SPRAY", "SPREE", "SPRIG", "SPUNK", "SPURN", "SPURS", "SPURT", "SQUAD", + "SQUAT", "SQUAW", "STABS", "STACK", "STAFF", "STAGE", "STAGS", "STAID", "STAIN", "STAIR", "STAKE", "STALE", + "STALK", "STALL", "STAMP", "STAND", "STANK", "STARE", "STARK", "STARS", "START", "STATE", "STAVE", "STAYS", + "STEAD", "STEAK", "STEAL", "STEAM", "STEED", "STEEL", "STEEP", "STEER", "STEMS", "STEPS", "STERN", "STEWS", + "STICK", "STIFF", "STILE", "STILL", "STING", "STINK", "STINT", "STIRS", "STOCK", "STOIC", "STOLE", "STONE", + "STONY", "STOOD", "STOOL", "STOOP", "STOPS", "STORE", "STORK", "STORM", "STORY", "STOUT", "STOVE", "STRAP", + "STRAW", "STRAY", "STREW", "STRIP", "STRUT", "STUCK", "STUDS", "STUDY", "STUFF", "STUMP", "STUNG", "STUNT", + "STYLE", "SUAVE", "SUCKS", "SUGAR", "SUING", "SUITE", "SUITS", "SULKS", "SULKY", "SULLY", "SUNNY", "SUPER", + "SURER", "SURGE", "SURLY", "SWAIN", "SWAMP", "SWANS", "SWARD", "SWARM", "SWAYS", "SWEAR", "SWEAT", "SWEEP", + "SWEET", "SWELL", "SWEPT", "SWIFT", "SWILL", "SWIMS", "SWINE", "SWING", "SWIRL", "SWISH", "SWOON", "SWOOP", + "SWORD", "SWORE", "SWORN", "SWUNG", "SYNOD", "SYRUP", "TABBY", "TABLE", "TABOO", "TACIT", "TACKS", "TAILS", + "TAINT", "TAKEN", "TAKES", "TALES", "TALKS", "TALLY", "TALON", "TAMED", "TAMER", "TANKS", "TAPER", "TAPES", + "TARDY", "TARES", "TARRY", "TARTS", "TASKS", "TASTE", "TASTY", "TAUNT", "TAWNY", "TAXED", "TAXES", "TEACH", + "TEAMS", "TEARS", "TEASE", "TEEMS", "TEENS", "TEETH", "TELLS", "TEMPI", "TEMPO", "TEMPS", "TENDS", "TENET", + "TENOR", "TENSE", "TENTH", "TENTS", "TEPEE", "TEPID", "TERMS", "TERSE", "TESTS", "TESTY", "TEXTS", "THANK", + "THEFT", "THEIR", "THEME", "THERE", "THESE", "THICK", "THIEF", "THIGH", "THINE", "THING", "THINK", "THIRD", + "THONG", "THORN", "THOSE", "THREE", "THREW", "THROB", "THROE", "THROW", "THUMB", "THUMP", "THYME", "TIARA", + "TIBIA", "TICKS", "TIDAL", "TIDES", "TIERS", "TIGER", "TIGHT", "TILDE", "TILED", "TILES", "TILLS", "TILTS", + "TIMED", "TIMES", "TIMID", "TINGE", "TINTS", "TIPSY", "TIRED", "TIRES", "TITHE", "TITLE", "TOADS", "TOAST", + "TODAY", "TODDY", "TOILS", "TOKEN", "TOLLS", "TOMBS", "TOMES", "TONED", "TONES", "TONGS", "TONIC", "TOOLS", + "TOOTH", "TOPAZ", "TOPIC", "TOQUE", "TORCH", "TORSO", "TORTS", "TOTAL", "TOTEM", "TOUCH", "TOUGH", "TOURS", + "TOWED", "TOWEL", "TOWER", "TOWNS", "TOXIC", "TOYED", "TRACE", "TRACK", "TRACT", "TRADE", "TRAIL", "TRAIN", + "TRAIT", "TRAMP", "TRAMS", "TRAPS", "TRASH", "TRAYS", "TREAD", "TREAT", "TREED", "TREES", "TREND", "TRESS", + "TRIAD", "TRIAL", "TRIBE", "TRICE", "TRICK", "TRIED", "TRIES", "TRILL", "TRIPE", "TRIPS", "TRITE", "TROLL", + "TROOP", "TROTH", "TROTS", "TROUT", "TRUCE", "TRUCK", "TRUER", "TRULY", "TRUMP", "TRUNK", "TRUSS", "TRUST", + "TRUTH", "TRYST", "TUBES", "TUFTS", "TULIP", "TULLE", "TUNED", "TUNES", "TUNIC", "TURNS", "TUSKS", "TUTOR", + "TWAIN", "TWANG", "TWEED", "TWICE", "TWIGS", "TWINE", "TWINS", "TWIRL", "TWIST", "TYING", "TYPED", "TYPES", + "UDDER", "ULCER", "ULTRA", "UNCLE", "UNCUT", "UNDER", "UNDID", "UNDUE", "UNFIT", "UNION", "UNITE", "UNITS", + "UNITY", "UNSAY", "UNTIE", "UNTIL", "UPPER", "UPSET", "URBAN", "URGED", "URGES", "URINE", "USAGE", "USERS", + "USHER", "USING", "USUAL", "USURP", "USURY", "UTTER", "VAGUE", "VALES", "VALET", "VALID", "VALUE", "VALVE", + "VANES", "VAPID", "VASES", "VAULT", "VAUNT", "VEILS", "VEINS", "VELDT", "VENAL", "VENOM", "VENTS", "VENUE", + "VERBS", "VERGE", "VERSE", "VERVE", "VESTS", "VEXED", "VEXES", "VIALS", "VICAR", "VICES", "VIDEO", "VIEWS", + "VIGIL", "VILER", "VILLA", "VINES", "VIOLA", "VIPER", "VIRUS", "VISIT", "VISOR", "VISTA", "VITAL", "VIVID", + "VIXEN", "VIZOR", "VOCAL", "VODKA", "VOGUE", "VOICE", "VOILE", "VOLTS", "VOMIT", "VOTED", "VOTER", "VOTES", + "VOUCH", "VOWED", "VOWEL", "VYING", "WADED", "WAFER", "WAFTS", "WAGED", "WAGER", "WAGES", "WAGON", "WAIFS", + "WAILS", "WAIST", "WAITS", "WAIVE", "WAKED", "WAKEN", "WAKES", "WALKS", "WALLS", "WALTZ", "WANDS", "WANED", + "WANES", "WANTS", "WARDS", "WARES", "WARMS", "WARNS", "WARTS", "WASPS", "WASTE", "WATCH", "WATER", "WAVED", + "WAVER", "WAVES", "WAXED", "WAXEN", "WAXES", "WEARS", "WEARY", "WEAVE", "WEDGE", "WEEDS", "WEEDY", "WEEKS", + "WEEPS", "WEIGH", "WEIRD", "WELCH", "WELLS", "WENCH", "WHACK", "WHALE", "WHARF", "WHEAT", "WHEEL", "WHELP", + "WHERE", "WHICH", "WHIFF", "WHILE", "WHIMS", "WHINE", "WHIPS", "WHIRL", "WHIRR", "WHISK", "WHIST", "WHITE", + "WHOLE", "WHOOP", "WHORE", "WHOSE", "WICKS", "WIDEN", "WIDER", "WIDOW", "WIDTH", "WIELD", "WIGHT", "WILDS", + "WILES", "WILLS", "WINCE", "WINCH", "WINDS", "WINDY", "WINES", "WINGS", "WINKS", "WIPED", "WIPES", "WIRED", + "WIRES", "WISER", "WISPS", "WITCH", "WITTY", "WIVES", "WOMAN", "WOMEN", "WOODS", "WOODY", "WOOED", "WOOER", + "WORDS", "WORDY", "WORKS", "WORLD", "WORMS", "WORRY", "WORSE", "WORST", "WORTH", "WOULD", "WOUND", "WRACK", + "WRAPS", "WRAPT", "WRATH", "WREAK", "WRECK", "WREST", "WRING", "WRIST", "WRITE", "WRITS", "WRONG", "WROTE", + "WROTH", "YACHT", "YARDS", "YARNS", "YAWNS", "YEARN", "YEARS", "YEAST", "YELLS", "YELPS", "YIELD", "YOKED", + "YOKES", "YOLKS", "YOUNG", "YOURS", "YOUTH", "ZEBRA", "ZONES", "COLOR", "CINCO", +] + +expanded_list = [ + "WHICH", "THEIR", "WOULD", "THERE", "COULD", "OTHER", "ABOUT", "GREAT", "THESE", "AFTER", "FIRST", "NEVER", + "WHERE", "THOSE", "SHALL", "BEING", "MIGHT", "EVERY", "THINK", "UNDER", "FOUND", "STILL", "WHILE", "AGAIN", + "PLACE", "YOUNG", "YEARS", "THREE", "RIGHT", "HOUSE", "WHOLE", "WORLD", "THING", "NIGHT", "GOING", "HEARD", + "HEART", "AMONG", "ASKED", "SMALL", "WOMAN", "WHOSE", "QUITE", "WORDS", "GIVEN", "TAKEN", "HANDS", "UNTIL", + "SINCE", "LIGHT", "BEGAN", "LARGE", "WATER", "WORKS", "OFTEN", "STOOD", "POWER", "MONEY", "ORDER", "MEANS", + "ROUND", "VOICE", "WHITE", "POINT", "STATE", "ABOVE", "DEATH", "LEAST", "KNOWN", "ALONG", "LEAVE", "ALONE", + "WOMEN", "TIMES", "SPEAK", "FORTH", "TERMS", "CRIED", "CHILD", "HUMAN", "SHORT", "CAUSE", "SEEMS", "BRING", + "DOUBT", "BLACK", "SENSE", "CLOSE", "TRUTH", "OUGHT", "PARTY", "READY", "FORCE", "EARLY", "EARTH", "EBOOK", + "SIGHT", "SPOKE", "STORY", "LATER", "ADDED", "STAND", "NICHT", "MILES", "COMES", "TABLE", "HOURS", "RIVER", + "HAPPY", "CLEAR", "SOUND", "MAKES", "BLOOD", "COMME", "DOING", "AVAIT", "TRIED", "FRONT", "QUILL", "PEACE", + "LIVED", "HORSE", "WROTE", "PAPER", "CETTE", "CHIEF", "PARIS", "BOOKS", "VISIT", "HEAVY", "KNOWS", "LOVED", + "CARRY", "PLAIN", "SWEET", "WRITE", "TREES", "BELOW", "WRONG", "REACH", "NOBLE", "PARTS", "AGREE", "MOVED", + "ENEMY", "WORTH", "GREEN", "THIRD", "MOUTH", "SLEEP", "FRESH", "FAITH", "SMILE", "USUAL", "BOUND", "QUIET", + "ETEXT", "COURT", "YOUTH", "PIECE", "SOUTH", "MEANT", "SEVEN", "TEARS", "VALUE", "BROKE", "FIGHT", "STONE", + "BEGIN", "HENRY", "LEARN", "LINES", "GRAND", "TAKES", "MONTH", "GIRLS", "GIVES", "EIGHT", "SCENE", "LIVES", + "DRAWN", "FIFTY", "FIELD", "CHAIR", "NAMED", "ALLOW", "MUSIC", "FIXED", "STUDY", "SPENT", "ROMAN", "TRUST", + "BREAK", "EQUAL", "NORTH", "THREW", "WATCH", "LOOKS", "BUILT", "USING", "SPITE", "MORAL", "WALLS", "TOUCH", + "JAMES", "STEPS", "OFFER", "DRESS", "LYING", "GRAVE", "LEGAL", "QUEEN", "LOWER", "CASES", "SHOWN", "NAMES", + "GREEK", "BOARD", "FAIRE", "GLASS", "SHARE", "FORMS", "CLASS", "START", "SHOOK", "TRAIN", "ENTER", "PROVE", + "FLOOR", "XPAGE", "WORSE", "SORRY", "PRIDE", "MARCH", "MARRY", "CROWD", "SHORE", "DRINK", "JUDGE", "SERVE", + "LAUGH", "TRADE", "BROAD", "GRACE", "PETER", "JESUS", "ROYAL", "LOUIS", "HEADS", "PROUD", "SPACE", "FULLY", + "QUICK", "IDEAS", "FANCY", "TASTE", "SWORD", "SHIPS", "DAILY", "GLORY", "BRAVE", "HONOR", "DREAM", "WEEKS", + "THICK", "CLAIM", "CHECK", "ASIDE", "REPLY", "FALSE", "SIDES", "CROSS", "SHARP", "FACTS", "HILLS", "BREAD", + "COAST", "DAVID", "AWARE", "GROUP", "FACES", "GROWN", "BIRDS", "MIDST", "TELLS", "LIKED", "THROW", "HABIT", + "STAGE", "ANGRY", "BROWN", "OWNER", "TIRED", "TRULY", "RULES", "TOTAL", "GRASS", "STYLE", "SAVED", "DRIVE", + "TWICE", "GUARD", "BURST", "PRICE", "WANTS", "THANK", "BASED", "APRIL", "GUESS", "CHOSE", "HOPES", "UNCLE", + "WOODS", "FINAL", "FORTY", "DROVE", "SPAIN", "TITLE", "UPPER", "MINDS", "NOISE", "HOPED", "BEGUN", "ALIVE", + "ITALY", "CRUEL", "SHAPE", "SLAVE", "BIRTH", "YOURS", "STORM", "CATCH", "LOOSE", "EMPTY", "CIVIL", "DOZEN", + "SHOWS", "ADMIT", "SMOKE", "DYING", "BRIEF", "APPLY", "PROOF", "FLESH", "FRUIT", "ENJOY", "WORST", "SHAME", + "ROUGH", "COVER", "ROCKS", "COUNT", "CRIME", "GRIEF", "IRISH", "NOTES", "NEEDS", "LANDS", "BLIND", "BRAIN", + "PRINT", "CLEAN", "DURCH", "ETAIT", "LEVEL", "RAISE", "EAGER", "STARS", "FAINT", "TEETH", "LABOR", "ROOMS", + "OLDER", "EINEN", "KNEES", "MERCY", "AWFUL", "AVOID", "DOORS", "INDIA", "ENDED", "DEVIL", "WEARY", "AROSE", + "FAULT", "CROWN", "COLOR", "AUSSI", "EVENT", "GOODS", "QUAND", "YARDS", "UNION", "ASCII", "TEMPS", "FAVOR", + "VOTRE", "SOULS", "CALLS", "AHEAD", "HARRY", "SMITH", "ANGER", "PLANS", "LOCAL", "LOVER", "PAGES", "LEURS", + "VIEWS", "SIGNS", "TEACH", "STOCK", "KINDS", "APART", "GUIDE", "ARMED", "EXACT", "HOMME", "TOUTE", "LATIN", + "TRIAL", "HOTEL", "SPEND", "SKILL", "KINGS", "SHALT", "LINKS", "DANCE", "SWEPT", "FATAL", "EINER", "WOUND", + "STORE", "SLEPT", "RANGE", "HATTE", "HENCE", "IMAGE", "ARISE", "FILES", "EINEM", "BANKS", "RAPID", "WASTE", + "ENTRE", "DARED", "PLANT", "SHADE", "ACTED", "CLOUD", "PRESS", "LOVES", "UTTER", "WINGS", "FLUNG", "BOWED", + "EGYPT", "GAZED", "THINE", "STICK", "FRANK", "REIGN", "MAJOR", "URGED", "FLEET", "TURNS", "SUGAR", "SPARE", + "SOLID", "BLAME", "BORNE", "WAVES", "TOWNS", "BOSOM", "CORPS", "SPEED", "GRANT", "FINDS", "MOINS", "BUILD", + "COSTS", "ERROR", "CEASE", "BOATS", "MIXED", "DELAY", "AGENT", "CLOTH", "ISSUE", "CHARM", "TREAT", "EMAIL", + "FRAME", "SHEEP", "ALIKE", "DUTCH", "PAUSE", "SEINE", "MERRY", "FEMME", "CRIES", "VAGUE", "TRACE", "VERSE", + "NOTRE", "FLAME", "HELEN", "IDEAL", "AVOIR", "HASTE", "ROUTE", "DREAD", "BIBLE", "EXIST", "OWING", "STERN", + "SAINT", "SORTS", "FALLS", "YIELD", "OCCUR", "GATES", "BONES", "OCEAN", "JEUNE", "SHAKE", "CARED", "STAFF", + "HURRY", "BEAST", "LOFTY", "BLESS", "TROIS", "INNER", "SHONE", "DRANK", "NOTED", "WINDS", "CHEEK", "CHAIN", + "KNIFE", "FEARS", "SWIFT", "WIVES", "WIDOW", "HATED", "HOLDS", "SIXTY", "MERIT", "GROSS", "DIESE", "ANGEL", + "MARIE", "FEVER", "FIRED", "AINSI", "ROADS", "CHINA", "QUEER", "GIFTS", "SWEAR", "NURSE", "CABIN", "MARKS", + "TRIBE", "ALOUD", "PAINS", "ALARM", "SCOTT", "OUTER", "WALKS", "NAKED", "FOLKS", "ELDER", "POETS", "MATCH", + "FOLLY", "WRATH", "DWELL", "SHOES", "SLAIN", "WAREN", "COACH", "ALICE", "TOWER", "DEEDS", "HABEN", "STEEL", + "TRAIL", "DEPTH", "BEARS", "PORTE", "SHOCK", "MOSES", "GUEST", "SCHON", "PLATE", "SAITH", "FEELS", "CLERK", + "STUFF", "TRACK", "POEMS", "PLAYS", "MAGIC", "KEEPS", "MONDE", "SONGS", "LEADS", "DRUNK", "AWAKE", "COEUR", + "SHOOT", "SMELL", "PETIT", "ALTAR", "AUTRE", "LIMBS", "EATEN", "TONES", "BASIS", "STEEP", "FEAST", "NOVEL", + "SILLY", "GROWS", "BADLY", "STOUT", "ALLES", "MARIA", "RISEN", "CREPT", "SCALE", "RANKS", "FLASH", "BURNT", + "RIVAL", "BRIDE", "TALES", "BOOTS", "CHEST", "SEATS", "GRAIN", "PRIZE", "FETCH", "DIRTY", "MOUNT", "CURSE", + "STIFF", "BEARD", "BARON", "NEGRO", "ASSEZ", "UNTER", "MODEL", "GRASP", "FIRES", "FLOOD", "ALORS", "JOURS", + "FLOUR", "STOLE", "POUND", "SWUNG", "ALTER", "SHEET", "CENTS", "DWELT", "CLOAK", "ROSES", "ROOTS", "CLOCK", + "SCORE", "GLOOM", "AGONY", "SEIZE", "PIOUS", "WAGES", "BLOWS", "WHEEL", "SAGTE", "CARES", "VITAL", "RISES", + "BILLY", "AVANT", "OWNED", "SCORN", "METAL", "ABIDE", "IMMER", "JONES", "STUCK", "SHOUT", "PAINT", "DRIED", + "AMPLE", "RUINS", "MAYBE", "FILLE", "SIEGE", "STEAM", "BETTY", "MILLE", "TRICK", "TYPES", "SHELL", "REFER", + "GIANT", "LORDS", "ROGER", "FIFTH", "FACED", "DATES", "MEINE", "HOMES", "LINEN", "VIVID", "STRAW", "BRASS", + "NAVAL", "MADAM", "BONNE", "FADED", "SPORT", "DROPS", "DINED", "BEACH", "LOYAL", "CELUI", "LIKES", "CARDS", + "RACES", "CHEAP", "TRUNK", "RALPH", "CHEER", "JOINT", "EINES", "SPELL", "ABODE", "WAVED", "DRAMA", "ARRAY", + "CRAFT", "CANOE", "GEGEN", "RIFLE", "CODES", "FIERY", "MIEUX", "TAXES", "AWOKE", "LIMIT", "ASHES", "JACOB", + "GHOST", "BELLS", "STEAL", "ALLER", "CLIMB", "HEELS", "TEXAS", "TERRE", "MUTTA", "SOBER", "HARSH", "SHINE", + "KNOCK", "PEINE", "SPAKE", "VIRUS", "DEALT", "MINOR", "LAURA", "SLOPE", "HUMOR", "BENCH", "WHEAT", "MAMMA", + "PIANO", "FUNNY", "SIMON", "ROCKY", "SWEEP", "SADLY", "ERECT", "BELLE", "PURSE", "PARCE", "CLUNG", "SWORE", + "FENCE", "WORRY", "SUITE", "JAPAN", "WAGON", "POLLY", "EVILS", "CELLE", "HENRI", "JETZT", "PETTY", "BANDS", + "CHASE", "GLEAM", "UNITY", "HASTY", "ACRES", "MAINS", "LUCKY", "PERIL", "ADAMS", "HIRED", "FILED", "EXTRA", + "FLIES", "DOUTE", "TIGHT", "BLANK", "JULIA", "ENFIN", "SEEDS", "IHRER", "FAIRY", "SITES", "SMART", "ETWAS", + "WURDE", "SHIRT", "ELECT", "SPOIL", "GUILT", "ADOPT", "BILLS", "CLARA", "HOLES", "ELLES", "SAILS", "RINGS", + "EDITH", "FANNY", "BLOWN", "RIDGE", "DENSE", "TODAY", "SHEER", "ABUSE", "RESTE", "QUOTH", "LUNCH", "NIECE", + "TIMID", "MANLY", "SWORN", "PRIME", "PROSE", "BOXES", "VILLE", "LEWIS", "CREEK", "ORGAN", "BRUSH", "OPERA", + "FLOCK", "SUSAN", "BOAST", "PITCH", "ANGLE", "ELLEN", "SUNNY", "AUGHT", "FOOLS", "SWAMP", "VEINS", "NEWLY", + "COUCH", "VIEUX", "JOLLY", "OPENS", "SPEAR", "BRICK", "CREAM", "THIEF", "WAIST", "RULED", "KNELT", "SARAH", + "BRUTE", "SENDS", "MINES", "DATED", "PATHS", "MOVES", "SHOPS", "PORCH", "IHREN", "TENTS", "BACKS", "KITTY", + "SANDY", "PUPIL", "LODGE", "BLOOM", "BONDS", "BLOCK", "SATAN", "SWELL", "RIGID", "OLLUT", "GAMES", "SANTA", + "DEBTS", "FELIX", "SPOTS", "SCENT", "HEEFT", "CIGAR", "ALLAH", "ENTRY", "SAXON", "AVAIL", "MAINE", "EFFET", + "TREAD", "WIDER", "OLISI", "CHILL", "BROOK", "AMUSE", "ALLEN", "COMTE", "ROBIN", "SKALL", "VEXED", "BALLS", + "KEINE", "CROPS", "UEBER", "REALM", "EMILY", "MONKS", "REBEL", "WALES", "LAMPS", "EXILE", "HEARS", "UNITE", + "ROBES", "SKIES", "SIXTH", "SEULE", "CRAZY", "ISAAC", "IHNEN", "RULER", "WRECK", "DRAWS", "DIDST", "ARROW", + "STARE", "DROIT", "BLUSH", "DONNE", "SOBRE", "PRIOR", "VOTES", "MATIN", "THEME", "FINER", "PSEUD", "NERVE", + "APPLE", "RESTS", "PENNY", "STAKE", "BROWS", "READS", "BUNCH", "TWAIN", "TOKEN", "TALKS", "WISER", "HORNS", + "APRES", "BLAZE", "SORTE", "CLIFF", "STRIP", "TEXTS", "AWAIT", "ELBOW", "GLOBE", "FLUSH", "UPSET", "AVONS", + "TURKS", "JANET", "GENUS", "LOCKS", "HONEY", "CARGO", "LEBEN", "STOVE", "CANAL", "JERRY", "CRACK", "POSTS", + "PIETY", "LADEN", "BLADE", "LINED", "HONOM", "NOISY", "DRIFT", "DECAY", "FUNDS", "MIRTH", "TOOLS", "ACUTE", + "AUGEN", "PACES", "SPLIT", "BREED", "VENUS", "JUICE", "PLATO", "FREED", "FAILS", "PORTS", "BLISS", "USAGE", + "HEURE", "PILED", "RATES", "AIDED", "BYRON", "WIPED", "HOMER", "STAMP", "VICES", "VATER", "TOMMY", "PIEDS", + "TROOP", "SAGDE", "BLAST", "MOTOR", "RURAL", "WEARS", "CREED", "VOTED", "DELLA", "GLARE", "SKINS", "TODOS", + "SHAFT", "VENIR", "NANCY", "CROIS", "DAVIS", "QUOTE", "PLEAD", "ITEMS", "SWING", "SEEKS", "CREST", "BASIN", + "LAKES", "SNAKE", "SALES", "DUSTY", "CRUSH", "MEETS", "TENTH", "SALLY", "MEALS", "GROVE", "SUITS", "SMOTE", + "LANGE", "ACTOR", "ARGUE", "FLOWS", "DIMLY", "REEDS", "PLANE", "GREET", "SWEAT", "DIANA", "BRUIT", "ROOFS", + "SANOI", "BEAMS", "JIMMY", "PILOT", "ARABS", "FROST", "CELLS", "MOORE", "AYANT", "PERCY", "EDGAR", "PIPES", + "MOLLY", "QUEST", "FOLDS", "CURED", "PASSE", "RHINE", "MAIDS", "MULES", "REINS", "SALON", "CANST", "AUCUN", + "ALERT", "RENEW", "EDGES", "IHREM", "SWISS", "PATCH", "ALOFT", "CRUDE", "AIMED", "NAILS", "RECHT", "SAGEN", + "VIGOR", "TRIES", "FLANK", "TRACT", "VERGE", "ELSIE", "TRAMP", "IVORY", "CRASH", "CURVE", "CAKES", "EAGLE", + "HEDGE", "VOULU", "RITES", "LOGIC", "CLARK", "SPARK", "SINGS", "EPOCH", "MAYOR", "WEIGH", "ONDER", "ABBEY", + "KEITH", "AGNES", "ESSAY", "VALET", "HOSTS", "HANGS", "COMIC", "PANIC", "PEDRO", "COATS", "SCOPE", "FRAIL", + "BURKE", "GOALS", "MODES", "SKULL", "FLOAT", "RHODE", "PULSE", "LOSES", "DANTE", "TOPIC", "TIGER", "FEWER", + "PINES", "SHIFT", "HEAPS", "VIENT", "FILLS", "REINE", "FORTS", "LIONS", "MILAN", "ZIJNE", "LUNGS", "SANDS", + "PHASE", "HALLS", "ROUSE", "DAISY", "PARTE", "FARMS", "SKIRT", "SATIN", "RAINS", "HINTS", "CABLE", "GARDE", + "THUMB", "QUART", "HARDY", "ELLER", "POLES", "AARON", "SYRIA", "GROAN", "ROPES", "MOULD", "JENNY", "ROLLS", + "BACON", "MOIST", "FLING", "LANCE", "PARTI", "SHELF", "ETHEL", "MALES", "PLUCK", "SHOTS", "STOPS", "ALIEN", + "FRAUD", "CLUBS", "NEWBY", "CREEP", "TILDE", "SHAWL", "FORME", "AFORE", "RAOUL", "FROWN", "SNOWY", "LISTS", + "STONY", "FLAGS", "BORED", "MELAN", "PGLAF", "CURLS", "EFTER", "STAID", "TOUGH", "CHOIR", "HASTA", "DITCH", + "CLARE", "WITCH", "RIDER", "SEHEN", "MASON", "WEEDS", "TENDS", "PALMS", "FLUID", "VILLA", "STADT", "IRENE", + "SAUCE", "PAVED", "ANNIE", "MUDDY", "SEXES", "USERS", "STRAY", "DAMIT", "OATHS", "WIDTH", "STAIN", "STEED", + "UNFIT", "BEADS", "HERDS", "INDEM", "WENIG", "VINES", "BLAKE", "GORGE", "DEMON", "CANON", "BRISK", "GANZE", + "TUTOR", "SIGHS", "RAINY", "TANTO", "MOYEN", "HELPS", "GRADE", "PEAKS", "ELLOS", "PARMI", "PARLE", "ALLAN", + "FLORA", "PELLE", "APRON", "COINS", "CITED", "LIEBE", "DEREN", "WELSH", "HOOFS", "PAIRS", "DONNA", "VOWED", + "WEIRD", "IRONY", "BRUCE", "BEANS", "DOLLY", "CLING", "LAPSE", "GAILY", "DUSKY", "TEGEN", "WITTY", "WRIST", + "TORCH", "MONTE", "SHADY", "AMOUR", "BESET", "ATHOS", "COUPS", "TRUCE", "GOATS", "VAULT", "SPURS", "GAINS", + "TELLE", "TOAST", "VINGT", "LAYER", "ARMOR", "JUDAH", "KOMMT", "MOODS", "IMPLY", "LEVER", "JOTKA", "EXERT", + "FOULE", "SPIES", "YACHT", "ALTEN", "PAGAN", "TENSE", "ALOOF", "VALOR", "HYMNS", "ENVOY", "PINCH", "PRONE", + "HELLO", "BROOD", "MABEL", "FOWLS", "HERRN", "DROWN", "ROUGE", "BURNS", "MISMO", "CALME", "LIVRE", "SLIDE", + "LYONS", "CAIRO", "PATTY", "CASTE", "STOOP", "BAKER", "MILLS", "BLEST", "TAILS", "VOICI", "LOGIN", "FARED", + "CHANT", "SEITE", "FABLE", "MISTS", "DIEGO", "ROWED", "WORDT", "DUCKS", "MAXIM", "BOERS", "LEVEN", "SWARM", + "QUANT", "MUSED", "WAITS", "CHALK", "BEIDE", "FORTE", "CAVES", "JEDER", "SENOR", "INFER", "TRAIT", "TASKS", + "DIARY", "HOUND", "SAVEZ", "BLUFF", "PENSE", "CECIL", "JUDAS", "OUNCE", "STARK", "DRAIN", "LEAPT", "WEISE", + "CLAWS", "TWINS", "IDOLS", "SUJET", "ALTSP", "WILLS", "RAGED", "HAVDE", "BEAUX", "FRITZ", "DARES", "DEPOT", + "DAZED", "RODIN", "LOWLY", "STUNG", "NASTY", "DEITY", "BOBBY", "ELIZA", "SIZES", "GAUNT", "LYDIA", "KNEEL", + "RISKS", "DURST", "TENOR", "STEMS", "FAITE", "LIVER", "STEAD", "EVANS", "TESTS", "GILES", "HUNNE", "SEALS", + "PLANK", "SINKS", "WHIRL", "SCREW", "SPOON", "GEHEN", "HABIA", "JAHRE", "BARRY", "AMPLY", "FRANZ", "TENIR", + "SLICE", "CARTS", "EDWIN", "DEALS", "JUSTE", "WHALE", "VIVRE", "PARUT", "HAUSE", "WESEN", "GEESE", "NAMEN", + "PASTE", "NIETS", "SCRAP", "LIBRE", "ADORN", "NOSES", "MADLY", "USTED", "WELKE", "ARDOR", "TROUT", "WELLS", + "ZEIDE", "DINGY", "SOEUR", "KNAVE", "KNOTS", "TIENE", "SEELE", "ESSEX", "CRAWL", "THEFT", "WEDER", "AREAS", + "STAIR", "ORION", "HEUTE", "DADDY", "COUGH", "GUISE", "PARMA", "FISTS", "GENUG", "FAUST", "AQUEL", "SNEER", + "PLOTS", "DAVON", "JENER", "PLUME", "GEBEN", "HAUTE", "PADRE", "ABBOT", "VOILA", "SELON", "ODOUR", "MEDAL", + "OPIUM", "DRAKE", "AMISS", "ETTEI", "GLOVE", "BLEAK", "SACHE", "GENRE", "RATIO", "DRUGS", "WINES", "HINDU", + "POOLS", "VISTA", "SCANT", "CORDS", "RAILS", "STAYS", "GENOA", "WHARF", "COSAS", "STEHT", "PRIMA", "DEUCE", + "KILLS", "MADGE", "BLUNT", "PASSA", "WAKED", "PUNCH", "SABLE", "FLOWN", "DRILL", "LIFTS", "ILLUS", "CRISP", + "WORMS", "FLINT", "HANDY", "ALLEY", "MASTS", "FIEND", "STUMP", "ADULT", "LEUTE", "SCRUB", "DECKS", "PANGS", + "CAMEL", "DEINE", "TITUS", "INDEX", "HAVEN", "HILDA", "GEIST", "ORDRE", "THORN", "TOILS", "CEDAR", "LOADS", + "WAXED", "SOPHY", "DAHER", "GAUGE", "WAGER", "JULIE", "WILDE", "PEERS", "AISLE", "BLANC", "LONGS", "ELLIS", + "MINUA", "CARLO", "SCARE", "LEVIN", "KUNDE", "DINGE", "SNARE", "WEISS", "STALE", "FAITS", "AVERT", "HADST", + "BATHS", "XVIII", "TIDES", "TINTS", "BULLY", "AZURE", "WHIGS", "VIELE", "ATOMS", "PERRY", "BLICK", "GIDDY", + "SOLAR", "FARCE", "BOWER", "WARES", "FROID", "SLIPS", "RENTS", "FEATS", "LEASE", "BLAIR", "BLAND", "CONDE", + "MARSH", "RUMOR", "BERTH", "BATHE", "BOOTY", "GLIDE", "ENNEN", "BERRY", "PAULA", "AIVAN", "FRIED", "DANES", + "WORTE", "BOOTH", "OTROS", "TAXED", "TWIGS", "ERSTE", "UNDUE", "CLUMP", "RICHE", "LIEGT", "LYRIC", "IHRES", + "LASTS", "ANNOY", "WEGEN", "OOGEN", "SELFE", "DABEI", "CASTS", "BOUGH", "ROUEN", "DIGNE", "SILAS", "LOCKE", + "CHART", "MAKER", "PEGGY", "SNOWS", "DETTA", "CHRIS", "ARENA", "QUIEN", "RELIC", "SECTS", "DOUCE", "BRIBE", + "FRIAR", "TINGE", "COURS", "VIENS", "HACER", "PEPYS", "LANES", "ETHER", "VICAR", "STEVE", "JASON", "VOCAL", + "JOINS", "BASES", "UNSER", "LIGNE", "DINAH", "VASES", "TEDDY", "NEUEN", "CURLY", "VALID", "CLEFT", "CHAPS", + "BARGE", "HAGUE", "BOLTS", "DARIN", "SMELT", "TULEE", "VOYEZ", "PURER", "ENTRA", "ODDLY", "VAPOR", "EENEN", + "SPIED", "SLATE", "CREWE", "BRACE", "CYRIL", "OSCAR", "IRONS", "DESDE", "LAMBS", "VROEG", "VROUW", "DICKY", + "KRAFT", "RALLY", "RESTA", "CRAVE", "GAYLY", "LEAPS", "FLUTE", "ONCLE", "SCOUT", "GOWNS", "SWINE", "GREED", + "ONION", "SAGES", "AMBER", "TARRY", "GRATE", "GESTE", "ANDRE", "MOEST", "JUURI", "CESSE", "SHEWN", "THIGH", + "LEAFY", "BENDS", "FERME", "BRAKE", "HURTS", "GRIND", "SILKS", "CLASH", "JELLY", "SOOTH", "DIZZY", "NEBEN", + "INCUR", "DOVER", "VERRE", "HAIRY", "STALK", "TOMBE", "HEAVE", "MUCHO", "INTER", "SPADE", "CREWS", "DENIS", + "HIELT", "FOSSE", "BULLS", "ALLEZ", "SOMME", "SNUFF", "BLIEB", "HAVOC", "ALLEM", "SMITE", "LACKS", "THROB", + "GUTEN", "PUEDE", "CLICK", "TIRER", "KORAN", "FLASK", "GRUND", "TERRA", "CAUSA", "GAUDY", "HIRAM", "JETER", + "FIBRE", "VERBS", "FORGE", "WAERE", "SPECK", "OLDEN", "SHRUB", "FLEDA", "GODLY", "HOOFD", "SONNE", "EDICT", + "DAGEN", "ISLES", "CHASM", "EDGED", "TRAPS", "BIRCH", "TRUER", "PAPAL", "ASSIS", "MASSE", "MORTE", "HETTY", + "REPEL", "INGEN", "SHRUG", "KOMEN", "PRISE", "HENNE", "LIVID", "BRIAN", "SALAD", "MATES", "BOVEN", "DOUZE", + "MEATS", "FEEDS", "TANTE", "WENCH", "DARTS", "FLATS", "TUBES", "UNITS", "HECHO", "PERCH", "POURS", "TRIPS", + "CIVIC", "MUNDO", "SPRAK", "ANTES", "SCARF", "DELLE", "HYVIN", "VASTE", "FOLIO", "NEEDY", "YELLS", "FAUTE", + "REGAL", "HOOKS", "ELIOT", "PALER", "TRENT", "MALTA", "MAKEN", "LUCAS", "NOUNS", "ATTIC", "WINDY", "SPIRE", + "SIEHT", "DERBY", "SAVOY", "MAUDE", "ILIAD", "DITTO", "PLINY", "FERNS", "BASIC", "GRAPE", "SOGAR", "TYING", + "DOUGH", "DERAS", "LEANT", "BELLA", "LICHT", "FILTH", "PEARL", "LUSTY", "OLIVE", "DROLL", "BADGE", "WAGED", + "SEEST", "ANKLE", "FERRY", "PENAL", "NEUER", "ESTOS", "FUMES", "SHEDS", "SHOWY", "SALEM", "REDEN", "STALL", + "JOYCE", "SANTO", "ZELFS", "HOTLY", "SCOLD", "CANDY", "LUCIA", "WEAVE", "MONTA", "SLACK", "VOGUE", "RHODA", + "SLANG", "FOODS", "PRIVY", "SERAI", "RENDU", "VALVE", "KUNST", "CYCLE", "EXCEL", "QUASI", "CRAIG", "VERTU", + "AVEVA", "MAYST", "HEATH", "MEDIO", "CELIA", "DEEZE", "BATES", "ATONE", "VENUE", "LURID", "SKOLA", "MDLLE", + "CHOKE", "PREND", "DARAN", "SAVES", "MANOR", "FORKS", "GENTE", "DITES", "TEAMS", "GOETH", "STOND", "COOKS", + "CHIEN", "PANEL", "GROWL", "MURAT", "BODEN", "DERES", "CAIUS", "FRERE", "HEIDI", "BROTH", "TEMPO", "JONAS", + "BROOM", "RACED", "SACKS", "TERRY", "NOOIT", "GENOM", "RAVEN", "FROGS", "ANNUM", "ACHED", "HEROD", "NOIRE", + "SURLY", "EIGEN", "EVADE", "BUGGY", "CONNU", "ATQUE", "WAKES", "REMIT", "REEFS", "ASSES", "MORSE", "RUFUS", + "ROMEO", "CHORD", "SLOOP", "LOANS", "TAWNY", "ETIAM", "NOCHE", "CONGO", "GHENT", "BINDS", "SLUNG", "CUBIC", + "GROOT", "DARUM", "MUSES", "GERMS", "NYMPH", "CUPID", "LUTTE", "FINES", "LUEGO", "PSALM", "HOARY", "XXIII", + "MUJER", "CROWS", "DECIR", "SULLA", "TITRE", "MAPLE", "SALTS", "POSTE", "DODGE", "ARMEN", "DISSE", "MACHT", + "FOCUS", "LITHE", "PASHA", "HIJOS", "BUSSY", "FLEUR", "JOLIE", "SMASH", "NORAH", "DOGMA", "TUTTO", "BOWLS", + "JEDEN", "LOATH", "JESTS", "MALAY", "DIGIT", "PENCE", "JOUER", "NORSE", "SLEEK", "DOWRY", "DEFER", "SELLS", + "CALEB", "BLEED", "DOWNS", "BABES", "DENTS", "ALKOI", "JEDEM", "PANES", "SINNE", "LEIGH", "MENOS", "WILDS", + "WOLFE", "ANDRA", "IDAHO", "DOWNE", "BETSY", "ADAPT", "MIJNE", "LIESS", "DICHO", "NELLY", "TUDOR", "BLOND", + "GRAVY", "FRAGE", "AELIG", "TIENT", "STEIN", "PLIED", "SABRE", "SQUAD", "STAAT", "REVEL", "YOLKS", "SEWED", + "CRANE", "SYRUP", "GIVER", "SEULS", "HAINE", "CASEY", "HICKS", "KATIE", "LOINS", "ABEND", "EXALT", "DENNA", + "STIRS", "SMOKY", "EUERY", "NICHE", "ONELY", "SINUN", "RABBI", "BRUST", "ELIAS", "JOSSA", "PAYER", "VEILS", + "CHAFF", "SULKY", "JUNGE", "PEACH", "MAMAN", "TUNES", "KAMEN", "DIANE", "LUMPS", "SHAVE", "CLOWN", "SHINY", + "LATEN", "HUSKY", "TOWEL", "REPOS", "GLADE", "GEORG", "NUEVA", "MANIA", "SAKES", "KEATS", "PULLS", "SIOUX", + "TAINT", "SUIVI", "CROIT", "MUUTA", "TILES", "BUSHY", "BLEND", "HAPLY", "ANJOU", "TRITT", "TURIN", "PLATZ", + "FAMED", "BARNS", "VECES", "DAMON", "LANGS", "HUMPH", "RELAX", "AUNTS", "SALUT", "CRAGS", "SHOAL", "COLIN", + "MOSSY", "GAULS", "CASKS", "WRING", "AHORA", "SPICE", "BONNY", "CURES", "BRUNO", "BRAVO", "ANDER", "CHESS", + "ALIAS", "SAUCY", "NOIRS", "TUNIC", "TOMBA", "LURED", "POKER", "RADIO", "CHAMP", "FINDE", "HULLO", "MECCA", + "SCALP", "FORMA", "SCARS", "MORTS", "BASSE", "SPINE", "PEREZ", "DIVAN", "HONTE", "LINDA", "ENVIE", "TEASE", + "BARED", "MADRE", "TUTTI", "OUTRE", "OMDAT", "LUCID", "SURGE", "CLAIR", "ONSET", "PONDS", "AUTEM", "MOODY", + "BALES", "SEDAN", "GALES", "LATCH", "LENDS", "LUIGI", "CAREY", "HOIST", "OTRAS", "GWINE", "WARUM", "DIVED", + "STRAP", "KELLY", "BEECH", "TROTH", "CHLOE", "PUNTO", "CIELO", "FROZE", "BUGLE", "POISE", "SIEUR", "TRUCK", + "ZEKER", "FOXES", "BALMY", "PICKS", "ISLAM", "AMEND", "DROOP", "LOPEZ", "DAIRY", "WALTZ", "BLOSS", "KYSYI", + "NAIVE", "SHORN", "URGES", "MAIZE", "WIELD", "HOLLY", "ARTER", "YORKE", "FRAIS", "NIGEL", "PAYNE", "COMET", + "SHUTS", "SOCKS", "HORDE", "SKIFF", "HACIA", "BESTE", "PARKS", "INERT", "PARLA", "INLET", "ODORS", "PRICK", + "KOREA", "FORUM", "LIEGE", "JEDES", "LIETH", "GASES", "SENZA", "TISCH", "PRATT", "ABNER", "BURLY", "BRAND", + "MINER", "TRACY", "LUGAR", "LAWNS", "GOTHS", "TARDY", "WIGHT", "DRURY", "VADER", "COOKE", "LAIRD", "TUFTS", + "FIXES", "SOLES", "VOWEL", "CLIVE", "KOSKA", "TAPER", "GUSTS", "RIANT", "FERAI", "COILS", "GROND", "SOINS", + "ABATE", "AFOOT", "EMILE", "POCHE", "LASSE", "ADELA", "PORTA", "BULKY", "MASKS", "DORIS", "KOMMA", "TAMEN", + "CRIER", "LOBBY", "ERNST", "EXPEL", "MANGE", "PACKS", "FATED", "DOVES", "DOLLS", "METRE", "AVAIS", "NEWER", + "LOGAN", "FREER", "DENNE", "MILLY", "GENIE", "STILE", "WARDS", "GEVEN", "WROTH", "BOONE", "COMER", "ANGUS", + "USHER", "CEDED", "PUFFS", "EBONY", "MIMIC", "SIEVE", "MEDIA", "ATLAS", "DESSA", "DUKES", "TERUG", "GRIMM", + "HAYES", "INDRA", "SHEWS", "DUMAS", "MYTHS", "FACON", "LILAC", "MISER", "TACIT", "SABER", "TUNIS", "NELLA", + "PORTO", "EERST", "ENNUI", "CIDER", "POUCH", "JUIST", "SHYLY", "NEVIL", "FROTH", "GIPSY", "EINST", "HERRA", + "ALLAY", "DANDY", "TOWNE", "HARTE", "HOHEN", "PLUIE", "NEIGE", "FASSE", "WAKEN", "YEAST", "CADIZ", "MINCE", + "WOODY", "AVOIT", "BOIRE", "PIERS", "FANGS", "KRIEG", "SAHIB", "DRYLY", "BELTS", "LACED", "WAYNE", "KOMME", + "AWARD", "FRANC", "FUERA", "IDEES", "COUPE", "VIOLA", "QUERY", "VENOM", "WAZIR", "SLABS", "FATES", "DIXON", + "POKED", "TOURS", "CHEFS", "ABACK", "PAOLO", "BECKY", "OAKEN", "KLEIN", "MOONS", "POLAR", "PRAYS", "DEIGN", + "CROOK", "SOLON", "LEANS", "PUPPY", "FINIT", "XXVII", "FOLIE", "POTTS", "LABEL", "SUSIE", "STACK", "POIDS", + "JESSE", "BLEEF", "HILLY", "BITTE", "WADED", "AIKAA", "EAVES", "MINUS", "REPAS", "NANCE", "DELHI", "HAZEL", + "ORBIT", "SURER", "TYLER", "DELIA", "RAJAH", "JUDEA", "STATT", "MENGE", "WHIMS", "BOYNE", "WHIPS", "PAINE", + "LIBEL", "MANOS", "TAMED", "CHIPS", "REIHE", "SWANN", "QUALE", "SPASM", "VRAIE", "TWINE", "CLIME", "ABHOR", + "DOMES", "KNOLL", "VISTO", "WEDGE", "GRIMY", "ROVER", "WRAPS", "LEVEE", "LEGTE", "AMITY", "NIHIL", "HALLO", + "CARVE", "TREND", "DAYES", "HEARE", "DEZEN", "SAISI", "COCOA", "QUEUE", "SWOON", "WAGEN", "PUMPS", "TRASH", + "SHARK", "MEADE", "RECUR", "DREGS", "VOILE", "CANTO", "HAVRE", "FATTO", "SNEAK", "MISMA", "SHOVE", "SOYEZ", + "CRANK", "FINED", "JUMPS", "BRYAN", "VENEZ", "ENSUE", "WILES", "ROARS", "FREAK", "POSSE", "SINUA", "FUSIL", + "DAHIN", "BEGAT", "ELUDE", "CROIX", "CLANG", "HOARD", "HATCH", "SOILS", "SAMMY", "STARR", "POIKA", "ELISE", + "LISZT", "JENES", "WIRST", "RUHIG", "MEDEA", "RAIDS", "NOOSE", "LETTY", "CHERE", "EASED", "SERVI", "MEJOR", + "FEARE", "SMACK", "ALTRI", "HITCH", "GETAN", "MITEN", "MAVIS", "ACIDS", "BEGET", "GARTH", "TIERS", "ANTON", + "NICER", "TANKS", "CESAR", "CANES", "PLUMB", "VANCE", "BEGOT", "DETER", "FLARE", "BOMBS", "FITLY", "JENEN", + "FLIRT", "HABER", "JOITA", "CHUTE", "READE", "STETS", "STEAK", "GAUZE", "SPARS", "PEARS", "PIQUE", "GLACE", + "PATSY", "HOVER", "RIPEN", "SWANS", "VARRO", "MARSE", "SONGE", "GUILE", "CANNA", "NACHT", "SALLE", "CARTE", + "AMMON", "TASSO", "RAZOR", "WHIST", "OMENS", "NIMMT", "GOEDE", "GRUNT", "DIRAI", "SPIKE", "COCKS", "DIEUX", + "SUURI", "UTILE", "SEREZ", "SOTTO", "STAAN", "DACHT", "LUNAR", "WHOSO", "DINNA", "CRETE", "PADUA", "SERGE", + "CACHE", "CHILI", "TIBER", "TAGEN", "RIGOR", "BIDDY", "AIMER", "MOUSE", "OXIDE", "PESOS", "SLING", "SPILT", + "BOWEN", "SINON", "SINAI", "TULLA", "TONIC", "LOIRE", "JUGER", "HAUPT", "CADET", "COSMO", "OCCHI", "CLANS", + "MUSTY", "MUMMY", "NIGER", "GOULD", "SQUAW", "UNTEN", "PENSA", "BIJNA", "CONTI", "NASAL", "TAUNT", "SILKY", + "NUNCA", "KAKSI", "DUCHY", "MELTS", "VASTA", "DAVIE", "QUELS", "ANCHE", "LIEUT", "BARDS", "BUNNY", "RILEY", + "TIENS", "MOTHS", "OMNES", "TANTA", "WOOED", "BRENT", "RASCH", "JADED", "ERRED", "BRAGG", "GUIDO", "HURON", + "LEILA", "VEDAS", "DEEPS", "RHONE", "SOWED", "BADEN", "NOMME", "SLUNK", "DEARS", "GUTER", "HIELD", "FEUER", + "LUPIN", "HAGEN", "JAMIE", "PLUMS", "GUSTO", "WETEN", "TENER", "HAWKS", "FOLLE", "CHILE", "SAADA", "SQUAT", + "KNACK", "MOORS", "SCOWL", "JENEM", "PLAZA", "SHEEN", "UNSRE", "DETTE", "WEBER", "BITES", "MASSA", "TUOTA", + "WOORD", "DOCKS", "AGILE", "SERFS", "OASIS", "BEGON", "LACES", "CORDE", "MEYER", "REISE", "FOGGY", "DINER", + "HUMID", "QUAIL", "LILLY", "FADES", "WRAPT", "GRUFF", "WEEPS", "ETTAE", "MARAT", "REICH", "LUSTS", "DICHT", + "APACE", "BUENA", "SLIMY", "TRUMP", "JOUES", "DUCAL", "COVET", "GIEBT", "AMANT", "FEIGN", "MEIST", "RATED", + "DOIGT", "ELLAS", "CHOIX", "GLOWS", "PALED", "MOOSE", "SHEAF", "SWARD", "HAREM", "DORIA", "PIKES", "WREST", + "MITTE", "TOOKE", "CHUMS", "TENGO", "LOTUS", "CUFFS", "KIRBY", "BARKS", "GYPSY", "BLOIS", "URBAN", "SIBYL", + "TIRES", "JOSTA", "TEILS", "ODIUM", "PURGE", "DOSES", "WEZEN", "NEPHI", "LIBRO", "SLYLY", "VIGIL", "HADES", + "THUMP", "BUYER", "RYDER", "ARYAN", "SNARL", "CHIME", "ROBED", "AUREZ", "DESTO", "SLEET", "CUBAN", "RARER", + "SEVER", "WOLLE", "FOYER", "SHAKY", "CAGES", "VANHA", "BETER", "TARDE", "VNDER", "FOURS", "KARNA", "POORE", + "OMNIA", "ACTES", "GOWER", "NINTH", "GENAU", "CLAUS", "BIENS", "KOHTA", "NUITS", "BRAID", "FEUDS", "GANGS", + "ILMAN", "LEECH", "NEQUE", "ANTAA", "BARBE", "MAGNA", "PILLS", "PANTS", "EARLE", "MERCI", "PERTE", "BABEL", + "LUCHT", "TENIA", "SEUIL", "DEGLI", "HARES", "VILDE", "RIOTS", "GJORT", "PIPER", "ZEIGT", "TIBET", "KICKS", + "JADIS", "SPELT", "PLAID", "CERCA", "FALLU", "MURKY", "HEINE", "REVUE", "CAMPO", "AMAZE", "LOOMS", "COSTA", + "STYPE", "JONAH", "CHUCK", "RELAY", "FIBER", "GLINT", "SLOTH", "MUSIK", "MOTIF", "BATCH", "SOWIE", "PATER", + "DEANE", "DOETH", "OBJET", "GAIUS", "LAMPE", "DOZED", "GULLY", "SEAMS", "BOYCE", "MAILS", "USEIN", "HENDE", + "PSHAW", "WHINE", "PRINZ", "WILLY", "HALFE", "MILKY", "ANVIL", "SHRED", "CHIDE", "EFFIE", "MOANS", "LEITH", + "COPSE", "DRAGS", "FARRE", "BANDE", "BHIMA", "YATES", "NIVER", "DEVEZ", "LAGEN", "OMBRE", "JONGE", "WATTS", + "ISTUI", "TUSKS", "AMACR", "JAREN", "VARIT", "REDDY", "PRIAM", "PRIMO", "COWED", "TOWED", "AVERY", "AXIOM", + "HOVEL", "BRICE", "CONES", "KUNNA", "LEISE", "HOBBY", "MINUT", "POOLE", "SINGH", "BOILS", "SATTE", "CRABS", + "MERLE", "WARNS", "FOURE", "ACHES", "YEARN", "SAHEN", "AILES", "LORRY", "LOVEL", "LUEUR", "SLANT", "METER", + "PHONE", "TAGES", "SUPER", "FUSED", "GLOSS", "NULLA", "ADEPT", "COREY", "LEGER", "SUAVE", "GAZES", "NEMEN", + "NOGET", "PANDU", "CAREW", "XXXII", "SCAMP", "HEATS", "TIPSY", "HOWLS", "MEMES", "ROOMY", "SANOA", "HAYDN", + "JETTE", "AMORY", "CONTE", "VERRA", "TILLY", "WELCH", "PEALS", "SULLY", "BRINE", "MOINE", "FACTO", "BORDS", + "FIRMS", "FARES", "TUNSI", "WAXEN", "BULBS", "BLIVA", "MESSE", "BRISE", "BORIS", "KAUAN", "RAKED", "IBSEN", + "PINTS", "DENKT", "POPES", "WHIFF", "SYBIL", "QUITS", "ARGOS", "AGGIE", "ANDES", "DALLA", "ABOOT", "EINDE", + "GERNE", "TYROL", "FADER", "SORTI", "MEDES", "AMIGO", "CELTS", "LEONE", "LILLE", "AGLOW", "INJUN", "KEEPE", + "RUINE", "LIEUX", "GEENE", "AUDIO", "SEETH", "STREW", "GRAZE", "KHAKI", "ABRAM", "APTLY", "HEERE", "ETANT", + "GOOSE", "POSED", "CHOPS", "ALTRO", "TITHE", "ANGST", "WORIN", "HIGHT", "CREAK", "NULLE", "DOWER", "SENTI", + "QUELL", "CHINK", "DESIR", "SETZT", "JAUNE", "LYNCH", "SPOUT", "PORES", "GOMEZ", "GABLE", "ACRID", "COZEN", + "BROCK", "DOYLE", "ZWECK", "ACTON", "RARES", "PIPED", "ZONES", "LIARS", "BOYLE", "KAMPF", "ALBUM", "SNIFF", + "MARES", "SHACK", "OMITS", "CITES", "AROMA", "WITTE", "HILFE", "HEERD", "ARGUS", "PINED", "USURY", "EARLS", + "YKSIN", "CULTE", "NOBIS", "SPILL", "FLOTS", "PROIE", "LURCH", "ADOLF", "ARDEN", "CARNE", "INTET", "WREAK", + "VOLLE", "SLIME", "TONGS", "GUERE", "VALES", "MANNA", "ISLET", "MANER", "DUNNO", "EASEL", "ECRIT", "NOOKS", + "GLENN", "HOSEA", "BUSTS", "ENACT", "FETES", "CHAUD", "GUILD", "SIZED", "ENOCH", "SPIEL", "OILED", "NATAL", + "ROCHE", "BASER", "PODER", "QUOTA", "FINIR", "SELMA", "NADIE", "DRAME", "LARKS", "DOWNY", "GULLS", "ARBOR", + "CERES", "LANDE", "WYATT", "IDIOM", "QUILT", "TUTTA", "HEINZ", "MAMMY", "MONDO", "TACHE", "QUACK", "TALON", + "ENSIN", "VEUVE", "SODOM", "DUELS", "RANCH", "MINNA", "CRAPE", "WAVER", "HOOPS", "MEBBE", "VERSA", "KUTEN", + "AVIEZ", "WIJZE", "PLAIT", "BIGOT", "URSUS", "VOLTA", "DRONA", "PADDY", "AHMED", "GIBBS", "OGSAA", "PABLO", + "TOILE", "TERME", "GRAIL", "JUICY", "CINCO", "VOORT", "RERUM", "MUNRO", "SAMMA", "REMIS", "AVOND", "ALLOY", + "RIZAL", "SITTE", "NEWES", "ONZEN", "OFFEN", "SCOFF", "XXXIV", "NOUSI", "GLUED", "BEARE", "STARB", "FUITE", + "BRETT", "NOTCH", "AARDE", "BRUNT", "DEWEY", "GAMLE", "POBRE", "LAITY", "RAVED", "TROTZ", "SPRIG", "JEMMY", + "MEUSE", "FUEGO", "BUELL", "WARMS", "BIXBY", "TWIST", "ROLLO", "RISKY", "GROPE", "MEGET", "DANNY", "LONGE", + "LOOKE", "AYRES", "SUPRA", "CHANG", "FOLGE", "SWAIN", "ARRAS", "CHRON", "SEIEN", "OTTAA", "OBEYS", "HALTE", + "MOLTO", "HORUS", "HALLE", "DEVON", "HARPS", "PHIAL", "SELIM", "TIDAL", "TAPIS", "PRIER", "DENKE", "FONDO", + "OGDEN", "ENDOW", "ASTIR", "FAIRS", "BAKED", "ENGEL", "HANNA", "SZENE", "PIVOT", "WIRED", "RAFTS", "COTES", + "PLATS", "TIMON", "ANNAN", "MENTE", "RONDE", "STINT", "RAGES", "KENNY", "SADIE", "ZIJDE", "CALLE", "DIVIN", + "DUPED", "VOUCH", "FOOTE", "LEWES", "LIISA", "DARCY", "SIGER", "LUCAN", "IEDER", "ARMAS", "DONAL", "BRADY", + "WERKE", "ANNEX", "STATO", "HUNTS", "KAMER", "ALWAY", "PHILO", "SYNOD", "PROBE", "HORNY", "LEEDS", "FOUNT", + "NABOB", "ESTAR", "AVRIL", "DEFOE", "CAGED", "LOOPS", "NEVEU", "CLYDE", "GOEST", "MANES", "LEAUE", "TUNED", + "RISHI", "POSER", "STOIC", "NUEVO", "SAMOA", "SALIR", "SNORT", "ADIEU", "BOCHE", "LOGIS", "JAKOB", "LESSE", + "FILMY", "FEINT", "HOOGE", "LESEN", "TUTTE", "VEDIC", "BATON", "SCION", "SUBIR", "MASSY", "CLOVE", "WHOOP", + "TEILE", "DEBUT", "STOWE", "HELPE", "PAILS", "ADELE", "HERUM", "DESKS", "LIIAN", "HINZU", "SIDED", "TRITE", + "HORAS", "JINNY", "POPPY", "EQUIP", "NEERE", "URINE", "VIENE", "BERNE", "DUROC", "PERTH", "ARIEL", "GREGG", + "WOLLT", "MOREL", "DEEMS", "VIEIL", "MEZZO", "POETE", "HERON", "SHIRK", "SIETE", "HEMOS", "SORES", "EATON", + "ASHEN", "VERSO", "ALTON", "STORA", "SWOOP", "CZECH", "JOVEN", "SOFAS", "TIMED", "XXXVI", "KREIS", "WEILE", + "SCOUR", "JACKY", "ROLFE", "PIENI", "STIRN", "DREAR", "PIERO", "TRAYS", "SYLLA", "ERANO", "LURKS", "GOURD", + "MAAKT", "WANED", "LIBYA", "ALSOF", "FELON", "BREST", "ELENA", "WERDE", "JUNTO", "TENTE", "VERTE", "SILLE", + "VOLER", "ALBAN", "GALEN", "REDAN", "WALSH", "ARBRE", "ASTOR", "RAYON", "REEVE", "MUCHA", "NUORI", "LABAN", + "SIKSI", "LUCCA", "ALLUS", "PURTY", "BUDGE", "USURP", "MONTY", "YEMEN", "JOKED", "QUAYS", "LECOQ", "WYLIE", + "MACON", "UPTON", "NEALE", "SEENE", "OFFRE", "OTTER", "POING", "LEHRE", "ADOBE", "ABYSS", "SPURN", "PLATT", + "HUUSI", "PLUTO", "REMUS", "MOGUL", "DULLY", "BLOED", "SPICY", "GEEST", "FEIND", "KETCH", "LASSO", "ESTIS", + "BRYCE", "MENER", "PACHA", "ANTOI", "LEPER", "BAUER", "JOHNS", "FEREZ", "SWEDE", "HERAB", "VOTER", "SIREN", + "ELDON", "ROOKS", "EBERS", "NESTA", "YEERE", "ROLLE", "SLADE", "GLAND", "JOSEF", "BORES", "GALOP", "NELLE", + "HEGEL", "MEANE", "WIDEN", "DAWES", "PLUSH", "ERHOB", "FORSE", "SOLLE", "ZIEHT", "SUCHE", "BERGE", "HASAN", + "MANDY", "OEVER", "BAIRN", "ELGIN", "ZULKE", "AKBAR", "BUREN", "CRUMB", "BACKE", "LINGE", "WOHIN", "HACEN", + "BEENE", "LARVA", "REGEL", "BEVEL", "DUVAL", "KNAPP", "GEEFT", "REELS", "JUANA", "ESSER", "PLATA", "STIEG", + "RACEY", "KNELL", "LLAMA", "VOISI", "HINDI", "PASAR", "VENTS", "HOLDE", "PITIE", "PAGET", "SLUMS", "GIRTH", + "DROSS", "MAIRE", "LAGER", "FEHLT", "MAZES", "UNDID", "THYME", "WHORE", "WOGAN", "BLUES", "RIAIT", "PANZA", + "SALVE", "ZOUDE", "HERRE", "MIDAS", "PREIS", "SIRES", "GLUCK", "ROAST", "FABER", "ASIAN", "DEREK", "DIGBY", + "YARNS", "NUAGE", "SENCE", "HEWER", "SAWED", "SAVOR", "SWIMS", "CABOT", "TAFEL", "ANSON", "CAROL", "SNART", + "HUSKS", "SERRA", "LYMAN", "MARNE", "EDDIE", "GENII", "DAMEN", "RAMON", "FAUNA", "ARMEE", "CHUSE", "DEMIE", + "COMBS", "VENAL", "BLINK", "CYNIC", "JOLLA", "FUESE", "DUPES", "LOSER", "TERSE", "DUNES", "KENNE", "WINDE", + "GAYER", "CONAN", "MISSY", "BALLE", "RANGS", "REBUS", "FUSSY", "DRILY", "JUGES", "HABET", "IMACR", "CUZCO", + "FELLA", "HEWED", "AIENT", "SOULE", "CAPUA", "ULTRA", "EBBED", "VIPER", "CAFES", "KENNT", "CRAZE", "ELMER", + "DRONE", "SERAS", "SIENA", "WOLFF", "GILLS", "CRYPT", "GRAFT", "SANTE", "HALTS", "GAPED", "GELUK", "YOKED", + "FONDS", "FERNE", "NAOMI", "SECHS", "BUXOM", "TENEZ", "QUAKE", "TENUE", "ROUVA", "LOURD", "LYNDE", "REACT", + "SOUPS", "ODEUR", "KAZAN", "BOXED", "GRUEL", "ECLAT", "GLENS", "JOHON", "SOPRA", "COCKE", "RENEE", "SWISH", + "PFERD", "SCHIP", "KERRY", "ALDEN", "DANSE", "DELTA", "UNIDO", "SOORT", "EXULT", "ANDEN", "BLIVE", "HORNE", + "AMORE", "EORUM", "KINDE", "JEERS", "NAMUR", "ULICK", "SNAIL", "ALECK", "MEEST", "SUEUR", "DUMMY", "MARSA", + "CURRY", "ELTON", "NEDDA", "DOLOR", "GEVAL", "DEUIL", "THONG", "MULTA", "RESIN", "DIRGE", "LARGO", "INDUS", + "LEMMY", "BONUS", "PLEAS", "ALINE", "ABOUE", "CRAMP", "CORSE", "VERTY", "FALTA", "WOMIT", "EARNS", "PRATE", + "WEARE", "PHEBE", "OVERT", "BRACH", "CAMPS", "TRAUM", "TALTE", "MERGE", "SAUVE", "RIUER", "COALS", "FLOYD", + "ASSAI", "EMPOR", "NOTTE", "VINDT", "ENKEL", "SUCHT", "ZAGEN", "EPIST", "LYELL", "RILLS", "WOFUL", "MEKKA", + "ZULUS", "HUSSY", "GASPS", "JOTTA", "SOOTY", "ANNUL", "CAPES", "SHERE", "NENNT", "SORGE", "DUANE", "CALMS", + "DARBY", "TAHOE", "LIEST", "SAUTA", "SPURT", "VINCI", "ANNAT", "BOORD", "DROPT", "ZELVE", "DWARF", "ATTER", + "BLEUS", "DEANS", "JEWEL", "HEMEL", "AFIRE", "FLEAS", "ADAIR", "INSEL", "MUITA", "TALLY", "CABAL", "SAMME", + "MELON", "TEPID", "MIELI", "FOLGT", "POLLS", "ROSIE", "WALDO", "LEBTE", "HINGE", "SNOUT", "STURM", "BEALE", + "HAGAR", "SEWER", "SONIA", "TEXAN", "BEETS", "CHERS", "BURMA", "HOWEL", "EYING", "SAIDE", "PUNIC", "ANTIQ", + "UNTIE", "DILKE", "SOUPE", "WYNNE", "SKATE", "AMYAS", "AGUAS", "HESSE", "ADAGE", "ROOST", "WOLDE", "LEIPZ", + "TILED", "EAMES", "ASTUI", "SCOOP", "KNABE", "CLINK", "ELEVE", "RAZON", "VIEJO", "REYES", "ENDNU", "RIVEN", + "ORDEN", "PARRY", "GULFS", "EXTOL", "AUNTY", "TWEED", "RENDS", "STERK", "SIDON", "XXXIX", "SUDAN", "THANS", + "COMYN", "MATTI", "MONCK", "OHREN", "HADDE", "VREDE", "WAIVE", "LEJOS", "MITRE", "BEFEL", "PASTY", "WORKE", + "BLOTT", "LIEBT", "BARRE", "SOARS", "FISKE", "CERTO", "WRITS", "LENTE", "VAINE", "FALLE", "VERDE", "MOGEN", + "GRETA", "MOCKS", "TRANS", "CROAK", "DITTY", "VRAIS", "MINOS", "SALUA", "CHAOS", "PEKIN", "APPEL", "MAGIS", + "FINCH", "ELVES", "ISANG", "KOLME", "POESY", "DECOY", "STOPT", "LUCIE", "NETTA", "SOULT", "ALGUN", "IDIOT", + "DISKS", "CRISE", "EVOKE", "LIEVE", "KLEUR", "FERMA", "EENIG", "CIRCE", "SUELO", "MATER", "BATTU", "LINIE", + "MORNE", "SEGUN", "LARRY", "TOMAR", "GAGNE", "SCALY", "STAVE", "GLEAN", "KOVIN", "DORIC", "GORSE", "INDES", + "BEPPO", "GLANZ", "KUNTI", "OPFER", "DRIES", "TOTTA", "SHIRE", "SILVA", "OBRAS", "NAAST", "DETTO", "OMAHA", + "SHEBA", "TITAN", "BEAKS", "COWAN", "MARKT", "ADOWN", "GOUTY", "HAREN", "TOADS", "QUALI", "RENAN", "MANNE", + "SNORE", "COMMA", "ZYNDE", "LYGIA", "RUDER", "SCRIP", "WIERD", "ELEGY", "TWANG", "ANTTI", "SNIPE", "TABOO", + "MOHUN", "BRANT", "CAIRE", "DEMAS", "ARDAN", "ALTID", "COLLA", "BRAWL", "LEASH", "CHAFE", "PARED", "VOMIT", + "MYERS", "SOURD", "SANOO", "TAIRE", "ABDUL", "GNADE", "PALSY", "MIKSI", "JOHAN", "MELEE", "DRAWL", "HULOT", + "SOUCI", "WILLE", "AGATE", "NOMEN", "SEEKE", "GRIPS", "LEGEN", "SINEW", "JAMBE", "VRAAG", "EMACR", "FETID", + "LEWIN", "EDERS", "VIVES", "KALLE", "FILMS", "GUTES", "MARYA", "MYSIE", "JERKS", "PAARD", "NANNY", "EENER", + "ROVED", "CHICK", "BITCH", "HINAB", "IDEEN", "ESSET", "HOURE", "RHYME", "JULIO", "BELLO", "HAVIA", "DEARE", + "IDLER", "WOBEI", "JABEZ", "MIAMI", "ESTOY", "AULUS", "ANCOR", "MOVER", "WARRE", "IMPEL", "KRONE", "RACHE", + "FASTS", "NEPAL", "BANJO", "REINO", "DAWNS", "HETTA", "STAEL", "CHIRP", "PENTE", "SOLLT", "VOCES", "DEZER", + "LAINE", "MINDE", "ANENT", "ASSET", "PUITS", "SWAYS", "MOCHT", "WAHRE", "DEMUR", "BETTE", "LUZON", "TIESI", + "TONED", "FREDA", "TAINE", "DALLE", "SPITS", "DYKES", "TOLLS", "SOONE", "EURER", "ANDAR", "KITES", "KENBY", + "PROPS", "PUNKT", "TIEFE", "FLAPS", "NIMES", "ROHAN", "GUIDA", "GOODY", "LEROY", "BUCKS", "ONKEL", "ADLER", + "ALAIN", "SWIRL", "ZOORT", "GARRY", "AMAIN", "TEINT", "EERIE", "GUSTY", "AIDES", "ANIMO", "DOEST", "DREST", + "LAPSI", "FARBE", "RACKS", "VOEUX", "BELIE", "LEMON", "INCAS", "NECKS", "LOGIK", "RIVET", "DOTTY", "EWING", + "LENOX", "ZORZI", "TUNNE", "COLDS", "INNES", "CARON", "TULLY", "KULKI", "NIEUW", "RIPER", "HATER", "ROSEN", + "WAIFE", "FATTY", "VOGEL", "YPRES", "CASAS", "SINUT", "WINKS", "NOLAN", "BOARS", "BIRON", "MINAE", "KRAAL", + "STUHL", "PODIA", "MARLY", "MOROK", "DINES", "VENTE", "WHACK", "AMINE", "PALMA", "DANDO", "TAVIA", "QUILS", + "PATIO", "TRICE", "MARIN", "FOUET", "WAILS", "HAKON", "WALLY", "WINCE", "VOLGA", "TEKEE", "TURNE", "REFUS", + "SERRE", "LEAKY", "PROXY", "CAPPY", "BLARE", "BUSEN", "MAORI", "ALOES", "SLAVS", "BANCS", "VESTA", "METED", + "NEUES", "ALWYN", "GURTH", "JOSUE", "STOLZ", "JOZEF", "JOSIE", "MAYER", "STUBE", "ZADIG", "SLOEG", "STRUT", + "REARS", "MARTA", "BROIL", "CARTA", "DEGEN", "HURST", "MUNDE", "EPICS", "VILJA", "PESAR", "SPAWN", "MINUN", + "TALKE", "CROFT", "TESTA", "SEARS", "MYRRH", "CASED", "LISTE", "CHURL", "DICEN", "KURUS", "CORAL", "LIVIA", + "COLTS", "DRAYS", "ESTAN", "POETA", "CABUL", "JUDEN", "LATHE", "LITLE", "LEPIC", "EATER", "DIJON", "PAVIA", + "CLODS", "BYRNE", "JULEY", "GENTS", "VERNE", "ASSAY", "CHINS", "LIZZY", "PUHUI", "CANBY", "KNOBS", "KRING", + "BEZIG", "STINK", "STUND", "BUTTS", "DONOR", "SLICK", "WASPS", "HAMAN", "LIMES", "MEURT", "HAMEL", "CITIE", + "DIVIL", "SLAYS", "CHUMP", "DIKES", "PEEPS", "LISLE", "MOTTE", "FORET", "MACHE", "BREVE", "STROM", "BUENO", + "JEHAN", "BONTE", "GETAL", "HOBBS", "TAHDO", "VOLTE", "GIUEN", "TEXTE", "CLANK", "VITAE", "MAUVE", "LIDIA", + "SMEAR", "CIRCA", "JOUIR", "PRETE", "ARTEN", "PARKE", "CAVIL", "XLIII", "STUDS", "VALLE", "BOUTS", "KOMMO", + "NEITI", "GRIPE", "JOKES", "SOFIA", "ASPEN", "HEROS", "LOSSE", "BRAUT", "HOODS", "KRANK", "LINER", "TIDEN", + "FLAWS", "HAUTS", "UMHER", "MAATA", "HELAS", "SULLE", "JERKY", "OUTDO", "RABID", "CINNA", "MANIE", "TUSEN", + "BATED", "BELLY", "BORST", "MOLES", "YUKON", "STORK", "RAZED", "OMACR", "SLITS", "SERIE", "HAITI", "MORAN", + "RESTO", "WOVON", "BISON", "TULIP", "DARYA", "REEDY", "AILED", "DIESS", "RIRES", "SELBY", "ELITE", "FIXER", + "MIHIN", "NORMA", "MEADS", "TIERE", "OOZED", "POSES", "MIENE", "GEHAD", "DIENT", "RECIT", "AZTEC", "EILTE", + "GNATS", "JETTY", "JOIES", "KOHTI", "BARTO", "MCKEE", "SAUDI", "STAGS", "PARME", "SINGE", "ALVAN", "CLAMS", + "CRONE", "CORSO", "PUHUA", "SATIS", "DENRY", "FURRY", "FURZE", "PUEDO", "COUDE", "DIDNA", "SOIRS", "ACORN", + "KNOWE", "KREEG", "TOOTH", "SERBS", "DUNCE", "THEIL", "SANFT", "ASHER", "NOGEN", "TETES", "AFFIX", "CREER", + "PITHY", "MARIO", "WORDY", "SIMLA", "ROTEN", "VISOR", "ZAKEN", "BOGEN", "HERAN", "ILLUD", "JAHRH", "CUBIT", + "PRISM", "BAIRD", "DUPRE", "TORRE", "REGNE", "SUCRE", "WERRY", "MORIR", "SLASH", "MENEE", "SERUE", "WIENS", + "BUNKS", "SEERS", "ZOGEN", "CORKS", "HOGAN", "GARDA", "TARTS", "FROME", "POCOS", "SPRAY", "CAIRN", "BAITH", + "DAGAR", "DEJAH", "ICFTU", "ZUNGE", "CHARY", "CHURN", "FLOES", "SIENS", "VAPID", "GAVIN", "GRIER", "BLOTS", + "GRAPH", "CUBES", "BIGHT", "ARLES", "MICAH", "NEBEL", "DIVER", "STABS", "BESSY", "KUULI", "SITZT", "SENAT", + "PINKS", "SPANS", "VINER", "ALKAA", "HAELT", "BAYLE", "CREUX", "ABBAS", "CROCE", "JAFFA", "ABLER", "GULCH", + "KANSA", "LIENS", "TITEL", "MATED", "MEGAN", "ALACK", "COHEN", "HELGI", "SAMOS", "ALDER", "FORAN", "LANKY", + "GRISE", "OATES", "LAVER", "IONIC", "ALIBI", "OUVRE", "GERTY", "PONER", "KEENE", "FESTE", "SANAT", "ESSEN", + "ADDER", "KURZE", "REALS", "CRAYE", "SANON", "TULEN", "VIDES", "POATE", "WIPES", "ANITA", "GAMLA", "DELFT", + "CESSA", "FORDS", "SERIA", "ASAPH", "BUDDY", "GRAAF", "BENNY", "KREBS", "CLAPS", "AMIES", "DUPIN", "VEXES", + "COMUS", "ROUES", "IRATE", "MANSE", "TACKS", "BRACY", "TABOR", "STORT", "JINKS", "SHEIK", "PARVA", "SKUNK", + "ALPES", "VERUS", "BOGUS", "RIVES", "NINON", "FILLY", "JENKS", "DOLCE", "ZETTE", "EPSOM", "DENYS", "LOBES", + "SLUSH", "BONUM", "CANOT", "MYLES", "OTWAY", "TREUE", "DANKE", "FREIE", "FITCH", "SUMME", "CAPER", "BETTS", + "SPREE", "TEENS", "IRWIN", "STAHL", "MIXES", "TIARA", "MOTTO", "NOGHT", "STURT", "BLEUE", "DALES", "STING", + "SMOCK", "BLOCH", "FUROR", "SATED", "VENNE", "JAPON", "RIGBY", "DULCE", "HEIRS", "WISPS", "PROWL", "REGEN", + "PENIS", "SICUT", "TOSTO", "HULDA", "HOFFE", "SEEDY", "DRIER", "LILLA", "ZEVEN", "LADLE", "SAURA", "VESTE", + "AINOA", "ALICK", "RUGBY", "CORPO", "BLYTH", "JANUS", "DICTA", "VAUNT", "LAVAL", "DIGHT", "PHOTO", "ZUVOR", + "ELSJE", "LUNGE", "TOMBS", "STOFF", "SURAT", "BEVOR", "OUDEN", "TRESS", "WIRES", "TIMOR", "RUSSE", "TABAC", + "WIRFT", "BERYL", "CACAO", "HEALS", "ROODE", "SWARE", "LESER", "LIDDY", "ANIMA", "ILLIS", "LUONA", "PECHE", + "ROPER", "TORAH", "GOTTA", "SOMIT", "KOMIK", "ANNEE", "ERDEN", "BANGS", "PREST", "HOUDT", "ONZER", "RAKES", + "PAIRE", "SOLVE", "TYSON", "UTICA", "TROUS", "GAMUT", "PEPIN", "CHUNK", "MAMIE", "GUYON", "ORIEL", "MIEDO", + "BUTTE", "OMNIS", "QUISO", "GERDA", "DIETH", "SIRUP", "WHISK", "FAERY", "REGIS", "INANE", "MULLE", "SHINS", + "TAMAR", "ETTEN", "CIBOT", "BOGGY", "CAPUT", "ETEEN", "GRETE", "WADNA", "CRUST", "IPSUM", "BLIGH", "LOUER", + "ONKIN", "BOGGS", "HOLDT", "MEINT", "SKEIN", "TOCHT", "VIALS", "AIDER", "RECUT", "GRECS", "LOUGH", "SPOOR", + "SEEME", "KNOPF", "STACY", "FIEFS", "WEEDY", "BENIN", "CHINE", "BREDA", "BEKAM", "SPINS", "WARST", "BOXER", + "TROST", "STUNT", "ANIMI", "AURAS", "SUMMA", "YHDEN", "YOUSE", "GROOM", "PETTO", "POMPS", "MIRZA", "HALLA", + "DENNY", "JAILS", "PUFFY", "DELLS", "MUREN", "UNCUT", "LEACH", "LIKEN", "MURAL", "OSMAN", "TARDA", "DESEO", + "MODER", "MODUS", "MANON", "COLIC", "PRUNE", "VIVIR", "CLINT", "RIGGS", "CANNE", "SALVO", "AIRES", "PIKKU", + "SAMEN", "BASTE", "ARCIS", "EASIE", "SHAMS", "MATTY", "CANNY", "NEIGH", "HYMEN", "DAUNT", "BRUNE", "FORZA", + "YAZOO", "VENDU", "NICKY", "WINNA", "PESTS", "WARME", "BENET", "TOSIN", "VASCO", "BETES", "LIEUE", "PETAL", + "ANNOS", "COLLE", "TRIEB", "KLAUS", "PUNIR", "VODKA", "THRON", "DECIA", "BRAUN", "SHUNS", "UUDEN", "ALPHA", + "FLOUT", "SELLE", "STOOL", "NEUEM", "BASLE", "HERNE", "INFRA", "ERASE", "NESTS", "BERGS", "MEDAN", "NOITA", + "SUCKS", "IESUS", "CITER", "FRAGT", "VEDER", "DRUID", "SCALD", "ALIDA", "QUITO", "HOERT", "JUNOT", "PERDU", + "GEARY", "BOOKE", "KENDE", "CHICO", "MEATH", "ROGUE", "SMYTH", "DIVES", "FILET", "FLAKE", "PUTTY", "SIDAN", + "DANAE", "ILAND", "BORDE", "HERBS", "LIMBO", "PEINT", "ROLES", "BRINY", "ETRES", "FLANC", "BLATT", "DERRY", + "MUEHE", "RODDY", "DAMES", "INGER", "XLVII", "AIRED", "CHATS", "PAULO", "EJECT", "FUNGI", "SIKES", "CADRE", + "COOLS", "SNAPS", "BLENT", "BOOBY", "VIIME", "DALLY", "HIVES", "LEDDY", "BALDY", "PRANK", "NONCE", "OVENS", + "SCHAL", "AUGUR", "CALYX", "MOWED", "TOITS", "CIMON", "SWEYN", "EDLEN", "PALES", "SPERM", "THANE", "BEZIT", + "EILEN", "UUTTA", "NAMAN", "MACKO", "SHANG", "LITEN", "BAZIN", "THORD", "ARMES", "YEARE", "CALLY", "TRYST", + "CAPRI", "ETHAN", "HOEHE", "AOUDA", "HALEY", "BERTA", "KAREL", "CLUES", "CURST", "FORAY", "ITHER", "LASST", + "RICHT", "YAWNS", "WILKS", "GRIFF", "JOKER", "MOCHE", "ATLEE", "BAIZE", "SITAE", "TEMOR", "TENET", "HAYTI", + "MANGY", "LEVIS", "SHAKS", "CURIO", "ERICA", "FRETS", "TAKAA", "KLAAS", "FROCK", "SHIED", "TRUSS", "WACHT", + "LAMME", "SAMAA", "ARMER", "SLINK", "BLEEK", "BIBBS", "ARMIE", "BANDY", "HARDI", "MINNE", "MORIN", "COURU", + "CLUNY", "MOLTI", "PLOMB", "AHMAD", "ROCCO", "SLAKE", "HAARE", "ARIAN", "WOCHE", "ZILAH", "LURES", "KLEID", + "NUOVO", "TUMOR", "CLEON", "CORTE", "SUNNE", "ELFIN", "GLAZE", "HEDEN", "VREES", "BODED", "HVIDE", "TRIBU", + "ZACHT", "YUSUF", "FRUEH", "LEDEN", "SITIO", "LITET", "MAURY", "PRYOR", "ROBUR", "GRIST", "RUFEN", "VAERE", + "VILKA", "JORAM", "FONTE", "NOGLE", "BASEL", "FIRMA", "HUNCH", "STAVA", "ALLIE", "FIERI", "HOLEN", "TROTS", + "KNEAD", "MANGO", "OEUFS", "PONCE", "QUEED", "KLANG", "AMENI", "PONTE", "EIGNE", "BRILL", "LOCUM", "NERFS", + "CLEVE", "COPIE", "EHREN", "BOTHE", "SIKHS", "SHREW", "HODGE", "WILEY", "ALTRE", "SIGLO", "CORFU", "HAIRS", + "MALER", "OZONE", "IGUAL", "VORST", "ANGES", "INNAN", "NOMAD", "DOMIN", "FUTUR", "ROPED", "HALED", "NAVEL", + "PEASE", "SAMBO", "HABLA", "ALTYD", "BREIT", "RENTE", "BAGGY", "LANGT", "NOCES", "SHEAR", "BULGE", "SLEDS", + "TRURO", "ZWARE", "BANNS", "NAPPE", "PLASH", "TARES", "WHIRR", "SCOTS", "WANDA", "CREDO", "FUMED", "GABEN", + "POJAT", "SISTA", "VIVIE", "HALVE", "SICKE", "SIMUL", "WHELP", "BREEN", "EBENE", "HAWKE", "STAUB", "MOVIE", + "PECHO", "BUONA", "CULPA", "GAGNA", "HEELE", "FLITS", "HIESS", "SOLUM", "STEHN", "FIRME", "ESTER", "MACAO", + "LIBER", "MCKAY", "PETRA", "TOKYO", "SOLCH", "BAYOU", "HELGA", "LOCUS", "ZWART", "FABIO", "ROSNY", "CORNE", + "MESMO", "BASTA", "DISCS", "GRINS", "FLEES", "PIGMY", "MAUER", "FRISK", "HUNDE", "LOGOS", "TROMP", "HOHER", + "SOGEN", "BOEUF", "CUORE", "FACIE", "RANDY", "ALTRA", "ANKER", "DROLE", "LARGA", "POJAN", "ORTON", "DEJAR", + "GIBES", "MARIS", "SETTE", "SUBIT", "VARMA", "WISHT", "GUPPY", "THOTH", "MANUS", "TRILL", "FIRTH", "BANAL", + "METTE", "SNAKY", "ESIIN", "LUNGA", "BOOMS", "GRUBS", "NEEMT", "TASSE", "OHEIM", "ENCOR", "FELLS", "VERTS", + "GUNST", "SYKES", "VESTS", "COLON", "AGONE", "AIMEZ", "BAHIA", "LEDGE", "LICET", "BEGAB", "FOAMY", "HULKS", + "NEGER", "KEYED", "MAIST", "FABEL", "LORNA", "DICHA", "SACRE", "SITEN", "GODEN", "DARKY", "DRAVE", "NIXON", + "LAULU", "FIGUR", "GANGE", "KLAMM", "LACEY", "KORTE", "RUDES", "TUERE", "LUNGO", "VERRY", "VAURA", "CUANT", + "DRAUF", "OFFAL", "GORGO", "MENUS", "MINED", "OPTIC", "DRANG", "GRIME", "CORNY", "KETTE", "HEADY", "IRAIT", + "PAPEL", "BLITZ", "HUGHS", "DROEG", "TENDU", "WIERP", "DOANE", "ELATE", "LACHE", "PIEUX", "TULLE", "TONGA", + "NAEHE", "CLASP", "FANNS", "WAFER", "DRUCK", "SAUTE", "NEVIS", "PITTI", "FALDT", "USQUE", "EWELL", "GUNGA", + "MORAY", "FISHY", "FLAIL", "MEBBY", "DAGNY", "CLOGS", "ELEND", "MEAUX", "GOLFE", "HACHE", "SAILE", "GIRTY", + "NADIA", "PAVEL", "PAHAA", "SAEPE", "KIRKE", "WALDE", "EXITS", "DOBBS", "HAMAR", "MARCO", "NOYES", "HAILS", + "MALEN", "SHALE", "TAGER", "VELDT", "GREIF", "MASHA", "LOUIE", "DEDEN", "LEZEN", "NETTE", "CROWE", "MOYNE", + "GILDS", "CREON", "GOTHA", "KAREN", "BRUME", "VERIE", "BOSCH", "MEURS", "HYENA", "LLENO", "HOCHE", "CONDY", + "PYOTR", "CALMA", "SAMAN", "STEHE", "BERIC", "LIBBY", "MONTS", "TRONE", "JUSSI", "ALCUN", "BEGGE", "COVES", + "NYODA", "ALLZU", "DAIES", "MULTE", "JUNKS", "MUSST", "DRAPS", "HAAST", "JACKS", "FAYRE", "RUNDT", "FREUD", + "COBRA", "FUMEE", "SAGST", "PURDY", "BEELD", "TORTS", "VERVE", "BRAZO", "JOYED", "WAGTE", "JOURN", "MUNGO", + "PAPUA", "FUELS", "HALBE", "PALMY", "REGLE", "ELISA", "JUSTO", "TWIRL", "CONST", "PASSO", "AMANG", "LICKS", + "NEDER", "DEGRE", "DIREZ", "FRILL", "KORAK", "KREUZ", "TYCHO", "POVAS", "ASTON", "GOSSE", "ASILE", "BOOZE", + "BUMPS", "SOMOS", "WASNA", "LORCS", "CRATE", "GROEN", "WOOER", "HEBER", "JUNTA", "TURCS", "VOLTO", "WHILK", + "DEEPE", "NEBST", "CODEX", "JUIFS", "ASIAA", "FULCO", "DOMED", "OUTEN", "STEPT", "GALBA", "CHEAT", "COULE", + "MEETE", "PAWED", "BUNCE", "SANIN", "JOLIS", "PORED", "PYYSI", "HOWAT", "LAURE", "ONNEN", "PRIED", "SCHAR", + "BAZAR", "MITAD", "MOUND", "FUERE", "TRYON", "BAISA", "CROUP", "KUNNE", "DOONE", "FOLEY", "NEHME", "DUOMO", + "SONNA", "FACIT", "KENYA", "GIVIT", "BLEAT", "HURLS", "EIFER", "AILIE", "BASIL", "HARMS", "SUTRA", "LARME", + "BRITT", "GRADY", "MOROS", "TRIPE", "ADMIS", "HERTE", "VOIES", "LANKA", "FUSSE", "GLOAT", "GROAT", "CLEEK", + "MESES", "MOOIE", "HAMIL", "FORDI", "MENOR", "SATYR", "TODDY", "TANNO", "GAMIN", "SOLOS", "YOURN", "AHMET", + "FATTA", "ERWIN", "POWIS", "SEGEN", "AMENE", "BASAL", "MOSTE", "TOYED", "MAGDA", "DIEPE", "GENOU", "OCHRE", + "WEITE", "VOLKE", "POORT", "AIKEN", "JOPPA", "SLUYS", "FIERE", "JOUST", "LACHT", "SAYDE", "DELOS", "LETHE", + "TAGUS", "ALIIS", "HIVER", "TENGA", "ECOLE", "FUENF", "ROTHA", "VETCH", "GLANS", "LAIDE", "FLYNN", "KHASI", + "KELLS", "BOONS", "SLUGS", "ULCER", "VENTO", "BRAWN", "ENTRO", "VAIMO", "AGNEW", "CREES", "MEIGS", "GOUGH", + "PUSSY", "VERBA", "RICKS", "ROWAN", "FERRO", "LAGDE", "PRESO", "RUPEE", "FRANS", "ALDUS", "CLARO", "HANNO", + "VIEJA", "MARFA", "OSTEN", "GAUZY", "BOULE", "DRAFT", "GREVE", "SETON", "HEEDS", "SONNY", "HANSE", "LUISE", + "ALIUD", "AMASS", "EARES", "ROWDY", "STEWS", "KLARA", "ROSSI", "HENKI", "NOSTI", "ZOUDT", "GROTE", "DEALE", + "FREMD", "KLONK", "LIBRI", "CAPET", "FLOSI", "SONST", "CULTS", "DIKKE", "ALGER", "DIXIE", "NUNEZ", "QUAFF", + "QUALM", "TAFFY", "QUEDA", "SEDGE", "GUERT", "ETIEZ", "ICING", "PHIPS", "PICTS", "ABBIA", "THEER", "COREA", + "KABUL", "COMEN", "GOLDE", "VIXEN", "ZWAAR", "EADEM", "FREES", "MARGE", "MEREN", "SANAN", "EDLER", "LUOGO", + "PEDAL", "VAMOS", "OSTIA", "ALTES", "COUTE", "MANDO", "AESOP", "SAPOR", "PIEDI", "GIRLE", "EMORY", "MADDY", + "HULLS", "REVES", "BAITS", "MARTS", "PROUE", "RIFTS", "IFRCS", "SANER", "EISEN", "MORGA", "WISSE", "DANCY", + "HAZEN", "LASKI", "ESTAS", "AVOUE", "CACHA", "EWIGE", "LOOPT", "WAMBA", "ACASO", "MOLLE", "JUXON", "BOCCA", + "COLUI", "TRONC", "DAUER", "PARET", "CIEUX", "DOERS", "MICHT", "NADAT", "ROULE", "DURER", "GRADO", "SUFFI", + "WAYES", "HAWES", "VOREN", "MILCH", "HVERT", "COTTA", "ZAIRE", "ALLOT", "PANSY", "FOILS", "LEAKS", "SEKIN", + "SEUEN", "WHOME", "BERRI", "BIGGS", "PARAS", "PROSY", "RACER", "BOHUN", "OOTAH", "NIOBE", "COPRA", "PLAGE", + "SPEKE", "AUDIT", "FINIS", "ROMPU", "BANDA", "DAMPS", "QUARE", "TOQUE", "BRIER", "DOREN", "NANON", "BELLI", + "TYRAN", "HILFT", "PUEDA", "MOLDS", "OELIG", "ATHEN", "LARCH", "PUNTA", "CAMEO", "MALIN", "SONYA", "JAUNT", + "LAPIS", "FINIE", "POMPE", "SIMMS", "BEADY", "GUANO", "KEHRT", "RUIDO", "WRANG", "FAHRT", "URIAH", "BLEVO", + "DUQUE", "HACKS", "TASTY", "MAINZ", "COWER", "FUORI", "TEHTY", "ARRAN", "TOOTS", "UTHER", "CROCK", "HELLE", + "QUUNE", "LINGO", "CLLIA", "ELIHU", "APPUI", "BLUER", "DIRIS", "BYLES", "FEDER", "INJIN", "STENO", "AGORA", + "COMUN", "GILET", "GRAUE", "MISES", "WOHER", "MENKO", "AMBOS", "MUTES", "MOREA", "YONGE", "LAKOU", "TECLE", + "BRATS", "LAMED", "PENDU", "TOPAZ", "DRUSE", "LAPEL", "WANDS", "AINDA", "FOOLE", "FUSES", "DEBBY", "EUGEN", + "FABRE", "MALTE", "FEINE", "GANTS", "HETKI", "SENTE", "AMICI", "LOUPS", "OPDAT", "LAUNE", "TOULD", "DURAS", + "VERDI", "STUMM", "CHENG", "FLUSS", "SACRA", "SPADA", "CRONY", "LEGDE", "OIKEA", "ALVAR", "ABAJO", "CURDS", + "PRINS", "THEYR", "BOWIE", "REDET", "WRACK", "FLORE", "ABAFT", "ANDET", "CENCI", "CLAES", "GONGS", "OWENS", + "CUIUS", "EXIGE", "IGNIS", "SLAAP", "OWAIN", "MALLE", "SACHS", "SIGNE", "BOESE", "MUSTA", "TORNO", "GORKA", + "ERANT", "REGNO", "SWART", "DORMI", "MASAI", "FIDES", "KARMA", "RAIDE", "ILIUM", "MARTY", "ROLLA", "DEERE", + "TOMES", "KNOOP", "DOLCH", "PAIGE", "ISTUU", "WAIFS", "COLES", "ASKEW", "EUREN", "HELST", "CLAUD", "QUENU", + "ROJAS", "VYASA", "KULTA", "LEYES", "BOYER", "FUCHS", "NAWAB", "BIDED", "BUSCA", "LORNE", "AIKOI", "BEFAL", + "DUCAT", "ELOPE", "FERAS", "LLEVA", "BEINE", "EMERY", "BRIAR", "DIXIT", "MURAD", "TODES", "OLELO", "SALUE", + "TOTEM", "DILLY", "HAFEN", "NOEMI", "UNCAS", "JUGEA", "QUIVI", "ALIXE", "CASSE", "GAVEN", "GRAMS", "TORSO", + "DAMIS", "NILES", "NORTE", "OXLEY", "PASOS", "AGIAS", "ESSAI", "REFIT", "FAGIN", "HARUN", "AENNU", "DECRY", + "HOOSE", "PONTS", "RENDE", "UARDA", "DRUMS", "PLIES", "PRIUS", "USKOA", "GAHAN", "MIRAR", "MELAS", "ECHTE", + "DARIO", "RHEIN", "ROSAS", "FRUTO", "SLAPS", "BRAHE", "OGIER", "ROQUE", "BEFIT", "VIAJE", "ETATS", "FINOT", + "LINDE", "ZISKA", "LAHAT", "MITAE", "AUTOR", "FERRE", "LAMES", "LLENA", "HOLME", "MAHON", "MUDGE", "SAVVA", + "SEDER", "THUGS", "CERTE", "EILIG", "RAMPE", "GHANA", "TALER", "LEERE", "BURGH", "CLASE", "DIRAS", "MITES", + "PULPY", "CUFFE", "PLADS", "HALEN", "PAWNS", "FAKIR", "MANET", "BRUNG", "TEPEE", "CERTA", "LULLS", "BUTCH", + "ARSON", "DUMPS", "EMITS", "FORTO", "PESTE", "SHANK", "HARPE", "KAMES", "MEERE", "ENGEN", "SUING", "VITAM", + "LLOYD", "THEOL", "WITWE", "DOTED", "DUETS", "MONET", "AVERS", "CADDY", "SINFI", "CALOR", "CORNS", "GEBET", + "HAYNE", "TOTEN", "GUTEM", "TILLS", "JULES", "ROSSE", "LECKY", "STAMM", "WAUGH", "ALLEE", "COMED", "COVEY", + "LONGA", "ZATEN", "BEVIS", "MATTH", "AVERE", "VERUM", "MIRAH", "SOREL", "BOURG", "CREPE", "PRUDE", "MAGUS", + "TAPIO", "ZOCHT", "TOMAS", "TURAN", "BLOUD", "BOORS", "DRAGA", "HELLS", "DECKE", "GEMMA", "CONGE", "ETAIS", + "SANAA", "SLONE", "AXLES", "FAVRE", "ORTEN", "CAPON", "STODO", "POSTO", "DANBY", "SLIGO", "VACHE", "BOTHA", + "CANTY", "CONEY", "LAVES", "NEUVE", "TALAR", "CAKED", "POCHI", "CAULD", "DAAGS", "GNRAL", "RAVES", "OLSEN", + "AMICO", "CINCH", "TIGRE", "MIKKO", "SABIN", "FECIT", "GARNI", "ILLUM", "OOREN", "RUBAN", "BEGAF", "QUIRE", + "TALUS", "FINNA", "HARER", "MORTO", "BHAER", "KOPFE", "LEMME", "BOITE", "UUSIA", "VENIA", "ASAKO", "BOTTE", + "CLEFS", "FAWNS", "MUNDI", "NUBES", "SEGUR", "ANTIC", "JURER", "CRAIK", "BANGE", "KOUDE", "PARER", "VOBIS", + "YOKES", "SAKRA", "SAULT", "BODEM", "PASSI", "SMIRK", "WHAUR", "GYGES", "MONNY", "REIMS", "KOPJE", "POSSA", + "CHIOS", "VARUS", "CLOUT", "LOUSE", "PLAIE", "PROWS", "HOLTE", "DANEY", "WONNE", "FESTA", "SPOOL", "TOREN", + "MATEO", "NINNY", "JUPES", "PLEBE", "TRAHI", "DACRE", "LEIBE", "LUBIN", "AGAPE", "HIDES", "OPERE", "LAWES", + "GALLS", "ASHBY", "ASSAM", "SHIEL", "FREYA", "RODEN", "TYLOR", "CUIRE", "RAKAS", "SPAET", "THUIS", "MINGO", + "NILUS", "FIERS", "MOLTE", "SCAPE", "SKIPS", "ZAMEN", "FINNS", "JUMBO", "PAPST", "ZIELE", "ABASE", "GREEP", + "HONRA", "TESTY", "ZYNEN", "BRUIN", "FLUCH", "PRUDY", "DEPIT", "LUNDI", "TUULI", "UEBEL", "DAGON", "COUPA", + "LUULI", "MORES", "STATA", "KEBLE", "PEKKA", "KLAAR", "MOURN", "DRAXY", "CLAVE", "JOLLE", "VENGA", "OSAGE", + "POLEN", "DIGNO", "HELFT", "GABON", "KHOJA", "SIDEN", "DARKE", "LUMPY", "POPPA", "HECLA", "AGING", "FREUT", + "HALES", "TANIS", "DRAMS", "NEGEN", "STEER", "EULER", "ETOIT", "MAAND", "PHAON", "CREME", "FULLE", "SALUD", + "CHUNG", "FELDE", "RUBEN", "FALLA", "PELEG", "CUYAS", "FACTA", "KNITS", "NOEMT", "TUPIA", "URREA", "SYNES", + "TROUW", "JEWRY", "KAMAR", "PETRI", "STEEN", "HECHA", "WEAKE", "AVILA", "HAFIZ", "HARDE", "MOQUE", "THEIM", + "FARIA", "GRYCE", "PASSY", "OPHIR", "ROSEY", "WEIBE", "GANGA", "PALUS", "AGENS", "COOED", "TRINA", "BETEL", + "UNDAN", "BALLY", "DELLO", "HAIRE", "PEONS", "RAVIN", "WAXES", "VIDAL", "ELKEN", "MAGNO", "RINSE", "VERBO", + "RIPON", "ADORE", "FAGOT", "ROAMS", "BOTEN", "NOBLY", "ROULA", "THAIS", "LOGER", "VOLGT", "MOSCA", "SABES", + "DAMER", "SCALA", "BASSO", "GORED", "NOEUD", "PISTI", "SLATS", "SIXTE", "BUOYS", "PAYED", "REAPS", "RUSES", + "HEALY", "AILLE", "BLASE", "BRULE", "OASES", "PLUGS", "HEARN", "IYONG", "PRIMI", "GRAYS", "PALOS", "TICKS", + "ESTRE", "POUCE", "DAMME", "FLECK", "GENET", "HOUGH", "JEBEL", "CLAPP", "HYDRA", "TAMER", "BLUME", "AVIDE", + "DOWDY", "GIMME", "ESCAP", "POSEY", "CRASS", "EGARD", "NUDGE", "TURBA", "WARTS", "KLAGE", "LENNY", "ELLEI", + "GONNA", "LETTO", "IONIA", "SCHAM", "EPHOD", "MIENS", "TAGIT", "DOUAY", "NICOL", "STOKE", "FLICK", "HUOLI", + "REEKS", "FAROE", "FRUEN", "FICHU", "KERTA", "JARED", "WUNDE", "AYONS", "PAESE", "PATTE", "SINGT", "SCAND", + "ADHUC", "LYMPH", "PARVE", "SCHOB", "STRAX", "PERON", "BUSTE", "DARLE", "EODEM", "MOTES", "ORSON", "RAUME", + "BRINK", "GNOME", "LASTA", "CRUMP", "SKENE", "GRILL", "NIEVE", "BUBEN", "JOSUA", "CEDER", "INNEN", "MANGA", + "OLEVA", "SIXES", "ERICK", "GERRY", "STUFE", "CLAYS", "FACHE", "GELYK", "SALTY", "TEEMS", "AISNE", "RANCE", + "KAUAS", "VYING", "DOUAI", "CASUS", "CORES", "GENIO", "JEUDI", "RADIC", "RUNES", "CLARY", "BRISA", "MUCUS", + "NATUR", "COGIA", "MOXON", "LUTES", "MARKE", "POOTY", "SLAAN", "HAVAS", "MALOS", "SOWER", "DOMUS", "SEGNO", + "DONDE", "ETHIE", "CONCH", "MERES", "CHING", "KITTS", "MARCY", "REPAY", "COMBE", "PAOLI", "ARDEA", "ENCYC", + "KONGE", "MCCOY", "VESEY", "BURRO", "FURIE", "GEZAG", "JIFFY", "QUIPS", "COLLO", "MAGIE", "MYNEN", "COWES", + "OMEGA", "SKEAT", "BELGE", "PAEAN", "EMLYN", "CALDO", "KINDA", "AFTRE", "CITTA", "FETUS", "ONHAN", "TULTA", + "WINCH", "ADIGE", "EMMET", "EUREM", "NATTY", "TALMA", "WILNA", "FORBI", "BARTH", "MONRO", "ZADOK", "BARBA", + "BARCO", "GAZON", "VAART", "ANNAS", "MAHDI", "WERTH", "RAHAA", "SMAAK", "WIRKT", "YEELD", "YERBA", "FINGO", + "HEBEN", "VEINE", "LLEGA", "USKON", "SEMEN", "STOEL", "ZZZZZ", "HOWDY", "REGAN", "CLACK", "SELVE", "GLEGG", + "RAUCH", "DESTA", "FUMER", "KOMST", "BLOCS", "TRIAD", "VENIT", "BALIN", "UPANI", "VISST", "LOMAX", "RYMER", + "SOTTE", "ZOETE", "FOKER", "MILDE", "TATEN", "AKING", "DAVOR", "NITRE", "ERROL", "CLERC", "CONTA", "DEBAR", + "KREET", "MODEM", "CULCH", "FLERE", "OLIKO", "PREYS", "SLOPS", "WHIPT", "ADDIE", "BEULE", "BIJOU", "ECHAR", + "FUOCO", "HATHE", "QUAIS", "SOLDE", "FUZZY", "PISTE", "SAINE", "WEREN", "ZEBRA", "MASKE", "ANIME", "SAYST", + "LVIII", "MOIRA", "RAINA", "WOTAN", "ARMIS", "ASIAT", "BAGUE", "PRESE", "VOLSE", "NUOVA", "PARIA", "ALTOS", + "ENSAM", "TROLL", "ABBIE", "WILTS", "CESTE", "CUYOS", "VIGNE", "BEEBE", "GLIED", "RASHI", "DEVRA", "HOERE", + "LEZER", "NAHEN", "BERUF", "HIGGS", "LEEKS", "POLKA", "UMACR", "DOOMS", "FASTE", "GLEBE", "NOSED", "LOWTH", + "DOORE", "HIRES", "SKULK", "BILDE", "CHEBE", "WAITE", "ACEST", "DAMAS", "HOJAS", "MOMMA", "PERLE", "BALAK", + "LADIE", "MARCK", "YAQUI", "YEATS", "RECAL", "SINAE", "RUDRA", "AMENA", "CUJUS", "SIGNA", "SOLUS", "KISSE", + "NULLO", "PALJO", "SIEHE", "TOLDE", "VISCH", "HOVEY", "ROOME", "ETZEL", "SHURE", "FIFES", "BALTY", "FRITH", + "MICHU", "MOCHA", "TURKE", "FATTI", "GULES", "GALLO", "HANNE", "IZAAK", "MINTO", "ROYCE", "SARUM", "DEBIT", + "TENNE", "MANDI", "WELNU", "GOLLY", "JEUGD", "KELLO", "FULKE", "GUEUX", "VALSE", "WOEDE", "BORGO", "GALLE", + "HEMAN", "LODER", "SHANE", "FUGUE", "PESER", "CYRUS", "HOVED", "MACES", "NADER", "BUCKY", "HOFER", "KARIN", + "MELUN", "WAITZ", "ACTIF", "THEWS", "BOSIO", "MARYS", "CEOUS", "WOHNT", "OISIN", "ARTES", "ILLAM", "MASTE", + "LAMAR", "MULTO", "SABIA", "SAGAS", "SCANS", "VITRE", "ZUMAL", "KRIPA", "MORUS", "PINTO", "COCHE", "DEDIT", + "TALAT", "LEBEL", "SLOOT", "WUCHS", "BABET", "BEUTE", "FESCH", "NUBIA", "DELVE", "DEVAS", "NOHOW", "BAKOM", + "CIOUS", "MANDA", "OVALS", "PURES", "DYAKS", "ECTOR", "HERBE", "HUVUD", "LAIRS", "LIUES", "PLENA", "DOLED", + "MINST", "VELEN", "COROT", "OLNEY", "TELLO", "CARLE", "GNAWS", "HAAND", "ISSUS", "VINCY", "ARAGO", "BORAX", + "CENTO", "NAGOT", "SAUVA", "SOUSE", "VOUTE", "GRECE", "HEYST", "MILNE", "NANDA", "PRETS", "SLIPT", "STATU", + "WEICH", "ACTUS", "LOTYS", "HOORT", "PERES", "SOGGY", "BUSCH", "PUSEY", "MENEN", "SACAR", "GESTO", "TOTUM", + "AUBRY", "BEARN", "KAUAI", "BRAHM", "LAING", "BODIE", "LACKE", "OVARY", "SAYES", "SNAGS", "THATS", "ALPEN", + "MAYNE", "SELAH", "AUGER", "BETEN", "CORRE", "RUNNE", "TRANK", "TYPED", "MULEY", "SPOTT", "IGLOO", "KEELS", + "CASSY", "FAGON", "ACCES", "CXIUJ", "INTRA", "PITTY", "AHURA", "BAMBI", "BAYNE", "HIERO", "MAGOG", "MOONE", + "TODAS", "WENNA", "CAPAZ", "FONDE", "NAGON", "SLAGS", "RILLA", "GIVET", "POMME", "ANNAL", "BARTY", "CURIA", + "LEGGE", "PERSE", "SAALE", "THORA", "EDELE", "PERDS", "POULE", "BOGAN", "KAABA", "EGALE", "VEDEN", "PINKY", + "GERME", "MOULT", "VARIE", "DEWAN", "TEKLA", "FUDGE", "OPINE", "OSIER", "LAUFE", "LERAT", "PINUS", "THULE", + "EUERN", "PERRO", "SENDE", "WRENN", "DATUM", "AILSA", "GHITA", "WACHE", "ATOLL", "PELTS", "PLOWS", "POCAS", + "SHOON", "BEBER", "KATSO", "SOLEN", "HORRY", "PLASE", "EDIFY", "LADER", "TENUS", "JIMSY", "MAIJA", "SOLIS", + "AIMEE", "KUOLI", "ORTER", "OLAVI", "ORMUZ", "COSEY", "JAMAS", "PUOLI", "HEDDA", "TAMPA", "GRABS", "GRATA", + "LOUSY", "TRATO", "UDDER", "BEZUG", "BINET", "AMBLE", "HELMS", "LEVAI", "PAPPI", "COPIA", "SIRVE", "LIGUE", + "ORMUS", "GOUTS", "SINKT", "VRIJE", "LOTTA", "GENOT", "GRUPO", "MOEGE", "SLILY", "ARIES", "FRIST", "KYNGE", + "LLEGO", "MESSO", "NUEVE", "ALAMO", "OJEDA", "ROCCA", "THIER", "ALIOS", "BIDES", "DIEDE", "RUHEN", "TESTE", + "OZIAS", "QATAR", "PILES", "ALMAS", "BARBS", "BODDE", "BRIGS", "GWYNE", "SUELE", "GEOFF", "OAKUM", "ROCAS", + "GESTA", "HUBER", "VILNA", "CLAMP", "KUKIN", "NUQUE", "PELOS", "VIDEO", "WEKEN", "CRECY", "GRABE", "WESEL", + "ABORD", "AIMES", "HOHEM", "TULIN", "AUTUN", "BASHA", "JADIN", "CORAM", "LOOKT", "NEARS", "CLYST", "INIGO", + "NEPOS", "THERM", "FLUES", "FOCAL", "MUETS", "NOYER", "OCKSA", "SHOLD", "FRITS", "HITZE", "LAMAS", "MAHAL", + "MOMUS", "POWYS", "MAGST", "MENNE", "GOLPE", "WAFTS", "DORFE", "IRVIN", "RYDAL", "WYMAN", "CAVED", "CUEUR", + "FLIER", "SIGUE", "STRAF", "TRAJE", "DIRCK", "KANAG", "WESER", "WHYTE", "NKARA", "PECKS", "SAFER", "TRONO", + "ASURA", "MENTZ", "SASHA", "APPAL", "ETAGE", "LASTE", "USETH", "VOIMA", "VORAN", "LEMAN", "SHOOP", "AGITE", + "DABAN", "OMAAN", "BEATS", "TUBBS", "COLPA", "MATAR", "JORGE", "MCGEE", "AVOWS", "HUSET", "JOASH", "KARTE", + "AVISO", "MEATE", "MKHYA", "MUCHE", "ASCOT", "GALLA", "ISORA", "ROMER", "EPOUX", "FALSO", "BALBI", "SUNDA", + "LECON", "SPIJT", "SPUNK", "TRAMS", "VARAS", "LOTTE", "MAZDA", "MOXOS", "AMADO", "BEURT", "BOOST", "CATER", + "NEBER", "NEGRA", "BREDE", "MYRON", "ARTIG", "CIEGO", "FILER", "GAYON", "HABLO", "HINDS", "KUULU", "LIGGA", + "MARDI", "PRIVE", "VLOOG", "CRABB", "SOANE", "DALEN", "DIGNA", "IDLED", "INNIG", "LATHS", "SANZA", "TRAGE", + "WELKS", "BERKS", "BRECK", "ELIAB", "HAUCH", "HERAT", "AVARE", "HAFVA", "PRIJS", "GROIN", "NUIRE", "OBESE", + "OLTRE", "RIJKE", "WEEPE", "SNELL", "SYRIE", "CONTO", "FURIA", "ILLOS", "MEALY", "QUEDO", "ROSIN", "SNOBS", + "JUNIA", "LUKIN", "MESTY", "HELIX", "WALKE", "BUSSI", "TIBBY", "KURTZ", "DOTES", "FAUVE", "GAINE", "LEEFT", + "TAELS", "ACHAB", "EMIRS", "JEPPE", "KNIPP", "PILAR", "REUSS", "WEGES", "DROOM", "EYRIE", "JAGEN", "SLUMP", + "MAREN", "MODUM", "TREIN", "BEBEL", "KRUPP", "LAURI", "LYNNE", "VINET", "FANNO", "HADNA", "MENDS", "TIOUS", + "AMREI", "ARNOT", "FLOSS", "HYLDA", "DEBEN", "OUBLI", "TYLKO", "AVUTO", "BOATE", "LUKEA", "TRATA", "ECLAC", + "WIESE", "ZWEIG", "LINKE", "PRESA", "VIRTU", "BINES", "HERSE", "LUPUS", "MATTA", "FIVES", "INSTR", "KLEED", + "POLVO", "GWILT", "UNHCR", "DOSED", "JUROR", "RUNGS", "SILKE", "ISHAM", "KURDS", "SILLA", "SPINK", "ACTOS", + "DORST", "RIMES", "VIISI", "ZUCHT", "ATTEN", "BUCHE", "BYERS", "CISSY", "FIONN", "KATRI", "ASSIM", "HUIUS", + "MENTI", "PARSE", "VIDER", "KAYAN", "KRANZ", "NIELS", "FOIRE", "KIOSK", "OBERN", "RECTE", "LIPPO", "CABBY", + "IMBUE", "DOLPH", "RADHA", "FELIZ", "HOMEM", "TONTY", "YAKOV", "AEGIS", "PEDIR", "ROUTS", "UNICO", "VELUT", + "CROLL", "ALZOO", "FALSA", "KLARE", "PONEN", "AMEER", "BOSSU", "ORAGE", "POSEE", "PUTEA", "SELIG", "CHAKA", + "DISKO", "FALCO", "TEXEL", "ASCHE", "DONAU", "DUFFY", "LERMA", "QUINN", "SISSY", "TORCY", "ALTAS", "BEEST", + "BOUGE", "MONEN", "REGNA", "SNACK", "LADKO", "PIPPA", "WEALD", "ILLAN", "KALLA", "KOTIA", "BARCA", "CENIS", + "KATZE", "KORAH", "PRADO", "SANNA", "BURNE", "IDYLL", "SITTA", "VIRUM", "BEATA", "GUION", "RASSI", "SHAND", + "GAGES", "PUHUU", "RAHAT", "CERRO", "FARAO", "PAZZI", "RIZZO", "SCENA", "KALTE", "PATES", "TEUER", "CASOS", + "COLPO", "FLUME", "MAIOR", "SABIO", "VULGO", "ARIUS", "FLAMM", "MARET", "COTON", "DUCTS", "FORGO", "GRITO", + "POSSO", "ZULKS", "ETAIN", "POSEN", "TAMIL", "ZUEGE", "BIPED", "BLURT", "THEOS", "FLUKE", "MUNCH", "YOURE", + "BANTU", "BAREE", "BEGUM", "CHILO", "DINKS", "FADEN", "MAHAN", "PEAUX", "REGNI", "EINAR", "ISLAS", "RAYNE", + "TAAVI", "ARABE", "SCEAU", "DUCIE", "LORDY", "CHOUX", "GAMMA", "HAIES", "HORTE", "REMUE", "SCULL", "ANTAR", + "ENGLE", "NOVUM", "TONYA", "BAUEN", "IPSIS", "LOIKE", "LOUES", "SINGW", "STRID", "VILER", "SHAWN", "AURAI", + "GUISA", "KUULE", "SALEN", "WONEN", "CARBO", "MELKY", "DUMPY", "SPICK", "SPORE", "BARAK", "ONGAR", "VOLKS", + "DIETS", "LAVAS", "MUUAN", "ANMUT", "BEMIS", "BOLES", "GOOCH", "KAFIR", "LAGOS", "LUNDY", "BESOM", "JURIS", + "MESAS", "TYMES", "WORAN", "FOUAN", "HOOKE", "PUGET", "SIGEL", "DEARY", "LUMEN", "MEURE", "SERUM", "GREIS", + "PFEIL", "BONIS", "EIKAE", "ZWANG", "ASIEN", "CREAN", "LENIN", "CYTEE", "HALVT", "LOCIS", "NENNE", "SPINY", + "TOXIC", "FERDY", "MAROT", "WOVEN", "AEONS", "DEBIA", "DRAPE", "REAMS", "RISER", "RONDS", "TAGET", "AMASA", + "JANIE", "NIZZA", "ICILY", "TENTA", "FUNDY", "FANES", "GOERA", "VALDE", "PINTA", "PITTS", "SEIDE", "BILGE", + "DOMUM", "GEHST", "GNASH", "HETTE", "DORAN", "FABRY", "HEIGH", "SARTO", "SCHAU", "DIENS", "DOGME", "FAUNS", + "FROMM", "TAPES", "DREUX", "FAGAN", "HELLA", "SKYLD", "ARMUT", "JUBAL", "LUISA", "NOYON", "PIOTR", "PYLOS", + "BRAGT", "MEENT", "VAREN", "ILION", "NARES", "STUBB", "ASTER", "DANNO", "MOPED", "ONNEA", "PALAA", "WORDE", + "DONAT", "MACAN", "POVEY", "BODES", "DRIPS", "EGGED", "FEELE", "KLUGE", "MUROS", "ORBIS", "TABBY", "WICKS", + "JUMNA", "SYLVI", "SUNNI", "SUYOS", "JINGO", "MONAT", "PIPPO", "APING", "CHACE", "FLUFF", "POLIS", "AUBER", + "RAMEE", "SEOUL", "LYDER", "RICOS", "UNSAY", "ELSON", "SNOOP", "ANNIS", "FANGE", "FIJNE", "HUMUS", "LENTS", + "PUREE", "STOUP", "YERES", "JONDO", "LIGNY", "BLOKE", "PROPE", "ELLIE", "ERNIE", "OAKES", "DURES", "NEEDE", + "PLIER", "GONDI", "HAMET", "DUNKT", "FOAMS", "LOGES", "SERES", "STELT", "ARRAH", "GRATZ", "VEDIA", "DICIT", + "HARTS", "REINA", "SIELU", "KAPPA", "LIPPE", "OLINE", "PROUT", "INGOT", "LENTO", "NEGLI", "PARIE", "VEZES", + "RUSTY", "SUKEY", "SURRY", "ANIGH", "CIMES", "CLOUS", "FARSI", "GRAAG", "TINHA", "ARLEE", "DONGO", "HAGAN", + "LXIII", "RAMAH", "CLEPT", "MULTI", "NIAIS", "VAINS", "LEONI", "LECHE", "RAINE", "HYDER", "LARES", "HAVET", + "MINHA", "BATTY", "ERICH", "KUGEL", "BRAGS", "CRIBS", "FAINE", "IHANA", "KAHTA", "SAATU", "SAYLE", "SEINS", + "SLATY", "SQUIB", "VIZIR", "YELPS", "ENDEN", "HINDE", "CORTA", "CRICK", "FLERA", "SVARA", "HIPPO", "DRONG", + "FETED", "LEHRT", "TULIT", "ADERN", "DIDON", "QUILP", "TREAS", "CARRE", "MALUM", "SULKS", "LEARY", "LYDDY", + "PETYA", "SALIM", "TOPSY", "BOWEL", "CRESS", "DESTE", "ENROL", "FLAKY", "BARBY", "BEERS", "SOMAL", "TIMMY", + "YEGOR", "JONNE", "POILS", "SOLDI", "ZARTE", "KROOL", "MARKO", "DERER", "TIETO", "VOSSA", "BULOW", "ALENE", + "DIMES", "DOIVE", "HOPEN", "MOLDY", "TOIVO", "AVICE", "HERMY", "BAINS", "DAGUE", "FLINK", "FRASE", "HANGT", + "LEGUA", "TEMPI", "VAITI", "CUTTS", "VANEL", "ALTHO", "EINIG", "FIORD", "HARME", "MOWER", "PAGAR", "ROEPT", + "FULDA", "MOSUL", "VIRGO", "DINAR", "KATSE", "KURJA", "LINEA", "NEARE", "RUINA", "TUVAN", "ASSUR", "LANDA", + "EVERE", "LIVRA", "PENSO", "TILTS", "VSQUE", "MILER", "NERVA", "SHINT", "STICH", "BEDST", "BLAUW", "COLDE", + "GLAUB", "MOATS", "PRISA", "SIGMA", "MAHEU", "CHENE", "MUITO", "POEME", "PUDGY", "RILED", "SILLS", "VERTA", + "AMRAM", "BONAR", "SVAVA", "CURSO", "FIDEM", "CAPEL", "JAINS", "PRAED", "GAVEL", "KIVEN", "PEDIS", "WAARD", + "FIUME", "GLYNN", "PIGOT", "BICHE", "DOOTY", "LADYE", "VENGE", "JUKES", "PETRO", "BONIE", "FOWLE", "INURE", + "KONDE", "POKES", "SIMBA", "LITRE", "PAREA", "HOREB", "TIMUR", "HEATE", "NAGRA", "NOYSE", "PAURA", "PEECE", + "AGNUS", "HYNDS", "MOSER", "GAWKY", "HYDRO", "ISTUA", "JACHT", "SCUDO", "UUREN", "MOSCO", "BLAUE", "LIVET", + "SECHE", "MAYDE", "PIEDE", "RUFFS", "KAPOR", "MARTE", "QUADE", "GLATT", "INDIO", "OMNEM", "SALIO", "SADOC", + "VOBAN", "ZICCI", "BRAES", "COELO", "CORTO", "IRKED", "SABEN", "DWYER", "JOUIT", "OFFRA", "SCIRE", "SELVA", + "TULET", "FLAGG", "ETUDE", "GOUGE", "HUJUS", "OLHOS", "ASOKA", "HURRA", "KEVIN", "NELLO", "FICHE", "JETAI", + "LECHO", "LIENE", "MUMPS", "ORICE", "VIZOR", "HARAN", "LUCRE", "MAGEN", "MOORD", "WEEKE", "GOREE", "KIMON", + "AVASI", "FINNY", "POSTA", "REGTE", "REMET", "SILEX", "TELCO", "TERVE", "SOHNE", "TOBIN", "GEZET", "GRIDO", + "IVIED", "ODDER", "FLACK", "JESSY", "DIECI", "EPAIS", "THRUE", "DICES", "GREYS", "MORTI", "PERCE", "BASSA", + "FASTI", "MCTEE", "SOUSA", "VIGNY", "WOLKE", "DUROS", "GRAVI", "PROVA", "SWILL", "VEELE", "WANES", "BASAN", + "CUFFY", "XVIIE", "HUZZA", "VIDAS", "ALLIS", "BEUST", "BRESL", "SWASH", "PEPLE", "GOWDY", "HADJI", "NAHUM", + "THESS", "FROND", "HAINT", "AMOOR", "LARNE", "NADAB", "NEILL", "SAGET", "CREDE", "HUMPS", "MOSSE", "MUTED", + "OLLEN", "VERBE", "ALVIN", "CLERY", "JOVIS", "SEYNS", "SONEY", "SYLPH", "VITRY", "WIGAN", "HILTS", "LINTU", + "SINDS", "UMBRA", "VISER", "CASCA", "MERAN", "MONNA", "PINEY", "BRAUE", "LAPIN", "MUEDE", "POWRE", "STAAR", + "BONEY", "MAGUA", "MAULE", "COYLY", "GUMMY", "HAULS", "RUEGO", "ARCHY", "BAUME", "BEVAN", "DEGAS", "KAISA", + "LESTE", "MOULE", "REDLY", "VETUS", "AFRIC", "BINGO", "LXVII", "OLLIE", "SURYA", "ABRIR", "DEDOS", "EXCES", + "LAITA", "MINTS", "POTER", "VARDA", "BACHE", "SALIC", "HURLA", "MEWED", "NECKE", "NEITO", "PARKA", "VAPAA", + "WAPEN", "BIJAH", "GUETE", "ZANTE", "SCUDI", "IDUNA", "KONTO", "LUXOR", "DIEST", "REGEM", "SPANK", "TIEDE", + "URSEL", "GENTI", "NAVES", "ONDAS", "PRZED", "FOLCO", "NABAL", "NOSEY", "PAULI", "TOKIO", "ATING", "DICTE", + "GUDAR", "RHEUM", "CUTTY", "OSMIA", "MAGER", "SANOT", "TIBIA", "TRAER", "LIANE", "MATHO", "MUNDY", "PATTI", + "REESE", "SEELY", "HARPY", "HUTTE", "LEGES", "LIETO", "LOBEN", "RAVIE", "SOAPY", "SOUGH", "VUOLE", "BOGES", + "LUTHA", "MAPPO", "BONED", "FLYER", "VISTE", "ENDOR", "FAYAL", "HALSE", "KEANE", "SIHON", "ANCHO", "ATTAR", + "AVETE", "KROPP", "MIMES", "OPALS", "PLUMA", "UNLIT", "AKLIS", "ELSLI", "HERDE", "SKALE", "ANDAN", "JEANS", + "PREVU", "BAIAE", "GOETZ", "HYLAS", "JANIN", "CHYLE", "DAUBS", "HANDE", "KNAAP", "NATUS", "ZONEN", "MONTI", + "SUDRA", "LIKER", "LYCKA", "UKASE", "VIMOS", "VOYCE", "AMIEL", "PANGO", "RANDE", "SHIVA", "SPION", "TIECK", + "DECEM", "MOLAR", "WENTE", "IMLAC", "PONTO", "WASTL", "BORTA", "FANDT", "JOKIN", "LYFTE", "RESET", "RIENS", + "SEMEL", "TWIXT", "WISST", "BEORN", "CEUTA", "NABAB", "EVIGT", "LEGNO", "LERNT", "LILAS", "MYLEN", "PAPPA", + "PREUX", "SPOOK", "FOGER", "TILDA", "CIVET", "ERRER", "GEARS", "JUGEZ", "MAGNI", "SEZEE", "SPISE", "AYEAR", + "BYEAR", "DUNNE", "JEZUS", "LUCIO", "MATEY", "SKALA", "TIBUR", "BAADE", "HOOTS", "LOONS", "TUNGA", "BIORN", + "DAGLI", "OUTRO", "GAETA", "JANEY", "MENIN", "SITKA", "FOIST", "JEHER", "LAVED", "LINUS", "PAWLE", "VICKY", + "RIDES", "TREED", "ARIST", "AWEEL", "CELIE", "BURRS", "CLIPS", "DICEA", "MOLTA", "NAVVY", "OLIKA", "RAYOS", + "SEYDE", "VANES", "ALLYN", "GEBOT", "GROSE", "BAHAY", "DUREE", "FAZER", "GOADS", "GULPH", "HAVER", "NIGRA", + "NOCTE", "PARUM", "SAATA", "SKIMS", "WAART", "WRAAK", "MUZIO", "OLSON", "RABAT", "REGIN", "RUNIC", "MANDE", + "RAUHA", "AYMER", "NAURU", "DREEF", "GAAET", "IRREN", "LESKI", "MECUM", "SAVON", "ARIAS", "HANKS", "ROZEL", + "ZEUGE", "DEUGD", "ETHIC", "GOODE", "TROVA", "AETNA", "BENJY", "ORMES", "TUBAL", "BRIMS", "BULKS", "FILCH", + "MONAD", "OOZES", "REGIA", "SARVE", "VENTI", "VOERT", "ARION", "COOTE", "SNECK", "TESSA", "BASON", "GIENG", + "INTIL", "NABIJ", "PRZEZ", "RAMAS", "SITUE", "CATHY", "GREER", "LINUX", "TOBIT", "DIDNT", "DROGO", "NEHMT", + "TIGES", "EDRIC", "TROUP", "AYUDA", "BEVAT", "DAPAT", "OVATE", "BALCH", "COGAN", "AIRTH", "GOWNE", "JUEGO", + "REBUT", "SALSA", "WIJDE", "JUBEL", "NEDDY", "RONNY", "FLAIR", "PASEO", "BRONX", "EWALD", "STURZ", "WODEN", + "KUMMA", "LETTE", "VANTE", "CAERE", "MISHA", "AMBAS", "FAAET", "RUNDE", "EYLAU", "MENGS", "FEEST", "LOWED", + "VENDE", "CUMAE", "GASSE", "HILMA", "IZUMO", "PISAN", "SOFIE", "CARRO", "CONIC", "CRIMP", "MUSKY", "SAYTH", + "STANK", "BOWES", "FAHNE", "THOME", "VIION", "ASTUU", "DICKE", "FASST", "PAROI", "LAWRY", "PRATO", "TENCH", + "CERTI", "ISCHT", "NOONE", "SENTO", "TAULD", "VLOOT", "VOIRE", "VOLTS", "BELUS", "CAVOR", "JORIS", "SEPPI", + "ERGAB", "BABIE", "CATON", "ABAHT", "BEDRE", "OPPIA", "POUCO", "SCHEU", "SEALE", "SUMUS", "VASTY", "FRIDA", + "LIPPI", "LOUVE", "BANCO", "BUONO", "CLOMB", "EDELN", "HOULD", "PELAS", "BODIN", "FAUGH", "JUDAR", "SEITZ", + "TOTTY", "WERTE", "STANE", "WINNE", "COCOS", "RIATT", "WERFF", "FIERA", "HINAN", "MATKA", "OATEN", "PLACA", + "CONOR", "DARRY", "HERTZ", "LAMON", "BEYNG", "CONTR", "INEPT", "SAKEN", "SIAMO", "STEDE", "MORNA", "AGERE", + "LANCA", "OSASI", "SADAN", "WOONT", "EVERS", "TROVE", "FACIL", "GRENE", "KUUSI", "PAPAS", "RECUS", "UUDET", + "VENTA", "ALOIS", "GILLY", "GONDY", "NAIRN", "BAILE", "CLAPT", "CLUCK", "CULTO", "EETEN", "GAPES", "NIVEL", + "TREKT", "WARLD", "ABIES", "AUTOS", "BARKE", "BRDER", "EASES", "GIRDS", "HAILL", "HIJAS", "RETRO", "SOMMA", + "STEEG", "BAYER", "CAMUS", "DEVIN", "HAMID", "HUANG", "IMOLA", "DICED", "HOCHA", "KASTA", "MULGA", "MYNDE", + "PIENO", "YAONG", "FURST", "GATTE", "SPASS", "SVEIN", "THEMA", "ANSAH", "CHAUX", "HEVIG", "PRIES", "ZOMER", + "BAGGS", "DOURO", "HIPPY", "SAONE", "VAVEL", "WETTE", "ALNAR", "EUILL", "PEATY", "VENIS", "CRITO", "LYNDA", + "OGRAM", "TEMPE", "ABOON", "FACEA", "FINEM", "KNOUT", "LLAMO", "LOAMY", "MENAR", "TISSU", "VERAS", "VIRES", + "COLBY", "JAINA", "TEUSE", "TYNAN", "YANKS", "BEZAT", "KLOOF", "SEAMY", "SIDDE", "STEIL", "TYYNI", "CARIA", + "HENTY", "LOWRY", "SOUZA", "ALIUS", "FEETE", "GRANO", "HALKI", "KELPO", "PESKY", "PLEBS", "POVIS", "TOPER", + "VLOER", "VOULD", "EDMEE", "GAZEN", "GITON", "HOLLO", "KAROL", "REIZE", "ROACH", "ULLOA", "VERBY", "CLEWS", + "EVANG", "FERAL", "GLICH", "THROE", "ZIJNS", "EURIE", "FERIA", "JEEMS", "PAAVO", "SAUTI", "DOODE", "PIENA", + "RADII", "UNICA", "CROLY", "HADAD", "KEYES", "NIKKY", "SANDE", "SEGEL", "SOFYA", "TEXAR", "GAZER", "LEDIG", + "NOTAR", "OBEIR", "VIMES", "GENUA", "KANAL", "LIMON", "RAHAB", "SUOMI", "VEJEN", "BATEN", "FJORD", "LEVES", + "PHARE", "RECTO", "AAGOT", "GIZUR", "NADJA", "RUGGE", "SNEYD", "DECKT", "FAILE", "GAOLS", "MELER", "PACTO", + "PAHAN", "CALIF", "MENON", "SEPOY", "WADDY", "ZELLE", "AENDA", "ASTUA", "KROON", "LETRA", "MINIT", "NEEWA", + "PATNA", "URIEL", "HITTE", "HOMOJ", "JOUET", "KOSKI", "LYSTE", "MINAS", "RUHTE", "SETZE", ] alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] -def most_used_letters(): +def most_used_letters(words=legal_list): ''' Outputs how many times each letter is used in the words array. ''' @@ -337,7 +963,7 @@ def most_used_letters(): print(f"{k.upper()} | {dicto[k]}") -def list_of_valid_words(letters): +def list_of_valid_words(letters, words=legal_list): ''' Outputs the array of valid words that can be made with the combination of letters. ''' @@ -356,33 +982,67 @@ def list_of_valid_words(letters): return legal_words -def print_valid_words(letters): +def rearrange_words_by_uniqueness(words): + unique = [word for word in words if len(word) == len(set(word))] + duplicates = [word for word in words if len(word) != len(set(word))] + return unique + duplicates, len(unique) + +def capitalize_all_and_remove_duplicates(arr): + for i,word in enumerate(arr): + arr[i] = word.upper() + result = [] + for word in arr: + if word not in result: + result.append(word) + return arr + + +def print_valid_words(letters=alphabet): ''' Prints the array of valid words that the wordle_face.c can use ''' - legal_words = list_of_valid_words(letters) - for i,word in enumerate(legal_words): - legal_words[i] = word.upper().replace("D","d") + items_per_row = 9 + legal_words = list_of_valid_words(letters, legal_list) + legal_words = capitalize_all_and_remove_duplicates(legal_words) random.shuffle(legal_words) # Just in case the watch's random function is too pseudo, better to shuffle th elist so it's less likely to always have the same starting letter - + legal_words, num_uniq = rearrange_words_by_uniqueness(legal_words) + print("static const char _valid_letters[] = {", end='') for letter in letters[:-1]: print(f"'{letter}', ", end='') print(f"'{letters[-1]}'" + "};") print("") - + print("// From: https://gist.github.com/shmookey/b28e342e1b1756c4700f42f17102c2ff") print(f"// Number of words found: {len(legal_words)}") - items_per_row = 9 i = 0 print("static const char _legal_words[][WORDLE_LENGTH + 1] = {") while i < len(legal_words): print(" ", end='') - for j in range(min(items_per_row, len(legal_words)-i)): + for _ in range(min(items_per_row, len(legal_words)-i)): print(f'"{legal_words[i]}", ', end='') i+=1 print('') + print("};\n") + + expanded_words = list_of_valid_words(letters, expanded_list) + expanded_words = [word for word in expanded_words if word not in legal_list] + expanded_words = capitalize_all_and_remove_duplicates(expanded_words) + + print("// These are words that'll never be used, but still need to be in the dictionary for guesses.") + print("// Top 100K most common words from Wiktionary https://gist.github.com/h3xx/1976236") + print(f"// Number of words found: {len(expanded_words)}") + i = 0 + print("static const char _expanded_words[][WORDLE_LENGTH + 1] = {") + while i < len(expanded_words): + print(" ", end='') + for j in range(min(items_per_row, len(expanded_words)-i)): + print(f'"{expanded_words[i]}", ', end='') + i+=1 + print('') print("};") + + print(f"\nstatic const uint16_t _num_unique_words = {num_uniq}; // The _legal_words array begins with this many words where each letter is different.") def get_sec_val_and_units(seconds): @@ -449,6 +1109,6 @@ def txt_of_all_letter_combos(num_letters_in_set): if __name__ == "__main__": - most_used_letters() + #most_used_letters() print_valid_words(['A', 'C', 'E', 'I', 'L', 'N', 'O', 'P', 'R', 'S']) #txt_of_all_letter_combos(10) \ No newline at end of file From 6dd46b46b1037cbfcd64b8594acb39dc5eeb85c0 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sat, 17 Aug 2024 06:22:24 -0400 Subject: [PATCH 059/161] Able to turn the expanded dict on and off with a variable --- movement/watch_faces/complication/wordle_face.c | 8 ++++++++ movement/watch_faces/complication/wordle_face.h | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 9b1db1b..2b991e3 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -67,11 +67,14 @@ static const char _legal_words[][WORDLE_LENGTH + 1] = { "SCOOP", "EASEL", "LOONS", "CONIC", "SPANS", "SPIES", "PRIOR", "SALSA", "SELLS", "PILLS", "RISES", "RARES", "SNEER", "SOILS", "ARENA", "CASES", "CANAL", "SAILS", "LASSO", "COCOA", "ERROR", "CALLS", +#if (USE_EXPANDED_DICT != 1) }; // These are words that'll never be used, but still need to be in the dictionary for guesses. // Top 100K most common words from Wiktionary https://gist.github.com/h3xx/1976236 // Number of words found: 469 static const char _expanded_words[][WORDLE_LENGTH + 1] = { +#endif +#if (USE_EXPANDED_DICT != 0) "PARIS", "APRIL", "SPAIN", "EINEN", "ASCII", "EINER", "SEINE", "AINSI", "ALICE", "ALLES", "ALORS", "EINES", "ALLER", "PEINE", "PARCE", "CELLE", "CLARA", "ELLES", "ELLEN", "OLISI", "ALLEN", "ISAAC", "APRES", "CROIS", "SANOI", "PASSE", "ELSIE", @@ -125,8 +128,13 @@ static const char _expanded_words[][WORDLE_LENGTH + 1] = { "NOONE", "SEPPI", "OPPIA", "SEALE", "LIPPI", "PELAS", "COCOS", "PLACA", "CONOR", "LANCA", "OSASI", "ALOIS", "NAIRN", "PIENO", "SPASS", "SAONE", "ALNAR", "CARIA", "PIENA", +#endif }; +#if (USE_EXPANDED_DICT == 1) +static const char _expanded_words[][WORDLE_LENGTH + 1] = {}; +#endif + static const uint16_t _num_unique_words = 155; // The _legal_words array begins with this many words where each letter is different. static const uint16_t _num_words = (sizeof(_legal_words) / sizeof(_legal_words[0])); static const uint16_t _num_expanded_words = (sizeof(_expanded_words) / sizeof(_expanded_words[0])); diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index b137512..1860984 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -60,6 +60,13 @@ #define WORDLE_MAX_ATTEMPTS 6 #define USE_DAILY_STREAK false +/* USE_EXPANDED_DICT + * 0 = don't use it at all (saves 2.8KB of ROM) + * 1 = Include the expanded dict in answers + * 2 = Only include it in the dict for guessing, but it's never an answer +*/ +#define USE_EXPANDED_DICT 2 + typedef enum { WORDLE_LETTER_WRONG = 0, WORDLE_LETTER_WRONG_LOC, From 1675af64496535b76d5c4e7e46a3e1453159e353 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sat, 17 Aug 2024 09:49:48 -0400 Subject: [PATCH 060/161] bug fix on max score --- .../watch_faces/complication/wordle_face.c | 165 +++++++++--------- .../watch_faces/complication/wordle_face.h | 3 +- utils/wordle_face/wordle_list.py | 10 +- 3 files changed, 84 insertions(+), 94 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 2b991e3..c6f28bb 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -33,45 +33,45 @@ static const char _valid_letters[] = {'A', 'C', 'E', 'I', 'L', 'N', 'O', 'P', 'R', 'S'}; // From: https://gist.github.com/shmookey/b28e342e1b1756c4700f42f17102c2ff -// Number of words found: 283 +// Number of words found: 282 static const char _legal_words[][WORDLE_LENGTH + 1] = { - "PROSE", "SLOPE", "CLAPS", "CAIRN", "PLANE", "SPACE", "ARISE", "NEARS", "OPERA", - "EPICS", "LAIRS", "AISLE", "APRON", "SCRIP", "CARES", "PERIL", "PILES", "CLEAN", - "CLANS", "CANOE", "COALS", "RELIC", "CRANE", "SLICE", "RESIN", "NAILS", "LIARS", - "RISEN", "COILS", "RINSE", "PRIES", "PLEAS", "CAPON", "PARSE", "ROLES", "SPICE", - "CORAL", "LEARN", "SNIPE", "CAPER", "SIREN", "CORES", "CRAPE", "SPOIL", "EARNS", - "LANES", "PEALS", "RAINS", "ACRES", "SINCE", "LACES", "ROPES", "CLEAR", "PANIC", - "SCALE", "SANER", "CLOSE", "LINER", "CORNS", "SPINE", "SCARE", "ACORN", "PLANS", - "POSER", "SCORN", "AROSE", "LINES", "LIONS", "OCEAN", "SNAIL", "OSIER", "SCOPE", - "OPINE", "REINS", "COINS", "LOSER", "PANES", "RAISE", "RAILS", "ALIEN", "PLAIN", - "SNARE", "LOANS", "IRONS", "COPRA", "PLIES", "COPSE", "PEARS", "PALER", "SLAIN", - "LAPSE", "CONES", "ARSON", "POISE", "OPENS", "SPORE", "CAROL", "PACES", "LEAPS", - "ALONE", "ASPEN", "SNORE", "CRIES", "OPALS", "PENAL", "PAINS", "ENROL", "POLAR", - "SPEAR", "SCION", "POLES", "SCORE", "LEANS", "PEONS", "ROSIN", "REAPS", "SOLAR", - "EARLS", "PENIS", "PRICE", "REALS", "SCALP", "CLASP", "NOISE", "PANEL", "NICER", - "CRISP", "SPARE", "PEARL", "CORPS", "SALON", "RACES", "LOINS", "RIPEN", "PLACE", - "CRONE", "CANES", "LANCE", "LIENS", "ALOES", "PIANO", "PRONE", "PIERS", "SNARL", - "AEONS", "PINES", "SPIRE", "PAILS", "CAPES", "CLIPS", "PALES", "CROPS", "PAIRS", - "SCRAP", "PORES", "SALES", "COOLS", "SLOPS", "APACE", "CRIER", "ROLLS", "PIPES", - "SLIPS", "EASES", "PEACE", "CELLS", "POLLS", "POPPA", "SPREE", "RILLS", "SPILL", - "RIPER", "ONION", "ISLES", "CROSS", "PAPAS", "NOSES", "PAPAL", "SLEEP", "EERIE", - "SEALS", "PAPER", "COLOR", "SORES", "SPELL", "SNAPS", "SEERS", "SPARS", "PIPER", - "POSES", "LOOPS", "APPAL", "LOSES", "LINEN", "ROARS", "RARER", "NIECE", "LOCAL", - "NONCE", "CREEP", "SCARS", "SPOOR", "PEASE", "SIRES", "PRESS", "CLASS", "PAEAN", - "SPOOL", "LOOSE", "CRASS", "LILAC", "COLIC", "RACER", "CREPE", "NASAL", "SOLOS", - "ROSES", "COLON", "REPEL", "ASSES", "INANE", "SCENE", "SLAPS", "PEEPS", "LAPEL", - "PIECE", "LEPER", "REELS", "INNER", "PROPS", "SILLS", "LEASE", "SOLES", "CIRCA", - "CRESS", "SCANS", "SPOON", "REARS", "CACAO", "ERASE", "CANON", "SOARS", "SLOOP", - "AREAS", "SPINS", "OASIS", "OASES", "POPES", "ELOPE", "CEASE", "CINCO", "APPLE", - "NOOSE", "PEERS", "SENSE", "POOLS", "RISER", "PENCE", "POSSE", "ALIAS", "PESOS", - "SCOOP", "EASEL", "LOONS", "CONIC", "SPANS", "SPIES", "PRIOR", "SALSA", "SELLS", - "PILLS", "RISES", "RARES", "SNEER", "SOILS", "ARENA", "CASES", "CANAL", "SAILS", - "LASSO", "COCOA", "ERROR", "CALLS", + "CRISP", "SALSA", "PRESS", "LIONS", "SPIRE", "CAPES", "ROLLS", "LOOSE", "ALOES", + "COPSE", "ENROL", "SLOPE", "CAPER", "SCORE", "SLAPS", "PLEAS", "CANOE", "REAPS", + "PEASE", "SLEEP", "LEAPS", "CORAL", "PILLS", "LOCAL", "ARENA", "PROSE", "SALES", + "OPENS", "REPEL", "REALS", "COLIC", "APRON", "LOINS", "COINS", "LASSO", "SIREN", + "SCARS", "RISER", "CRIES", "CRESS", "POSES", "NEARS", "CAIRN", "PARSE", "SCENE", + "SCOOP", "SPINS", "CORNS", "NOSES", "CLEAR", "LANES", "LOSES", "PIERS", "SLAIN", + "ROPES", "ALIEN", "LINER", "PRIES", "PROPS", "CRANE", "SCARE", "PEONS", "POLLS", + "LINEN", "SLIPS", "CAROL", "PEEPS", "SPANS", "ARISE", "POLES", "SCRAP", "OASIS", + "PAPAS", "PAINS", "SPOOL", "RELIC", "ALONE", "SLOPS", "PIANO", "PERIL", "SPICE", + "SPIES", "SPORE", "CLEAN", "SOLOS", "CREEP", "NONCE", "POISE", "COALS", "LEASE", + "SEALS", "COILS", "PILES", "RARES", "APPAL", "OASES", "RINSE", "POPES", "CONIC", + "SLICE", "SPACE", "ACRES", "ACORN", "ROLES", "CASES", "RESIN", "CREPE", "SOILS", + "PANEL", "SNEER", "INANE", "SCANS", "APACE", "EASEL", "CORES", "SOLAR", "PALES", + "SCOPE", "SCRIP", "LOANS", "ASSES", "EARNS", "CANON", "PLAIN", "POPPA", "SPOIL", + "APPLE", "ROSIN", "PANIC", "RISES", "AISLE", "CAPON", "COLON", "CLANS", "IRONS", + "RISEN", "PAILS", "LEANS", "PRICE", "AREAS", "SPARE", "LEARN", "PANES", "PRIOR", + "CRAPE", "LINES", "LEPER", "SNAPS", "POOLS", "SIRES", "SNARE", "COCOA", "PALER", + "CLOSE", "CRIER", "SANER", "PEARL", "CIRCA", "PAEAN", "RAISE", "SELLS", "OPINE", + "CEASE", "CANES", "ONION", "REELS", "RIPER", "SPARS", "RIPEN", "EPICS", "PLIES", + "CELLS", "SCALP", "ELOPE", "CANAL", "ROARS", "EASES", "OPERA", "SLOOP", "RARER", + "LIENS", "CROPS", "LACES", "LAIRS", "AEONS", "SOLES", "SNIPE", "PIECE", "NOOSE", + "NICER", "PENAL", "SILLS", "LANCE", "LOOPS", "SNORE", "PACES", "PLACE", "SPILL", + "PAIRS", "ARSON", "LAPSE", "CLASS", "EERIE", "PEERS", "PLANS", "LOONS", "SPOON", + "POSER", "SEERS", "REARS", "ROSES", "INNER", "NASAL", "OCEAN", "OPALS", "ALIAS", + "RACES", "ERASE", "SPINE", "SAILS", "CACAO", "CLASP", "REINS", "PAPER", "PIPER", + "EARLS", "PINES", "POLAR", "SNARL", "SCALE", "SPEAR", "SCION", "CRONE", "PRONE", + "SCORN", "RACER", "LILAC", "AROSE", "ISLES", "LAPEL", "PLANE", "PEARS", "POSSE", + "SORES", "PENCE", "CLAPS", "PESOS", "PENIS", "CALLS", "ASPEN", "COOLS", "CRASS", + "OSIER", "CARES", "PAPAL", "SOARS", "RILLS", "ERROR", "NAILS", "COPRA", "LOSER", + "SPELL", "CONES", "PORES", "SNAIL", "CROSS", "NIECE", "COLOR", "SALON", "SINCE", + "CORPS", "LIARS", "PIPES", "RAINS", "SPREE", "CLIPS", "NOISE", "PEALS", "SPOOR", + "SENSE", "PEACE", "RAILS", #if (USE_EXPANDED_DICT != 1) }; // These are words that'll never be used, but still need to be in the dictionary for guesses. // Top 100K most common words from Wiktionary https://gist.github.com/h3xx/1976236 -// Number of words found: 469 +// Number of words found: 470 static const char _expanded_words[][WORDLE_LENGTH + 1] = { #endif #if (USE_EXPANDED_DICT != 0) @@ -86,48 +86,48 @@ static const char _expanded_words[][WORDLE_LENGTH + 1] = { "NELLA", "PAOLO", "SOLON", "REPAS", "NANCE", "PAINE", "SAISI", "ELISE", "CESAR", "CANNA", "SALLE", "SINON", "SINAI", "LOIRE", "PENSA", "LEILA", "REISE", "ELLAS", "POORE", "EARLE", "CERCA", "LEISE", "POOLE", "AILES", "SANOA", "LEONE", "LILLE", - "PROIE", "CARNE", "SPIEL", "CERES", "ENSIN", "ROLLO", "ARRAS", "SEIEN", "PRIER", - "ANNAN", "CALLE", "LIISA", "SALIR", "LESSE", "LESEN", "LIIAN", "NEERE", "ARIEL", - "PIENI", "PIERO", "ERANO", "ELENA", "SILLE", "NEALE", "SEENE", "ROLLE", "NELLE", - "SOLLE", "ESSER", "PASAR", "PREIS", "ASIAN", "SENCE", "ANSON", "SERRA", "CONAN", - "SERAS", "SIENA", "SOPRA", "RENEE", "ALINE", "CORSE", "ASSAI", "INSEL", "ROSIE", - "SONIA", "APPEL", "CRISE", "CIRCE", "LINIE", "RENAN", "CAIRE", "COLLA", "SANOO", - "EENER", "ANCOR", "NEPAL", "REINO", "LAINE", "SOONE", "ALAIN", "LAPSI", "INCAS", - "INNES", "CARON", "ROSEN", "CASAS", "NOLAN", "SERRE", "PESAR", "SEARS", "LEPIC", - "LISLE", "LOSSE", "CINNA", "SERIE", "RIRES", "CORSO", "SOIRS", "CREER", "POCOS", - "SIENS", "ARLES", "CROCE", "IONIC", "PONER", "ESSEN", "SANON", "CESSA", "SERIA", - "ALPES", "NINON", "LILLA", "AINOA", "CORPO", "LESER", "ILLIS", "ROPER", "ANNEE", - "PAIRE", "PEPIN", "ORIEL", "CANNE", "AIRES", "ARCIS", "EASIE", "ANNOS", "COLLE", - "SELLE", "EILEN", "CAPRI", "ERICA", "ROCCO", "ARIAN", "CLEON", "ALLIE", "PONCE", - "COPIE", "INNAN", "NOCES", "NAPPE", "CORNE", "ESIIN", "ENCOR", "LORNA", "SACRE", - "PAPEL", "SAILE", "SAEPE", "CREON", "LLENO", "ELISA", "PASSO", "ASILE", "LORCS", - "ASIAA", "SANIN", "ONNEN", "SONNA", "AILIE", "ALIIS", "ECOLE", "CREES", "PRESO", - "CLARO", "EARES", "ROSSI", "COREA", "SANAN", "AESOP", "SAPOR", "EISEN", "ACASO", - "PARAS", "NANON", "LAPIS", "ARRAN", "CLLIA", "SACRA", "PRINS", "CENCI", "CLAES", - "SLAAP", "ROLLA", "COLES", "LORNE", "OLELO", "CASSE", "NILES", "PASOS", "ESSAI", - "ROSAS", "LLENA", "LEERE", "CLASE", "CALOR", "ROSSE", "ALLEE", "SOREL", "SANAA", - "SLONE", "OLSEN", "OOREN", "PARER", "PASSI", "POSSA", "PLAIE", "OPERE", "SCAPE", - "POLEN", "RIPON", "SCALA", "AILLE", "PALOS", "CLAPP", "ESCAP", "ELLEI", "IONIA", - "NICOL", "PAESE", "PERON", "ORSON", "INNEN", "AISNE", "RANCE", "SLAAN", "PAOLI", - "COLLO", "ANNAS", "ERROL", "CLERC", "SAINE", "RAINA", "PRESE", "PARIA", "PERLE", - "RECAL", "SINAE", "PESER", "OISIN", "PLENA", "CARLE", "PERES", "SACAR", "ALPEN", - "CORRE", "ACCES", "RILLA", "ANNAL", "PERSE", "SAALE", "PERRO", "AILSA", "POCAS", - "SOLEN", "PLASE", "SOLIS", "PAPPI", "COPIA", "ARIES", "ROCCA", "ALIOS", "ROCAS", - "PELOS", "NEPOS", "COLPA", "ISORA", "LECON", "SOANE", "SNELL", "ILLOS", "PILAR", - "ECLAC", "PRESA", "SILLA", "NIELS", "LIPPO", "CROLL", "PONEN", "POSEE", "PIPPA", - "ILLAN", "CENIS", "SANNA", "RASSI", "CERRO", "SCENA", "CASOS", "COLPO", "POSSO", - "POSEN", "EINAR", "ISLAS", "IPSIS", "SALEN", "ASIEN", "CREAN", "LENIN", "LOCIS", - "NENNE", "ILION", "NARES", "ONNEA", "PALAA", "PIPPO", "POLIS", "RICOS", "ELSON", - "SNOOP", "ANNIS", "PROPE", "ELLIE", "ERNIE", "PLIER", "SERES", "REINA", "LIPPE", - "OLINE", "PARIE", "ARLEE", "NIAIS", "LEONI", "RAINE", "LARES", "SEINS", "CARRE", - "POILS", "ALENE", "LINEA", "NEARE", "PENSO", "PRISA", "CAPEL", "PAREA", "PEECE", - "SALIO", "COELO", "SCIRE", "NELLO", "LIENE", "ORICE", "EPAIS", "PERCE", "ALLIS", - "PEPLE", "LARNE", "NEILL", "OLLEN", "CASCA", "LAPIN", "OLLIE", "SALIC", "LIANE", - "REESE", "ELSLI", "SPION", "RIENS", "LILAS", "PAPPA", "ERRER", "SPISE", "CELIE", - "OLSON", "IRREN", "ARIAS", "ARION", "PASEO", "CAERE", "PISAN", "CARRO", "PAROI", - "NOONE", "SEPPI", "OPPIA", "SEALE", "LIPPI", "PELAS", "COCOS", "PLACA", "CONOR", - "LANCA", "OSASI", "ALOIS", "NAIRN", "PIENO", "SPASS", "SAONE", "ALNAR", "CARIA", - "PIENA", + "PROIE", "CARNE", "SPIEL", "CERES", "ENSIN", "CINCO", "ROLLO", "ARRAS", "SEIEN", + "PRIER", "ANNAN", "CALLE", "LIISA", "SALIR", "LESSE", "LESEN", "LIIAN", "NEERE", + "ARIEL", "PIENI", "PIERO", "ERANO", "ELENA", "SILLE", "NEALE", "SEENE", "ROLLE", + "NELLE", "SOLLE", "ESSER", "PASAR", "PREIS", "ASIAN", "SENCE", "ANSON", "SERRA", + "CONAN", "SERAS", "SIENA", "SOPRA", "RENEE", "ALINE", "CORSE", "ASSAI", "INSEL", + "ROSIE", "SONIA", "APPEL", "CRISE", "CIRCE", "LINIE", "RENAN", "CAIRE", "COLLA", + "SANOO", "EENER", "ANCOR", "NEPAL", "REINO", "LAINE", "SOONE", "ALAIN", "LAPSI", + "INCAS", "INNES", "CARON", "ROSEN", "CASAS", "NOLAN", "SERRE", "PESAR", "SEARS", + "LEPIC", "LISLE", "LOSSE", "CINNA", "SERIE", "RIRES", "CORSO", "SOIRS", "CREER", + "POCOS", "SIENS", "ARLES", "CROCE", "IONIC", "PONER", "ESSEN", "SANON", "CESSA", + "SERIA", "ALPES", "NINON", "LILLA", "AINOA", "CORPO", "LESER", "ILLIS", "ROPER", + "ANNEE", "PAIRE", "PEPIN", "ORIEL", "CANNE", "AIRES", "ARCIS", "EASIE", "ANNOS", + "COLLE", "SELLE", "EILEN", "CAPRI", "ERICA", "ROCCO", "ARIAN", "CLEON", "ALLIE", + "PONCE", "COPIE", "INNAN", "NOCES", "NAPPE", "CORNE", "ESIIN", "ENCOR", "LORNA", + "SACRE", "PAPEL", "SAILE", "SAEPE", "CREON", "LLENO", "ELISA", "PASSO", "ASILE", + "LORCS", "ASIAA", "SANIN", "ONNEN", "SONNA", "AILIE", "ALIIS", "ECOLE", "CREES", + "PRESO", "CLARO", "EARES", "ROSSI", "COREA", "SANAN", "AESOP", "SAPOR", "EISEN", + "ACASO", "PARAS", "NANON", "LAPIS", "ARRAN", "CLLIA", "SACRA", "PRINS", "CENCI", + "CLAES", "SLAAP", "ROLLA", "COLES", "LORNE", "OLELO", "CASSE", "NILES", "PASOS", + "ESSAI", "ROSAS", "LLENA", "LEERE", "CLASE", "CALOR", "ROSSE", "ALLEE", "SOREL", + "SANAA", "SLONE", "OLSEN", "OOREN", "PARER", "PASSI", "POSSA", "PLAIE", "OPERE", + "SCAPE", "POLEN", "RIPON", "SCALA", "AILLE", "PALOS", "CLAPP", "ESCAP", "ELLEI", + "IONIA", "NICOL", "PAESE", "PERON", "ORSON", "INNEN", "AISNE", "RANCE", "SLAAN", + "PAOLI", "COLLO", "ANNAS", "ERROL", "CLERC", "SAINE", "RAINA", "PRESE", "PARIA", + "PERLE", "RECAL", "SINAE", "PESER", "OISIN", "PLENA", "CARLE", "PERES", "SACAR", + "ALPEN", "CORRE", "ACCES", "RILLA", "ANNAL", "PERSE", "SAALE", "PERRO", "AILSA", + "POCAS", "SOLEN", "PLASE", "SOLIS", "PAPPI", "COPIA", "ARIES", "ROCCA", "ALIOS", + "ROCAS", "PELOS", "NEPOS", "COLPA", "ISORA", "LECON", "SOANE", "SNELL", "ILLOS", + "PILAR", "ECLAC", "PRESA", "SILLA", "NIELS", "LIPPO", "CROLL", "PONEN", "POSEE", + "PIPPA", "ILLAN", "CENIS", "SANNA", "RASSI", "CERRO", "SCENA", "CASOS", "COLPO", + "POSSO", "POSEN", "EINAR", "ISLAS", "IPSIS", "SALEN", "ASIEN", "CREAN", "LENIN", + "LOCIS", "NENNE", "ILION", "NARES", "ONNEA", "PALAA", "PIPPO", "POLIS", "RICOS", + "ELSON", "SNOOP", "ANNIS", "PROPE", "ELLIE", "ERNIE", "PLIER", "SERES", "REINA", + "LIPPE", "OLINE", "PARIE", "ARLEE", "NIAIS", "LEONI", "RAINE", "LARES", "SEINS", + "CARRE", "POILS", "ALENE", "LINEA", "NEARE", "PENSO", "PRISA", "CAPEL", "PAREA", + "PEECE", "SALIO", "COELO", "SCIRE", "NELLO", "LIENE", "ORICE", "EPAIS", "PERCE", + "ALLIS", "PEPLE", "LARNE", "NEILL", "OLLEN", "CASCA", "LAPIN", "OLLIE", "SALIC", + "LIANE", "REESE", "ELSLI", "SPION", "RIENS", "LILAS", "PAPPA", "ERRER", "SPISE", + "CELIE", "OLSON", "IRREN", "ARIAS", "ARION", "PASEO", "CAERE", "PISAN", "CARRO", + "PAROI", "NOONE", "SEPPI", "OPPIA", "SEALE", "LIPPI", "PELAS", "COCOS", "PLACA", + "CONOR", "LANCA", "OSASI", "ALOIS", "NAIRN", "PIENO", "SPASS", "SAONE", "ALNAR", + "CARIA", "PIENA", #endif }; @@ -135,7 +135,6 @@ static const char _expanded_words[][WORDLE_LENGTH + 1] = { static const char _expanded_words[][WORDLE_LENGTH + 1] = {}; #endif -static const uint16_t _num_unique_words = 155; // The _legal_words array begins with this many words where each letter is different. static const uint16_t _num_words = (sizeof(_legal_words) / sizeof(_legal_words[0])); static const uint16_t _num_expanded_words = (sizeof(_expanded_words) / sizeof(_expanded_words[0])); static const uint8_t _num_valid_letters = (sizeof(_valid_letters) / sizeof(_valid_letters[0])); @@ -157,8 +156,8 @@ static uint8_t get_first_pos(WordleLetterResult *word_elements_result) { } static uint8_t get_next_pos(uint8_t curr_pos, WordleLetterResult *word_elements_result) { - for (size_t pos = curr_pos+1; pos < WORDLE_LENGTH; pos++) { - if (word_elements_result[pos] != WORDLE_LETTER_CORRECT) + for (size_t pos = curr_pos; pos < WORDLE_LENGTH;) { + if (word_elements_result[++pos] != WORDLE_LETTER_CORRECT) return pos; } return WORDLE_LENGTH; @@ -166,8 +165,8 @@ static uint8_t get_next_pos(uint8_t curr_pos, WordleLetterResult *word_elements_ 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-1; pos >= 0; pos--) { - if (word_elements_result[pos] != WORDLE_LETTER_CORRECT) + for (int8_t pos = curr_pos; pos >= 0;) { + if (word_elements_result[--pos] != WORDLE_LETTER_CORRECT) return pos; } return curr_pos; @@ -262,7 +261,7 @@ static bool check_word(wordle_state_t *state) { } static void display_attempt(uint8_t attempt) { - char buf[2]; + char buf[3]; sprintf(buf, "%d", attempt+1); watch_display_string(buf, 3); } @@ -455,7 +454,7 @@ static void get_result(wordle_state_t *state) { #endif return; } - if (state->attempt++ > WORDLE_MAX_ATTEMPTS) { + if (++state->attempt >= WORDLE_MAX_ATTEMPTS) { state->playing = false; state->curr_screen = SCREEN_LOSE; state->streak = 0; diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index 1860984..aee40f4 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -94,10 +94,9 @@ typedef struct { uint8_t word_elements[WORDLE_LENGTH]; WordleLetterResult word_elements_result[WORDLE_LENGTH]; uint16_t guessed_words[WORDLE_MAX_ATTEMPTS]; - uint8_t attempt : 3; + uint8_t attempt : 4; uint8_t position : 3; bool playing : 1; - bool unused : 1; uint16_t curr_answer; uint8_t streak; WordleScreen curr_screen; diff --git a/utils/wordle_face/wordle_list.py b/utils/wordle_face/wordle_list.py index d508a8b..87f2f82 100644 --- a/utils/wordle_face/wordle_list.py +++ b/utils/wordle_face/wordle_list.py @@ -260,7 +260,7 @@ legal_list = [ "WORDS", "WORDY", "WORKS", "WORLD", "WORMS", "WORRY", "WORSE", "WORST", "WORTH", "WOULD", "WOUND", "WRACK", "WRAPS", "WRAPT", "WRATH", "WREAK", "WRECK", "WREST", "WRING", "WRIST", "WRITE", "WRITS", "WRONG", "WROTE", "WROTH", "YACHT", "YARDS", "YARNS", "YAWNS", "YEARN", "YEARS", "YEAST", "YELLS", "YELPS", "YIELD", "YOKED", - "YOKES", "YOLKS", "YOUNG", "YOURS", "YOUTH", "ZEBRA", "ZONES", "COLOR", "CINCO", + "YOKES", "YOLKS", "YOUNG", "YOURS", "YOUTH", "ZEBRA", "ZONES", "COLOR", ] expanded_list = [ @@ -982,11 +982,6 @@ def list_of_valid_words(letters, words=legal_list): return legal_words -def rearrange_words_by_uniqueness(words): - unique = [word for word in words if len(word) == len(set(word))] - duplicates = [word for word in words if len(word) != len(set(word))] - return unique + duplicates, len(unique) - def capitalize_all_and_remove_duplicates(arr): for i,word in enumerate(arr): arr[i] = word.upper() @@ -1006,7 +1001,6 @@ def print_valid_words(letters=alphabet): legal_words = capitalize_all_and_remove_duplicates(legal_words) random.shuffle(legal_words) # Just in case the watch's random function is too pseudo, better to shuffle th elist so it's less likely to always have the same starting letter - legal_words, num_uniq = rearrange_words_by_uniqueness(legal_words) print("static const char _valid_letters[] = {", end='') for letter in letters[:-1]: @@ -1041,8 +1035,6 @@ def print_valid_words(letters=alphabet): i+=1 print('') print("};") - - print(f"\nstatic const uint16_t _num_unique_words = {num_uniq}; // The _legal_words array begins with this many words where each letter is different.") def get_sec_val_and_units(seconds): From 1e76022146fa7f8e40e308c1e4395c1b064d2a9a Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sat, 17 Aug 2024 11:36:35 -0400 Subject: [PATCH 061/161] Gave the wordle face the ability to give random guesses on the first word --- movement/movement_config.h | 1 + .../watch_faces/complication/wordle_face.c | 98 ++++++++++++------- .../watch_faces/complication/wordle_face.h | 9 +- utils/wordle_face/wordle_list.py | 8 ++ 4 files changed, 80 insertions(+), 36 deletions(-) diff --git a/movement/movement_config.h b/movement/movement_config.h index abceacf..f8a1958 100644 --- a/movement/movement_config.h +++ b/movement/movement_config.h @@ -29,6 +29,7 @@ const watch_face_t watch_faces[] = { simple_clock_face, + wordle_face, world_clock_face, sunrise_sunset_face, moon_phase_face, diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index c6f28bb..32c396b 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -35,38 +35,38 @@ static const char _valid_letters[] = {'A', 'C', 'E', 'I', 'L', 'N', 'O', 'P', 'R // From: https://gist.github.com/shmookey/b28e342e1b1756c4700f42f17102c2ff // Number of words found: 282 static const char _legal_words[][WORDLE_LENGTH + 1] = { - "CRISP", "SALSA", "PRESS", "LIONS", "SPIRE", "CAPES", "ROLLS", "LOOSE", "ALOES", - "COPSE", "ENROL", "SLOPE", "CAPER", "SCORE", "SLAPS", "PLEAS", "CANOE", "REAPS", - "PEASE", "SLEEP", "LEAPS", "CORAL", "PILLS", "LOCAL", "ARENA", "PROSE", "SALES", - "OPENS", "REPEL", "REALS", "COLIC", "APRON", "LOINS", "COINS", "LASSO", "SIREN", - "SCARS", "RISER", "CRIES", "CRESS", "POSES", "NEARS", "CAIRN", "PARSE", "SCENE", - "SCOOP", "SPINS", "CORNS", "NOSES", "CLEAR", "LANES", "LOSES", "PIERS", "SLAIN", - "ROPES", "ALIEN", "LINER", "PRIES", "PROPS", "CRANE", "SCARE", "PEONS", "POLLS", - "LINEN", "SLIPS", "CAROL", "PEEPS", "SPANS", "ARISE", "POLES", "SCRAP", "OASIS", - "PAPAS", "PAINS", "SPOOL", "RELIC", "ALONE", "SLOPS", "PIANO", "PERIL", "SPICE", - "SPIES", "SPORE", "CLEAN", "SOLOS", "CREEP", "NONCE", "POISE", "COALS", "LEASE", - "SEALS", "COILS", "PILES", "RARES", "APPAL", "OASES", "RINSE", "POPES", "CONIC", - "SLICE", "SPACE", "ACRES", "ACORN", "ROLES", "CASES", "RESIN", "CREPE", "SOILS", - "PANEL", "SNEER", "INANE", "SCANS", "APACE", "EASEL", "CORES", "SOLAR", "PALES", - "SCOPE", "SCRIP", "LOANS", "ASSES", "EARNS", "CANON", "PLAIN", "POPPA", "SPOIL", - "APPLE", "ROSIN", "PANIC", "RISES", "AISLE", "CAPON", "COLON", "CLANS", "IRONS", - "RISEN", "PAILS", "LEANS", "PRICE", "AREAS", "SPARE", "LEARN", "PANES", "PRIOR", - "CRAPE", "LINES", "LEPER", "SNAPS", "POOLS", "SIRES", "SNARE", "COCOA", "PALER", - "CLOSE", "CRIER", "SANER", "PEARL", "CIRCA", "PAEAN", "RAISE", "SELLS", "OPINE", - "CEASE", "CANES", "ONION", "REELS", "RIPER", "SPARS", "RIPEN", "EPICS", "PLIES", - "CELLS", "SCALP", "ELOPE", "CANAL", "ROARS", "EASES", "OPERA", "SLOOP", "RARER", - "LIENS", "CROPS", "LACES", "LAIRS", "AEONS", "SOLES", "SNIPE", "PIECE", "NOOSE", - "NICER", "PENAL", "SILLS", "LANCE", "LOOPS", "SNORE", "PACES", "PLACE", "SPILL", - "PAIRS", "ARSON", "LAPSE", "CLASS", "EERIE", "PEERS", "PLANS", "LOONS", "SPOON", - "POSER", "SEERS", "REARS", "ROSES", "INNER", "NASAL", "OCEAN", "OPALS", "ALIAS", - "RACES", "ERASE", "SPINE", "SAILS", "CACAO", "CLASP", "REINS", "PAPER", "PIPER", - "EARLS", "PINES", "POLAR", "SNARL", "SCALE", "SPEAR", "SCION", "CRONE", "PRONE", - "SCORN", "RACER", "LILAC", "AROSE", "ISLES", "LAPEL", "PLANE", "PEARS", "POSSE", - "SORES", "PENCE", "CLAPS", "PESOS", "PENIS", "CALLS", "ASPEN", "COOLS", "CRASS", - "OSIER", "CARES", "PAPAL", "SOARS", "RILLS", "ERROR", "NAILS", "COPRA", "LOSER", - "SPELL", "CONES", "PORES", "SNAIL", "CROSS", "NIECE", "COLOR", "SALON", "SINCE", - "CORPS", "LIARS", "PIPES", "RAINS", "SPREE", "CLIPS", "NOISE", "PEALS", "SPOOR", - "SENSE", "PEACE", "RAILS", + "ROPES", "RESIN", "PACES", "RIPEN", "ALIEN", "SPINE", "ROSIN", "PIERS", "CAPER", + "SNORE", "SANER", "RAILS", "SCORN", "PENIS", "NEARS", "ENROL", "PROSE", "CANES", + "POSER", "ACORN", "PAILS", "SLAIN", "REALS", "CLAPS", "PLIES", "PALES", "LIENS", + "PLAIN", "SLOPE", "REAPS", "CRAPE", "ASPEN", "COINS", "ARISE", "ALOES", "PANES", + "SCION", "SNARL", "COPRA", "PALER", "CLIPS", "PANIC", "PARSE", "PENAL", "SPARE", + "LIONS", "LINES", "SNARE", "PEONS", "CLEAN", "SPACE", "SCALE", "COILS", "SCRAP", + "OPINE", "NICER", "LOANS", "RACES", "RELIC", "NOISE", "PIANO", "CRANE", "SNAIL", + "SCORE", "CLEAR", "CROPS", "CORES", "COPSE", "PINES", "PANEL", "RINSE", "LOINS", + "PRONE", "ALONE", "RAISE", "OSIER", "LEARN", "SPICE", "SPOIL", "EARLS", "NAILS", + "PLANE", "CARES", "CRIES", "CORNS", "CORPS", "CLASP", "LACES", "ARSON", "LANES", + "OPENS", "SALON", "SINCE", "PLANS", "SCARE", "SPORE", "OCEAN", "AEONS", "PRICE", + "IRONS", "SCALP", "EPICS", "LIARS", "SPIRE", "LINER", "PILES", "SLICE", "LEANS", + "RAINS", "PLEAS", "SOLAR", "CAPES", "APRON", "RISEN", "POISE", "CONES", "PEARS", + "PERIL", "COALS", "OPALS", "ROLES", "CLOSE", "CAPON", "POLES", "EARNS", "CRISP", + "AROSE", "SCOPE", "AISLE", "CLANS", "CORAL", "SCRIP", "PAINS", "OPERA", "PAIRS", + "PEARL", "SIREN", "ACRES", "CAROL", "LAIRS", "PORES", "PRIES", "CRONE", "CANOE", + "LAPSE", "LEAPS", "SNIPE", "REINS", "PEALS", "SPEAR", "LOSER", "POLAR", "LANCE", + "CAIRN", "PLACE", "SILLS", "CELLS", "EERIE", "PIECE", "ISLES", "NOOSE", "SNEER", + "SOLOS", "ELOPE", "INNER", "SLOOP", "SOARS", "SPREE", "SPANS", "PAPAL", "RIPER", + "COLON", "SCANS", "RARES", "PILLS", "CANON", "POLLS", "POPPA", "ERROR", "REARS", + "PESOS", "CRESS", "PENCE", "SPOOL", "COLOR", "NONCE", "CLASS", "SELLS", "NASAL", + "ERASE", "RILLS", "LAPEL", "COOLS", "EASEL", "COLIC", "SPELL", "SPOOR", "LASSO", + "APPAL", "PEACE", "SALSA", "SCENE", "NIECE", "CONIC", "APPLE", "SNAPS", "PEERS", + "ROARS", "SPARS", "SAILS", "SLOPS", "APACE", "POSES", "SENSE", "PEEPS", "CASES", + "CANAL", "CIRCA", "SLAPS", "SCOOP", "ROLLS", "PIPES", "SCARS", "LOOSE", "ROSES", + "LILAC", "OASES", "SOLES", "PAEAN", "PAPAS", "CRASS", "PROPS", "SEALS", "CACAO", + "LINEN", "SORES", "EASES", "POPES", "OASIS", "LOSES", "NOSES", "SIRES", "SPILL", + "CREPE", "ALIAS", "CROSS", "ARENA", "SPINS", "REPEL", "SPIES", "PRIOR", "POOLS", + "PRESS", "RISER", "AREAS", "SPOON", "SALES", "CREEP", "CEASE", "LOOPS", "ASSES", + "CALLS", "CRIER", "COCOA", "SEERS", "LOONS", "SLIPS", "PAPER", "REELS", "RISES", + "POSSE", "RARER", "SOILS", "PIPER", "INANE", "LOCAL", "PEASE", "ONION", "SLEEP", + "LEASE", "RACER", "LEPER", #if (USE_EXPANDED_DICT != 1) }; // These are words that'll never be used, but still need to be in the dictionary for guesses. @@ -127,7 +127,7 @@ static const char _expanded_words[][WORDLE_LENGTH + 1] = { "CELIE", "OLSON", "IRREN", "ARIAS", "ARION", "PASEO", "CAERE", "PISAN", "CARRO", "PAROI", "NOONE", "SEPPI", "OPPIA", "SEALE", "LIPPI", "PELAS", "COCOS", "PLACA", "CONOR", "LANCA", "OSASI", "ALOIS", "NAIRN", "PIENO", "SPASS", "SAONE", "ALNAR", - "CARIA", "PIENA", + "CARIA", "PIENA", #endif }; @@ -135,6 +135,9 @@ static const char _expanded_words[][WORDLE_LENGTH + 1] = { static const char _expanded_words[][WORDLE_LENGTH + 1] = {}; #endif +static const uint16_t _num_unique_words = 155; // The _legal_words array begins with this many words where each letter is different. + + static const uint16_t _num_words = (sizeof(_legal_words) / sizeof(_legal_words[0])); static const uint16_t _num_expanded_words = (sizeof(_expanded_words) / sizeof(_expanded_words[0])); static const uint8_t _num_valid_letters = (sizeof(_valid_letters) / sizeof(_valid_letters[0])); @@ -286,6 +289,7 @@ static void reset_board(wordle_state_t *state) { state->guessed_words[i] = _num_words + _num_expanded_words; } state->curr_answer = get_random(_num_words); + state->using_random_guess = false; state->attempt = 0; watch_clear_colon(); watch_display_string(" ", 4); @@ -464,6 +468,23 @@ static void get_result(wordle_state_t *state) { return; } +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_unique_words); + } while (random_guess == state->curr_answer); + for (size_t i = 0; i < WORDLE_LENGTH; i++) { + for (size_t j = 0; j < _num_valid_letters; j++) + { + if (_legal_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; +} + void wordle_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { (void) settings; (void) watch_face_index; @@ -484,6 +505,7 @@ void wordle_face_activate(movement_settings_t *settings, void *context) { if (state->prev_day <= (now + (60 *60 * 24))) state->streak = 0; if (state->curr_day != now) state->playing = false; #endif + state->using_random_guess = false; movement_request_tick_frequency(2); display_title(state); } @@ -530,9 +552,17 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi display_letter(state, true); if (state->word_elements[state->position] == _num_valid_letters) break; state->playing = true; + + if (watch_get_pin_level(BTN_LIGHT) && + (state->using_random_guess || (state->attempt == 0 && state->position == 0))) { + insert_random_guess(state); + break; + } state->position = get_next_pos(state->position, state->word_elements_result); - if (state->position >= WORDLE_LENGTH) + 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; diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index aee40f4..64a1665 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -49,7 +49,11 @@ * Else: None * * Alarm Press - * If Playing: Next position + * If Playing: If 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 + * Next position * Else: Next screen * Alarm Hold * If Playing: Previous position @@ -97,7 +101,8 @@ typedef struct { uint8_t attempt : 4; uint8_t position : 3; bool playing : 1; - uint16_t curr_answer; + uint16_t curr_answer : 15; + bool using_random_guess : 1; uint8_t streak; WordleScreen curr_screen; #if USE_DAILY_STREAK diff --git a/utils/wordle_face/wordle_list.py b/utils/wordle_face/wordle_list.py index 87f2f82..38e9510 100644 --- a/utils/wordle_face/wordle_list.py +++ b/utils/wordle_face/wordle_list.py @@ -982,6 +982,11 @@ def list_of_valid_words(letters, words=legal_list): return legal_words +def rearrange_words_by_uniqueness(words): + unique = [word for word in words if len(word) == len(set(word))] + duplicates = [word for word in words if len(word) != len(set(word))] + return unique + duplicates, len(unique) + def capitalize_all_and_remove_duplicates(arr): for i,word in enumerate(arr): arr[i] = word.upper() @@ -1001,6 +1006,7 @@ def print_valid_words(letters=alphabet): legal_words = capitalize_all_and_remove_duplicates(legal_words) random.shuffle(legal_words) # Just in case the watch's random function is too pseudo, better to shuffle th elist so it's less likely to always have the same starting letter + legal_words, num_uniq = rearrange_words_by_uniqueness(legal_words) print("static const char _valid_letters[] = {", end='') for letter in letters[:-1]: @@ -1035,6 +1041,8 @@ def print_valid_words(letters=alphabet): i+=1 print('') print("};") + + print(f"\nstatic const uint16_t _num_unique_words = {num_uniq}; // The _legal_words array begins with this many words where each letter is different.") def get_sec_val_and_units(seconds): From 4bb4bc85faca225e11910c2c4b7cadfb7e1d164b Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sat, 17 Aug 2024 11:50:07 -0400 Subject: [PATCH 062/161] USE_RANDOM_GUESS variable added --- movement/watch_faces/complication/wordle_face.c | 10 ++++++---- movement/watch_faces/complication/wordle_face.h | 9 ++++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 32c396b..be466cd 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -135,8 +135,6 @@ static const char _expanded_words[][WORDLE_LENGTH + 1] = { static const char _expanded_words[][WORDLE_LENGTH + 1] = {}; #endif -static const uint16_t _num_unique_words = 155; // The _legal_words array begins with this many words where each letter is different. - static const uint16_t _num_words = (sizeof(_legal_words) / sizeof(_legal_words[0])); static const uint16_t _num_expanded_words = (sizeof(_expanded_words) / sizeof(_expanded_words[0])); @@ -468,10 +466,12 @@ static void get_result(wordle_state_t *state) { return; } +#if (USE_RANDOM_GUESS != 0) +static const uint16_t _num_unique_words = 155; // The _legal_words array begins with this many words where each letter is different. 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_unique_words); + random_guess = get_random(USE_RANDOM_GUESS == 2 ? _num_unique_words : _num_words); } while (random_guess == state->curr_answer); for (size_t i = 0; i < WORDLE_LENGTH; i++) { for (size_t j = 0; j < _num_valid_letters; j++) @@ -484,6 +484,7 @@ static void insert_random_guess(wordle_state_t *state) { display_all_letters(state); state->using_random_guess = true; } +#endif void wordle_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { (void) settings; @@ -552,12 +553,13 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi display_letter(state, true); if (state->word_elements[state->position] == _num_valid_letters) break; state->playing = true; - +#if (USE_RANDOM_GUESS != 0) if (watch_get_pin_level(BTN_LIGHT) && (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); diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index 64a1665..97c3261 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -49,7 +49,7 @@ * Else: None * * Alarm Press - * If Playing: If Light btn held and + * If Playing: If 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 @@ -64,6 +64,13 @@ #define WORDLE_MAX_ATTEMPTS 6 #define USE_DAILY_STREAK false +/* 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 +*/ +#define USE_RANDOM_GUESS 2 + /* USE_EXPANDED_DICT * 0 = don't use it at all (saves 2.8KB of ROM) * 1 = Include the expanded dict in answers From 2a10402d19166b81e0294fd96144bb8daf06b896 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sat, 17 Aug 2024 11:52:42 -0400 Subject: [PATCH 063/161] Removed wordle from movmeent face after testing --- movement/movement_config.h | 1 - 1 file changed, 1 deletion(-) diff --git a/movement/movement_config.h b/movement/movement_config.h index f8a1958..abceacf 100644 --- a/movement/movement_config.h +++ b/movement/movement_config.h @@ -29,7 +29,6 @@ const watch_face_t watch_faces[] = { simple_clock_face, - wordle_face, world_clock_face, sunrise_sunset_face, moon_phase_face, From a0111fbe24fdf0085ca9ee89d9f56e1d1603af63 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sat, 17 Aug 2024 12:35:48 -0400 Subject: [PATCH 064/161] Swapped the Nice and Job so the Nice text is more likely to appear first. --- movement/watch_faces/complication/wordle_face.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index be466cd..2838b7f 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -357,7 +357,7 @@ static void display_lose(wordle_state_t *state, uint8_t subsecond) { static void display_win(wordle_state_t *state, uint8_t subsecond) { (void) state; char buf[13]; - sprintf(buf," W %s ", subsecond % 2 ? "NICE" : "JOb "); + sprintf(buf," W %s ", subsecond % 2 ? "JOb " : "NICE"); watch_display_string(buf, 0); } From 67c1089fb2c2b7133f3e93803b4f911c8ba87379 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sat, 17 Aug 2024 13:22:03 -0400 Subject: [PATCH 065/161] Don't delete the submitted characters if already guessed or not in dict --- movement/watch_faces/complication/wordle_face.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 2838b7f..94b28e5 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -415,7 +415,9 @@ static bool act_on_btn(wordle_state_t *state) { return true; case SCREEN_NO_DICT: case SCREEN_ALREADY_GUESSED: - show_start_of_attempt(state); + state->position= state->position = get_first_pos(state->word_elements_result); + display_all_letters(state); + state->curr_screen = SCREEN_PLAYING; return true; #if USE_DAILY_STREAK case SCREEN_WAIT: From 4257b71562084fbe0e81ccd915a9557ef5e45ba4 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sat, 17 Aug 2024 19:36:19 -0400 Subject: [PATCH 066/161] combined two areas of code into one function --- .../watch_faces/complication/wordle_face.c | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 94b28e5..6802a22 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -267,10 +267,12 @@ static void display_attempt(uint8_t attempt) { watch_display_string(buf, 3); } -static void show_start_of_attempt(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] = _num_valid_letters; +static void show_start_of_attempt(wordle_state_t *state, bool reset_all_letters) { + if (reset_all_letters) { + for (size_t i = 0; i < WORDLE_LENGTH; i++) { + if (state->word_elements_result[i] != WORDLE_LETTER_CORRECT) + state->word_elements[i] = _num_valid_letters; + } } display_attempt(state->attempt); display_all_letters(state); @@ -291,7 +293,7 @@ static void reset_board(wordle_state_t *state) { state->attempt = 0; watch_clear_colon(); watch_display_string(" ", 4); - show_start_of_attempt(state); + show_start_of_attempt(state, true); watch_display_string("-", 5); #if __EMSCRIPTEN__ printf("ANSWER: %s\r\n", _legal_words[state->curr_answer]); @@ -389,7 +391,7 @@ static bool act_on_btn(wordle_state_t *state) { switch (state->curr_screen) { case SCREEN_RESULT: - show_start_of_attempt(state); + show_start_of_attempt(state, true); return true; case SCREEN_TITLE: #if USE_DAILY_STREAK @@ -399,7 +401,7 @@ static bool act_on_btn(wordle_state_t *state) { } #endif if (state->playing) - show_start_of_attempt(state); + show_start_of_attempt(state, true); else display_streak(state); return true; @@ -415,9 +417,7 @@ static bool act_on_btn(wordle_state_t *state) { return true; case SCREEN_NO_DICT: case SCREEN_ALREADY_GUESSED: - state->position= state->position = get_first_pos(state->word_elements_result); - display_all_letters(state); - state->curr_screen = SCREEN_PLAYING; + show_start_of_attempt(state, false); return true; #if USE_DAILY_STREAK case SCREEN_WAIT: From 935ede9fda862b94ba5ec300232bca8d1f92ad63 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 18 Aug 2024 10:35:36 -0400 Subject: [PATCH 067/161] Fixed leaving the screen and coming back --- .../watch_faces/complication/wordle_face.c | 35 ++++++++++++------- .../watch_faces/complication/wordle_face.h | 2 +- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 6802a22..361faf5 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -267,19 +267,19 @@ static void display_attempt(uint8_t attempt) { watch_display_string(buf, 3); } -static void show_start_of_attempt(wordle_state_t *state, bool reset_all_letters) { - if (reset_all_letters) { - for (size_t i = 0; i < WORDLE_LENGTH; i++) { - if (state->word_elements_result[i] != WORDLE_LETTER_CORRECT) - state->word_elements[i] = _num_valid_letters; - } - } +static void display_playing(wordle_state_t *state) { display_attempt(state->attempt); display_all_letters(state); - state->position = get_first_pos(state->word_elements_result); state->curr_screen = SCREEN_PLAYING; } +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] = _num_valid_letters; + } +} + static void reset_board(wordle_state_t *state) { for (size_t i = 0; i < WORDLE_LENGTH; i++) { state->word_elements[i] = _num_valid_letters; @@ -293,7 +293,9 @@ static void reset_board(wordle_state_t *state) { state->attempt = 0; watch_clear_colon(); watch_display_string(" ", 4); - show_start_of_attempt(state, true); + reset_incorrect_elements(state); + state->position = get_first_pos(state->word_elements_result); + display_playing(state); watch_display_string("-", 5); #if __EMSCRIPTEN__ printf("ANSWER: %s\r\n", _legal_words[state->curr_answer]); @@ -391,7 +393,9 @@ static bool act_on_btn(wordle_state_t *state) { switch (state->curr_screen) { case SCREEN_RESULT: - show_start_of_attempt(state, true); + reset_incorrect_elements(state); + state->position = get_first_pos(state->word_elements_result); + display_playing(state); return true; case SCREEN_TITLE: #if USE_DAILY_STREAK @@ -401,7 +405,7 @@ static bool act_on_btn(wordle_state_t *state) { } #endif if (state->playing) - show_start_of_attempt(state, true); + display_playing(state); else display_streak(state); return true; @@ -417,7 +421,8 @@ static bool act_on_btn(wordle_state_t *state) { return true; case SCREEN_NO_DICT: case SCREEN_ALREADY_GUESSED: - show_start_of_attempt(state, false); + state->position = get_first_pos(state->word_elements_result); + display_playing(state); return true; #if USE_DAILY_STREAK case SCREEN_WAIT: @@ -509,6 +514,10 @@ void wordle_face_activate(movement_settings_t *settings, void *context) { if (state->curr_day != now) state->playing = false; #endif state->using_random_guess = false; + if (state->playing && state->curr_screen >= SCREEN_WIN) { + reset_incorrect_elements(state); + state->position = get_first_pos(state->word_elements_result); + } movement_request_tick_frequency(2); display_title(state); } @@ -581,7 +590,7 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi display_title(state); break; case EVENT_LOW_ENERGY_UPDATE: - if (state->curr_screen == SCREEN_TITLE) + if (state->curr_screen != SCREEN_TITLE) display_title(state); break; default: diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index 97c3261..8ad9155 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -87,7 +87,6 @@ typedef enum { typedef enum { SCREEN_PLAYING = 0, - SCREEN_RESULT, SCREEN_TITLE, SCREEN_STREAK, #if USE_DAILY_STREAK @@ -95,6 +94,7 @@ typedef enum { #endif SCREEN_WIN, SCREEN_LOSE, + SCREEN_RESULT, SCREEN_NO_DICT, SCREEN_ALREADY_GUESSED, SCREEN_COUNT From 57ca74b25338c10a8d6cb58410b69d6bf79ba010 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 18 Aug 2024 10:46:33 -0400 Subject: [PATCH 068/161] Added SONIC and LASER to the allowed words --- movement/watch_faces/complication/wordle_face.c | 8 ++++---- utils/wordle_face/wordle_list.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 361faf5..58ad55a 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -33,7 +33,7 @@ static const char _valid_letters[] = {'A', 'C', 'E', 'I', 'L', 'N', 'O', 'P', 'R', 'S'}; // From: https://gist.github.com/shmookey/b28e342e1b1756c4700f42f17102c2ff -// Number of words found: 282 +// Number of words found: 284 static const char _legal_words[][WORDLE_LENGTH + 1] = { "ROPES", "RESIN", "PACES", "RIPEN", "ALIEN", "SPINE", "ROSIN", "PIERS", "CAPER", "SNORE", "SANER", "RAILS", "SCORN", "PENIS", "NEARS", "ENROL", "PROSE", "CANES", @@ -52,7 +52,7 @@ static const char _legal_words[][WORDLE_LENGTH + 1] = { "AROSE", "SCOPE", "AISLE", "CLANS", "CORAL", "SCRIP", "PAINS", "OPERA", "PAIRS", "PEARL", "SIREN", "ACRES", "CAROL", "LAIRS", "PORES", "PRIES", "CRONE", "CANOE", "LAPSE", "LEAPS", "SNIPE", "REINS", "PEALS", "SPEAR", "LOSER", "POLAR", "LANCE", - "CAIRN", "PLACE", "SILLS", "CELLS", "EERIE", "PIECE", "ISLES", "NOOSE", "SNEER", + "CAIRN", "PLACE", "LASER", "SONIC", "EERIE", "PIECE", "ISLES", "NOOSE", "SNEER", "SOLOS", "ELOPE", "INNER", "SLOOP", "SOARS", "SPREE", "SPANS", "PAPAL", "RIPER", "COLON", "SCANS", "RARES", "PILLS", "CANON", "POLLS", "POPPA", "ERROR", "REARS", "PESOS", "CRESS", "PENCE", "SPOOL", "COLOR", "NONCE", "CLASS", "SELLS", "NASAL", @@ -66,7 +66,7 @@ static const char _legal_words[][WORDLE_LENGTH + 1] = { "PRESS", "RISER", "AREAS", "SPOON", "SALES", "CREEP", "CEASE", "LOOPS", "ASSES", "CALLS", "CRIER", "COCOA", "SEERS", "LOONS", "SLIPS", "PAPER", "REELS", "RISES", "POSSE", "RARER", "SOILS", "PIPER", "INANE", "LOCAL", "PEASE", "ONION", "SLEEP", - "LEASE", "RACER", "LEPER", + "LEASE", "RACER", "LEPER", "SILLS", "CELLS", #if (USE_EXPANDED_DICT != 1) }; // These are words that'll never be used, but still need to be in the dictionary for guesses. @@ -474,7 +474,7 @@ static void get_result(wordle_state_t *state) { } #if (USE_RANDOM_GUESS != 0) -static const uint16_t _num_unique_words = 155; // The _legal_words array begins with this many words where each letter is different. +static const uint16_t _num_unique_words = 157; // The _legal_words array begins with this many words where each letter is different. 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 diff --git a/utils/wordle_face/wordle_list.py b/utils/wordle_face/wordle_list.py index 38e9510..da370b7 100644 --- a/utils/wordle_face/wordle_list.py +++ b/utils/wordle_face/wordle_list.py @@ -260,7 +260,7 @@ legal_list = [ "WORDS", "WORDY", "WORKS", "WORLD", "WORMS", "WORRY", "WORSE", "WORST", "WORTH", "WOULD", "WOUND", "WRACK", "WRAPS", "WRAPT", "WRATH", "WREAK", "WRECK", "WREST", "WRING", "WRIST", "WRITE", "WRITS", "WRONG", "WROTE", "WROTH", "YACHT", "YARDS", "YARNS", "YAWNS", "YEARN", "YEARS", "YEAST", "YELLS", "YELPS", "YIELD", "YOKED", - "YOKES", "YOLKS", "YOUNG", "YOURS", "YOUTH", "ZEBRA", "ZONES", "COLOR", + "YOKES", "YOLKS", "YOUNG", "YOURS", "YOUTH", "ZEBRA", "ZONES", "COLOR", "LASER", "SONIC", ] expanded_list = [ @@ -938,7 +938,7 @@ expanded_list = [ "NOTAR", "OBEIR", "VIMES", "GENUA", "KANAL", "LIMON", "RAHAB", "SUOMI", "VEJEN", "BATEN", "FJORD", "LEVES", "PHARE", "RECTO", "AAGOT", "GIZUR", "NADJA", "RUGGE", "SNEYD", "DECKT", "FAILE", "GAOLS", "MELER", "PACTO", "PAHAN", "CALIF", "MENON", "SEPOY", "WADDY", "ZELLE", "AENDA", "ASTUA", "KROON", "LETRA", "MINIT", "NEEWA", - "PATNA", "URIEL", "HITTE", "HOMOJ", "JOUET", "KOSKI", "LYSTE", "MINAS", "RUHTE", "SETZE", + "PATNA", "URIEL", "HITTE", "HOMOJ", "JOUET", "KOSKI", "LYSTE", "MINAS", "RUHTE", "SETZE", "LASER", "SONIC", ] alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] From 1868f8446ae6f2a99fc36d2a0f5574e606ae10a9 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 18 Aug 2024 18:33:38 -0400 Subject: [PATCH 069/161] Added continue screen --- .../watch_faces/complication/wordle_face.c | 44 +++++++++++++++---- .../watch_faces/complication/wordle_face.h | 6 ++- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 58ad55a..3638255 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -307,6 +307,13 @@ static void display_title(wordle_state_t *state) { watch_display_string("WO WordLE", 0); } +static void display_continue(wordle_state_t *state) { + char buf[7]; + state->curr_screen = SCREEN_CONTINUE; + sprintf(buf, "Cont %c", state->continuing ? 'y' : 'n'); + watch_display_string(buf, 4); +} + static void display_streak(wordle_state_t *state) { char buf[12]; state->curr_screen = SCREEN_STREAK; @@ -389,7 +396,7 @@ static void display_result(wordle_state_t *state, uint8_t subsecond) { watch_display_string(buf, 5); } -static bool act_on_btn(wordle_state_t *state) { +static bool act_on_btn(wordle_state_t *state, const uint8_t pin) { switch (state->curr_screen) { case SCREEN_RESULT: @@ -401,13 +408,15 @@ static bool act_on_btn(wordle_state_t *state) { #if USE_DAILY_STREAK if (state->prev_day == get_day_unix_time()) { display_wait(state); - return true; } -#endif - if (state->playing) - display_playing(state); +#else + if (state->playing) { + state->continuing = true; + display_continue(state); + } else display_streak(state); +#endif return true; case SCREEN_STREAK: #if USE_DAILY_STREAK @@ -424,6 +433,23 @@ static bool act_on_btn(wordle_state_t *state) { state->position = get_first_pos(state->word_elements_result); display_playing(state); return true; + case SCREEN_CONTINUE: + switch (pin) + { + case BTN_ALARM: + if (state->continuing) + display_playing(state); + else { + reset_board(state); + display_streak(state); + } + break; + case BTN_LIGHT: + state->continuing = !state->continuing; + display_continue(state); + break; + } + return true; #if USE_DAILY_STREAK case SCREEN_WAIT: display_title(state); @@ -514,7 +540,7 @@ void wordle_face_activate(movement_settings_t *settings, void *context) { if (state->curr_day != now) state->playing = false; #endif state->using_random_guess = false; - if (state->playing && state->curr_screen >= SCREEN_WIN) { + if (state->playing && state->curr_screen >= SCREEN_RESULT) { reset_incorrect_elements(state); state->position = get_first_pos(state->word_elements_result); } @@ -550,7 +576,7 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi } break; case EVENT_LIGHT_BUTTON_UP: - if (act_on_btn(state)) break; + if (act_on_btn(state, BTN_LIGHT)) break; get_next_letter(state->position, state->word_elements); display_letter(state, true); break; @@ -560,7 +586,7 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi display_letter(state, true); break; case EVENT_ALARM_BUTTON_UP: - if (act_on_btn(state)) break; + if (act_on_btn(state, BTN_ALARM)) break; display_letter(state, true); if (state->word_elements[state->position] == _num_valid_letters) break; state->playing = true; @@ -586,7 +612,7 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi case EVENT_ACTIVATE: break; case EVENT_TIMEOUT: - if (state->curr_screen >= SCREEN_WIN) + if (state->curr_screen >= SCREEN_RESULT) display_title(state); break; case EVENT_LOW_ENERGY_UPDATE: diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index 8ad9155..fe92492 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -89,12 +89,13 @@ typedef enum { SCREEN_PLAYING = 0, SCREEN_TITLE, SCREEN_STREAK, + SCREEN_CONTINUE, #if USE_DAILY_STREAK SCREEN_WAIT, #endif + SCREEN_RESULT, SCREEN_WIN, SCREEN_LOSE, - SCREEN_RESULT, SCREEN_NO_DICT, SCREEN_ALREADY_GUESSED, SCREEN_COUNT @@ -108,8 +109,9 @@ typedef struct { uint8_t attempt : 4; uint8_t position : 3; bool playing : 1; - uint16_t curr_answer : 15; + uint16_t curr_answer : 14; bool using_random_guess : 1; + bool continuing : 1; uint8_t streak; WordleScreen curr_screen; #if USE_DAILY_STREAK From ce31db3712faf22957cabf26d5c4a4217cd8056b Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 18 Aug 2024 22:30:41 -0400 Subject: [PATCH 070/161] Continue test now blinks --- movement/watch_faces/complication/wordle_face.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 3638255..1a3c2f8 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -307,11 +307,17 @@ static void display_title(wordle_state_t *state) { watch_display_string("WO WordLE", 0); } +static void display_continue_result(bool continuing, uint8_t subsecond) { + char buf[2]; + char result = subsecond % 2 ? ' ' : (continuing ? 'y' : 'n'); + sprintf(buf,"%c", result); + watch_display_string(buf, 9); +} + static void display_continue(wordle_state_t *state) { - char buf[7]; state->curr_screen = SCREEN_CONTINUE; - sprintf(buf, "Cont %c", state->continuing ? 'y' : 'n'); - watch_display_string(buf, 4); + watch_display_string("Cont ", 4); + display_continue_result(state->continuing, 0); } static void display_streak(wordle_state_t *state) { @@ -446,7 +452,7 @@ static bool act_on_btn(wordle_state_t *state, const uint8_t pin) { break; case BTN_LIGHT: state->continuing = !state->continuing; - display_continue(state); + display_continue_result(state->continuing, 0); break; } return true; @@ -571,6 +577,9 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi case SCREEN_WIN: display_win(state, event.subsecond); break; + case SCREEN_CONTINUE: + display_continue_result(state->continuing, event.subsecond); + break; default: break; } From cb57ef237d9b9518b3b9d804affe9b52f7cf9226 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 18 Aug 2024 22:33:03 -0400 Subject: [PATCH 071/161] Got rid of continue's blinking --- movement/watch_faces/complication/wordle_face.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 1a3c2f8..f232786 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -307,17 +307,14 @@ static void display_title(wordle_state_t *state) { watch_display_string("WO WordLE", 0); } -static void display_continue_result(bool continuing, uint8_t subsecond) { - char buf[2]; - char result = subsecond % 2 ? ' ' : (continuing ? 'y' : 'n'); - sprintf(buf,"%c", result); - watch_display_string(buf, 9); +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); - display_continue_result(state->continuing, 0); + display_continue_result(state->continuing); } static void display_streak(wordle_state_t *state) { @@ -452,7 +449,7 @@ static bool act_on_btn(wordle_state_t *state, const uint8_t pin) { break; case BTN_LIGHT: state->continuing = !state->continuing; - display_continue_result(state->continuing, 0); + display_continue_result(state->continuing); break; } return true; @@ -577,9 +574,6 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi case SCREEN_WIN: display_win(state, event.subsecond); break; - case SCREEN_CONTINUE: - display_continue_result(state->continuing, event.subsecond); - break; default: break; } From 7ceb68267538e0565fc8b73e010e8d201caa5b2f Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 18 Aug 2024 22:52:10 -0400 Subject: [PATCH 072/161] Better handling of seeing if currently playing --- .../watch_faces/complication/wordle_face.c | 49 ++++++++++++------- .../watch_faces/complication/wordle_face.h | 3 +- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index f232786..bd82fba 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -273,6 +273,18 @@ static void display_playing(wordle_state_t *state) { state->curr_screen = SCREEN_PLAYING; } +static void reset_all_elements(wordle_state_t *state) { + for (size_t i = 0; i < WORDLE_LENGTH; i++) { + state->word_elements[i] = _num_valid_letters; + state->word_elements_result[i] = WORDLE_LETTER_WRONG; + } + for (size_t i = 0; i < WORDLE_MAX_ATTEMPTS; i++) { + state->guessed_words[i] = _num_words + _num_expanded_words; + } + 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) @@ -281,22 +293,12 @@ static void reset_incorrect_elements(wordle_state_t *state) { } static void reset_board(wordle_state_t *state) { - for (size_t i = 0; i < WORDLE_LENGTH; i++) { - state->word_elements[i] = _num_valid_letters; - state->word_elements_result[i] = WORDLE_LETTER_WRONG; - } - for (size_t i = 0; i < WORDLE_MAX_ATTEMPTS; i++) { - state->guessed_words[i] = _num_words + _num_expanded_words; - } + reset_all_elements(state); state->curr_answer = get_random(_num_words); - state->using_random_guess = false; - state->attempt = 0; watch_clear_colon(); - watch_display_string(" ", 4); - reset_incorrect_elements(state); state->position = get_first_pos(state->word_elements_result); display_playing(state); - watch_display_string("-", 5); + watch_display_string(" -", 4); #if __EMSCRIPTEN__ printf("ANSWER: %s\r\n", _legal_words[state->curr_answer]); #endif @@ -371,10 +373,19 @@ static void display_lose(wordle_state_t *state, uint8_t subsecond) { static void display_win(wordle_state_t *state, uint8_t subsecond) { (void) state; char buf[13]; - sprintf(buf," W %s ", subsecond % 2 ? "JOb " : "NICE"); + 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] != _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++) @@ -413,7 +424,7 @@ static bool act_on_btn(wordle_state_t *state, const uint8_t pin) { display_wait(state); } #else - if (state->playing) { + if (is_playing(state)) { state->continuing = true; display_continue(state); } @@ -483,7 +494,7 @@ static void get_result(wordle_state_t *state) { state->guessed_words[state->attempt] = in_dict; bool exact_match = check_word(state); if (exact_match) { - state->playing = false; + state->attempt = 0; state->curr_screen = SCREEN_WIN; if (state->streak < 0x7F) state->streak++; @@ -493,7 +504,7 @@ static void get_result(wordle_state_t *state) { return; } if (++state->attempt >= WORDLE_MAX_ATTEMPTS) { - state->playing = false; + state->attempt = 0; state->curr_screen = SCREEN_LOSE; state->streak = 0; return; @@ -530,6 +541,7 @@ void wordle_face_setup(movement_settings_t *settings, uint8_t watch_face_index, memset(*context_ptr, 0, sizeof(wordle_state_t)); wordle_state_t *state = (wordle_state_t *)*context_ptr; state->curr_screen = SCREEN_TITLE; + reset_all_elements(state); } // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep. } @@ -540,10 +552,10 @@ void wordle_face_activate(movement_settings_t *settings, void *context) { #if USE_DAILY_STREAK uint32_t now = get_day_unix_time() ; if (state->prev_day <= (now + (60 *60 * 24))) state->streak = 0; - if (state->curr_day != now) state->playing = false; + if (state->curr_day != now) state->attempt = 0; #endif state->using_random_guess = false; - if (state->playing && state->curr_screen >= SCREEN_RESULT) { + if (is_playing(state) && state->curr_screen >= SCREEN_RESULT) { reset_incorrect_elements(state); state->position = get_first_pos(state->word_elements_result); } @@ -592,7 +604,6 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi if (act_on_btn(state, BTN_ALARM)) break; display_letter(state, true); if (state->word_elements[state->position] == _num_valid_letters) break; - state->playing = true; #if (USE_RANDOM_GUESS != 0) if (watch_get_pin_level(BTN_LIGHT) && (state->using_random_guess || (state->attempt == 0 && state->position == 0))) { diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index fe92492..00c97b3 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -108,9 +108,8 @@ typedef struct { uint16_t guessed_words[WORDLE_MAX_ATTEMPTS]; uint8_t attempt : 4; uint8_t position : 3; - bool playing : 1; - uint16_t curr_answer : 14; bool using_random_guess : 1; + uint16_t curr_answer : 15; bool continuing : 1; uint8_t streak; WordleScreen curr_screen; From a0ffd0ca7f78a628439738d63cbdd67d04bea121 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 18 Aug 2024 23:10:17 -0400 Subject: [PATCH 073/161] Fix on playing with daily streak --- movement/watch_faces/complication/wordle_face.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index bd82fba..59edb9d 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -309,6 +309,7 @@ static void display_title(wordle_state_t *state) { watch_display_string("WO WordLE", 0); } +#if !USE_DAILY_STREAK static void display_continue_result(bool continuing) { watch_display_string(continuing ? "y" : "n", 9); } @@ -318,6 +319,7 @@ static void display_continue(wordle_state_t *state) { watch_display_string("Cont ", 4); display_continue_result(state->continuing); } +#endif static void display_streak(wordle_state_t *state) { char buf[12]; @@ -423,6 +425,10 @@ static bool act_on_btn(wordle_state_t *state, const uint8_t pin) { if (state->prev_day == 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; @@ -447,6 +453,12 @@ static bool act_on_btn(wordle_state_t *state, const uint8_t pin) { state->position = get_first_pos(state->word_elements_result); display_playing(state); return true; +#if USE_DAILY_STREAK + case SCREEN_WAIT: + (void) pin; + display_title(state); + return true; +#else case SCREEN_CONTINUE: switch (pin) { @@ -464,10 +476,6 @@ static bool act_on_btn(wordle_state_t *state, const uint8_t pin) { break; } return true; -#if USE_DAILY_STREAK - case SCREEN_WAIT: - display_title(state); - return true; #endif default: return false; From af6f6002ba2f490bc061f8d4b15d9ec5015f23e6 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 18 Aug 2024 23:55:54 -0400 Subject: [PATCH 074/161] Fixed the bug of the text not resetting after a timeout --- movement/watch_faces/complication/wordle_face.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 59edb9d..90cec69 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -634,8 +634,11 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi case EVENT_ACTIVATE: break; case EVENT_TIMEOUT: - if (state->curr_screen >= SCREEN_RESULT) + 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) From 0d58f0d77dd79596201b647be87654ebbbf3ce9d Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 18 Aug 2024 23:58:39 -0400 Subject: [PATCH 075/161] JOB to JOb --- movement/watch_faces/complication/wordle_face.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 90cec69..f5b69a6 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -375,7 +375,7 @@ static void display_lose(wordle_state_t *state, uint8_t subsecond) { static void display_win(wordle_state_t *state, uint8_t subsecond) { (void) state; char buf[13]; - sprintf(buf," W %s ", subsecond % 2 ? "NICE" : "JOB "); + sprintf(buf," W %s ", subsecond % 2 ? "NICE" : "JOb "); watch_display_string(buf, 0); } From 1a1560b59d614db26847ce464b113276946d1e76 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Mon, 19 Aug 2024 00:13:18 -0400 Subject: [PATCH 076/161] Bugfix on elements not resetting when winning or losing --- movement/watch_faces/complication/wordle_face.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index f5b69a6..35953bb 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -502,7 +502,7 @@ static void get_result(wordle_state_t *state) { state->guessed_words[state->attempt] = in_dict; bool exact_match = check_word(state); if (exact_match) { - state->attempt = 0; + reset_all_elements(state); state->curr_screen = SCREEN_WIN; if (state->streak < 0x7F) state->streak++; @@ -512,7 +512,7 @@ static void get_result(wordle_state_t *state) { return; } if (++state->attempt >= WORDLE_MAX_ATTEMPTS) { - state->attempt = 0; + reset_all_elements(state); state->curr_screen = SCREEN_LOSE; state->streak = 0; return; @@ -560,7 +560,7 @@ void wordle_face_activate(movement_settings_t *settings, void *context) { #if USE_DAILY_STREAK uint32_t now = get_day_unix_time() ; if (state->prev_day <= (now + (60 *60 * 24))) state->streak = 0; - if (state->curr_day != now) state->attempt = 0; + if (state->curr_day != now) reset_all_elements(state); #endif state->using_random_guess = false; if (is_playing(state) && state->curr_screen >= SCREEN_RESULT) { From 7f38f8e4169194ac3241eadbc30f2da902b31d7c Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Fri, 23 Aug 2024 16:39:53 -0400 Subject: [PATCH 077/161] modified Wordle list to use Wordle's own list and add 2 more letters --- .../watch_faces/complication/wordle_face.c | 336 ++- .../watch_faces/complication/wordle_face.h | 7 - utils/wordle_face/wordle_list.py | 2174 +++++++++-------- 3 files changed, 1406 insertions(+), 1111 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 35953bb..f27ae4a 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -30,114 +30,220 @@ #endif -static const char _valid_letters[] = {'A', 'C', 'E', 'I', 'L', 'N', 'O', 'P', 'R', 'S'}; +static const char _valid_letters[] = {'A', 'C', 'E', 'G', 'H', 'I', 'L', 'N', 'O', 'P', 'R', 'S'}; -// From: https://gist.github.com/shmookey/b28e342e1b1756c4700f42f17102c2ff -// Number of words found: 284 -static const char _legal_words[][WORDLE_LENGTH + 1] = { - "ROPES", "RESIN", "PACES", "RIPEN", "ALIEN", "SPINE", "ROSIN", "PIERS", "CAPER", - "SNORE", "SANER", "RAILS", "SCORN", "PENIS", "NEARS", "ENROL", "PROSE", "CANES", - "POSER", "ACORN", "PAILS", "SLAIN", "REALS", "CLAPS", "PLIES", "PALES", "LIENS", - "PLAIN", "SLOPE", "REAPS", "CRAPE", "ASPEN", "COINS", "ARISE", "ALOES", "PANES", - "SCION", "SNARL", "COPRA", "PALER", "CLIPS", "PANIC", "PARSE", "PENAL", "SPARE", - "LIONS", "LINES", "SNARE", "PEONS", "CLEAN", "SPACE", "SCALE", "COILS", "SCRAP", - "OPINE", "NICER", "LOANS", "RACES", "RELIC", "NOISE", "PIANO", "CRANE", "SNAIL", - "SCORE", "CLEAR", "CROPS", "CORES", "COPSE", "PINES", "PANEL", "RINSE", "LOINS", - "PRONE", "ALONE", "RAISE", "OSIER", "LEARN", "SPICE", "SPOIL", "EARLS", "NAILS", - "PLANE", "CARES", "CRIES", "CORNS", "CORPS", "CLASP", "LACES", "ARSON", "LANES", - "OPENS", "SALON", "SINCE", "PLANS", "SCARE", "SPORE", "OCEAN", "AEONS", "PRICE", - "IRONS", "SCALP", "EPICS", "LIARS", "SPIRE", "LINER", "PILES", "SLICE", "LEANS", - "RAINS", "PLEAS", "SOLAR", "CAPES", "APRON", "RISEN", "POISE", "CONES", "PEARS", - "PERIL", "COALS", "OPALS", "ROLES", "CLOSE", "CAPON", "POLES", "EARNS", "CRISP", - "AROSE", "SCOPE", "AISLE", "CLANS", "CORAL", "SCRIP", "PAINS", "OPERA", "PAIRS", - "PEARL", "SIREN", "ACRES", "CAROL", "LAIRS", "PORES", "PRIES", "CRONE", "CANOE", - "LAPSE", "LEAPS", "SNIPE", "REINS", "PEALS", "SPEAR", "LOSER", "POLAR", "LANCE", - "CAIRN", "PLACE", "LASER", "SONIC", "EERIE", "PIECE", "ISLES", "NOOSE", "SNEER", - "SOLOS", "ELOPE", "INNER", "SLOOP", "SOARS", "SPREE", "SPANS", "PAPAL", "RIPER", - "COLON", "SCANS", "RARES", "PILLS", "CANON", "POLLS", "POPPA", "ERROR", "REARS", - "PESOS", "CRESS", "PENCE", "SPOOL", "COLOR", "NONCE", "CLASS", "SELLS", "NASAL", - "ERASE", "RILLS", "LAPEL", "COOLS", "EASEL", "COLIC", "SPELL", "SPOOR", "LASSO", - "APPAL", "PEACE", "SALSA", "SCENE", "NIECE", "CONIC", "APPLE", "SNAPS", "PEERS", - "ROARS", "SPARS", "SAILS", "SLOPS", "APACE", "POSES", "SENSE", "PEEPS", "CASES", - "CANAL", "CIRCA", "SLAPS", "SCOOP", "ROLLS", "PIPES", "SCARS", "LOOSE", "ROSES", - "LILAC", "OASES", "SOLES", "PAEAN", "PAPAS", "CRASS", "PROPS", "SEALS", "CACAO", - "LINEN", "SORES", "EASES", "POPES", "OASIS", "LOSES", "NOSES", "SIRES", "SPILL", - "CREPE", "ALIAS", "CROSS", "ARENA", "SPINS", "REPEL", "SPIES", "PRIOR", "POOLS", - "PRESS", "RISER", "AREAS", "SPOON", "SALES", "CREEP", "CEASE", "LOOPS", "ASSES", - "CALLS", "CRIER", "COCOA", "SEERS", "LOONS", "SLIPS", "PAPER", "REELS", "RISES", - "POSSE", "RARER", "SOILS", "PIPER", "INANE", "LOCAL", "PEASE", "ONION", "SLEEP", - "LEASE", "RACER", "LEPER", "SILLS", "CELLS", -#if (USE_EXPANDED_DICT != 1) +// From: https://matthewminer.name/projects/calculators/wordle-words-left/ +// Number of words found: 298 +static const char _valid_words[][WORDLE_LENGTH + 1] = { + "PLACE", "SHONE", "POSER", "CHAIN", "CAPER", "POLAR", "LEARN", "SHORN", "PORCH", + "GRAPE", "GNASH", "CHAIR", "SCORE", "CIGAR", "GRASP", "SINCE", "SPIRE", "NEIGH", + "SHORE", "CHASE", "RAISE", "CAIRN", "PLIER", "LOSER", "GRACE", "LEASH", "PENAL", + "SLING", "RISEN", "LOGIC", "PRICE", "POISE", "SCALE", "SINGE", "SNARL", "LINER", + "ANGEL", "SNAIL", "PALER", "SCION", "ALONE", "AGILE", "APRON", "PERIL", "GRIPE", + "SPICE", "LOGIN", "REGAL", "CAROL", "SLICE", "CRONE", "LEACH", "COPSE", "SHEAR", + "ALIGN", "LARGE", "LAPSE", "AISLE", "NICER", "OCEAN", "OPERA", "ALIEN", "ACORN", + "ASHEN", "SHINE", "PANEL", "SPORE", "SCOPE", "SPACE", "PHASE", "AROSE", "CHOIR", + "SNIPE", "CHAOS", "RALPH", "EPOCH", "GRAIN", "SANER", "GROIN", "SLANG", "SLAIN", + "CRASH", "CLASP", "SHIRE", "SCONE", "ALONG", "APING", "NICHE", "CHEAP", "CHIRP", + "LAGER", "CHORE", "SNORE", "SHAPE", "RESIN", "PERCH", "PECAN", "GLARE", "GROAN", + "RHINO", "RENAL", "SALON", "GRAIL", "SEPIA", "LANCE", "PRONG", "RECAP", "CLONE", + "CLASH", "HORSE", "SOLAR", "HERON", "PEACH", "ARSON", "HINGE", "CLEAN", "CLING", + "PHONE", "CRANE", "CLANG", "SCORN", "SPEAR", "PLAIN", "PROSE", "SPOIL", "GONER", + "SHOAL", "REIGN", "CLEAR", "ANGER", "CHINA", "GRAPH", "PEARL", "CARGO", "CHOSE", + "SCALP", "CANOE", "RINSE", "RANGE", "LINGO", "RANCH", "PLANE", "SPINE", "REACH", + "CRISP", "PARSE", "RIPEN", "SNARE", "CLOSE", "SHARE", "CORAL", "NOISE", "SHARP", + "SPARE", "SONIC", "SCRAP", "SPIEL", "RELIC", "OPINE", "SCARE", "SPRIG", "SHALE", + "PANIC", "SONAR", "GROPE", "SLOPE", "ANGLE", "ORGAN", "PIANO", "PINCH", "GLEAN", + "PRONE", "ARISE", "ROACH", "SIREN", "CLASS", "POSSE", "INANE", "HENCE", "SNEER", + "PAGAN", "PREEN", "ROGER", "SPELL", "SHEEP", "SENSE", "INNER", "ALPHA", "SHEEN", + "SCREE", "CIRCA", "PRIOR", "RARER", "PEACE", "GENRE", "HELLO", "CACAO", "GORGE", + "GLOSS", "CRIER", "CROSS", "CREPE", "COLON", "CHILL", "ONION", "LINEN", "PIPER", + "SLOOP", "LEGAL", "SNOOP", "PAPER", "ALGAE", "LAPEL", "CHEER", "HIPPO", "PIECE", + "LILAC", "HONOR", "PAPAL", "ARENA", "APNEA", "RIPER", "SCENE", "SHALL", "NASAL", + "SPREE", "RIGOR", "EAGER", "LIEGE", "LEPER", "LEASE", "CORER", "SPOON", "GROSS", + "COACH", "CEASE", "GENIE", "HARSH", "PENCE", "CHILI", "SHELL", "CREEP", "RISER", + "ERASE", "CINCH", "SIEGE", "GOING", "SCOOP", "SPILL", "NOOSE", "EAGLE", "AGING", + "NIECE", "SPOOL", "APPLE", "SALSA", "LEECH", "GREEN", "IONIC", "LASSO", "CONCH", + "PENNE", "SLASH", "CANAL", "CRASS", "REPEL", "COCOA", "CRESS", "AGAPE", "EASEL", + "CELLO", "CONIC", "IGLOO", "RACER", "GOOSE", "ICING", "POOCH", "ILIAC", "GRASS", + "SHEER", "CANON", "ELOPE", "LOCAL", "EERIE", "COLOR", "AGREE", "PRESS", "GEESE", + "SLOSH", "SLEEP", "GRILL", "AGAIN", "GLASS", "PARER", "CHESS", "CACHE", "ERROR", + "LOOSE", }; + // These are words that'll never be used, but still need to be in the dictionary for guesses. -// Top 100K most common words from Wiktionary https://gist.github.com/h3xx/1976236 -// Number of words found: 470 -static const char _expanded_words[][WORDLE_LENGTH + 1] = { -#endif -#if (USE_EXPANDED_DICT != 0) - "PARIS", "APRIL", "SPAIN", "EINEN", "ASCII", "EINER", "SEINE", "AINSI", "ALICE", - "ALLES", "ALORS", "EINES", "ALLER", "PEINE", "PARCE", "CELLE", "CLARA", "ELLES", - "ELLEN", "OLISI", "ALLEN", "ISAAC", "APRES", "CROIS", "SANOI", "PASSE", "ELSIE", - "REINE", "ELLER", "AARON", "CLARE", "IRENE", "ANNIE", "ELLOS", "PARLE", "ALLAN", - "PELLE", "CAIRO", "SENOR", "PENSE", "CECIL", "SEELE", "ORION", "SELON", "COSAS", - "PASSA", "ELLIS", "CARLO", "ENNEN", "SILAS", "EENEN", "OSCAR", "ONCLE", "CESSE", - "SONNE", "ASSIS", "PRISE", "SERAI", "CELIA", "NOIRE", "NORSE", "SINNE", "LIESS", - "ELIAS", "REPOS", "COLIN", "NOIRS", "CLAIR", "CIELO", "PARLA", "SOINS", "LASSE", - "NELLA", "PAOLO", "SOLON", "REPAS", "NANCE", "PAINE", "SAISI", "ELISE", "CESAR", - "CANNA", "SALLE", "SINON", "SINAI", "LOIRE", "PENSA", "LEILA", "REISE", "ELLAS", - "POORE", "EARLE", "CERCA", "LEISE", "POOLE", "AILES", "SANOA", "LEONE", "LILLE", - "PROIE", "CARNE", "SPIEL", "CERES", "ENSIN", "CINCO", "ROLLO", "ARRAS", "SEIEN", - "PRIER", "ANNAN", "CALLE", "LIISA", "SALIR", "LESSE", "LESEN", "LIIAN", "NEERE", - "ARIEL", "PIENI", "PIERO", "ERANO", "ELENA", "SILLE", "NEALE", "SEENE", "ROLLE", - "NELLE", "SOLLE", "ESSER", "PASAR", "PREIS", "ASIAN", "SENCE", "ANSON", "SERRA", - "CONAN", "SERAS", "SIENA", "SOPRA", "RENEE", "ALINE", "CORSE", "ASSAI", "INSEL", - "ROSIE", "SONIA", "APPEL", "CRISE", "CIRCE", "LINIE", "RENAN", "CAIRE", "COLLA", - "SANOO", "EENER", "ANCOR", "NEPAL", "REINO", "LAINE", "SOONE", "ALAIN", "LAPSI", - "INCAS", "INNES", "CARON", "ROSEN", "CASAS", "NOLAN", "SERRE", "PESAR", "SEARS", - "LEPIC", "LISLE", "LOSSE", "CINNA", "SERIE", "RIRES", "CORSO", "SOIRS", "CREER", - "POCOS", "SIENS", "ARLES", "CROCE", "IONIC", "PONER", "ESSEN", "SANON", "CESSA", - "SERIA", "ALPES", "NINON", "LILLA", "AINOA", "CORPO", "LESER", "ILLIS", "ROPER", - "ANNEE", "PAIRE", "PEPIN", "ORIEL", "CANNE", "AIRES", "ARCIS", "EASIE", "ANNOS", - "COLLE", "SELLE", "EILEN", "CAPRI", "ERICA", "ROCCO", "ARIAN", "CLEON", "ALLIE", - "PONCE", "COPIE", "INNAN", "NOCES", "NAPPE", "CORNE", "ESIIN", "ENCOR", "LORNA", - "SACRE", "PAPEL", "SAILE", "SAEPE", "CREON", "LLENO", "ELISA", "PASSO", "ASILE", - "LORCS", "ASIAA", "SANIN", "ONNEN", "SONNA", "AILIE", "ALIIS", "ECOLE", "CREES", - "PRESO", "CLARO", "EARES", "ROSSI", "COREA", "SANAN", "AESOP", "SAPOR", "EISEN", - "ACASO", "PARAS", "NANON", "LAPIS", "ARRAN", "CLLIA", "SACRA", "PRINS", "CENCI", - "CLAES", "SLAAP", "ROLLA", "COLES", "LORNE", "OLELO", "CASSE", "NILES", "PASOS", - "ESSAI", "ROSAS", "LLENA", "LEERE", "CLASE", "CALOR", "ROSSE", "ALLEE", "SOREL", - "SANAA", "SLONE", "OLSEN", "OOREN", "PARER", "PASSI", "POSSA", "PLAIE", "OPERE", - "SCAPE", "POLEN", "RIPON", "SCALA", "AILLE", "PALOS", "CLAPP", "ESCAP", "ELLEI", - "IONIA", "NICOL", "PAESE", "PERON", "ORSON", "INNEN", "AISNE", "RANCE", "SLAAN", - "PAOLI", "COLLO", "ANNAS", "ERROL", "CLERC", "SAINE", "RAINA", "PRESE", "PARIA", - "PERLE", "RECAL", "SINAE", "PESER", "OISIN", "PLENA", "CARLE", "PERES", "SACAR", - "ALPEN", "CORRE", "ACCES", "RILLA", "ANNAL", "PERSE", "SAALE", "PERRO", "AILSA", - "POCAS", "SOLEN", "PLASE", "SOLIS", "PAPPI", "COPIA", "ARIES", "ROCCA", "ALIOS", - "ROCAS", "PELOS", "NEPOS", "COLPA", "ISORA", "LECON", "SOANE", "SNELL", "ILLOS", - "PILAR", "ECLAC", "PRESA", "SILLA", "NIELS", "LIPPO", "CROLL", "PONEN", "POSEE", - "PIPPA", "ILLAN", "CENIS", "SANNA", "RASSI", "CERRO", "SCENA", "CASOS", "COLPO", - "POSSO", "POSEN", "EINAR", "ISLAS", "IPSIS", "SALEN", "ASIEN", "CREAN", "LENIN", - "LOCIS", "NENNE", "ILION", "NARES", "ONNEA", "PALAA", "PIPPO", "POLIS", "RICOS", - "ELSON", "SNOOP", "ANNIS", "PROPE", "ELLIE", "ERNIE", "PLIER", "SERES", "REINA", - "LIPPE", "OLINE", "PARIE", "ARLEE", "NIAIS", "LEONI", "RAINE", "LARES", "SEINS", - "CARRE", "POILS", "ALENE", "LINEA", "NEARE", "PENSO", "PRISA", "CAPEL", "PAREA", - "PEECE", "SALIO", "COELO", "SCIRE", "NELLO", "LIENE", "ORICE", "EPAIS", "PERCE", - "ALLIS", "PEPLE", "LARNE", "NEILL", "OLLEN", "CASCA", "LAPIN", "OLLIE", "SALIC", - "LIANE", "REESE", "ELSLI", "SPION", "RIENS", "LILAS", "PAPPA", "ERRER", "SPISE", - "CELIE", "OLSON", "IRREN", "ARIAS", "ARION", "PASEO", "CAERE", "PISAN", "CARRO", - "PAROI", "NOONE", "SEPPI", "OPPIA", "SEALE", "LIPPI", "PELAS", "COCOS", "PLACA", - "CONOR", "LANCA", "OSASI", "ALOIS", "NAIRN", "PIENO", "SPASS", "SAONE", "ALNAR", - "CARIA", "PIENA", -#endif +static const char _possible_words[][WORDLE_LENGTH + 1] = { + "AALII", "AARGH", "ACAIS", "ACARI", "ACCAS", "ACERS", "ACHAR", "ACHES", "ACHOO", + "ACING", "ACINI", "ACNES", "ACRES", "ACROS", "AECIA", "AEGIS", "AEONS", "AERIE", + "AEROS", "AESIR", "AGARS", "AGENE", "AGERS", "AGGER", "AGGIE", "AGGRI", "AGGRO", + "AGHAS", "AGILA", "AGIOS", "AGLEE", "AGLOO", "AGOGE", "AGONE", "AGONS", "AGORA", + "AGRIA", "AGRIN", "AGROS", "AHEAP", "AHIGH", "AHING", "AIGAS", "AINEE", "AINGA", + "AIOLI", "AIRER", "AIRNS", "ALAAP", "ALANE", "ALANG", "ALANS", "ALAPA", "ALAPS", + "ALCOS", "ALECS", "ALEPH", "ALGAL", "ALGAS", "ALGIN", "ALGOR", "ALIAS", "ALINE", + "ALLEE", "ALLEL", "ALLIS", "ALOES", "ALOHA", "ALOIN", "ALOOS", "ANANA", "ANCHO", + "ANCLE", "ANCON", "ANEAR", "ANELE", "ANGAS", "ANGLO", "ANIGH", "ANILE", "ANILS", + "ANION", "ANISE", "ANLAS", "ANNAL", "ANNAS", "ANOAS", "ANOLE", "ANSAE", "APACE", + "APAGE", "APERS", "APGAR", "APHIS", "APIAN", "APIOL", "APISH", "APOOP", "APPAL", + "APPEL", "APPRO", "APRES", "APSES", "APSIS", "APSOS", "ARARS", "ARCHI", "ARCOS", + "AREAE", "AREAL", "AREAR", "AREAS", "ARECA", "AREIC", "ARENE", "AREPA", "ARERE", + "ARGAL", "ARGAN", "ARGIL", "ARGLE", "ARGOL", "ARGON", "ARIAS", "ARIEL", "ARILS", + "ARISH", "ARLES", "ARNAS", "AROHA", "ARPAS", "ARPEN", "ARRAH", "ARRAS", "ARRIS", + "ARSES", "ARSIS", "ASANA", "ASCON", "ASHES", "ASPEN", "ASPER", "ASPIC", "ASPIE", + "ASPIS", "ASPRO", "ASSAI", "ASSES", "CACAS", "CAECA", "CAESE", "CAGER", "CAGES", + "CAINS", "CALLA", "CALLS", "CALOS", "CALPA", "CALPS", "CANEH", "CANER", "CANES", + "CANGS", "CANNA", "CANNS", "CANSO", "CAPAS", "CAPES", "CAPHS", "CAPLE", "CAPON", + "CAPOS", "CAPRI", "CARAP", "CARER", "CARES", "CARLE", "CARLS", "CARNS", "CARON", + "CARPI", "CARPS", "CARRS", "CARSE", "CASAS", "CASCO", "CASES", "CECAL", "CEILI", + "CEILS", "CELLA", "CELLI", "CELLS", "CENSE", "CEORL", "CEPES", "CERCI", "CERES", + "CERGE", "CERIA", "CERIC", "CERNE", "CEROC", "CEROS", "CESSE", "CHACE", "CHACO", + "CHAIS", "CHALS", "CHANA", "CHANG", "CHAPE", "CHAPS", "CHARA", "CHARE", "CHARR", + "CHARS", "CHEEP", "CHELA", "CHELP", "CHERE", "CHIAO", "CHIAS", "CHICA", "CHICH", + "CHICO", "CHICS", "CHIEL", "CHILE", "CHINE", "CHING", "CHINO", "CHINS", "CHIPS", + "CHIRL", "CHIRO", "CHIRR", "CHOCO", "CHOCS", "CHOGS", "CHOIL", "CHOLA", "CHOLI", + "CHOLO", "CHONS", "CHOON", "CHOPS", "CIELS", "CILIA", "CILLS", "CINES", "CIONS", + "CIPPI", "CIRCS", "CIRES", "CIRLS", "CIRRI", "CISCO", "CLACH", "CLAES", "CLAGS", + "CLANS", "CLAPS", "CLARO", "CLEEP", "CLEGS", "CLEPE", "CLIES", "CLINE", "CLIPE", + "CLIPS", "CLOGS", "CLONS", "CLOOP", "CLOPS", "COALA", "COALS", "COCAS", "COCCI", + "COCCO", "COCOS", "COGIE", "COGON", "COHEN", "COHOE", "COHOG", "COHOS", "COIGN", + "COILS", "COINS", "COIRS", "COLAS", "COLES", "COLIC", "COLIN", "COLLS", "COLOG", + "CONES", "CONGA", "CONGE", "CONGO", "CONIA", "CONIN", "CONNE", "CONNS", "COOCH", + "COOEE", "COOER", "COOLS", "COONS", "COOPS", "COPAL", "COPEN", "COPER", "COPES", + "COPRA", "CORES", "CORGI", "CORIA", "CORNI", "CORNO", "CORNS", "CORPS", "CORSE", + "CORSO", "COSEC", "COSES", "COSIE", "CRAAL", "CRAGS", "CRAIC", "CRAIG", "CRANS", + "CRAPE", "CRAPS", "CRARE", "CREEL", "CREES", "CRENA", "CREPS", "CRIAS", "CRIES", + "CRINE", "CRIOS", "CRIPE", "CRIPS", "CRISE", "CROCI", "CROCS", "CROGS", "CRONS", + "CROOL", "CROON", "CROPS", "CRORE", "EAGRE", "EALES", "EARLS", "EARNS", "EASER", + "EASES", "EASLE", "ECHES", "ECHOS", "EGERS", "EGGAR", "EGGER", "EHING", "EIGNE", + "EISEL", "ELAIN", "ELANS", "ELCHI", "ELOGE", "ELOIN", "ELOPS", "ELPEE", "ELSIN", + "ENIAC", "ENNOG", "ENOLS", "ENROL", "EORLS", "EOSIN", "EPEES", "EPHAH", "EPHAS", + "EPHOR", "EPICS", "EPRIS", "ERGON", "ERGOS", "ERICA", "ERICS", "ERING", "ERNES", + "EROSE", "ERSES", "ESCAR", "ESILE", "ESNES", "ESSES", "GAGER", "GAGES", "GAINS", + "GAIRS", "GALAH", "GALAS", "GALEA", "GALES", "GALLS", "GALOP", "GANCH", "GANGS", + "GAOLS", "GAPER", "GAPES", "GAPOS", "GARES", "GARIS", "GARNI", "GARRE", "GASES", + "GASPS", "GEALS", "GEANS", "GEARE", "GEARS", "GEEPS", "GELEE", "GENAL", "GENAS", + "GENES", "GENIC", "GENII", "GENIP", "GENOA", "GENRO", "GERAH", "GERES", "GERLE", + "GERNE", "GESSE", "GESSO", "GHEES", "GIGAS", "GIGHE", "GILAS", "GILLS", "GINCH", + "GINGE", "GINGS", "GIPON", "GIPPO", "GIRLS", "GIRNS", "GIRON", "GIROS", "GIRRS", + "GIRSH", "GLACE", "GLAIR", "GLANS", "GLEES", "GLEIS", "GLENS", "GLIAL", "GLIAS", + "GLOGG", "GLOOP", "GLOPS", "GNARL", "GNARR", "GNARS", "GOALS", "GOELS", "GOERS", + "GOGGA", "GOGOS", "GOIER", "GOLES", "GOLPE", "GOLPS", "GONCH", "GONGS", "GONIA", + "GONNA", "GOOGS", "GOOLS", "GOONS", "GOOPS", "GOORS", "GORAL", "GORAS", "GORES", + "GORIS", "GORPS", "GORSE", "GOSSE", "GRAAL", "GRAIP", "GRANA", "GRANS", "GRECE", + "GREES", "GREGE", "GREGO", "GREIN", "GRENS", "GRESE", "GRICE", "GRIGS", "GRINS", + "GRIPS", "GRISE", "GROGS", "GRONE", "GRRLS", "GRRRL", "HAARS", "HAGGS", "HAHAS", + "HAILS", "HAINS", "HAIRS", "HALAL", "HALER", "HALES", "HALLO", "HALLS", "HALON", + "HALOS", "HALSE", "HANAP", "HANCE", "HANCH", "HANGI", "HANGS", "HANSA", "HANSE", + "HAOLE", "HAPPI", "HARES", "HARLS", "HARNS", "HAROS", "HARPS", "HASPS", "HEALS", + "HEAPS", "HEARE", "HEARS", "HEELS", "HEIGH", "HEILS", "HEIRS", "HELES", "HELIO", + "HELLS", "HELOS", "HELPS", "HENCH", "HENGE", "HENNA", "HEPAR", "HERES", "HERLS", + "HERNS", "HEROS", "HERSE", "HESPS", "HIGHS", "HILAR", "HILCH", "HILLO", "HILLS", + "HINGS", "HIOIS", "HIREE", "HIRER", "HIRES", "HOARS", "HOERS", "HOGAN", "HOGEN", + "HOGGS", "HOGHS", "HOING", "HOISE", "HOLES", "HOLLA", "HOLLO", "HOLON", "HOLOS", + "HONAN", "HONER", "HONES", "HONGI", "HONGS", "HOOCH", "HOONS", "HOOPS", "HOORS", + "HOOSH", "HOPER", "HOPES", "HORAH", "HORAL", "HORAS", "HORIS", "HORNS", "HOSEL", + "HOSEN", "HOSER", "HOSES", "ICERS", "ICHES", "ICHOR", "ICIER", "ICONS", "IGAPO", + "ILEAC", "ILEAL", "ILIAL", "ILLER", "INCEL", "INCLE", "INCOG", "INGAN", "INGLE", + "INION", "INSPO", "IPPON", "IRING", "IRONE", "IRONS", "ISHES", "ISLES", "ISNAE", + "ISSEI", "LAARI", "LACER", "LACES", "LAERS", "LAGAN", "LAHAL", "LAHAR", "LAICH", + "LAICS", "LAIGH", "LAIRS", "LALLS", "LANAI", "LANAS", "LANCH", "LANES", "LAPIN", + "LAPIS", "LARCH", "LAREE", "LARES", "LARGO", "LARIS", "LARNS", "LASER", "LASES", + "LASSI", "LEANS", "LEAPS", "LEARE", "LEARS", "LEEAR", "LEEPS", "LEERS", "LEESE", + "LEGER", "LEGES", "LEGGE", "LEGGO", "LEHRS", "LEIRS", "LEISH", "LENES", "LENGS", + "LENIS", "LENOS", "LENSE", "LEONE", "LEPRA", "LERES", "LERPS", "LESES", "LIANA", + "LIANE", "LIANG", "LIARS", "LICHI", "LIENS", "LIERS", "LIGAN", "LIGER", "LIGGE", + "LIGNE", "LILLS", "LILOS", "LINAC", "LINCH", "LINES", "LINGA", "LINGS", "LININ", + "LINNS", "LINOS", "LIONS", "LIPAS", "LIPES", "LIPIN", "LIPOS", "LIRAS", "LISLE", + "LISPS", "LLANO", "LOACH", "LOANS", "LOCHE", "LOCHS", "LOCIE", "LOCIS", "LOCOS", + "LOESS", "LOGAN", "LOGES", "LOGIA", "LOGIE", "LOGOI", "LOGON", "LOGOS", "LOHAN", + "LOINS", "LOIPE", "LOIRS", "LOLLS", "LOLOG", "LONER", "LONGA", "LONGE", "LONGS", + "LOOIE", "LOONS", "LOOPS", "LOPER", "LOPES", "LORAL", "LORAN", "LOREL", "LORES", + "LORIC", "LORIS", "LOSEL", "LOSEN", "LOSES", "NAANS", "NACHE", "NACHO", "NACRE", + "NAGAS", "NAGOR", "NAHAL", "NAILS", "NAIRA", "NALAS", "NALLA", "NANAS", "NANCE", + "NANNA", "NANOS", "NAPAS", "NAPES", "NAPOO", "NAPPA", "NAPPE", "NARAS", "NARCO", + "NARCS", "NARES", "NARIC", "NARIS", "NARRE", "NASHI", "NEALS", "NEAPS", "NEARS", + "NEELE", "NEEPS", "NEESE", "NEGRO", "NELIS", "NENES", "NEONS", "NEPER", "NERAL", + "NEROL", "NGAIO", "NGANA", "NICOL", "NIGER", "NIGHS", "NIHIL", "NILLS", "NINER", + "NINES", "NINON", "NIPAS", "NIRLS", "NISEI", "NISSE", "NOAHS", "NOELS", "NOGGS", + "NOILS", "NOIRS", "NOLES", "NOLLS", "NOLOS", "NONAS", "NONCE", "NONES", "NONGS", + "NONIS", "NOONS", "NOOPS", "NOPAL", "NORIA", "NORIS", "NOSER", "NOSES", "OASES", + "OASIS", "OCHER", "OCHES", "OCHRE", "OCREA", "OGEES", "OGGIN", "OGLER", "OGLES", + "OGRES", "OHIAS", "OHING", "OHONE", "OILER", "OLEIC", "OLEIN", "OLEOS", "OLIOS", + "OLLAS", "OLLER", "OLLIE", "OLPAE", "OLPES", "ONCER", "ONCES", "ONERS", "OORIE", + "OOSES", "OPAHS", "OPALS", "OPENS", "OPEPE", "OPING", "OPPOS", "OPSIN", "ORACH", + "ORALS", "ORANG", "ORCAS", "ORCIN", "ORGIA", "ORGIC", "ORIEL", "ORLES", "ORLON", + "ORLOP", "ORNIS", "ORPIN", "ORRIS", "OSCAR", "OSHAC", "OSIER", "OSSIA", "PAALS", + "PAANS", "PACAS", "PACER", "PACES", "PACHA", "PACOS", "PAEAN", "PAEON", "PAGER", + "PAGES", "PAGLE", "PAGRI", "PAILS", "PAINS", "PAIRE", "PAIRS", "PAISA", "PAISE", + "PALAS", "PALEA", "PALES", "PALIS", "PALLA", "PALLS", "PALPI", "PALPS", "PALSA", + "PANCE", "PANES", "PANGA", "PANGS", "PANNE", "PANNI", "PAOLI", "PAOLO", "PAPAS", + "PAPES", "PAPPI", "PARAE", "PARAS", "PARCH", "PAREN", "PAREO", "PARES", "PARGE", + "PARGO", "PARIS", "PARLE", "PAROL", "PARPS", "PARRA", "PARRS", "PASEO", "PASES", + "PASHA", "PASSE", "PEAGE", "PEAGS", "PEALS", "PEANS", "PEARE", "PEARS", "PEASE", + "PECHS", "PEECE", "PEELS", "PEENS", "PEEPE", "PEEPS", "PEERS", "PEGHS", "PEINS", + "PEISE", "PELAS", "PELES", "PELLS", "PELON", "PENES", "PENGO", "PENIE", "PENIS", + "PENNA", "PENNI", "PEONS", "PEPLA", "PEPOS", "PEPSI", "PERAI", "PERCE", "PERCS", + "PEREA", "PERES", "PERIS", "PERNS", "PEROG", "PERPS", "PERSE", "PESOS", "PHAGE", + "PHANG", "PHARE", "PHEER", "PHENE", "PHEON", "PHESE", "PHIAL", "PHISH", "PHOCA", + "PHONO", "PHONS", "PIANI", "PIANS", "PICAL", "PICAS", "PICRA", "PIERS", "PIING", + "PILAE", "PILAO", "PILAR", "PILCH", "PILEA", "PILEI", "PILER", "PILES", "PILIS", + "PILLS", "PINAS", "PINES", "PINGO", "PINGS", "PINNA", "PINON", "PIONS", "PIPAL", + "PIPAS", "PIPES", "PIPIS", "PIRAI", "PIRLS", "PIRNS", "PIROG", "PISCO", "PISES", + "PISOS", "PLAAS", "PLAGE", "PLANS", "PLAPS", "PLASH", "PLEAS", "PLENA", "PLEON", + "PLESH", "PLICA", "PLIES", "PLING", "PLONG", "PLOPS", "POACH", "POEPS", "POGGE", + "POGOS", "POLER", "POLES", "POLIO", "POLIS", "POLLS", "POLOS", "PONCE", "PONES", + "PONGA", "PONGO", "PONGS", "POOHS", "POOLS", "POONS", "POOPS", "POORI", "POPES", + "POPPA", "PORAE", "PORAL", "PORER", "PORES", "PORGE", "PORIN", "PORNO", "PORNS", + "POSES", "POSHO", "PRANA", "PRANG", "PRAOS", "PRASE", "PREES", "PREON", "PREOP", + "PREPS", "PRESA", "PRESE", "PRIAL", "PRIER", "PRIES", "PRIGS", "PRILL", "PRION", + "PRISE", "PRISS", "PROAS", "PROGS", "PROIN", "PROLE", "PROLL", "PROPS", "PRORE", + "PROSO", "PROSS", "PSION", "PSOAE", "PSOAI", "PSOAS", "PSORA", "RACES", "RACHE", + "RACON", "RAGAS", "RAGEE", "RAGER", "RAGES", "RAGGA", "RAGGS", "RAGIS", "RAIAS", + "RAILE", "RAILS", "RAINE", "RAINS", "RALES", "RANAS", "RANCE", "RANEE", "RANGA", + "RANGI", "RANGS", "RANIS", "RAPER", "RAPES", "RAPHE", "RAPPE", "RAREE", "RARES", + "RASER", "RASES", "RASPS", "RASSE", "REAIS", "REALO", "REALS", "REANS", "REAPS", + "REARS", "RECAL", "RECCE", "RECCO", "RECON", "REECH", "REELS", "REENS", "REGAR", + "REGES", "REGGO", "REGIE", "REGNA", "REGOS", "REINS", "RELIE", "RELLO", "RENGA", + "RENIG", "RENIN", "RENNE", "RENOS", "REOIL", "REORG", "REPEG", "REPIN", "REPLA", + "REPOS", "REPPS", "REPRO", "RERAN", "RERIG", "RESEE", "RESES", "RHEAS", "RHIES", + "RHINE", "RHONE", "RIALS", "RICER", "RICES", "RICIN", "RIELS", "RIGGS", "RIGOL", + "RILES", "RILLE", "RILLS", "RINES", "RINGS", "RIPES", "RIPPS", "RISES", "RISHI", + "RISPS", "ROANS", "ROARS", "ROHES", "ROILS", "ROINS", "ROLAG", "ROLES", "ROLLS", + "RONEO", "RONES", "RONIN", "RONNE", "ROONS", "ROOPS", "ROOSA", "ROOSE", "ROPER", + "ROPES", "RORAL", "RORES", "RORIC", "RORIE", "ROSES", "ROSHI", "ROSIN", "SAAGS", + "SACRA", "SAGAS", "SAGER", "SAGES", "SAGOS", "SAICE", "SAICS", "SAIGA", "SAILS", + "SAINE", "SAINS", "SAIRS", "SALAL", "SALEP", "SALES", "SALIC", "SALLE", "SALOL", + "SALOP", "SALPA", "SALPS", "SALSE", "SANES", "SANGA", "SANGH", "SANGO", "SANGS", + "SANSA", "SAOLA", "SAPAN", "SAPOR", "SARAN", "SAREE", "SARGE", "SARGO", "SARIN", + "SARIS", "SAROS", "SASER", "SASIN", "SASSE", "SCAGS", "SCAIL", "SCALA", "SCALL", + "SCANS", "SCAPA", "SCAPE", "SCAPI", "SCARP", "SCARS", "SCENA", "SCOGS", "SCOOG", + "SCOPA", "SCOPS", "SCRAE", "SCRAG", "SCRAN", "SCRIP", "SCROG", "SEALS", "SEANS", + "SEARE", "SEARS", "SEASE", "SECCO", "SECHS", "SEELS", "SEEPS", "SEERS", "SEGAR", + "SEGNI", "SEGNO", "SEGOL", "SEGOS", "SEHRI", "SEILS", "SEINE", "SEIRS", "SEISE", + "SELAH", "SELES", "SELLA", "SELLE", "SELLS", "SENAS", "SENES", "SENGI", "SENNA", + "SENOR", "SENSA", "SENSI", "SEPAL", "SEPIC", "SERAC", "SERAI", "SERAL", "SERER", + "SERES", "SERGE", "SERIC", "SERIN", "SERON", "SERRA", "SERRE", "SERRS", "SESSA", + "SHAGS", "SHAHS", "SHANS", "SHAPS", "SHARN", "SHASH", "SHCHI", "SHEAL", "SHEAS", + "SHEEL", "SHEOL", "SHERE", "SHERO", "SHIAI", "SHIEL", "SHIER", "SHIES", "SHILL", + "SHINS", "SHIPS", "SHIRR", "SHIRS", "SHISH", "SHISO", "SHLEP", "SHOER", "SHOES", + "SHOGI", "SHOGS", "SHOLA", "SHOOL", "SHOON", "SHOOS", "SHOPE", "SHOPS", "SHORL", + "SHRIS", "SIALS", "SICES", "SIENS", "SIGHS", "SIGIL", "SIGLA", "SIGNA", "SIGNS", + "SILEN", "SILER", "SILES", "SILLS", "SILOS", "SINES", "SINGS", "SINHS", "SIPES", + "SIREE", "SIRES", "SIRIH", "SIRIS", "SIROC", "SIRRA", "SISAL", "SISES", "SLAES", + "SLAGS", "SLANE", "SLAPS", "SLEER", "SLIER", "SLIPE", "SLIPS", "SLISH", "SLOAN", + "SLOES", "SLOGS", "SLOPS", "SNAGS", "SNAPS", "SNARS", "SNASH", "SNEAP", "SNEES", + "SNELL", "SNIES", "SNIGS", "SNIPS", "SNOEP", "SNOGS", "SNOOL", "SOAPS", "SOARE", + "SOARS", "SOCAS", "SOCES", "SOCLE", "SOGER", "SOILS", "SOLAH", "SOLAN", "SOLAS", + "SOLEI", "SOLER", "SOLES", "SOLON", "SOLOS", "SONCE", "SONES", "SONGS", "SONNE", + "SONSE", "SOOLE", "SOOLS", "SOOPS", "SOPHS", "SOPOR", "SOPRA", "SORAL", "SORAS", + "SOREE", "SOREL", "SORER", "SORES", "SORGO", "SORNS", "SORRA", "SPAER", "SPAES", + "SPAGS", "SPAHI", "SPAIL", "SPAIN", "SPALE", "SPALL", "SPANE", "SPANG", "SPANS", + "SPARS", "SPEAL", "SPEAN", "SPECS", "SPEEL", "SPEER", "SPEIL", "SPEIR", "SPEOS", + "SPIAL", "SPICA", "SPICS", "SPIER", "SPIES", "SPILE", "SPINA", "SPINS", "SPLOG", + "SPOOR", "SPOSH", "SPRAG", "SPROG", }; -#if (USE_EXPANDED_DICT == 1) -static const char _expanded_words[][WORDLE_LENGTH + 1] = {}; -#endif - - -static const uint16_t _num_words = (sizeof(_legal_words) / sizeof(_legal_words[0])); -static const uint16_t _num_expanded_words = (sizeof(_expanded_words) / sizeof(_expanded_words[0])); +static const uint16_t _num_words = (sizeof(_valid_words) / sizeof(_valid_words[0])); +static const uint16_t _num_possible_words = (sizeof(_possible_words) / sizeof(_possible_words[0])); static const uint8_t _num_valid_letters = (sizeof(_valid_letters) / sizeof(_valid_letters[0])); static uint32_t get_random(uint32_t max) { @@ -211,24 +317,24 @@ static uint32_t check_word_in_dict(uint8_t *word_elements) { for (uint16_t i = 0; i < _num_words; i++) { is_exact_match = true; for (size_t j = 0; j < WORDLE_LENGTH; j++) { - if (_valid_letters[word_elements[j]] != _legal_words[i][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 < _num_expanded_words; i++) { + for (uint16_t i = 0; i < _num_possible_words; i++) { is_exact_match = true; for (size_t j = 0; j < WORDLE_LENGTH; j++) { - if (_valid_letters[word_elements[j]] != _expanded_words[i][j]) { + if (_valid_letters[word_elements[j]] != _possible_words[i][j]) { is_exact_match = false; break; } } if (is_exact_match) return _num_words + i; } - return _num_words + _num_expanded_words; + return _num_words + _num_possible_words; } static bool check_word(wordle_state_t *state) { @@ -236,7 +342,7 @@ static bool check_word(wordle_state_t *state) { 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]] == _legal_words[state->curr_answer][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; } @@ -251,7 +357,7 @@ static bool check_word(wordle_state_t *state) { 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]] == _legal_words[state->curr_answer][j]) { + 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; @@ -279,7 +385,7 @@ static void reset_all_elements(wordle_state_t *state) { state->word_elements_result[i] = WORDLE_LETTER_WRONG; } for (size_t i = 0; i < WORDLE_MAX_ATTEMPTS; i++) { - state->guessed_words[i] = _num_words + _num_expanded_words; + state->guessed_words[i] = _num_words + _num_possible_words; } state->using_random_guess = false; state->attempt = 0; @@ -300,7 +406,7 @@ static void reset_board(wordle_state_t *state) { display_playing(state); watch_display_string(" -", 4); #if __EMSCRIPTEN__ - printf("ANSWER: %s\r\n", _legal_words[state->curr_answer]); + printf("ANSWER: %s\r\n", _valid_words[state->curr_answer]); #endif } @@ -368,7 +474,7 @@ static void display_already_guessed(wordle_state_t *state) { static void display_lose(wordle_state_t *state, uint8_t subsecond) { char buf[WORDLE_LENGTH + 6]; - sprintf(buf," L %s", subsecond % 2 ? _legal_words[state->curr_answer] : " "); + sprintf(buf," L %s", subsecond % 2 ? _valid_words[state->curr_answer] : " "); watch_display_string(buf, 0); } @@ -486,7 +592,7 @@ static bool act_on_btn(wordle_state_t *state, const uint8_t pin) { static void get_result(wordle_state_t *state) { // Check if it's in the dict uint16_t in_dict = check_word_in_dict(state->word_elements); - if (in_dict == _num_words + _num_expanded_words) { + if (in_dict == _num_words + _num_possible_words) { display_not_in_dict(state); return; } @@ -522,7 +628,7 @@ static void get_result(wordle_state_t *state) { } #if (USE_RANDOM_GUESS != 0) -static const uint16_t _num_unique_words = 157; // The _legal_words array begins with this many words where each letter is different. +static const uint16_t _num_unique_words = 157; // The _valid_words array begins with this many words where each letter is different. 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 @@ -531,7 +637,7 @@ static void insert_random_guess(wordle_state_t *state) { for (size_t i = 0; i < WORDLE_LENGTH; i++) { for (size_t j = 0; j < _num_valid_letters; j++) { - if (_legal_words[random_guess][i] == _valid_letters[j]) + if (_valid_words[random_guess][i] == _valid_letters[j]) state->word_elements[i] = j; } } diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index 00c97b3..62a4571 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -71,13 +71,6 @@ */ #define USE_RANDOM_GUESS 2 -/* USE_EXPANDED_DICT - * 0 = don't use it at all (saves 2.8KB of ROM) - * 1 = Include the expanded dict in answers - * 2 = Only include it in the dict for guessing, but it's never an answer -*/ -#define USE_EXPANDED_DICT 2 - typedef enum { WORDLE_LETTER_WRONG = 0, WORDLE_LETTER_WRONG_LOC, diff --git a/utils/wordle_face/wordle_list.py b/utils/wordle_face/wordle_list.py index da370b7..ed90988 100644 --- a/utils/wordle_face/wordle_list.py +++ b/utils/wordle_face/wordle_list.py @@ -1,953 +1,1101 @@ import random, itertools, time, ast -# From: https://gist.github.com/shmookey/b28e342e1b1756c4700f42f17102c2ff -legal_list = [ - "ABACK", "ABAFT", "ABASE", "ABATE", "ABBEY", "ABBOT", "ABHOR", "ABIDE", "ABLER", "ABODE", "ABOUT", "ABOVE", - "ABUSE", "ABYSS", "ACHED", "ACHES", "ACIDS", "ACORN", "ACRES", "ACRID", "ACTED", "ACTOR", "ACUTE", "ADAGE", - "ADAPT", "ADDED", "ADDER", "ADEPT", "ADIEU", "ADMIT", "ADOBE", "ADOPT", "ADORE", "ADORN", "ADULT", "AEGIS", - "AEONS", "AFFIX", "AFIRE", "AFOOT", "AFTER", "AGAIN", "AGAPE", "AGATE", "AGENT", "AGILE", "AGING", "AGLOW", - "AGONY", "AGREE", "AHEAD", "AIDED", "AIDES", "AILED", "AIMED", "AIRED", "AISLE", "ALARM", "ALBUM", "ALDER", - "ALERT", "ALIAS", "ALIBI", "ALIEN", "ALIKE", "ALIVE", "ALLAY", "ALLEY", "ALLOT", "ALLOW", "ALLOY", "ALOES", - "ALOFT", "ALONE", "ALONG", "ALOOF", "ALOUD", "ALPHA", "ALTAR", "ALTER", "ALTOS", "AMASS", "AMAZE", "AMBER", - "AMBLE", "AMEND", "AMIGO", "AMISS", "AMITY", "AMONG", "AMOUR", "AMPLE", "AMPLY", "AMUSE", "ANGEL", "ANGER", - "ANGLE", "ANGRY", "ANGST", "ANIME", "ANKLE", "ANNEX", "ANNOY", "ANNUL", "ANTES", "ANTIC", "ANVIL", "APACE", - "APART", "APING", "APPAL", "APPLE", "APPLY", "APRON", "APTLY", "AREAS", "ARENA", "ARGUE", "ARISE", "ARMED", - "AROMA", "AROSE", "ARRAY", "ARROW", "ARSON", "ASHEN", "ASHES", "ASIDE", "ASKED", "ASKEW", "ASPEN", "ASSAY", - "ASSES", "ASSET", "ASTER", "ASTIR", "ATLAS", "ATOLL", "ATOMS", "ATONE", "ATTAR", "ATTIC", "AUDIO", "AUDIT", - "AUGER", "AUGHT", "AUGUR", "AUNTS", "AURAS", "AUTOS", "AVAIL", "AVERS", "AVERT", "AVOID", "AVOWS", "AWAIT", - "AWAKE", "AWARD", "AWARE", "AWFUL", "AWOKE", "AXIOM", "AXLES", "AZURE", "BABEL", "BABES", "BACKS", "BACON", - "BADGE", "BADLY", "BAGGY", "BAITS", "BAIZE", "BAKED", "BAKER", "BALES", "BALLS", "BALMY", "BANAL", "BANDS", - "BANDY", "BANGS", "BANJO", "BANKS", "BANNS", "BARBS", "BARDS", "BARED", "BARGE", "BARKS", "BARNS", "BARON", - "BASAL", "BASED", "BASER", "BASES", "BASIC", "BASIL", "BASIN", "BASIS", "BASSO", "BASTE", "BATCH", "BATED", - "BATHE", "BATHS", "BATON", "BAYOU", "BEACH", "BEADS", "BEADY", "BEAKS", "BEAMS", "BEANS", "BEARD", "BEARS", - "BEAST", "BEAUX", "BEECH", "BEETS", "BEFIT", "BEGAN", "BEGAT", "BEGET", "BEGIN", "BEGOT", "BEGUN", "BEING", - "BELIE", "BELLE", "BELLS", "BELLY", "BELOW", "BELTS", "BENCH", "BENDS", "BERGS", "BERRY", "BERTH", "BERYL", - "BESET", "BESOM", "BEVEL", "BIBLE", "BIDED", "BIDES", "BIGHT", "BIGOT", "BILGE", "BILLS", "BILLY", "BINDS", - "BIPED", "BIRCH", "BIRDS", "BIRTH", "BISON", "BITCH", "BITES", "BLACK", "BLADE", "BLAME", "BLAND", "BLANK", - "BLARE", "BLAST", "BLAZE", "BLEAK", "BLEAT", "BLEED", "BLEND", "BLENT", "BLESS", "BLEST", "BLIND", "BLINK", - "BLISS", "BLOCK", "BLOCS", "BLOND", "BLOOD", "BLOOM", "BLOTS", "BLOWN", "BLOWS", "BLUER", "BLUES", "BLUFF", - "BLUNT", "BLURT", "BLUSH", "BOARD", "BOARS", "BOAST", "BOATS", "BODED", "BODES", "BOGGY", "BOGUS", "BOILS", - "BOLES", "BOLTS", "BOMBS", "BONDS", "BONED", "BONES", "BONNY", "BONUS", "BOOBY", "BOOKS", "BOOMS", "BOONS", - "BOORS", "BOOST", "BOOTH", "BOOTS", "BOOTY", "BOOZE", "BORAX", "BORED", "BORES", "BORNE", "BOSOM", "BOUGH", - "BOUND", "BOUTS", "BOWED", "BOWEL", "BOWER", "BOWLS", "BOXED", "BOXER", "BOXES", "BRACE", "BRAGS", "BRAID", - "BRAIN", "BRAKE", "BRAND", "BRASS", "BRATS", "BRAVE", "BRAVO", "BRAWL", "BRAWN", "BREAD", "BREAK", "BREED", - "BRIAR", "BRIBE", "BRICK", "BRIDE", "BRIEF", "BRIER", "BRIGS", "BRIMS", "BRINE", "BRING", "BRINK", "BRINY", - "BRISK", "BROAD", "BROIL", "BROKE", "BROOD", "BROOK", "BROOM", "BROTH", "BROWN", "BROWS", "BRUIN", "BRUNT", - "BRUSH", "BRUTE", "BUCKS", "BUDGE", "BUGGY", "BUGLE", "BUILD", "BUILT", "BULBS", "BULGE", "BULKS", "BULKY", - "BULLS", "BULLY", "BUMPS", "BUNCH", "BUNKS", "BUOYS", "BURLY", "BURNS", "BURNT", "BURRO", "BURRS", "BURST", - "BUSHY", "BUSTS", "BUTTE", "BUTTS", "BUXOM", "BUYER", "CABAL", "CABBY", "CABIN", "CABLE", "CACAO", "CACHE", - "CADET", "CADRE", "CAGED", "CAGES", "CAIRN", "CAKED", "CAKES", "CALLS", "CALMS", "CALYX", "CAMEL", "CAMEO", - "CAMPS", "CANAL", "CANDY", "CANES", "CANNY", "CANOE", "CANON", "CANTO", "CAPER", "CAPES", "CAPON", "CARDS", - "CARED", "CARES", "CARGO", "CAROL", "CARRY", "CARTS", "CARVE", "CASED", "CASES", "CASKS", "CASTE", "CASTS", - "CATCH", "CATER", "CAUSE", "CAVED", "CAVES", "CAVIL", "CEASE", "CEDAR", "CEDED", "CELLS", "CENTS", "CHAFE", - "CHAFF", "CHAIN", "CHAIR", "CHALK", "CHAMP", "CHANT", "CHAOS", "CHAPS", "CHARM", "CHART", "CHARY", "CHASE", - "CHASM", "CHATS", "CHEAP", "CHEAT", "CHECK", "CHEEK", "CHEER", "CHEFS", "CHESS", "CHEST", "CHICK", "CHIDE", - "CHIEF", "CHILD", "CHILL", "CHIME", "CHINA", "CHINK", "CHINS", "CHIPS", "CHIRP", "CHOIR", "CHOKE", "CHOPS", - "CHORD", "CHOSE", "CHUCK", "CHUMP", "CHUMS", "CHUNK", "CHURL", "CHURN", "CHUTE", "CIDER", "CIGAR", "CINCH", - "CIRCA", "CITED", "CITES", "CIVET", "CIVIC", "CIVIL", "CLACK", "CLAIM", "CLAMP", "CLAMS", "CLANG", "CLANK", - "CLANS", "CLAPS", "CLASH", "CLASP", "CLASS", "CLAWS", "CLEAN", "CLEAR", "CLEFS", "CLEFT", "CLERK", "CLEWS", - "CLICK", "CLIFF", "CLIMB", "CLIME", "CLING", "CLINK", "CLIPS", "CLOAK", "CLOCK", "CLODS", "CLOGS", "CLOSE", - "CLOTH", "CLOUD", "CLOUT", "CLOVE", "CLOWN", "CLUBS", "CLUCK", "CLUES", "CLUMP", "CLUNG", "COACH", "COALS", - "COAST", "COATS", "COBRA", "COCKS", "COCOA", "CODES", "COILS", "COINS", "COLDS", "COLIC", "COLON", "COLTS", - "COMBS", "COMER", "COMES", "COMET", "COMIC", "COMMA", "CONCH", "CONES", "CONIC", "COOED", "COOKS", "COOLS", - "COPRA", "COPSE", "CORAL", "CORDS", "CORES", "CORKS", "CORNS", "CORPS", "COSTS", "COTES", "COUCH", "COUGH", - "COULD", "COUNT", "COUPE", "COUPS", "COURT", "COVER", "COVES", "COVET", "COVEY", "COWED", "COWER", "COYLY", - "COZEN", "CRABS", "CRACK", "CRAFT", "CRAGS", "CRAMP", "CRANE", "CRANK", "CRAPE", "CRASH", "CRASS", "CRATE", - "CRAVE", "CRAWL", "CRAZE", "CRAZY", "CREAK", "CREAM", "CREDO", "CREED", "CREEK", "CREEP", "CREPE", "CREPT", - "CRESS", "CREST", "CREWS", "CRIBS", "CRICK", "CRIED", "CRIER", "CRIES", "CRIME", "CRIMP", "CRISP", "CROAK", - "CROCK", "CRONE", "CRONY", "CROOK", "CROPS", "CROSS", "CROUP", "CROWD", "CROWN", "CROWS", "CRUDE", "CRUEL", - "CRUMB", "CRUSH", "CRUST", "CRYPT", "CUBES", "CUBIC", "CUBIT", "CUFFS", "CULTS", "CURDS", "CURED", "CURES", - "CURLS", "CURLY", "CURRY", "CURSE", "CURST", "CURVE", "CYCLE", "CYNIC", "DADDY", "DAILY", "DAIRY", "DAISY", - "DALES", "DALLY", "DAMES", "DAMPS", "DANCE", "DANDY", "DARED", "DARES", "DARTS", "DATED", "DATES", "DATUM", - "DAUBS", "DAUNT", "DAWNS", "DAZED", "DEALS", "DEALT", "DEANS", "DEARS", "DEATH", "DEBAR", "DEBIT", "DEBTS", - "DEBUT", "DECAY", "DECKS", "DECOY", "DECRY", "DEEDS", "DEEMS", "DEEPS", "DEFER", "DEIGN", "DEITY", "DELAY", - "DELLS", "DELTA", "DELVE", "DEMON", "DEMUR", "DENSE", "DENTS", "DEPOT", "DEPTH", "DERBY", "DESKS", "DETER", - "DEUCE", "DEVIL", "DIARY", "DICED", "DICES", "DICTA", "DIETS", "DIGIT", "DIKES", "DIMES", "DIMLY", "DINED", - "DINER", "DINES", "DINGY", "DIRGE", "DIRTY", "DISCS", "DISKS", "DITCH", "DITTO", "DITTY", "DIVAN", "DIVED", - "DIVER", "DIVES", "DIZZY", "DOCKS", "DODGE", "DOERS", "DOGMA", "DOING", "DOLED", "DOLLS", "DOMED", "DOMES", - "DONOR", "DOOMS", "DOORS", "DOSED", "DOSES", "DOTED", "DOTES", "DOUBT", "DOUGH", "DOVES", "DOWDY", "DOWNS", - "DOWNY", "DOWRY", "DOZED", "DOZEN", "DRAFT", "DRAGS", "DRAIN", "DRAKE", "DRAMA", "DRAMS", "DRANK", "DRAPE", - "DRAWL", "DRAWN", "DRAWS", "DRAYS", "DREAD", "DREAM", "DREGS", "DRESS", "DRIED", "DRIER", "DRIES", "DRIFT", - "DRILL", "DRILY", "DRINK", "DRIPS", "DRIVE", "DROLL", "DRONE", "DROOP", "DROPS", "DROSS", "DROVE", "DROWN", - "DRUGS", "DRUMS", "DRUNK", "DRYLY", "DUCAL", "DUCAT", "DUCHY", "DUCKS", "DUCTS", "DUELS", "DUETS", "DUKES", - "DULLY", "DUMMY", "DUMPS", "DUMPY", "DUNCE", "DUNES", "DUNNO", "DUPED", "DUPES", "DUSKY", "DUSTY", "DWARF", - "DWELL", "DWELT", "DYING", "DYKES", "EAGER", "EAGLE", "EARLS", "EARLY", "EARNS", "EARTH", "EASED", "EASEL", - "EASES", "EATEN", "EATER", "EAVES", "EBBED", "EBONY", "EDGED", "EDGES", "EDICT", "EDIFY", "EERIE", "EGGED", - "EIGHT", "EJECT", "ELATE", "ELBOW", "ELDER", "ELECT", "ELEGY", "ELFIN", "ELITE", "ELOPE", "ELUDE", "ELVES", - "EMAIL", "EMITS", "EMPTY", "ENACT", "ENDED", "ENDOW", "ENEMY", "ENJOY", "ENNUI", "ENROL", "ENSUE", "ENTER", - "ENTRY", "ENVOY", "EPICS", "EPOCH", "EQUAL", "EQUIP", "ERASE", "ERECT", "ERRED", "ERROR", "ESSAY", "ETHER", - "ETHIC", "EVADE", "EVENT", "EVERY", "EVILS", "EVOKE", "EXACT", "EXALT", "EXCEL", "EXERT", "EXILE", "EXIST", - "EXITS", "EXPEL", "EXTOL", "EXTRA", "EXULT", "EYING", "EYRIE", "FABLE", "FACED", "FACES", "FACTS", "FADED", - "FADES", "FAILS", "FAINT", "FAIRS", "FAIRY", "FAITH", "FAKIR", "FALLS", "FALSE", "FAMED", "FANCY", "FANGS", - "FARCE", "FARED", "FARES", "FARMS", "FASTS", "FATAL", "FATED", "FATES", "FATTY", "FAULT", "FAUNA", "FAUNS", - "FAWNS", "FEARS", "FEAST", "FEATS", "FEEDS", "FEELS", "FEIGN", "FEINT", "FELLS", "FELON", "FENCE", "FERAL", - "FERNS", "FERRY", "FETCH", "FETED", "FETID", "FETUS", "FEUDS", "FEVER", "FEWER", "FICHE", "FIEFS", "FIELD", - "FIEND", "FIERY", "FIFES", "FIFTH", "FIFTY", "FIGHT", "FILCH", "FILED", "FILES", "FILET", "FILLS", "FILLY", - "FILMS", "FILMY", "FILTH", "FINAL", "FINCH", "FINDS", "FINED", "FINER", "FINES", "FINIS", "FINNY", "FIORD", - "FIRED", "FIRES", "FIRMS", "FIRST", "FISHY", "FISTS", "FITLY", "FIVES", "FIXED", "FIXER", "FIXES", "FJORD", - "FLAGS", "FLAIL", "FLAIR", "FLAKE", "FLAKY", "FLAME", "FLANK", "FLAPS", "FLARE", "FLASH", "FLASK", "FLATS", - "FLAWS", "FLEAS", "FLECK", "FLEES", "FLEET", "FLESH", "FLICK", "FLIER", "FLIES", "FLING", "FLINT", "FLIRT", - "FLITS", "FLOAT", "FLOCK", "FLOES", "FLOOD", "FLOOR", "FLORA", "FLOSS", "FLOUR", "FLOUT", "FLOWN", "FLOWS", - "FLUES", "FLUFF", "FLUID", "FLUKE", "FLUME", "FLUNG", "FLUSH", "FLUTE", "FLYER", "FOAMS", "FOAMY", "FOCAL", - "FOCUS", "FOGGY", "FOILS", "FOIST", "FOLDS", "FOLIO", "FOLKS", "FOLLY", "FOODS", "FOOLS", "FORAY", "FORCE", - "FORDS", "FORGE", "FORGO", "FORKS", "FORMS", "FORTE", "FORTH", "FORTS", "FORTY", "FORUM", "FOUND", "FOUNT", - "FOURS", "FOWLS", "FOXES", "FOYER", "FRAIL", "FRAME", "FRANC", "FRANK", "FRAUD", "FREAK", "FREED", "FREER", - "FREES", "FRESH", "FRETS", "FRIAR", "FRIED", "FRILL", "FRISK", "FROCK", "FROGS", "FROND", "FRONT", "FROST", - "FROTH", "FROWN", "FROZE", "FRUIT", "FUDGE", "FUELS", "FUGUE", "FULLY", "FUMED", "FUMES", "FUNDS", "FUNGI", - "FUNNY", "FURRY", "FURZE", "FUSED", "FUSES", "FUSSY", "FUZZY", "GABLE", "GAILY", "GAINS", "GALES", "GALLS", - "GAMES", "GAMIN", "GAMMA", "GAMUT", "GANGS", "GAPED", "GAPES", "GASES", "GASPS", "GATES", "GAUDY", "GAUGE", - "GAUNT", "GAUZE", "GAUZY", "GAVEL", "GAWKY", "GAYER", "GAYLY", "GAZED", "GAZER", "GAZES", "GEARS", "GEESE", - "GENIE", "GENII", "GENRE", "GENTS", "GENUS", "GERMS", "GHOST", "GIANT", "GIBES", "GIDDY", "GIFTS", "GILDS", - "GILLS", "GIMME", "GIPSY", "GIRDS", "GIRLS", "GIRTH", "GIVEN", "GIVES", "GLADE", "GLAND", "GLARE", "GLASS", - "GLAZE", "GLEAM", "GLEAN", "GLENS", "GLIDE", "GLINT", "GLOAT", "GLOBE", "GLOOM", "GLORY", "GLOSS", "GLOVE", - "GLOWS", "GLUED", "GNASH", "GNATS", "GNAWS", "GNOME", "GOADS", "GOALS", "GOATS", "GODLY", "GOING", "GOLLY", - "GONGS", "GONNA", "GOODS", "GOODY", "GOOSE", "GORED", "GORGE", "GORSE", "GOTTA", "GOUGE", "GOURD", "GOUTY", - "GOWNS", "GRABS", "GRACE", "GRADE", "GRAFT", "GRAIN", "GRAMS", "GRAND", "GRANT", "GRAPE", "GRAPH", "GRASP", - "GRASS", "GRATE", "GRAVE", "GRAVY", "GRAZE", "GREAT", "GREED", "GREEN", "GREET", "GREYS", "GRIEF", "GRILL", - "GRIME", "GRIMY", "GRIND", "GRINS", "GRIPE", "GRIPS", "GRIST", "GROAN", "GROIN", "GROOM", "GROPE", "GROSS", - "GROUP", "GROVE", "GROWL", "GROWN", "GROWS", "GRUBS", "GRUEL", "GRUFF", "GRUNT", "GUANO", "GUARD", "GUESS", - "GUEST", "GUIDE", "GUILD", "GUILE", "GUILT", "GUISE", "GULCH", "GULFS", "GULLS", "GULLY", "GUMMY", "GUSTO", - "GUSTS", "GUSTY", "GYPSY", "HABIT", "HACKS", "HAILS", "HAIRS", "HAIRY", "HALED", "HALLS", "HALTS", "HALVE", - "HANDS", "HANDY", "HANGS", "HAPPY", "HARDY", "HAREM", "HARES", "HARMS", "HARPS", "HARPY", "HARRY", "HARSH", - "HARTS", "HASTE", "HASTY", "HATCH", "HATED", "HATER", "HAULS", "HAVEN", "HAVOC", "HAWKS", "HAZEL", "HEADS", - "HEADY", "HEALS", "HEAPS", "HEARD", "HEARS", "HEART", "HEATH", "HEATS", "HEAVE", "HEAVY", "HEDGE", "HEEDS", - "HEELS", "HEIRS", "HELIX", "HELLO", "HELMS", "HELPS", "HENCE", "HERBS", "HERDS", "HERON", "HEROS", "HEWED", - "HIDES", "HILLS", "HILLY", "HILTS", "HINDS", "HINGE", "HINTS", "HIRED", "HIRES", "HITCH", "HIVES", "HOARD", - "HOARY", "HOBBY", "HOIST", "HOLDS", "HOLES", "HOLLY", "HOMES", "HONEY", "HOODS", "HOOFS", "HOOKS", "HOOPS", - "HOOTS", "HOPED", "HOPES", "HORDE", "HORNS", "HORNY", "HORSE", "HOSTS", "HOTEL", "HOTLY", "HOUND", "HOURS", - "HOUSE", "HOVEL", "HOVER", "HOWLS", "HULKS", "HULLS", "HUMAN", "HUMID", "HUMPS", "HUMUS", "HUNCH", "HUNTS", - "HURLS", "HURRY", "HURTS", "HUSKS", "HUSKY", "HUSSY", "HYDRA", "HYENA", "HYMNS", "ICILY", "ICING", "IDEAL", - "IDEAS", "IDIOM", "IDIOT", "IDLED", "IDLER", "IDOLS", "IDYLL", "IGLOO", "IMAGE", "IMBUE", "IMPEL", "IMPLY", - "INANE", "INCUR", "INDEX", "INEPT", "INERT", "INFER", "INGOT", "INLET", "INNER", "INTER", "INURE", "IRATE", - "IRKED", "IRONS", "IRONY", "ISLES", "ISLET", "ISSUE", "ITEMS", "IVORY", "JACKS", "JADED", "JAILS", "JAUNT", - "JEANS", "JEERS", "JELLY", "JERKS", "JERKY", "JESTS", "JETTY", "JEWEL", "JIFFY", "JOINS", "JOINT", "JOKED", - "JOKER", "JOKES", "JOLLY", "JOUST", "JOYED", "JUDGE", "JUICE", "JUICY", "JUMPS", "JUNKS", "JUNTA", "JUROR", - "KARMA", "KEELS", "KEEPS", "KETCH", "KEYED", "KHAKI", "KICKS", "KILLS", "KINDA", "KINDS", "KINGS", "KIOSK", - "KITES", "KNACK", "KNAVE", "KNEAD", "KNEEL", "KNEES", "KNELL", "KNELT", "KNIFE", "KNITS", "KNOBS", "KNOCK", - "KNOLL", "KNOTS", "KNOWN", "KNOWS", "LABEL", "LACED", "LACES", "LACKS", "LADEN", "LADLE", "LAGER", "LAIRS", - "LAITY", "LAKES", "LAMBS", "LAMED", "LAMES", "LAMPS", "LANCE", "LANDS", "LANES", "LANKY", "LAPEL", "LAPSE", - "LARCH", "LARGE", "LARGO", "LARKS", "LARVA", "LASSO", "LASTS", "LATCH", "LATER", "LATHE", "LATHS", "LAUGH", - "LAWNS", "LAYER", "LEADS", "LEAFY", "LEAKS", "LEAKY", "LEANS", "LEAPS", "LEAPT", "LEARN", "LEASE", "LEASH", - "LEAST", "LEAVE", "LEDGE", "LEECH", "LEEKS", "LEGAL", "LEMME", "LEMON", "LENDS", "LEPER", "LEVEE", "LEVEL", - "LEVER", "LIARS", "LIBEL", "LICKS", "LIEGE", "LIENS", "LIFTS", "LIGHT", "LIKED", "LIKEN", "LIKER", "LIKES", - "LILAC", "LIMBO", "LIMBS", "LIMES", "LIMIT", "LINED", "LINEN", "LINER", "LINES", "LINGO", "LINKS", "LIONS", - "LISTS", "LITHE", "LIVED", "LIVER", "LIVES", "LIVID", "LLAMA", "LOADS", "LOAMY", "LOANS", "LOATH", "LOBBY", - "LOBES", "LOCAL", "LOCKS", "LOCUS", "LODGE", "LOFTY", "LOGES", "LOGIC", "LOGIN", "LOINS", "LONGS", "LOOKS", - "LOOMS", "LOONS", "LOOPS", "LOOSE", "LORDS", "LOSER", "LOSES", "LOTUS", "LOUSE", "LOUSY", "LOVED", "LOVER", - "LOVES", "LOWED", "LOWER", "LOWLY", "LOYAL", "LUCID", "LUCKY", "LULLS", "LUMPS", "LUMPY", "LUNAR", "LUNCH", - "LUNGE", "LUNGS", "LURCH", "LURED", "LURES", "LURID", "LURKS", "LUSTS", "LUSTY", "LUTES", "LYING", "LYMPH", - "LYNCH", "LYRIC", "MACES", "MADAM", "MADLY", "MAGIC", "MAIDS", "MAILS", "MAINS", "MAIZE", "MAJOR", "MAKER", - "MAKES", "MALES", "MAMMA", "MANES", "MANGA", "MANGE", "MANGO", "MANGY", "MANIA", "MANLY", "MANNA", "MANOR", - "MANSE", "MAPLE", "MARCH", "MARES", "MARKS", "MARRY", "MARSH", "MARTS", "MASKS", "MASON", "MASTS", "MATCH", - "MATED", "MATES", "MAUVE", "MAXIM", "MAYBE", "MAYOR", "MAZES", "MEALS", "MEALY", "MEANS", "MEANT", "MEATS", - "MEDAL", "MEDIA", "MEETS", "MELON", "MELTS", "MEMES", "MENDS", "MENUS", "MERCY", "MERES", "MERGE", "MERIT", - "MERRY", "MESAS", "METAL", "METED", "METER", "MEWED", "MIDST", "MIENS", "MIGHT", "MILCH", "MILES", "MILKY", - "MILLS", "MIMES", "MIMIC", "MINCE", "MINDS", "MINED", "MINER", "MINES", "MINOR", "MINTS", "MINUS", "MIRTH", - "MISER", "MISTS", "MITES", "MIXED", "MIXES", "MOANS", "MOATS", "MOCKS", "MODEL", "MODEM", "MODES", "MOIST", - "MOLAR", "MOLES", "MOMMA", "MONEY", "MONKS", "MONTH", "MOODS", "MOODY", "MOONS", "MOORS", "MOOSE", "MOPED", - "MORAL", "MORES", "MOSSY", "MOTES", "MOTHS", "MOTIF", "MOTOR", "MOTTO", "MOUND", "MOUNT", "MOURN", "MOUSE", - "MOUTH", "MOVED", "MOVER", "MOVES", "MOVIE", "MOWED", "MOWER", "MUCUS", "MUDDY", "MULES", "MULTI", "MUMMY", - "MUMPS", "MUNCH", "MURAL", "MURKY", "MUSED", "MUSES", "MUSIC", "MUSKY", "MUSTY", "MUTED", "MUTES", "MYRRH", - "MYTHS", "NABOB", "NAILS", "NAIVE", "NAKED", "NAMED", "NAMES", "NASAL", "NASTY", "NATAL", "NATTY", "NAVAL", - "NAVEL", "NAVES", "NEARS", "NECKS", "NEEDS", "NEEDY", "NEIGH", "NERVE", "NESTS", "NEVER", "NEWER", "NEWLY", - "NICER", "NICHE", "NIECE", "NIGHT", "NINNY", "NOBLE", "NOBLY", "NOISE", "NOISY", "NOMAD", "NONCE", "NOOKS", - "NOOSE", "NORTH", "NOSED", "NOSES", "NOTCH", "NOTED", "NOTES", "NOUNS", "NOVEL", "NUDGE", "NURSE", "NYMPH", - "OAKEN", "OAKUM", "OASES", "OASIS", "OATEN", "OATHS", "OBESE", "OBEYS", "OCCUR", "OCEAN", "OCHRE", "ODDER", - "ODDLY", "ODIUM", "OFFAL", "OFFER", "OFTEN", "OILED", "OLDEN", "OLDER", "OMENS", "OMITS", "ONION", "ONSET", - "OOZED", "OOZES", "OPALS", "OPENS", "OPERA", "OPINE", "OPIUM", "OPTIC", "ORBIT", "ORDER", "ORGAN", "OSIER", - "OTHER", "OTTER", "OUGHT", "OUNCE", "OUTDO", "OUTER", "OVALS", "OVARY", "OVENS", "OVERT", "OWING", "OWNED", - "OWNER", "OXIDE", "OZONE", "PACES", "PACKS", "PADDY", "PADRE", "PAEAN", "PAGAN", "PAGES", "PAILS", "PAINS", - "PAINT", "PAIRS", "PALED", "PALER", "PALES", "PALMS", "PALMY", "PALSY", "PANEL", "PANES", "PANGS", "PANIC", - "PANSY", "PANTS", "PAPAL", "PAPAS", "PAPER", "PARED", "PARKA", "PARKS", "PARRY", "PARSE", "PARTS", "PARTY", - "PASHA", "PASTE", "PASTY", "PATCH", "PATES", "PATHS", "PATIO", "PAUSE", "PAVED", "PAWED", "PAWNS", "PAYED", - "PAYER", "PEACE", "PEACH", "PEAKS", "PEALS", "PEARL", "PEARS", "PEASE", "PECKS", "PEDAL", "PEEPS", "PEERS", - "PELTS", "PENAL", "PENCE", "PENIS", "PENNY", "PEONS", "PERCH", "PERIL", "PESKY", "PESOS", "PESTS", "PETAL", - "PETTY", "PHASE", "PHIAL", "PHONE", "PHOTO", "PIANO", "PICKS", "PIECE", "PIERS", "PIETY", "PIGMY", "PIKES", - "PILED", "PILES", "PILLS", "PILOT", "PINCH", "PINED", "PINES", "PINKS", "PINTO", "PINTS", "PIOUS", "PIPED", - "PIPER", "PIPES", "PIQUE", "PITCH", "PITHY", "PIVOT", "PLACE", "PLAID", "PLAIN", "PLAIT", "PLANE", "PLANK", - "PLANS", "PLANT", "PLATE", "PLAYS", "PLAZA", "PLEAD", "PLEAS", "PLIED", "PLIES", "PLOTS", "PLUCK", "PLUGS", - "PLUMB", "PLUME", "PLUMS", "PLUSH", "PODIA", "POEMS", "POESY", "POETS", "POINT", "POISE", "POKED", "POKER", - "POKES", "POLAR", "POLES", "POLKA", "POLLS", "PONDS", "POOLS", "POPES", "POPPA", "POPPY", "PORCH", "PORED", - "PORES", "PORTS", "POSED", "POSER", "POSES", "POSSE", "POSTS", "POUCH", "POUND", "POURS", "POWER", "PRANK", - "PRATE", "PRAYS", "PRESS", "PREYS", "PRICE", "PRICK", "PRIDE", "PRIED", "PRIES", "PRIME", "PRINT", "PRIOR", - "PRISM", "PRIVY", "PRIZE", "PROBE", "PRONE", "PROOF", "PROPS", "PROSE", "PROSY", "PROUD", "PROVE", "PROWL", - "PROWS", "PROXY", "PRUDE", "PRUNE", "PSALM", "PSHAW", "PUDGY", "PUFFS", "PUFFY", "PULLS", "PULPY", "PULSE", - "PUMPS", "PUNCH", "PUPIL", "PUPPY", "PUREE", "PURER", "PURGE", "PURSE", "PUSSY", "PUTTY", "QUACK", "QUAFF", - "QUAIL", "QUAKE", "QUALM", "QUART", "QUASI", "QUAYS", "QUEEN", "QUEER", "QUELL", "QUERY", "QUEST", "QUEUE", - "QUICK", "QUIET", "QUILL", "QUILT", "QUIPS", "QUIRE", "QUITE", "QUITS", "QUOTA", "QUOTE", "QUOTH", "RABBI", - "RABID", "RACED", "RACER", "RACES", "RACKS", "RADII", "RADIO", "RAFTS", "RAGED", "RAGES", "RAIDS", "RAILS", - "RAINS", "RAINY", "RAISE", "RAJAH", "RAKED", "RAKES", "RALLY", "RANCH", "RANGE", "RANKS", "RAPID", "RARER", - "RARES", "RATED", "RATES", "RATIO", "RAVED", "RAVEN", "RAVES", "RAYON", "RAZED", "RAZOR", "REACH", "REACT", - "READS", "READY", "REALM", "REALS", "REAMS", "REAPS", "REARS", "REBEL", "REBUS", "REBUT", "RECUR", "REEDS", - "REEDY", "REEFS", "REEKS", "REELS", "REEVE", "REFER", "REFIT", "REGAL", "REIGN", "REINS", "RELAX", "RELAY", - "RELIC", "REMIT", "RENDS", "RENEW", "RENTS", "REPAY", "REPEL", "REPLY", "RESET", "RESIN", "RESTS", "REVEL", - "REVUE", "RHEUM", "RHYME", "RICKS", "RIDER", "RIDES", "RIDGE", "RIFLE", "RIFTS", "RIGHT", "RIGID", "RILED", - "RILLS", "RIMES", "RINGS", "RINSE", "RIOTS", "RIPEN", "RIPER", "RISEN", "RISER", "RISES", "RISKS", "RISKY", - "RITES", "RIVAL", "RIVEN", "RIVER", "RIVET", "ROADS", "ROAMS", "ROARS", "ROAST", "ROBED", "ROBES", "ROBIN", - "ROCKS", "ROCKY", "ROGUE", "ROLES", "ROLLS", "ROMAN", "ROOFS", "ROOKS", "ROOMS", "ROOMY", "ROOST", "ROOTS", - "ROPED", "ROPES", "ROSES", "ROSIN", "ROUGE", "ROUGH", "ROUND", "ROUSE", "ROUTE", "ROUTS", "ROVED", "ROVER", - "ROWDY", "ROWED", "ROYAL", "RUDER", "RUFFS", "RUINS", "RULED", "RULER", "RULES", "RUNES", "RUNGS", "RUPEE", - "RURAL", "RUSES", "SABLE", "SABRE", "SACKS", "SADLY", "SAFER", "SAGAS", "SAGES", "SAHIB", "SAILS", "SAINT", - "SAITH", "SALAD", "SALES", "SALLY", "SALON", "SALSA", "SALTS", "SALTY", "SALVE", "SALVO", "SANDS", "SANDY", - "SANER", "SATED", "SATIN", "SATYR", "SAUCE", "SAUCY", "SAVED", "SAVES", "SAWED", "SCALD", "SCALE", "SCALP", - "SCALY", "SCAMP", "SCANS", "SCANT", "SCARE", "SCARF", "SCARS", "SCENE", "SCENT", "SCION", "SCOFF", "SCOLD", - "SCOOP", "SCOPE", "SCORE", "SCORN", "SCOUR", "SCOUT", "SCOWL", "SCRAP", "SCREW", "SCRIP", "SCRUB", "SCULL", - "SEALS", "SEAMS", "SEAMY", "SEATS", "SECTS", "SEDAN", "SEDGE", "SEEDS", "SEEDY", "SEEKS", "SEEMS", "SEERS", - "SEIZE", "SELLS", "SEMEN", "SENDS", "SENSE", "SERFS", "SERGE", "SERUM", "SERVE", "SEVEN", "SEVER", "SEWED", - "SEWER", "SEXES", "SHACK", "SHADE", "SHADY", "SHAFT", "SHAKE", "SHAKY", "SHALE", "SHALL", "SHALT", "SHAME", - "SHAMS", "SHANK", "SHAPE", "SHARE", "SHARK", "SHARP", "SHAVE", "SHAWL", "SHEAF", "SHEAR", "SHEDS", "SHEEN", - "SHEEP", "SHEER", "SHEET", "SHEIK", "SHELF", "SHELL", "SHIED", "SHIFT", "SHINE", "SHINS", "SHINY", "SHIPS", - "SHIRE", "SHIRK", "SHIRT", "SHOAL", "SHOCK", "SHOES", "SHONE", "SHOOK", "SHOON", "SHOOT", "SHOPS", "SHORE", - "SHORN", "SHORT", "SHOTS", "SHOUT", "SHOVE", "SHOWN", "SHOWS", "SHOWY", "SHRED", "SHREW", "SHRUB", "SHRUG", - "SHUNS", "SHUTS", "SHYLY", "SIBYL", "SIDED", "SIDES", "SIEGE", "SIEVE", "SIGHS", "SIGHT", "SIGMA", "SIGNS", - "SILKS", "SILKY", "SILLS", "SILLY", "SINCE", "SINEW", "SINGE", "SINGS", "SINKS", "SIREN", "SIRES", "SITES", - "SIXES", "SIXTH", "SIXTY", "SIZED", "SIZES", "SKATE", "SKEIN", "SKIES", "SKIFF", "SKILL", "SKIMS", "SKINS", - "SKIPS", "SKIRT", "SKULK", "SKULL", "SKUNK", "SLABS", "SLACK", "SLAGS", "SLAIN", "SLAKE", "SLANG", "SLANT", - "SLAPS", "SLASH", "SLATE", "SLATS", "SLAVE", "SLAYS", "SLEDS", "SLEEK", "SLEEP", "SLEET", "SLEPT", "SLICE", - "SLICK", "SLIDE", "SLILY", "SLIME", "SLIMY", "SLING", "SLINK", "SLIPS", "SLITS", "SLOOP", "SLOPE", "SLOPS", - "SLOTH", "SLUGS", "SLUMP", "SLUMS", "SLUNG", "SLUNK", "SLUSH", "SLYLY", "SMACK", "SMALL", "SMART", "SMASH", - "SMEAR", "SMELL", "SMELT", "SMILE", "SMIRK", "SMITE", "SMITH", "SMOCK", "SMOKE", "SMOKY", "SMOTE", "SNACK", - "SNAGS", "SNAIL", "SNAKE", "SNAKY", "SNAPS", "SNARE", "SNARL", "SNEAK", "SNEER", "SNIFF", "SNIPE", "SNOBS", - "SNORE", "SNORT", "SNOUT", "SNOWS", "SNOWY", "SNUFF", "SOAPY", "SOARS", "SOBER", "SOCKS", "SOFAS", "SOGGY", - "SOILS", "SOLAR", "SOLES", "SOLID", "SOLOS", "SOLVE", "SONGS", "SONNY", "SOOTH", "SOOTY", "SORES", "SORRY", - "SORTS", "SOUGH", "SOULS", "SOUND", "SOUPS", "SOUSE", "SOUTH", "SOWED", "SOWER", "SPACE", "SPADE", "SPAKE", - "SPANK", "SPANS", "SPARE", "SPARK", "SPARS", "SPASM", "SPAWN", "SPEAK", "SPEAR", "SPECK", "SPEED", "SPELL", - "SPELT", "SPEND", "SPENT", "SPERM", "SPICE", "SPICY", "SPIED", "SPIES", "SPIKE", "SPILL", "SPILT", "SPINE", - "SPINS", "SPINY", "SPIRE", "SPITE", "SPITS", "SPLIT", "SPOIL", "SPOKE", "SPOOK", "SPOOL", "SPOON", "SPOOR", - "SPORE", "SPORT", "SPOTS", "SPOUT", "SPRAY", "SPREE", "SPRIG", "SPUNK", "SPURN", "SPURS", "SPURT", "SQUAD", - "SQUAT", "SQUAW", "STABS", "STACK", "STAFF", "STAGE", "STAGS", "STAID", "STAIN", "STAIR", "STAKE", "STALE", - "STALK", "STALL", "STAMP", "STAND", "STANK", "STARE", "STARK", "STARS", "START", "STATE", "STAVE", "STAYS", - "STEAD", "STEAK", "STEAL", "STEAM", "STEED", "STEEL", "STEEP", "STEER", "STEMS", "STEPS", "STERN", "STEWS", - "STICK", "STIFF", "STILE", "STILL", "STING", "STINK", "STINT", "STIRS", "STOCK", "STOIC", "STOLE", "STONE", - "STONY", "STOOD", "STOOL", "STOOP", "STOPS", "STORE", "STORK", "STORM", "STORY", "STOUT", "STOVE", "STRAP", - "STRAW", "STRAY", "STREW", "STRIP", "STRUT", "STUCK", "STUDS", "STUDY", "STUFF", "STUMP", "STUNG", "STUNT", - "STYLE", "SUAVE", "SUCKS", "SUGAR", "SUING", "SUITE", "SUITS", "SULKS", "SULKY", "SULLY", "SUNNY", "SUPER", - "SURER", "SURGE", "SURLY", "SWAIN", "SWAMP", "SWANS", "SWARD", "SWARM", "SWAYS", "SWEAR", "SWEAT", "SWEEP", - "SWEET", "SWELL", "SWEPT", "SWIFT", "SWILL", "SWIMS", "SWINE", "SWING", "SWIRL", "SWISH", "SWOON", "SWOOP", - "SWORD", "SWORE", "SWORN", "SWUNG", "SYNOD", "SYRUP", "TABBY", "TABLE", "TABOO", "TACIT", "TACKS", "TAILS", - "TAINT", "TAKEN", "TAKES", "TALES", "TALKS", "TALLY", "TALON", "TAMED", "TAMER", "TANKS", "TAPER", "TAPES", - "TARDY", "TARES", "TARRY", "TARTS", "TASKS", "TASTE", "TASTY", "TAUNT", "TAWNY", "TAXED", "TAXES", "TEACH", - "TEAMS", "TEARS", "TEASE", "TEEMS", "TEENS", "TEETH", "TELLS", "TEMPI", "TEMPO", "TEMPS", "TENDS", "TENET", - "TENOR", "TENSE", "TENTH", "TENTS", "TEPEE", "TEPID", "TERMS", "TERSE", "TESTS", "TESTY", "TEXTS", "THANK", - "THEFT", "THEIR", "THEME", "THERE", "THESE", "THICK", "THIEF", "THIGH", "THINE", "THING", "THINK", "THIRD", - "THONG", "THORN", "THOSE", "THREE", "THREW", "THROB", "THROE", "THROW", "THUMB", "THUMP", "THYME", "TIARA", - "TIBIA", "TICKS", "TIDAL", "TIDES", "TIERS", "TIGER", "TIGHT", "TILDE", "TILED", "TILES", "TILLS", "TILTS", - "TIMED", "TIMES", "TIMID", "TINGE", "TINTS", "TIPSY", "TIRED", "TIRES", "TITHE", "TITLE", "TOADS", "TOAST", - "TODAY", "TODDY", "TOILS", "TOKEN", "TOLLS", "TOMBS", "TOMES", "TONED", "TONES", "TONGS", "TONIC", "TOOLS", - "TOOTH", "TOPAZ", "TOPIC", "TOQUE", "TORCH", "TORSO", "TORTS", "TOTAL", "TOTEM", "TOUCH", "TOUGH", "TOURS", - "TOWED", "TOWEL", "TOWER", "TOWNS", "TOXIC", "TOYED", "TRACE", "TRACK", "TRACT", "TRADE", "TRAIL", "TRAIN", - "TRAIT", "TRAMP", "TRAMS", "TRAPS", "TRASH", "TRAYS", "TREAD", "TREAT", "TREED", "TREES", "TREND", "TRESS", - "TRIAD", "TRIAL", "TRIBE", "TRICE", "TRICK", "TRIED", "TRIES", "TRILL", "TRIPE", "TRIPS", "TRITE", "TROLL", - "TROOP", "TROTH", "TROTS", "TROUT", "TRUCE", "TRUCK", "TRUER", "TRULY", "TRUMP", "TRUNK", "TRUSS", "TRUST", - "TRUTH", "TRYST", "TUBES", "TUFTS", "TULIP", "TULLE", "TUNED", "TUNES", "TUNIC", "TURNS", "TUSKS", "TUTOR", - "TWAIN", "TWANG", "TWEED", "TWICE", "TWIGS", "TWINE", "TWINS", "TWIRL", "TWIST", "TYING", "TYPED", "TYPES", - "UDDER", "ULCER", "ULTRA", "UNCLE", "UNCUT", "UNDER", "UNDID", "UNDUE", "UNFIT", "UNION", "UNITE", "UNITS", - "UNITY", "UNSAY", "UNTIE", "UNTIL", "UPPER", "UPSET", "URBAN", "URGED", "URGES", "URINE", "USAGE", "USERS", - "USHER", "USING", "USUAL", "USURP", "USURY", "UTTER", "VAGUE", "VALES", "VALET", "VALID", "VALUE", "VALVE", - "VANES", "VAPID", "VASES", "VAULT", "VAUNT", "VEILS", "VEINS", "VELDT", "VENAL", "VENOM", "VENTS", "VENUE", - "VERBS", "VERGE", "VERSE", "VERVE", "VESTS", "VEXED", "VEXES", "VIALS", "VICAR", "VICES", "VIDEO", "VIEWS", - "VIGIL", "VILER", "VILLA", "VINES", "VIOLA", "VIPER", "VIRUS", "VISIT", "VISOR", "VISTA", "VITAL", "VIVID", - "VIXEN", "VIZOR", "VOCAL", "VODKA", "VOGUE", "VOICE", "VOILE", "VOLTS", "VOMIT", "VOTED", "VOTER", "VOTES", - "VOUCH", "VOWED", "VOWEL", "VYING", "WADED", "WAFER", "WAFTS", "WAGED", "WAGER", "WAGES", "WAGON", "WAIFS", - "WAILS", "WAIST", "WAITS", "WAIVE", "WAKED", "WAKEN", "WAKES", "WALKS", "WALLS", "WALTZ", "WANDS", "WANED", - "WANES", "WANTS", "WARDS", "WARES", "WARMS", "WARNS", "WARTS", "WASPS", "WASTE", "WATCH", "WATER", "WAVED", - "WAVER", "WAVES", "WAXED", "WAXEN", "WAXES", "WEARS", "WEARY", "WEAVE", "WEDGE", "WEEDS", "WEEDY", "WEEKS", - "WEEPS", "WEIGH", "WEIRD", "WELCH", "WELLS", "WENCH", "WHACK", "WHALE", "WHARF", "WHEAT", "WHEEL", "WHELP", - "WHERE", "WHICH", "WHIFF", "WHILE", "WHIMS", "WHINE", "WHIPS", "WHIRL", "WHIRR", "WHISK", "WHIST", "WHITE", - "WHOLE", "WHOOP", "WHORE", "WHOSE", "WICKS", "WIDEN", "WIDER", "WIDOW", "WIDTH", "WIELD", "WIGHT", "WILDS", - "WILES", "WILLS", "WINCE", "WINCH", "WINDS", "WINDY", "WINES", "WINGS", "WINKS", "WIPED", "WIPES", "WIRED", - "WIRES", "WISER", "WISPS", "WITCH", "WITTY", "WIVES", "WOMAN", "WOMEN", "WOODS", "WOODY", "WOOED", "WOOER", - "WORDS", "WORDY", "WORKS", "WORLD", "WORMS", "WORRY", "WORSE", "WORST", "WORTH", "WOULD", "WOUND", "WRACK", - "WRAPS", "WRAPT", "WRATH", "WREAK", "WRECK", "WREST", "WRING", "WRIST", "WRITE", "WRITS", "WRONG", "WROTE", - "WROTH", "YACHT", "YARDS", "YARNS", "YAWNS", "YEARN", "YEARS", "YEAST", "YELLS", "YELPS", "YIELD", "YOKED", - "YOKES", "YOLKS", "YOUNG", "YOURS", "YOUTH", "ZEBRA", "ZONES", "COLOR", "LASER", "SONIC", +source_link = "https://matthewminer.name/projects/calculators/wordle-words-left/" +valid_list = [ + "CIGAR", "REBUT", "SISSY", "HUMPH", "AWAKE", "BLUSH", "FOCAL", "EVADE", "NAVAL", "SERVE", "HEATH", "DWARF", + "MODEL", "KARMA", "STINK", "GRADE", "QUIET", "BENCH", "ABATE", "FEIGN", "MAJOR", "DEATH", "FRESH", "CRUST", + "STOOL", "COLON", "ABASE", "MARRY", "REACT", "BATTY", "PRIDE", "FLOSS", "HELIX", "CROAK", "STAFF", "PAPER", + "UNFED", "WHELP", "TRAWL", "OUTDO", "ADOBE", "CRAZY", "SOWER", "REPAY", "DIGIT", "CRATE", "CLUCK", "SPIKE", + "MIMIC", "POUND", "MAXIM", "LINEN", "UNMET", "FLESH", "BOOBY", "FORTH", "FIRST", "STAND", "BELLY", "IVORY", + "SEEDY", "PRINT", "YEARN", "DRAIN", "BRIBE", "STOUT", "PANEL", "CRASS", "FLUME", "OFFAL", "AGREE", "ERROR", + "SWIRL", "ARGUE", "BLEED", "DELTA", "FLICK", "TOTEM", "WOOER", "FRONT", "SHRUB", "PARRY", "BIOME", "LAPEL", + "START", "GREET", "GONER", "GOLEM", "LUSTY", "LOOPY", "ROUND", "AUDIT", "LYING", "GAMMA", "LABOR", "ISLET", + "CIVIC", "FORGE", "CORNY", "MOULT", "BASIC", "SALAD", "AGATE", "SPICY", "SPRAY", "ESSAY", "FJORD", "SPEND", + "KEBAB", "GUILD", "ABACK", "MOTOR", "ALONE", "HATCH", "HYPER", "THUMB", "DOWRY", "OUGHT", "BELCH", "DUTCH", + "PILOT", "TWEED", "COMET", "JAUNT", "ENEMA", "STEED", "ABYSS", "GROWL", "FLING", "DOZEN", "BOOZY", "ERODE", + "WORLD", "GOUGE", "CLICK", "BRIAR", "GREAT", "ALTAR", "PULPY", "BLURT", "COAST", "DUCHY", "GROIN", "FIXER", + "GROUP", "ROGUE", "BADLY", "SMART", "PITHY", "GAUDY", "CHILL", "HERON", "VODKA", "FINER", "SURER", "RADIO", + "ROUGE", "PERCH", "RETCH", "WROTE", "CLOCK", "TILDE", "STORE", "PROVE", "BRING", "SOLVE", "CHEAT", "GRIME", + "EXULT", "USHER", "EPOCH", "TRIAD", "BREAK", "RHINO", "VIRAL", "CONIC", "MASSE", "SONIC", "VITAL", "TRACE", + "USING", "PEACH", "CHAMP", "BATON", "BRAKE", "PLUCK", "CRAZE", "GRIPE", "WEARY", "PICKY", "ACUTE", "FERRY", + "ASIDE", "TAPIR", "TROLL", "UNIFY", "REBUS", "BOOST", "TRUSS", "SIEGE", "TIGER", "BANAL", "SLUMP", "CRANK", + "GORGE", "QUERY", "DRINK", "FAVOR", "ABBEY", "TANGY", "PANIC", "SOLAR", "SHIRE", "PROXY", "POINT", "ROBOT", + "PRICK", "WINCE", "CRIMP", "KNOLL", "SUGAR", "WHACK", "MOUNT", "PERKY", "COULD", "WRUNG", "LIGHT", "THOSE", + "MOIST", "SHARD", "PLEAT", "ALOFT", "SKILL", "ELDER", "FRAME", "HUMOR", "PAUSE", "ULCER", "ULTRA", "ROBIN", + "CYNIC", "AROMA", "CAULK", "SHAKE", "DODGE", "SWILL", "TACIT", "OTHER", "THORN", "TROVE", "BLOKE", "VIVID", + "SPILL", "CHANT", "CHOKE", "RUPEE", "NASTY", "MOURN", "AHEAD", "BRINE", "CLOTH", "HOARD", "SWEET", "MONTH", + "LAPSE", "WATCH", "TODAY", "FOCUS", "SMELT", "TEASE", "CATER", "MOVIE", "SAUTE", "ALLOW", "RENEW", "THEIR", + "SLOSH", "PURGE", "CHEST", "DEPOT", "EPOXY", "NYMPH", "FOUND", "SHALL", "STOVE", "LOWLY", "SNOUT", "TROPE", + "FEWER", "SHAWL", "NATAL", "COMMA", "FORAY", "SCARE", "STAIR", "BLACK", "SQUAD", "ROYAL", "CHUNK", "MINCE", + "SHAME", "CHEEK", "AMPLE", "FLAIR", "FOYER", "CARGO", "OXIDE", "PLANT", "OLIVE", "INERT", "ASKEW", "HEIST", + "SHOWN", "ZESTY", "TRASH", "LARVA", "FORGO", "STORY", "HAIRY", "TRAIN", "HOMER", "BADGE", "MIDST", "CANNY", + "SHINE", "GECKO", "FARCE", "SLUNG", "TIPSY", "METAL", "YIELD", "DELVE", "BEING", "SCOUR", "GLASS", "GAMER", + "SCRAP", "MONEY", "HINGE", "ALBUM", "VOUCH", "ASSET", "TIARA", "CREPT", "BAYOU", "ATOLL", "MANOR", "CREAK", + "SHOWY", "PHASE", "FROTH", "DEPTH", "GLOOM", "FLOOD", "TRAIT", "GIRTH", "PIETY", "GOOSE", "FLOAT", "DONOR", + "ATONE", "PRIMO", "APRON", "BLOWN", "CACAO", "LOSER", "INPUT", "GLOAT", "AWFUL", "BRINK", "SMITE", "BEADY", + "RUSTY", "RETRO", "DROLL", "GAWKY", "HUTCH", "PINTO", "EGRET", "LILAC", "SEVER", "FIELD", "FLUFF", "AGAPE", + "VOICE", "STEAD", "BERTH", "MADAM", "NIGHT", "BLAND", "LIVER", "WEDGE", "ROOMY", "WACKY", "FLOCK", "ANGRY", + "TRITE", "APHID", "TRYST", "MIDGE", "POWER", "ELOPE", "CINCH", "MOTTO", "STOMP", "UPSET", "BLUFF", "CRAMP", + "QUART", "COYLY", "YOUTH", "RHYME", "BUGGY", "ALIEN", "SMEAR", "UNFIT", "PATTY", "CLING", "GLEAN", "LABEL", + "HUNKY", "KHAKI", "POKER", "GRUEL", "TWICE", "TWANG", "SHRUG", "TREAT", "WASTE", "MERIT", "WOVEN", "NEEDY", + "CLOWN", "IRONY", "RUDER", "GAUZE", "CHIEF", "ONSET", "PRIZE", "FUNGI", "CHARM", "GULLY", "INTER", "WHOOP", + "TAUNT", "LEERY", "CLASS", "THEME", "LOFTY", "TIBIA", "BOOZE", "ALPHA", "THYME", "DOUBT", "PARER", "CHUTE", + "STICK", "TRICE", "ALIKE", "RECAP", "SAINT", "GLORY", "GRATE", "ADMIT", "BRISK", "SOGGY", "USURP", "SCALD", + "SCORN", "LEAVE", "TWINE", "STING", "BOUGH", "MARSH", "SLOTH", "DANDY", "VIGOR", "HOWDY", "ENJOY", "VALID", + "IONIC", "EQUAL", "FLOOR", "CATCH", "SPADE", "STEIN", "EXIST", "QUIRK", "DENIM", "GROVE", "SPIEL", "MUMMY", + "FAULT", "FOGGY", "FLOUT", "CARRY", "SNEAK", "LIBEL", "WALTZ", "APTLY", "PINEY", "INEPT", "ALOUD", "PHOTO", + "DREAM", "STALE", "UNITE", "SNARL", "BAKER", "THERE", "GLYPH", "POOCH", "HIPPY", "SPELL", "FOLLY", "LOUSE", + "GULCH", "VAULT", "GODLY", "THREW", "FLEET", "GRAVE", "INANE", "SHOCK", "CRAVE", "SPITE", "VALVE", "SKIMP", + "CLAIM", "RAINY", "MUSTY", "PIQUE", "DADDY", "QUASI", "ARISE", "AGING", "VALET", "OPIUM", "AVERT", "STUCK", + "RECUT", "MULCH", "GENRE", "PLUME", "RIFLE", "COUNT", "INCUR", "TOTAL", "WREST", "MOCHA", "DETER", "STUDY", + "LOVER", "SAFER", "RIVET", "FUNNY", "SMOKE", "MOUND", "UNDUE", "SEDAN", "PAGAN", "SWINE", "GUILE", "GUSTY", + "EQUIP", "TOUGH", "CANOE", "CHAOS", "COVET", "HUMAN", "UDDER", "LUNCH", "BLAST", "STRAY", "MANGA", "MELEE", + "LEFTY", "QUICK", "PASTE", "GIVEN", "OCTET", "RISEN", "GROAN", "LEAKY", "GRIND", "CARVE", "LOOSE", "SADLY", + "SPILT", "APPLE", "SLACK", "HONEY", "FINAL", "SHEEN", "EERIE", "MINTY", "SLICK", "DERBY", "WHARF", "SPELT", + "COACH", "ERUPT", "SINGE", "PRICE", "SPAWN", "FAIRY", "JIFFY", "FILMY", "STACK", "CHOSE", "SLEEP", "ARDOR", + "NANNY", "NIECE", "WOOZY", "HANDY", "GRACE", "DITTO", "STANK", "CREAM", "USUAL", "DIODE", "VALOR", "ANGLE", + "NINJA", "MUDDY", "CHASE", "REPLY", "PRONE", "SPOIL", "HEART", "SHADE", "DINER", "ARSON", "ONION", "SLEET", + "DOWEL", "COUCH", "PALSY", "BOWEL", "SMILE", "EVOKE", "CREEK", "LANCE", "EAGLE", "IDIOT", "SIREN", "BUILT", + "EMBED", "AWARD", "DROSS", "ANNUL", "GOODY", "FROWN", "PATIO", "LADEN", "HUMID", "ELITE", "LYMPH", "EDIFY", + "MIGHT", "RESET", "VISIT", "GUSTO", "PURSE", "VAPOR", "CROCK", "WRITE", "SUNNY", "LOATH", "CHAFF", "SLIDE", + "QUEER", "VENOM", "STAMP", "SORRY", "STILL", "ACORN", "APING", "PUSHY", "TAMER", "HATER", "MANIA", "AWOKE", + "BRAWN", "SWIFT", "EXILE", "BIRCH", "LUCKY", "FREER", "RISKY", "GHOST", "PLIER", "LUNAR", "WINCH", "SNARE", + "NURSE", "HOUSE", "BORAX", "NICER", "LURCH", "EXALT", "ABOUT", "SAVVY", "TOXIN", "TUNIC", "PRIED", "INLAY", + "CHUMP", "LANKY", "CRESS", "EATER", "ELUDE", "CYCLE", "KITTY", "BOULE", "MORON", "TENET", "PLACE", "LOBBY", + "PLUSH", "VIGIL", "INDEX", "BLINK", "CLUNG", "QUALM", "CROUP", "CLINK", "JUICY", "STAGE", "DECAY", "NERVE", + "FLIER", "SHAFT", "CROOK", "CLEAN", "CHINA", "RIDGE", "VOWEL", "GNOME", "SNUCK", "ICING", "SPINY", "RIGOR", + "SNAIL", "FLOWN", "RABID", "PROSE", "THANK", "POPPY", "BUDGE", "FIBER", "MOLDY", "DOWDY", "KNEEL", "TRACK", + "CADDY", "QUELL", "DUMPY", "PALER", "SWORE", "REBAR", "SCUBA", "SPLAT", "FLYER", "HORNY", "MASON", "DOING", + "OZONE", "AMPLY", "MOLAR", "OVARY", "BESET", "QUEUE", "CLIFF", "MAGIC", "TRUCE", "SPORT", "FRITZ", "EDICT", + "TWIRL", "VERSE", "LLAMA", "EATEN", "RANGE", "WHISK", "HOVEL", "REHAB", "MACAW", "SIGMA", "SPOUT", "VERVE", + "SUSHI", "DYING", "FETID", "BRAIN", "BUDDY", "THUMP", "SCION", "CANDY", "CHORD", "BASIN", "MARCH", "CROWD", + "ARBOR", "GAYLY", "MUSKY", "STAIN", "DALLY", "BLESS", "BRAVO", "STUNG", "TITLE", "RULER", "KIOSK", "BLOND", + "ENNUI", "LAYER", "FLUID", "TATTY", "SCORE", "CUTIE", "ZEBRA", "BARGE", "MATEY", "BLUER", "AIDER", "SHOOK", + "RIVER", "PRIVY", "BETEL", "FRISK", "BONGO", "BEGUN", "AZURE", "WEAVE", "GENIE", "SOUND", "GLOVE", "BRAID", + "SCOPE", "WRYLY", "ROVER", "ASSAY", "OCEAN", "BLOOM", "IRATE", "LATER", "WOKEN", "SILKY", "WRECK", "DWELT", + "SLATE", "SMACK", "SOLID", "AMAZE", "HAZEL", "WRIST", "JOLLY", "GLOBE", "FLINT", "ROUSE", "CIVIL", "VISTA", + "RELAX", "COVER", "ALIVE", "BEECH", "JETTY", "BLISS", "VOCAL", "OFTEN", "DOLLY", "EIGHT", "JOKER", "SINCE", + "EVENT", "ENSUE", "SHUNT", "DIVER", "POSER", "WORST", "SWEEP", "ALLEY", "CREED", "ANIME", "LEAFY", "BOSOM", + "DUNCE", "STARE", "PUDGY", "WAIVE", "CHOIR", "STOOD", "SPOKE", "OUTGO", "DELAY", "BILGE", "IDEAL", "CLASP", + "SEIZE", "HOTLY", "LAUGH", "SIEVE", "BLOCK", "MEANT", "GRAPE", "NOOSE", "HARDY", "SHIED", "DRAWL", "DAISY", + "PUTTY", "STRUT", "BURNT", "TULIP", "CRICK", "IDYLL", "VIXEN", "FUROR", "GEEKY", "COUGH", "NAIVE", "SHOAL", + "STORK", "BATHE", "AUNTY", "CHECK", "PRIME", "BRASS", "OUTER", "FURRY", "RAZOR", "ELECT", "EVICT", "IMPLY", + "DEMUR", "QUOTA", "HAVEN", "CAVIL", "SWEAR", "CRUMP", "DOUGH", "GAVEL", "WAGON", "SALON", "NUDGE", "HAREM", + "PITCH", "SWORN", "PUPIL", "EXCEL", "STONY", "CABIN", "UNZIP", "QUEEN", "TROUT", "POLYP", "EARTH", "STORM", + "UNTIL", "TAPER", "ENTER", "CHILD", "ADOPT", "MINOR", "FATTY", "HUSKY", "BRAVE", "FILET", "SLIME", "GLINT", + "TREAD", "STEAL", "REGAL", "GUEST", "EVERY", "MURKY", "SHARE", "SPORE", "HOIST", "BUXOM", "INNER", "OTTER", + "DIMLY", "LEVEL", "SUMAC", "DONUT", "STILT", "ARENA", "SHEET", "SCRUB", "FANCY", "SLIMY", "PEARL", "SILLY", + "PORCH", "DINGO", "SEPIA", "AMBLE", "SHADY", "BREAD", "FRIAR", "REIGN", "DAIRY", "QUILL", "CROSS", "BROOD", + "TUBER", "SHEAR", "POSIT", "BLANK", "VILLA", "SHANK", "PIGGY", "FREAK", "WHICH", "AMONG", "FECAL", "SHELL", + "WOULD", "ALGAE", "LARGE", "RABBI", "AGONY", "AMUSE", "BUSHY", "COPSE", "SWOON", "KNIFE", "POUCH", "ASCOT", + "PLANE", "CROWN", "URBAN", "SNIDE", "RELAY", "ABIDE", "VIOLA", "RAJAH", "STRAW", "DILLY", "CRASH", "AMASS", + "THIRD", "TRICK", "TUTOR", "WOODY", "BLURB", "GRIEF", "DISCO", "WHERE", "SASSY", "BEACH", "SAUNA", "COMIC", + "CLUED", "CREEP", "CASTE", "GRAZE", "SNUFF", "FROCK", "GONAD", "DRUNK", "PRONG", "LURID", "STEEL", "HALVE", + "BUYER", "VINYL", "UTILE", "SMELL", "ADAGE", "WORRY", "TASTY", "LOCAL", "TRADE", "FINCH", "ASHEN", "MODAL", + "GAUNT", "CLOVE", "ENACT", "ADORN", "ROAST", "SPECK", "SHEIK", "MISSY", "GRUNT", "SNOOP", "PARTY", "TOUCH", + "MAFIA", "EMCEE", "ARRAY", "SOUTH", "VAPID", "JELLY", "SKULK", "ANGST", "TUBAL", "LOWER", "CREST", "SWEAT", + "CYBER", "ADORE", "TARDY", "SWAMI", "NOTCH", "GROOM", "ROACH", "HITCH", "YOUNG", "ALIGN", "READY", "FROND", + "STRAP", "PUREE", "REALM", "VENUE", "SWARM", "OFFER", "SEVEN", "DRYER", "DIARY", "DRYLY", "DRANK", "ACRID", + "HEADY", "THETA", "JUNTO", "PIXIE", "QUOTH", "BONUS", "SHALT", "PENNE", "AMEND", "DATUM", "BUILD", "PIANO", + "SHELF", "LODGE", "SUING", "REARM", "CORAL", "RAMEN", "WORTH", "PSALM", "INFER", "OVERT", "MAYOR", "OVOID", + "GLIDE", "USAGE", "POISE", "RANDY", "CHUCK", "PRANK", "FISHY", "TOOTH", "ETHER", "DROVE", "IDLER", "SWATH", + "STINT", "WHILE", "BEGAT", "APPLY", "SLANG", "TAROT", "RADAR", "CREDO", "AWARE", "CANON", "SHIFT", "TIMER", + "BYLAW", "SERUM", "THREE", "STEAK", "ILIAC", "SHIRK", "BLUNT", "PUPPY", "PENAL", "JOIST", "BUNNY", "SHAPE", + "BEGET", "WHEEL", "ADEPT", "STUNT", "STOLE", "TOPAZ", "CHORE", "FLUKE", "AFOOT", "BLOAT", "BULLY", "DENSE", + "CAPER", "SNEER", "BOXER", "JUMBO", "LUNGE", "SPACE", "AVAIL", "SHORT", "SLURP", "LOYAL", "FLIRT", "PIZZA", + "CONCH", "TEMPO", "DROOP", "PLATE", "BIBLE", "PLUNK", "AFOUL", "SAVOY", "STEEP", "AGILE", "STAKE", "DWELL", + "KNAVE", "BEARD", "AROSE", "MOTIF", "SMASH", "BROIL", "GLARE", "SHOVE", "BAGGY", "MAMMY", "SWAMP", "ALONG", + "RUGBY", "WAGER", "QUACK", "SQUAT", "SNAKY", "DEBIT", "MANGE", "SKATE", "NINTH", "JOUST", "TRAMP", "SPURN", + "MEDAL", "MICRO", "REBEL", "FLANK", "LEARN", "NADIR", "MAPLE", "COMFY", "REMIT", "GRUFF", "ESTER", "LEAST", + "MOGUL", "FETCH", "CAUSE", "OAKEN", "AGLOW", "MEATY", "GAFFE", "SHYLY", "RACER", "PROWL", "THIEF", "STERN", + "POESY", "ROCKY", "TWEET", "WAIST", "SPIRE", "GROPE", "HAVOC", "PATSY", "TRULY", "FORTY", "DEITY", "UNCLE", + "SWISH", "GIVER", "PREEN", "BEVEL", "LEMUR", "DRAFT", "SLOPE", "ANNOY", "LINGO", "BLEAK", "DITTY", "CURLY", + "CEDAR", "DIRGE", "GROWN", "HORDE", "DROOL", "SHUCK", "CRYPT", "CUMIN", "STOCK", "GRAVY", "LOCUS", "WIDER", + "BREED", "QUITE", "CHAFE", "CACHE", "BLIMP", "DEIGN", "FIEND", "LOGIC", "CHEAP", "ELIDE", "RIGID", "FALSE", + "RENAL", "PENCE", "ROWDY", "SHOOT", "BLAZE", "ENVOY", "POSSE", "BRIEF", "NEVER", "ABORT", "MOUSE", "MUCKY", + "SULKY", "FIERY", "MEDIA", "TRUNK", "YEAST", "CLEAR", "SKUNK", "SCALP", "BITTY", "CIDER", "KOALA", "DUVET", + "SEGUE", "CREME", "SUPER", "GRILL", "AFTER", "OWNER", "EMBER", "REACH", "NOBLY", "EMPTY", "SPEED", "GIPSY", + "RECUR", "SMOCK", "DREAD", "MERGE", "BURST", "KAPPA", "AMITY", "SHAKY", "HOVER", "CAROL", "SNORT", "SYNOD", + "FAINT", "HAUNT", "FLOUR", "CHAIR", "DETOX", "SHREW", "TENSE", "PLIED", "QUARK", "BURLY", "NOVEL", "WAXEN", + "STOIC", "JERKY", "BLITZ", "BEEFY", "LYRIC", "HUSSY", "TOWEL", "QUILT", "BELOW", "BINGO", "WISPY", "BRASH", + "SCONE", "TOAST", "EASEL", "SAUCY", "VALUE", "SPICE", "HONOR", "ROUTE", "SHARP", "BAWDY", "RADII", "SKULL", + "PHONY", "ISSUE", "LAGER", "SWELL", "URINE", "GASSY", "TRIAL", "FLORA", "UPPER", "LATCH", "WIGHT", "BRICK", + "RETRY", "HOLLY", "DECAL", "GRASS", "SHACK", "DOGMA", "MOVER", "DEFER", "SOBER", "OPTIC", "CRIER", "VYING", + "NOMAD", "FLUTE", "HIPPO", "SHARK", "DRIER", "OBESE", "BUGLE", "TAWNY", "CHALK", "FEAST", "RUDDY", "PEDAL", + "SCARF", "CRUEL", "BLEAT", "TIDAL", "SLUSH", "SEMEN", "WINDY", "DUSTY", "SALLY", "IGLOO", "NERDY", "JEWEL", + "SHONE", "WHALE", "HYMEN", "ABUSE", "FUGUE", "ELBOW", "CRUMB", "PANSY", "WELSH", "SYRUP", "TERSE", "SUAVE", + "GAMUT", "SWUNG", "DRAKE", "FREED", "AFIRE", "SHIRT", "GROUT", "ODDLY", "TITHE", "PLAID", "DUMMY", "BROOM", + "BLIND", "TORCH", "ENEMY", "AGAIN", "TYING", "PESKY", "ALTER", "GAZER", "NOBLE", "ETHOS", "BRIDE", "EXTOL", + "DECOR", "HOBBY", "BEAST", "IDIOM", "UTTER", "THESE", "SIXTH", "ALARM", "ERASE", "ELEGY", "SPUNK", "PIPER", + "SCALY", "SCOLD", "HEFTY", "CHICK", "SOOTY", "CANAL", "WHINY", "SLASH", "QUAKE", "JOINT", "SWEPT", "PRUDE", + "HEAVY", "WIELD", "FEMME", "LASSO", "MAIZE", "SHALE", "SCREW", "SPREE", "SMOKY", "WHIFF", "SCENT", "GLADE", + "SPENT", "PRISM", "STOKE", "RIPER", "ORBIT", "COCOA", "GUILT", "HUMUS", "SHUSH", "TABLE", "SMIRK", "WRONG", + "NOISY", "ALERT", "SHINY", "ELATE", "RESIN", "WHOLE", "HUNCH", "PIXEL", "POLAR", "HOTEL", "SWORD", "CLEAT", + "MANGO", "RUMBA", "PUFFY", "FILLY", "BILLY", "LEASH", "CLOUT", "DANCE", "OVATE", "FACET", "CHILI", "PAINT", + "LINER", "CURIO", "SALTY", "AUDIO", "SNAKE", "FABLE", "CLOAK", "NAVEL", "SPURT", "PESTO", "BALMY", "FLASH", + "UNWED", "EARLY", "CHURN", "WEEDY", "STUMP", "LEASE", "WITTY", "WIMPY", "SPOOF", "SANER", "BLEND", "SALSA", + "THICK", "WARTY", "MANIC", "BLARE", "SQUIB", "SPOON", "PROBE", "CREPE", "KNACK", "FORCE", "DEBUT", "ORDER", + "HASTE", "TEETH", "AGENT", "WIDEN", "ICILY", "SLICE", "INGOT", "CLASH", "JUROR", "BLOOD", "ABODE", "THROW", + "UNITY", "PIVOT", "SLEPT", "TROOP", "SPARE", "SEWER", "PARSE", "MORPH", "CACTI", "TACKY", "SPOOL", "DEMON", + "MOODY", "ANNEX", "BEGIN", "FUZZY", "PATCH", "WATER", "LUMPY", "ADMIN", "OMEGA", "LIMIT", "TABBY", "MACHO", + "AISLE", "SKIFF", "BASIS", "PLANK", "VERGE", "BOTCH", "CRAWL", "LOUSY", "SLAIN", "CUBIC", "RAISE", "WRACK", + "GUIDE", "FOIST", "CAMEO", "UNDER", "ACTOR", "REVUE", "FRAUD", "HARPY", "SCOOP", "CLIMB", "REFER", "OLDEN", + "CLERK", "DEBAR", "TALLY", "ETHIC", "CAIRN", "TULLE", "GHOUL", "HILLY", "CRUDE", "APART", "SCALE", "OLDER", + "PLAIN", "SPERM", "BRINY", "ABBOT", "RERUN", "QUEST", "CRISP", "BOUND", "BEFIT", "DRAWN", "SUITE", "ITCHY", + "CHEER", "BAGEL", "GUESS", "BROAD", "AXIOM", "CHARD", "CAPUT", "LEANT", "HARSH", "CURSE", "PROUD", "SWING", + "OPINE", "TASTE", "LUPUS", "GUMBO", "MINER", "GREEN", "CHASM", "LIPID", "TOPIC", "ARMOR", "BRUSH", "CRANE", + "MURAL", "ABLED", "HABIT", "BOSSY", "MAKER", "DUSKY", "DIZZY", "LITHE", "BROOK", "JAZZY", "FIFTY", "SENSE", + "GIANT", "SURLY", "LEGAL", "FATAL", "FLUNK", "BEGAN", "PRUNE", "SMALL", "SLANT", "SCOFF", "TORUS", "NINNY", + "COVEY", "VIPER", "TAKEN", "MORAL", "VOGUE", "OWING", "TOKEN", "ENTRY", "BOOTH", "VOTER", "CHIDE", "ELFIN", + "EBONY", "NEIGH", "MINIM", "MELON", "KNEED", "DECOY", "VOILA", "ANKLE", "ARROW", "MUSHY", "TRIBE", "CEASE", + "EAGER", "BIRTH", "GRAPH", "ODDER", "TERRA", "WEIRD", "TRIED", "CLACK", "COLOR", "ROUGH", "WEIGH", "UNCUT", + "LADLE", "STRIP", "CRAFT", "MINUS", "DICEY", "TITAN", "LUCID", "VICAR", "DRESS", "DITCH", "GYPSY", "PASTA", + "TAFFY", "FLAME", "SWOOP", "ALOOF", "SIGHT", "BROKE", "TEARY", "CHART", "SIXTY", "WORDY", "SHEER", "LEPER", + "NOSEY", "BULGE", "SAVOR", "CLAMP", "FUNKY", "FOAMY", "TOXIC", "BRAND", "PLUMB", "DINGY", "BUTTE", "DRILL", + "TRIPE", "BICEP", "TENOR", "KRILL", "WORSE", "DRAMA", "HYENA", "THINK", "RATIO", "COBRA", "BASIL", "SCRUM", + "BUSED", "PHONE", "COURT", "CAMEL", "PROOF", "HEARD", "ANGEL", "PETAL", "POUTY", "THROB", "MAYBE", "FETAL", + "SPRIG", "SPINE", "SHOUT", "CADET", "MACRO", "DODGY", "SATYR", "RARER", "BINGE", "TREND", "NUTTY", "LEAPT", + "AMISS", "SPLIT", "MYRRH", "WIDTH", "SONAR", "TOWER", "BARON", "FEVER", "WAVER", "SPARK", "BELIE", "SLOOP", + "EXPEL", "SMOTE", "BALER", "ABOVE", "NORTH", "WAFER", "SCANT", "FRILL", "AWASH", "SNACK", "SCOWL", "FRAIL", + "DRIFT", "LIMBO", "FENCE", "MOTEL", "OUNCE", "WREAK", "REVEL", "TALON", "PRIOR", "KNELT", "CELLO", "FLAKE", + "DEBUG", "ANODE", "CRIME", "SALVE", "SCOUT", "IMBUE", "PINKY", "STAVE", "VAGUE", "CHOCK", "FIGHT", "VIDEO", + "STONE", "TEACH", "CLEFT", "FROST", "PRAWN", "BOOTY", "TWIST", "APNEA", "STIFF", "PLAZA", "LEDGE", "TWEAK", + "BOARD", "GRANT", "MEDIC", "BACON", "CABLE", "BRAWL", "SLUNK", "RASPY", "FORUM", "DRONE", "WOMEN", "MUCUS", + "BOAST", "TODDY", "COVEN", "TUMOR", "TRUER", "WRATH", "STALL", "STEAM", "AXIAL", "PURER", "DAILY", "TRAIL", + "NICHE", "MEALY", "JUICE", "NYLON", "PLUMP", "MERRY", "FLAIL", "PAPAL", "WHEAT", "BERRY", "COWER", "ERECT", + "BRUTE", "LEGGY", "SNIPE", "SINEW", "SKIER", "PENNY", "JUMPY", "RALLY", "UMBRA", "SCARY", "MODEM", "GROSS", + "AVIAN", "GREED", "SATIN", "TONIC", "PARKA", "SNIFF", "LIVID", "STARK", "TRUMP", "GIDDY", "REUSE", "TABOO", + "AVOID", "QUOTE", "DEVIL", "LIKEN", "GLOSS", "GAYER", "BERET", "NOISE", "GLAND", "DEALT", "SLING", "RUMOR", + "OPERA", "THIGH", "TONGA", "FLARE", "WOUND", "WHITE", "BULKY", "ETUDE", "HORSE", "CIRCA", "PADDY", "INBOX", + "FIZZY", "GRAIN", "EXERT", "SURGE", "GLEAM", "BELLE", "SALVO", "CRUSH", "FRUIT", "SAPPY", "TAKER", "TRACT", + "OVINE", "SPIKY", "FRANK", "REEDY", "FILTH", "SPASM", "HEAVE", "MAMBO", "RIGHT", "CLANK", "TRUST", "LUMEN", + "BORNE", "SPOOK", "SAUCE", "AMBER", "LATHE", "CARAT", "CORER", "DIRTY", "SLYLY", "AFFIX", "ALLOY", "TAINT", + "SHEEP", "KINKY", "WOOLY", "MAUVE", "FLUNG", "YACHT", "FRIED", "QUAIL", "BRUNT", "GRIMY", "CURVY", "CAGEY", + "RINSE", "DEUCE", "STATE", "GRASP", "MILKY", "BISON", "GRAFT", "SANDY", "BASTE", "FLASK", "HEDGE", "GIRLY", + "SWASH", "BONEY", "COUPE", "ENDOW", "ABHOR", "WELCH", "BLADE", "TIGHT", "GEESE", "MISER", "MIRTH", "CLOUD", + "CABAL", "LEECH", "CLOSE", "TENTH", "PECAN", "DROIT", "GRAIL", "CLONE", "GUISE", "RALPH", "TANGO", "BIDDY", + "SMITH", "MOWER", "PAYEE", "SERIF", "DRAPE", "FIFTH", "SPANK", "GLAZE", "ALLOT", "TRUCK", "KAYAK", "VIRUS", + "TESTY", "TEPEE", "FULLY", "ZONAL", "METRO", "CURRY", "GRAND", "BANJO", "AXION", "BEZEL", "OCCUR", "CHAIN", + "NASAL", "GOOEY", "FILER", "BRACE", "ALLAY", "PUBIC", "RAVEN", "PLEAD", "GNASH", "FLAKY", "MUNCH", "DULLY", + "EKING", "THING", "SLINK", "HURRY", "THEFT", "SHORN", "PYGMY", "RANCH", "WRING", "LEMON", "SHORE", "MAMMA", + "FROZE", "NEWER", "STYLE", "MOOSE", "ANTIC", "DROWN", "VEGAN", "CHESS", "GUPPY", "UNION", "LEVER", "LORRY", + "IMAGE", "CABBY", "DRUID", "EXACT", "TRUTH", "DOPEY", "SPEAR", "CRIED", "CHIME", "CRONY", "STUNK", "TIMID", + "BATCH", "GAUGE", "ROTOR", "CRACK", "CURVE", "LATTE", "WITCH", "BUNCH", "REPEL", "ANVIL", "SOAPY", "METER", + "BROTH", "MADLY", "DRIED", "SCENE", "KNOWN", "MAGMA", "ROOST", "WOMAN", "THONG", "PUNCH", "PASTY", "DOWNY", + "KNEAD", "WHIRL", "RAPID", "CLANG", "ANGER", "DRIVE", "GOOFY", "EMAIL", "MUSIC", "STUFF", "BLEEP", "RIDER", + "MECCA", "FOLIO", "SETUP", "VERSO", "QUASH", "FAUNA", "GUMMY", "HAPPY", "NEWLY", "FUSSY", "RELIC", "GUAVA", + "RATTY", "FUDGE", "FEMUR", "CHIRP", "FORTE", "ALIBI", "WHINE", "PETTY", "GOLLY", "PLAIT", "FLECK", "FELON", + "GOURD", "BROWN", "THRUM", "FICUS", "STASH", "DECRY", "WISER", "JUNTA", "VISOR", "DAUNT", "SCREE", "IMPEL", + "AWAIT", "PRESS", "WHOSE", "TURBO", "STOOP", "SPEAK", "MANGY", "EYING", "INLET", "CRONE", "PULSE", "MOSSY", + "STAID", "HENCE", "PINCH", "TEDDY", "SULLY", "SNORE", "RIPEN", "SNOWY", "ATTIC", "GOING", "LEACH", "MOUTH", + "HOUND", "CLUMP", "TONAL", "BIGOT", "PERIL", "PIECE", "BLAME", "HAUTE", "SPIED", "UNDID", "INTRO", "BASAL", + "RODEO", "GUARD", "STEER", "LOAMY", "SCAMP", "SCRAM", "MANLY", "HELLO", "VAUNT", "ORGAN", "FERAL", "KNOCK", + "EXTRA", "CONDO", "ADAPT", "WILLY", "POLKA", "RAYON", "SKIRT", "FAITH", "TORSO", "MATCH", "MERCY", "TEPID", + "SLEEK", "RISER", "TWIXT", "PEACE", "FLUSH", "CATTY", "LOGIN", "EJECT", "ROGER", "RIVAL", "UNTIE", "REFIT", + "AORTA", "ADULT", "JUDGE", "ROWER", "ARTSY", "RURAL", "SHAVE", "BOBBY", "ECLAT", "FELLA", "GAILY", "HARRY", + "HASTY", "HYDRO", "LIEGE", "OCTAL", "OMBRE", "PAYER", "SOOTH", "UNSET", "UNLIT", "VOMIT", "FANNY", "FETUS", + "BUTCH", "STALK", "FLACK", "WIDOW", "AUGUR", "LITER", ] -expanded_list = [ - "WHICH", "THEIR", "WOULD", "THERE", "COULD", "OTHER", "ABOUT", "GREAT", "THESE", "AFTER", "FIRST", "NEVER", - "WHERE", "THOSE", "SHALL", "BEING", "MIGHT", "EVERY", "THINK", "UNDER", "FOUND", "STILL", "WHILE", "AGAIN", - "PLACE", "YOUNG", "YEARS", "THREE", "RIGHT", "HOUSE", "WHOLE", "WORLD", "THING", "NIGHT", "GOING", "HEARD", - "HEART", "AMONG", "ASKED", "SMALL", "WOMAN", "WHOSE", "QUITE", "WORDS", "GIVEN", "TAKEN", "HANDS", "UNTIL", - "SINCE", "LIGHT", "BEGAN", "LARGE", "WATER", "WORKS", "OFTEN", "STOOD", "POWER", "MONEY", "ORDER", "MEANS", - "ROUND", "VOICE", "WHITE", "POINT", "STATE", "ABOVE", "DEATH", "LEAST", "KNOWN", "ALONG", "LEAVE", "ALONE", - "WOMEN", "TIMES", "SPEAK", "FORTH", "TERMS", "CRIED", "CHILD", "HUMAN", "SHORT", "CAUSE", "SEEMS", "BRING", - "DOUBT", "BLACK", "SENSE", "CLOSE", "TRUTH", "OUGHT", "PARTY", "READY", "FORCE", "EARLY", "EARTH", "EBOOK", - "SIGHT", "SPOKE", "STORY", "LATER", "ADDED", "STAND", "NICHT", "MILES", "COMES", "TABLE", "HOURS", "RIVER", - "HAPPY", "CLEAR", "SOUND", "MAKES", "BLOOD", "COMME", "DOING", "AVAIT", "TRIED", "FRONT", "QUILL", "PEACE", - "LIVED", "HORSE", "WROTE", "PAPER", "CETTE", "CHIEF", "PARIS", "BOOKS", "VISIT", "HEAVY", "KNOWS", "LOVED", - "CARRY", "PLAIN", "SWEET", "WRITE", "TREES", "BELOW", "WRONG", "REACH", "NOBLE", "PARTS", "AGREE", "MOVED", - "ENEMY", "WORTH", "GREEN", "THIRD", "MOUTH", "SLEEP", "FRESH", "FAITH", "SMILE", "USUAL", "BOUND", "QUIET", - "ETEXT", "COURT", "YOUTH", "PIECE", "SOUTH", "MEANT", "SEVEN", "TEARS", "VALUE", "BROKE", "FIGHT", "STONE", - "BEGIN", "HENRY", "LEARN", "LINES", "GRAND", "TAKES", "MONTH", "GIRLS", "GIVES", "EIGHT", "SCENE", "LIVES", - "DRAWN", "FIFTY", "FIELD", "CHAIR", "NAMED", "ALLOW", "MUSIC", "FIXED", "STUDY", "SPENT", "ROMAN", "TRUST", - "BREAK", "EQUAL", "NORTH", "THREW", "WATCH", "LOOKS", "BUILT", "USING", "SPITE", "MORAL", "WALLS", "TOUCH", - "JAMES", "STEPS", "OFFER", "DRESS", "LYING", "GRAVE", "LEGAL", "QUEEN", "LOWER", "CASES", "SHOWN", "NAMES", - "GREEK", "BOARD", "FAIRE", "GLASS", "SHARE", "FORMS", "CLASS", "START", "SHOOK", "TRAIN", "ENTER", "PROVE", - "FLOOR", "XPAGE", "WORSE", "SORRY", "PRIDE", "MARCH", "MARRY", "CROWD", "SHORE", "DRINK", "JUDGE", "SERVE", - "LAUGH", "TRADE", "BROAD", "GRACE", "PETER", "JESUS", "ROYAL", "LOUIS", "HEADS", "PROUD", "SPACE", "FULLY", - "QUICK", "IDEAS", "FANCY", "TASTE", "SWORD", "SHIPS", "DAILY", "GLORY", "BRAVE", "HONOR", "DREAM", "WEEKS", - "THICK", "CLAIM", "CHECK", "ASIDE", "REPLY", "FALSE", "SIDES", "CROSS", "SHARP", "FACTS", "HILLS", "BREAD", - "COAST", "DAVID", "AWARE", "GROUP", "FACES", "GROWN", "BIRDS", "MIDST", "TELLS", "LIKED", "THROW", "HABIT", - "STAGE", "ANGRY", "BROWN", "OWNER", "TIRED", "TRULY", "RULES", "TOTAL", "GRASS", "STYLE", "SAVED", "DRIVE", - "TWICE", "GUARD", "BURST", "PRICE", "WANTS", "THANK", "BASED", "APRIL", "GUESS", "CHOSE", "HOPES", "UNCLE", - "WOODS", "FINAL", "FORTY", "DROVE", "SPAIN", "TITLE", "UPPER", "MINDS", "NOISE", "HOPED", "BEGUN", "ALIVE", - "ITALY", "CRUEL", "SHAPE", "SLAVE", "BIRTH", "YOURS", "STORM", "CATCH", "LOOSE", "EMPTY", "CIVIL", "DOZEN", - "SHOWS", "ADMIT", "SMOKE", "DYING", "BRIEF", "APPLY", "PROOF", "FLESH", "FRUIT", "ENJOY", "WORST", "SHAME", - "ROUGH", "COVER", "ROCKS", "COUNT", "CRIME", "GRIEF", "IRISH", "NOTES", "NEEDS", "LANDS", "BLIND", "BRAIN", - "PRINT", "CLEAN", "DURCH", "ETAIT", "LEVEL", "RAISE", "EAGER", "STARS", "FAINT", "TEETH", "LABOR", "ROOMS", - "OLDER", "EINEN", "KNEES", "MERCY", "AWFUL", "AVOID", "DOORS", "INDIA", "ENDED", "DEVIL", "WEARY", "AROSE", - "FAULT", "CROWN", "COLOR", "AUSSI", "EVENT", "GOODS", "QUAND", "YARDS", "UNION", "ASCII", "TEMPS", "FAVOR", - "VOTRE", "SOULS", "CALLS", "AHEAD", "HARRY", "SMITH", "ANGER", "PLANS", "LOCAL", "LOVER", "PAGES", "LEURS", - "VIEWS", "SIGNS", "TEACH", "STOCK", "KINDS", "APART", "GUIDE", "ARMED", "EXACT", "HOMME", "TOUTE", "LATIN", - "TRIAL", "HOTEL", "SPEND", "SKILL", "KINGS", "SHALT", "LINKS", "DANCE", "SWEPT", "FATAL", "EINER", "WOUND", - "STORE", "SLEPT", "RANGE", "HATTE", "HENCE", "IMAGE", "ARISE", "FILES", "EINEM", "BANKS", "RAPID", "WASTE", - "ENTRE", "DARED", "PLANT", "SHADE", "ACTED", "CLOUD", "PRESS", "LOVES", "UTTER", "WINGS", "FLUNG", "BOWED", - "EGYPT", "GAZED", "THINE", "STICK", "FRANK", "REIGN", "MAJOR", "URGED", "FLEET", "TURNS", "SUGAR", "SPARE", - "SOLID", "BLAME", "BORNE", "WAVES", "TOWNS", "BOSOM", "CORPS", "SPEED", "GRANT", "FINDS", "MOINS", "BUILD", - "COSTS", "ERROR", "CEASE", "BOATS", "MIXED", "DELAY", "AGENT", "CLOTH", "ISSUE", "CHARM", "TREAT", "EMAIL", - "FRAME", "SHEEP", "ALIKE", "DUTCH", "PAUSE", "SEINE", "MERRY", "FEMME", "CRIES", "VAGUE", "TRACE", "VERSE", - "NOTRE", "FLAME", "HELEN", "IDEAL", "AVOIR", "HASTE", "ROUTE", "DREAD", "BIBLE", "EXIST", "OWING", "STERN", - "SAINT", "SORTS", "FALLS", "YIELD", "OCCUR", "GATES", "BONES", "OCEAN", "JEUNE", "SHAKE", "CARED", "STAFF", - "HURRY", "BEAST", "LOFTY", "BLESS", "TROIS", "INNER", "SHONE", "DRANK", "NOTED", "WINDS", "CHEEK", "CHAIN", - "KNIFE", "FEARS", "SWIFT", "WIVES", "WIDOW", "HATED", "HOLDS", "SIXTY", "MERIT", "GROSS", "DIESE", "ANGEL", - "MARIE", "FEVER", "FIRED", "AINSI", "ROADS", "CHINA", "QUEER", "GIFTS", "SWEAR", "NURSE", "CABIN", "MARKS", - "TRIBE", "ALOUD", "PAINS", "ALARM", "SCOTT", "OUTER", "WALKS", "NAKED", "FOLKS", "ELDER", "POETS", "MATCH", - "FOLLY", "WRATH", "DWELL", "SHOES", "SLAIN", "WAREN", "COACH", "ALICE", "TOWER", "DEEDS", "HABEN", "STEEL", - "TRAIL", "DEPTH", "BEARS", "PORTE", "SHOCK", "MOSES", "GUEST", "SCHON", "PLATE", "SAITH", "FEELS", "CLERK", - "STUFF", "TRACK", "POEMS", "PLAYS", "MAGIC", "KEEPS", "MONDE", "SONGS", "LEADS", "DRUNK", "AWAKE", "COEUR", - "SHOOT", "SMELL", "PETIT", "ALTAR", "AUTRE", "LIMBS", "EATEN", "TONES", "BASIS", "STEEP", "FEAST", "NOVEL", - "SILLY", "GROWS", "BADLY", "STOUT", "ALLES", "MARIA", "RISEN", "CREPT", "SCALE", "RANKS", "FLASH", "BURNT", - "RIVAL", "BRIDE", "TALES", "BOOTS", "CHEST", "SEATS", "GRAIN", "PRIZE", "FETCH", "DIRTY", "MOUNT", "CURSE", - "STIFF", "BEARD", "BARON", "NEGRO", "ASSEZ", "UNTER", "MODEL", "GRASP", "FIRES", "FLOOD", "ALORS", "JOURS", - "FLOUR", "STOLE", "POUND", "SWUNG", "ALTER", "SHEET", "CENTS", "DWELT", "CLOAK", "ROSES", "ROOTS", "CLOCK", - "SCORE", "GLOOM", "AGONY", "SEIZE", "PIOUS", "WAGES", "BLOWS", "WHEEL", "SAGTE", "CARES", "VITAL", "RISES", - "BILLY", "AVANT", "OWNED", "SCORN", "METAL", "ABIDE", "IMMER", "JONES", "STUCK", "SHOUT", "PAINT", "DRIED", - "AMPLE", "RUINS", "MAYBE", "FILLE", "SIEGE", "STEAM", "BETTY", "MILLE", "TRICK", "TYPES", "SHELL", "REFER", - "GIANT", "LORDS", "ROGER", "FIFTH", "FACED", "DATES", "MEINE", "HOMES", "LINEN", "VIVID", "STRAW", "BRASS", - "NAVAL", "MADAM", "BONNE", "FADED", "SPORT", "DROPS", "DINED", "BEACH", "LOYAL", "CELUI", "LIKES", "CARDS", - "RACES", "CHEAP", "TRUNK", "RALPH", "CHEER", "JOINT", "EINES", "SPELL", "ABODE", "WAVED", "DRAMA", "ARRAY", - "CRAFT", "CANOE", "GEGEN", "RIFLE", "CODES", "FIERY", "MIEUX", "TAXES", "AWOKE", "LIMIT", "ASHES", "JACOB", - "GHOST", "BELLS", "STEAL", "ALLER", "CLIMB", "HEELS", "TEXAS", "TERRE", "MUTTA", "SOBER", "HARSH", "SHINE", - "KNOCK", "PEINE", "SPAKE", "VIRUS", "DEALT", "MINOR", "LAURA", "SLOPE", "HUMOR", "BENCH", "WHEAT", "MAMMA", - "PIANO", "FUNNY", "SIMON", "ROCKY", "SWEEP", "SADLY", "ERECT", "BELLE", "PURSE", "PARCE", "CLUNG", "SWORE", - "FENCE", "WORRY", "SUITE", "JAPAN", "WAGON", "POLLY", "EVILS", "CELLE", "HENRI", "JETZT", "PETTY", "BANDS", - "CHASE", "GLEAM", "UNITY", "HASTY", "ACRES", "MAINS", "LUCKY", "PERIL", "ADAMS", "HIRED", "FILED", "EXTRA", - "FLIES", "DOUTE", "TIGHT", "BLANK", "JULIA", "ENFIN", "SEEDS", "IHRER", "FAIRY", "SITES", "SMART", "ETWAS", - "WURDE", "SHIRT", "ELECT", "SPOIL", "GUILT", "ADOPT", "BILLS", "CLARA", "HOLES", "ELLES", "SAILS", "RINGS", - "EDITH", "FANNY", "BLOWN", "RIDGE", "DENSE", "TODAY", "SHEER", "ABUSE", "RESTE", "QUOTH", "LUNCH", "NIECE", - "TIMID", "MANLY", "SWORN", "PRIME", "PROSE", "BOXES", "VILLE", "LEWIS", "CREEK", "ORGAN", "BRUSH", "OPERA", - "FLOCK", "SUSAN", "BOAST", "PITCH", "ANGLE", "ELLEN", "SUNNY", "AUGHT", "FOOLS", "SWAMP", "VEINS", "NEWLY", - "COUCH", "VIEUX", "JOLLY", "OPENS", "SPEAR", "BRICK", "CREAM", "THIEF", "WAIST", "RULED", "KNELT", "SARAH", - "BRUTE", "SENDS", "MINES", "DATED", "PATHS", "MOVES", "SHOPS", "PORCH", "IHREN", "TENTS", "BACKS", "KITTY", - "SANDY", "PUPIL", "LODGE", "BLOOM", "BONDS", "BLOCK", "SATAN", "SWELL", "RIGID", "OLLUT", "GAMES", "SANTA", - "DEBTS", "FELIX", "SPOTS", "SCENT", "HEEFT", "CIGAR", "ALLAH", "ENTRY", "SAXON", "AVAIL", "MAINE", "EFFET", - "TREAD", "WIDER", "OLISI", "CHILL", "BROOK", "AMUSE", "ALLEN", "COMTE", "ROBIN", "SKALL", "VEXED", "BALLS", - "KEINE", "CROPS", "UEBER", "REALM", "EMILY", "MONKS", "REBEL", "WALES", "LAMPS", "EXILE", "HEARS", "UNITE", - "ROBES", "SKIES", "SIXTH", "SEULE", "CRAZY", "ISAAC", "IHNEN", "RULER", "WRECK", "DRAWS", "DIDST", "ARROW", - "STARE", "DROIT", "BLUSH", "DONNE", "SOBRE", "PRIOR", "VOTES", "MATIN", "THEME", "FINER", "PSEUD", "NERVE", - "APPLE", "RESTS", "PENNY", "STAKE", "BROWS", "READS", "BUNCH", "TWAIN", "TOKEN", "TALKS", "WISER", "HORNS", - "APRES", "BLAZE", "SORTE", "CLIFF", "STRIP", "TEXTS", "AWAIT", "ELBOW", "GLOBE", "FLUSH", "UPSET", "AVONS", - "TURKS", "JANET", "GENUS", "LOCKS", "HONEY", "CARGO", "LEBEN", "STOVE", "CANAL", "JERRY", "CRACK", "POSTS", - "PIETY", "LADEN", "BLADE", "LINED", "HONOM", "NOISY", "DRIFT", "DECAY", "FUNDS", "MIRTH", "TOOLS", "ACUTE", - "AUGEN", "PACES", "SPLIT", "BREED", "VENUS", "JUICE", "PLATO", "FREED", "FAILS", "PORTS", "BLISS", "USAGE", - "HEURE", "PILED", "RATES", "AIDED", "BYRON", "WIPED", "HOMER", "STAMP", "VICES", "VATER", "TOMMY", "PIEDS", - "TROOP", "SAGDE", "BLAST", "MOTOR", "RURAL", "WEARS", "CREED", "VOTED", "DELLA", "GLARE", "SKINS", "TODOS", - "SHAFT", "VENIR", "NANCY", "CROIS", "DAVIS", "QUOTE", "PLEAD", "ITEMS", "SWING", "SEEKS", "CREST", "BASIN", - "LAKES", "SNAKE", "SALES", "DUSTY", "CRUSH", "MEETS", "TENTH", "SALLY", "MEALS", "GROVE", "SUITS", "SMOTE", - "LANGE", "ACTOR", "ARGUE", "FLOWS", "DIMLY", "REEDS", "PLANE", "GREET", "SWEAT", "DIANA", "BRUIT", "ROOFS", - "SANOI", "BEAMS", "JIMMY", "PILOT", "ARABS", "FROST", "CELLS", "MOORE", "AYANT", "PERCY", "EDGAR", "PIPES", - "MOLLY", "QUEST", "FOLDS", "CURED", "PASSE", "RHINE", "MAIDS", "MULES", "REINS", "SALON", "CANST", "AUCUN", - "ALERT", "RENEW", "EDGES", "IHREM", "SWISS", "PATCH", "ALOFT", "CRUDE", "AIMED", "NAILS", "RECHT", "SAGEN", - "VIGOR", "TRIES", "FLANK", "TRACT", "VERGE", "ELSIE", "TRAMP", "IVORY", "CRASH", "CURVE", "CAKES", "EAGLE", - "HEDGE", "VOULU", "RITES", "LOGIC", "CLARK", "SPARK", "SINGS", "EPOCH", "MAYOR", "WEIGH", "ONDER", "ABBEY", - "KEITH", "AGNES", "ESSAY", "VALET", "HOSTS", "HANGS", "COMIC", "PANIC", "PEDRO", "COATS", "SCOPE", "FRAIL", - "BURKE", "GOALS", "MODES", "SKULL", "FLOAT", "RHODE", "PULSE", "LOSES", "DANTE", "TOPIC", "TIGER", "FEWER", - "PINES", "SHIFT", "HEAPS", "VIENT", "FILLS", "REINE", "FORTS", "LIONS", "MILAN", "ZIJNE", "LUNGS", "SANDS", - "PHASE", "HALLS", "ROUSE", "DAISY", "PARTE", "FARMS", "SKIRT", "SATIN", "RAINS", "HINTS", "CABLE", "GARDE", - "THUMB", "QUART", "HARDY", "ELLER", "POLES", "AARON", "SYRIA", "GROAN", "ROPES", "MOULD", "JENNY", "ROLLS", - "BACON", "MOIST", "FLING", "LANCE", "PARTI", "SHELF", "ETHEL", "MALES", "PLUCK", "SHOTS", "STOPS", "ALIEN", - "FRAUD", "CLUBS", "NEWBY", "CREEP", "TILDE", "SHAWL", "FORME", "AFORE", "RAOUL", "FROWN", "SNOWY", "LISTS", - "STONY", "FLAGS", "BORED", "MELAN", "PGLAF", "CURLS", "EFTER", "STAID", "TOUGH", "CHOIR", "HASTA", "DITCH", - "CLARE", "WITCH", "RIDER", "SEHEN", "MASON", "WEEDS", "TENDS", "PALMS", "FLUID", "VILLA", "STADT", "IRENE", - "SAUCE", "PAVED", "ANNIE", "MUDDY", "SEXES", "USERS", "STRAY", "DAMIT", "OATHS", "WIDTH", "STAIN", "STEED", - "UNFIT", "BEADS", "HERDS", "INDEM", "WENIG", "VINES", "BLAKE", "GORGE", "DEMON", "CANON", "BRISK", "GANZE", - "TUTOR", "SIGHS", "RAINY", "TANTO", "MOYEN", "HELPS", "GRADE", "PEAKS", "ELLOS", "PARMI", "PARLE", "ALLAN", - "FLORA", "PELLE", "APRON", "COINS", "CITED", "LIEBE", "DEREN", "WELSH", "HOOFS", "PAIRS", "DONNA", "VOWED", - "WEIRD", "IRONY", "BRUCE", "BEANS", "DOLLY", "CLING", "LAPSE", "GAILY", "DUSKY", "TEGEN", "WITTY", "WRIST", - "TORCH", "MONTE", "SHADY", "AMOUR", "BESET", "ATHOS", "COUPS", "TRUCE", "GOATS", "VAULT", "SPURS", "GAINS", - "TELLE", "TOAST", "VINGT", "LAYER", "ARMOR", "JUDAH", "KOMMT", "MOODS", "IMPLY", "LEVER", "JOTKA", "EXERT", - "FOULE", "SPIES", "YACHT", "ALTEN", "PAGAN", "TENSE", "ALOOF", "VALOR", "HYMNS", "ENVOY", "PINCH", "PRONE", - "HELLO", "BROOD", "MABEL", "FOWLS", "HERRN", "DROWN", "ROUGE", "BURNS", "MISMO", "CALME", "LIVRE", "SLIDE", - "LYONS", "CAIRO", "PATTY", "CASTE", "STOOP", "BAKER", "MILLS", "BLEST", "TAILS", "VOICI", "LOGIN", "FARED", - "CHANT", "SEITE", "FABLE", "MISTS", "DIEGO", "ROWED", "WORDT", "DUCKS", "MAXIM", "BOERS", "LEVEN", "SWARM", - "QUANT", "MUSED", "WAITS", "CHALK", "BEIDE", "FORTE", "CAVES", "JEDER", "SENOR", "INFER", "TRAIT", "TASKS", - "DIARY", "HOUND", "SAVEZ", "BLUFF", "PENSE", "CECIL", "JUDAS", "OUNCE", "STARK", "DRAIN", "LEAPT", "WEISE", - "CLAWS", "TWINS", "IDOLS", "SUJET", "ALTSP", "WILLS", "RAGED", "HAVDE", "BEAUX", "FRITZ", "DARES", "DEPOT", - "DAZED", "RODIN", "LOWLY", "STUNG", "NASTY", "DEITY", "BOBBY", "ELIZA", "SIZES", "GAUNT", "LYDIA", "KNEEL", - "RISKS", "DURST", "TENOR", "STEMS", "FAITE", "LIVER", "STEAD", "EVANS", "TESTS", "GILES", "HUNNE", "SEALS", - "PLANK", "SINKS", "WHIRL", "SCREW", "SPOON", "GEHEN", "HABIA", "JAHRE", "BARRY", "AMPLY", "FRANZ", "TENIR", - "SLICE", "CARTS", "EDWIN", "DEALS", "JUSTE", "WHALE", "VIVRE", "PARUT", "HAUSE", "WESEN", "GEESE", "NAMEN", - "PASTE", "NIETS", "SCRAP", "LIBRE", "ADORN", "NOSES", "MADLY", "USTED", "WELKE", "ARDOR", "TROUT", "WELLS", - "ZEIDE", "DINGY", "SOEUR", "KNAVE", "KNOTS", "TIENE", "SEELE", "ESSEX", "CRAWL", "THEFT", "WEDER", "AREAS", - "STAIR", "ORION", "HEUTE", "DADDY", "COUGH", "GUISE", "PARMA", "FISTS", "GENUG", "FAUST", "AQUEL", "SNEER", - "PLOTS", "DAVON", "JENER", "PLUME", "GEBEN", "HAUTE", "PADRE", "ABBOT", "VOILA", "SELON", "ODOUR", "MEDAL", - "OPIUM", "DRAKE", "AMISS", "ETTEI", "GLOVE", "BLEAK", "SACHE", "GENRE", "RATIO", "DRUGS", "WINES", "HINDU", - "POOLS", "VISTA", "SCANT", "CORDS", "RAILS", "STAYS", "GENOA", "WHARF", "COSAS", "STEHT", "PRIMA", "DEUCE", - "KILLS", "MADGE", "BLUNT", "PASSA", "WAKED", "PUNCH", "SABLE", "FLOWN", "DRILL", "LIFTS", "ILLUS", "CRISP", - "WORMS", "FLINT", "HANDY", "ALLEY", "MASTS", "FIEND", "STUMP", "ADULT", "LEUTE", "SCRUB", "DECKS", "PANGS", - "CAMEL", "DEINE", "TITUS", "INDEX", "HAVEN", "HILDA", "GEIST", "ORDRE", "THORN", "TOILS", "CEDAR", "LOADS", - "WAXED", "SOPHY", "DAHER", "GAUGE", "WAGER", "JULIE", "WILDE", "PEERS", "AISLE", "BLANC", "LONGS", "ELLIS", - "MINUA", "CARLO", "SCARE", "LEVIN", "KUNDE", "DINGE", "SNARE", "WEISS", "STALE", "FAITS", "AVERT", "HADST", - "BATHS", "XVIII", "TIDES", "TINTS", "BULLY", "AZURE", "WHIGS", "VIELE", "ATOMS", "PERRY", "BLICK", "GIDDY", - "SOLAR", "FARCE", "BOWER", "WARES", "FROID", "SLIPS", "RENTS", "FEATS", "LEASE", "BLAIR", "BLAND", "CONDE", - "MARSH", "RUMOR", "BERTH", "BATHE", "BOOTY", "GLIDE", "ENNEN", "BERRY", "PAULA", "AIVAN", "FRIED", "DANES", - "WORTE", "BOOTH", "OTROS", "TAXED", "TWIGS", "ERSTE", "UNDUE", "CLUMP", "RICHE", "LIEGT", "LYRIC", "IHRES", - "LASTS", "ANNOY", "WEGEN", "OOGEN", "SELFE", "DABEI", "CASTS", "BOUGH", "ROUEN", "DIGNE", "SILAS", "LOCKE", - "CHART", "MAKER", "PEGGY", "SNOWS", "DETTA", "CHRIS", "ARENA", "QUIEN", "RELIC", "SECTS", "DOUCE", "BRIBE", - "FRIAR", "TINGE", "COURS", "VIENS", "HACER", "PEPYS", "LANES", "ETHER", "VICAR", "STEVE", "JASON", "VOCAL", - "JOINS", "BASES", "UNSER", "LIGNE", "DINAH", "VASES", "TEDDY", "NEUEN", "CURLY", "VALID", "CLEFT", "CHAPS", - "BARGE", "HAGUE", "BOLTS", "DARIN", "SMELT", "TULEE", "VOYEZ", "PURER", "ENTRA", "ODDLY", "VAPOR", "EENEN", - "SPIED", "SLATE", "CREWE", "BRACE", "CYRIL", "OSCAR", "IRONS", "DESDE", "LAMBS", "VROEG", "VROUW", "DICKY", - "KRAFT", "RALLY", "RESTA", "CRAVE", "GAYLY", "LEAPS", "FLUTE", "ONCLE", "SCOUT", "GOWNS", "SWINE", "GREED", - "ONION", "SAGES", "AMBER", "TARRY", "GRATE", "GESTE", "ANDRE", "MOEST", "JUURI", "CESSE", "SHEWN", "THIGH", - "LEAFY", "BENDS", "FERME", "BRAKE", "HURTS", "GRIND", "SILKS", "CLASH", "JELLY", "SOOTH", "DIZZY", "NEBEN", - "INCUR", "DOVER", "VERRE", "HAIRY", "STALK", "TOMBE", "HEAVE", "MUCHO", "INTER", "SPADE", "CREWS", "DENIS", - "HIELT", "FOSSE", "BULLS", "ALLEZ", "SOMME", "SNUFF", "BLIEB", "HAVOC", "ALLEM", "SMITE", "LACKS", "THROB", - "GUTEN", "PUEDE", "CLICK", "TIRER", "KORAN", "FLASK", "GRUND", "TERRA", "CAUSA", "GAUDY", "HIRAM", "JETER", - "FIBRE", "VERBS", "FORGE", "WAERE", "SPECK", "OLDEN", "SHRUB", "FLEDA", "GODLY", "HOOFD", "SONNE", "EDICT", - "DAGEN", "ISLES", "CHASM", "EDGED", "TRAPS", "BIRCH", "TRUER", "PAPAL", "ASSIS", "MASSE", "MORTE", "HETTY", - "REPEL", "INGEN", "SHRUG", "KOMEN", "PRISE", "HENNE", "LIVID", "BRIAN", "SALAD", "MATES", "BOVEN", "DOUZE", - "MEATS", "FEEDS", "TANTE", "WENCH", "DARTS", "FLATS", "TUBES", "UNITS", "HECHO", "PERCH", "POURS", "TRIPS", - "CIVIC", "MUNDO", "SPRAK", "ANTES", "SCARF", "DELLE", "HYVIN", "VASTE", "FOLIO", "NEEDY", "YELLS", "FAUTE", - "REGAL", "HOOKS", "ELIOT", "PALER", "TRENT", "MALTA", "MAKEN", "LUCAS", "NOUNS", "ATTIC", "WINDY", "SPIRE", - "SIEHT", "DERBY", "SAVOY", "MAUDE", "ILIAD", "DITTO", "PLINY", "FERNS", "BASIC", "GRAPE", "SOGAR", "TYING", - "DOUGH", "DERAS", "LEANT", "BELLA", "LICHT", "FILTH", "PEARL", "LUSTY", "OLIVE", "DROLL", "BADGE", "WAGED", - "SEEST", "ANKLE", "FERRY", "PENAL", "NEUER", "ESTOS", "FUMES", "SHEDS", "SHOWY", "SALEM", "REDEN", "STALL", - "JOYCE", "SANTO", "ZELFS", "HOTLY", "SCOLD", "CANDY", "LUCIA", "WEAVE", "MONTA", "SLACK", "VOGUE", "RHODA", - "SLANG", "FOODS", "PRIVY", "SERAI", "RENDU", "VALVE", "KUNST", "CYCLE", "EXCEL", "QUASI", "CRAIG", "VERTU", - "AVEVA", "MAYST", "HEATH", "MEDIO", "CELIA", "DEEZE", "BATES", "ATONE", "VENUE", "LURID", "SKOLA", "MDLLE", - "CHOKE", "PREND", "DARAN", "SAVES", "MANOR", "FORKS", "GENTE", "DITES", "TEAMS", "GOETH", "STOND", "COOKS", - "CHIEN", "PANEL", "GROWL", "MURAT", "BODEN", "DERES", "CAIUS", "FRERE", "HEIDI", "BROTH", "TEMPO", "JONAS", - "BROOM", "RACED", "SACKS", "TERRY", "NOOIT", "GENOM", "RAVEN", "FROGS", "ANNUM", "ACHED", "HEROD", "NOIRE", - "SURLY", "EIGEN", "EVADE", "BUGGY", "CONNU", "ATQUE", "WAKES", "REMIT", "REEFS", "ASSES", "MORSE", "RUFUS", - "ROMEO", "CHORD", "SLOOP", "LOANS", "TAWNY", "ETIAM", "NOCHE", "CONGO", "GHENT", "BINDS", "SLUNG", "CUBIC", - "GROOT", "DARUM", "MUSES", "GERMS", "NYMPH", "CUPID", "LUTTE", "FINES", "LUEGO", "PSALM", "HOARY", "XXIII", - "MUJER", "CROWS", "DECIR", "SULLA", "TITRE", "MAPLE", "SALTS", "POSTE", "DODGE", "ARMEN", "DISSE", "MACHT", - "FOCUS", "LITHE", "PASHA", "HIJOS", "BUSSY", "FLEUR", "JOLIE", "SMASH", "NORAH", "DOGMA", "TUTTO", "BOWLS", - "JEDEN", "LOATH", "JESTS", "MALAY", "DIGIT", "PENCE", "JOUER", "NORSE", "SLEEK", "DOWRY", "DEFER", "SELLS", - "CALEB", "BLEED", "DOWNS", "BABES", "DENTS", "ALKOI", "JEDEM", "PANES", "SINNE", "LEIGH", "MENOS", "WILDS", - "WOLFE", "ANDRA", "IDAHO", "DOWNE", "BETSY", "ADAPT", "MIJNE", "LIESS", "DICHO", "NELLY", "TUDOR", "BLOND", - "GRAVY", "FRAGE", "AELIG", "TIENT", "STEIN", "PLIED", "SABRE", "SQUAD", "STAAT", "REVEL", "YOLKS", "SEWED", - "CRANE", "SYRUP", "GIVER", "SEULS", "HAINE", "CASEY", "HICKS", "KATIE", "LOINS", "ABEND", "EXALT", "DENNA", - "STIRS", "SMOKY", "EUERY", "NICHE", "ONELY", "SINUN", "RABBI", "BRUST", "ELIAS", "JOSSA", "PAYER", "VEILS", - "CHAFF", "SULKY", "JUNGE", "PEACH", "MAMAN", "TUNES", "KAMEN", "DIANE", "LUMPS", "SHAVE", "CLOWN", "SHINY", - "LATEN", "HUSKY", "TOWEL", "REPOS", "GLADE", "GEORG", "NUEVA", "MANIA", "SAKES", "KEATS", "PULLS", "SIOUX", - "TAINT", "SUIVI", "CROIT", "MUUTA", "TILES", "BUSHY", "BLEND", "HAPLY", "ANJOU", "TRITT", "TURIN", "PLATZ", - "FAMED", "BARNS", "VECES", "DAMON", "LANGS", "HUMPH", "RELAX", "AUNTS", "SALUT", "CRAGS", "SHOAL", "COLIN", - "MOSSY", "GAULS", "CASKS", "WRING", "AHORA", "SPICE", "BONNY", "CURES", "BRUNO", "BRAVO", "ANDER", "CHESS", - "ALIAS", "SAUCY", "NOIRS", "TUNIC", "TOMBA", "LURED", "POKER", "RADIO", "CHAMP", "FINDE", "HULLO", "MECCA", - "SCALP", "FORMA", "SCARS", "MORTS", "BASSE", "SPINE", "PEREZ", "DIVAN", "HONTE", "LINDA", "ENVIE", "TEASE", - "BARED", "MADRE", "TUTTI", "OUTRE", "OMDAT", "LUCID", "SURGE", "CLAIR", "ONSET", "PONDS", "AUTEM", "MOODY", - "BALES", "SEDAN", "GALES", "LATCH", "LENDS", "LUIGI", "CAREY", "HOIST", "OTRAS", "GWINE", "WARUM", "DIVED", - "STRAP", "KELLY", "BEECH", "TROTH", "CHLOE", "PUNTO", "CIELO", "FROZE", "BUGLE", "POISE", "SIEUR", "TRUCK", - "ZEKER", "FOXES", "BALMY", "PICKS", "ISLAM", "AMEND", "DROOP", "LOPEZ", "DAIRY", "WALTZ", "BLOSS", "KYSYI", - "NAIVE", "SHORN", "URGES", "MAIZE", "WIELD", "HOLLY", "ARTER", "YORKE", "FRAIS", "NIGEL", "PAYNE", "COMET", - "SHUTS", "SOCKS", "HORDE", "SKIFF", "HACIA", "BESTE", "PARKS", "INERT", "PARLA", "INLET", "ODORS", "PRICK", - "KOREA", "FORUM", "LIEGE", "JEDES", "LIETH", "GASES", "SENZA", "TISCH", "PRATT", "ABNER", "BURLY", "BRAND", - "MINER", "TRACY", "LUGAR", "LAWNS", "GOTHS", "TARDY", "WIGHT", "DRURY", "VADER", "COOKE", "LAIRD", "TUFTS", - "FIXES", "SOLES", "VOWEL", "CLIVE", "KOSKA", "TAPER", "GUSTS", "RIANT", "FERAI", "COILS", "GROND", "SOINS", - "ABATE", "AFOOT", "EMILE", "POCHE", "LASSE", "ADELA", "PORTA", "BULKY", "MASKS", "DORIS", "KOMMA", "TAMEN", - "CRIER", "LOBBY", "ERNST", "EXPEL", "MANGE", "PACKS", "FATED", "DOVES", "DOLLS", "METRE", "AVAIS", "NEWER", - "LOGAN", "FREER", "DENNE", "MILLY", "GENIE", "STILE", "WARDS", "GEVEN", "WROTH", "BOONE", "COMER", "ANGUS", - "USHER", "CEDED", "PUFFS", "EBONY", "MIMIC", "SIEVE", "MEDIA", "ATLAS", "DESSA", "DUKES", "TERUG", "GRIMM", - "HAYES", "INDRA", "SHEWS", "DUMAS", "MYTHS", "FACON", "LILAC", "MISER", "TACIT", "SABER", "TUNIS", "NELLA", - "PORTO", "EERST", "ENNUI", "CIDER", "POUCH", "JUIST", "SHYLY", "NEVIL", "FROTH", "GIPSY", "EINST", "HERRA", - "ALLAY", "DANDY", "TOWNE", "HARTE", "HOHEN", "PLUIE", "NEIGE", "FASSE", "WAKEN", "YEAST", "CADIZ", "MINCE", - "WOODY", "AVOIT", "BOIRE", "PIERS", "FANGS", "KRIEG", "SAHIB", "DRYLY", "BELTS", "LACED", "WAYNE", "KOMME", - "AWARD", "FRANC", "FUERA", "IDEES", "COUPE", "VIOLA", "QUERY", "VENOM", "WAZIR", "SLABS", "FATES", "DIXON", - "POKED", "TOURS", "CHEFS", "ABACK", "PAOLO", "BECKY", "OAKEN", "KLEIN", "MOONS", "POLAR", "PRAYS", "DEIGN", - "CROOK", "SOLON", "LEANS", "PUPPY", "FINIT", "XXVII", "FOLIE", "POTTS", "LABEL", "SUSIE", "STACK", "POIDS", - "JESSE", "BLEEF", "HILLY", "BITTE", "WADED", "AIKAA", "EAVES", "MINUS", "REPAS", "NANCE", "DELHI", "HAZEL", - "ORBIT", "SURER", "TYLER", "DELIA", "RAJAH", "JUDEA", "STATT", "MENGE", "WHIMS", "BOYNE", "WHIPS", "PAINE", - "LIBEL", "MANOS", "TAMED", "CHIPS", "REIHE", "SWANN", "QUALE", "SPASM", "VRAIE", "TWINE", "CLIME", "ABHOR", - "DOMES", "KNOLL", "VISTO", "WEDGE", "GRIMY", "ROVER", "WRAPS", "LEVEE", "LEGTE", "AMITY", "NIHIL", "HALLO", - "CARVE", "TREND", "DAYES", "HEARE", "DEZEN", "SAISI", "COCOA", "QUEUE", "SWOON", "WAGEN", "PUMPS", "TRASH", - "SHARK", "MEADE", "RECUR", "DREGS", "VOILE", "CANTO", "HAVRE", "FATTO", "SNEAK", "MISMA", "SHOVE", "SOYEZ", - "CRANK", "FINED", "JUMPS", "BRYAN", "VENEZ", "ENSUE", "WILES", "ROARS", "FREAK", "POSSE", "SINUA", "FUSIL", - "DAHIN", "BEGAT", "ELUDE", "CROIX", "CLANG", "HOARD", "HATCH", "SOILS", "SAMMY", "STARR", "POIKA", "ELISE", - "LISZT", "JENES", "WIRST", "RUHIG", "MEDEA", "RAIDS", "NOOSE", "LETTY", "CHERE", "EASED", "SERVI", "MEJOR", - "FEARE", "SMACK", "ALTRI", "HITCH", "GETAN", "MITEN", "MAVIS", "ACIDS", "BEGET", "GARTH", "TIERS", "ANTON", - "NICER", "TANKS", "CESAR", "CANES", "PLUMB", "VANCE", "BEGOT", "DETER", "FLARE", "BOMBS", "FITLY", "JENEN", - "FLIRT", "HABER", "JOITA", "CHUTE", "READE", "STETS", "STEAK", "GAUZE", "SPARS", "PEARS", "PIQUE", "GLACE", - "PATSY", "HOVER", "RIPEN", "SWANS", "VARRO", "MARSE", "SONGE", "GUILE", "CANNA", "NACHT", "SALLE", "CARTE", - "AMMON", "TASSO", "RAZOR", "WHIST", "OMENS", "NIMMT", "GOEDE", "GRUNT", "DIRAI", "SPIKE", "COCKS", "DIEUX", - "SUURI", "UTILE", "SEREZ", "SOTTO", "STAAN", "DACHT", "LUNAR", "WHOSO", "DINNA", "CRETE", "PADUA", "SERGE", - "CACHE", "CHILI", "TIBER", "TAGEN", "RIGOR", "BIDDY", "AIMER", "MOUSE", "OXIDE", "PESOS", "SLING", "SPILT", - "BOWEN", "SINON", "SINAI", "TULLA", "TONIC", "LOIRE", "JUGER", "HAUPT", "CADET", "COSMO", "OCCHI", "CLANS", - "MUSTY", "MUMMY", "NIGER", "GOULD", "SQUAW", "UNTEN", "PENSA", "BIJNA", "CONTI", "NASAL", "TAUNT", "SILKY", - "NUNCA", "KAKSI", "DUCHY", "MELTS", "VASTA", "DAVIE", "QUELS", "ANCHE", "LIEUT", "BARDS", "BUNNY", "RILEY", - "TIENS", "MOTHS", "OMNES", "TANTA", "WOOED", "BRENT", "RASCH", "JADED", "ERRED", "BRAGG", "GUIDO", "HURON", - "LEILA", "VEDAS", "DEEPS", "RHONE", "SOWED", "BADEN", "NOMME", "SLUNK", "DEARS", "GUTER", "HIELD", "FEUER", - "LUPIN", "HAGEN", "JAMIE", "PLUMS", "GUSTO", "WETEN", "TENER", "HAWKS", "FOLLE", "CHILE", "SAADA", "SQUAT", - "KNACK", "MOORS", "SCOWL", "JENEM", "PLAZA", "SHEEN", "UNSRE", "DETTE", "WEBER", "BITES", "MASSA", "TUOTA", - "WOORD", "DOCKS", "AGILE", "SERFS", "OASIS", "BEGON", "LACES", "CORDE", "MEYER", "REISE", "FOGGY", "DINER", - "HUMID", "QUAIL", "LILLY", "FADES", "WRAPT", "GRUFF", "WEEPS", "ETTAE", "MARAT", "REICH", "LUSTS", "DICHT", - "APACE", "BUENA", "SLIMY", "TRUMP", "JOUES", "DUCAL", "COVET", "GIEBT", "AMANT", "FEIGN", "MEIST", "RATED", - "DOIGT", "ELLAS", "CHOIX", "GLOWS", "PALED", "MOOSE", "SHEAF", "SWARD", "HAREM", "DORIA", "PIKES", "WREST", - "MITTE", "TOOKE", "CHUMS", "TENGO", "LOTUS", "CUFFS", "KIRBY", "BARKS", "GYPSY", "BLOIS", "URBAN", "SIBYL", - "TIRES", "JOSTA", "TEILS", "ODIUM", "PURGE", "DOSES", "WEZEN", "NEPHI", "LIBRO", "SLYLY", "VIGIL", "HADES", - "THUMP", "BUYER", "RYDER", "ARYAN", "SNARL", "CHIME", "ROBED", "AUREZ", "DESTO", "SLEET", "CUBAN", "RARER", - "SEVER", "WOLLE", "FOYER", "SHAKY", "CAGES", "VANHA", "BETER", "TARDE", "VNDER", "FOURS", "KARNA", "POORE", - "OMNIA", "ACTES", "GOWER", "NINTH", "GENAU", "CLAUS", "BIENS", "KOHTA", "NUITS", "BRAID", "FEUDS", "GANGS", - "ILMAN", "LEECH", "NEQUE", "ANTAA", "BARBE", "MAGNA", "PILLS", "PANTS", "EARLE", "MERCI", "PERTE", "BABEL", - "LUCHT", "TENIA", "SEUIL", "DEGLI", "HARES", "VILDE", "RIOTS", "GJORT", "PIPER", "ZEIGT", "TIBET", "KICKS", - "JADIS", "SPELT", "PLAID", "CERCA", "FALLU", "MURKY", "HEINE", "REVUE", "CAMPO", "AMAZE", "LOOMS", "COSTA", - "STYPE", "JONAH", "CHUCK", "RELAY", "FIBER", "GLINT", "SLOTH", "MUSIK", "MOTIF", "BATCH", "SOWIE", "PATER", - "DEANE", "DOETH", "OBJET", "GAIUS", "LAMPE", "DOZED", "GULLY", "SEAMS", "BOYCE", "MAILS", "USEIN", "HENDE", - "PSHAW", "WHINE", "PRINZ", "WILLY", "HALFE", "MILKY", "ANVIL", "SHRED", "CHIDE", "EFFIE", "MOANS", "LEITH", - "COPSE", "DRAGS", "FARRE", "BANDE", "BHIMA", "YATES", "NIVER", "DEVEZ", "LAGEN", "OMBRE", "JONGE", "WATTS", - "ISTUI", "TUSKS", "AMACR", "JAREN", "VARIT", "REDDY", "PRIAM", "PRIMO", "COWED", "TOWED", "AVERY", "AXIOM", - "HOVEL", "BRICE", "CONES", "KUNNA", "LEISE", "HOBBY", "MINUT", "POOLE", "SINGH", "BOILS", "SATTE", "CRABS", - "MERLE", "WARNS", "FOURE", "ACHES", "YEARN", "SAHEN", "AILES", "LORRY", "LOVEL", "LUEUR", "SLANT", "METER", - "PHONE", "TAGES", "SUPER", "FUSED", "GLOSS", "NULLA", "ADEPT", "COREY", "LEGER", "SUAVE", "GAZES", "NEMEN", - "NOGET", "PANDU", "CAREW", "XXXII", "SCAMP", "HEATS", "TIPSY", "HOWLS", "MEMES", "ROOMY", "SANOA", "HAYDN", - "JETTE", "AMORY", "CONTE", "VERRA", "TILLY", "WELCH", "PEALS", "SULLY", "BRINE", "MOINE", "FACTO", "BORDS", - "FIRMS", "FARES", "TUNSI", "WAXEN", "BULBS", "BLIVA", "MESSE", "BRISE", "BORIS", "KAUAN", "RAKED", "IBSEN", - "PINTS", "DENKT", "POPES", "WHIFF", "SYBIL", "QUITS", "ARGOS", "AGGIE", "ANDES", "DALLA", "ABOOT", "EINDE", - "GERNE", "TYROL", "FADER", "SORTI", "MEDES", "AMIGO", "CELTS", "LEONE", "LILLE", "AGLOW", "INJUN", "KEEPE", - "RUINE", "LIEUX", "GEENE", "AUDIO", "SEETH", "STREW", "GRAZE", "KHAKI", "ABRAM", "APTLY", "HEERE", "ETANT", - "GOOSE", "POSED", "CHOPS", "ALTRO", "TITHE", "ANGST", "WORIN", "HIGHT", "CREAK", "NULLE", "DOWER", "SENTI", - "QUELL", "CHINK", "DESIR", "SETZT", "JAUNE", "LYNCH", "SPOUT", "PORES", "GOMEZ", "GABLE", "ACRID", "COZEN", - "BROCK", "DOYLE", "ZWECK", "ACTON", "RARES", "PIPED", "ZONES", "LIARS", "BOYLE", "KAMPF", "ALBUM", "SNIFF", - "MARES", "SHACK", "OMITS", "CITES", "AROMA", "WITTE", "HILFE", "HEERD", "ARGUS", "PINED", "USURY", "EARLS", - "YKSIN", "CULTE", "NOBIS", "SPILL", "FLOTS", "PROIE", "LURCH", "ADOLF", "ARDEN", "CARNE", "INTET", "WREAK", - "VOLLE", "SLIME", "TONGS", "GUERE", "VALES", "MANNA", "ISLET", "MANER", "DUNNO", "EASEL", "ECRIT", "NOOKS", - "GLENN", "HOSEA", "BUSTS", "ENACT", "FETES", "CHAUD", "GUILD", "SIZED", "ENOCH", "SPIEL", "OILED", "NATAL", - "ROCHE", "BASER", "PODER", "QUOTA", "FINIR", "SELMA", "NADIE", "DRAME", "LARKS", "DOWNY", "GULLS", "ARBOR", - "CERES", "LANDE", "WYATT", "IDIOM", "QUILT", "TUTTA", "HEINZ", "MAMMY", "MONDO", "TACHE", "QUACK", "TALON", - "ENSIN", "VEUVE", "SODOM", "DUELS", "RANCH", "MINNA", "CRAPE", "WAVER", "HOOPS", "MEBBE", "VERSA", "KUTEN", - "AVIEZ", "WIJZE", "PLAIT", "BIGOT", "URSUS", "VOLTA", "DRONA", "PADDY", "AHMED", "GIBBS", "OGSAA", "PABLO", - "TOILE", "TERME", "GRAIL", "JUICY", "CINCO", "VOORT", "RERUM", "MUNRO", "SAMMA", "REMIS", "AVOND", "ALLOY", - "RIZAL", "SITTE", "NEWES", "ONZEN", "OFFEN", "SCOFF", "XXXIV", "NOUSI", "GLUED", "BEARE", "STARB", "FUITE", - "BRETT", "NOTCH", "AARDE", "BRUNT", "DEWEY", "GAMLE", "POBRE", "LAITY", "RAVED", "TROTZ", "SPRIG", "JEMMY", - "MEUSE", "FUEGO", "BUELL", "WARMS", "BIXBY", "TWIST", "ROLLO", "RISKY", "GROPE", "MEGET", "DANNY", "LONGE", - "LOOKE", "AYRES", "SUPRA", "CHANG", "FOLGE", "SWAIN", "ARRAS", "CHRON", "SEIEN", "OTTAA", "OBEYS", "HALTE", - "MOLTO", "HORUS", "HALLE", "DEVON", "HARPS", "PHIAL", "SELIM", "TIDAL", "TAPIS", "PRIER", "DENKE", "FONDO", - "OGDEN", "ENDOW", "ASTIR", "FAIRS", "BAKED", "ENGEL", "HANNA", "SZENE", "PIVOT", "WIRED", "RAFTS", "COTES", - "PLATS", "TIMON", "ANNAN", "MENTE", "RONDE", "STINT", "RAGES", "KENNY", "SADIE", "ZIJDE", "CALLE", "DIVIN", - "DUPED", "VOUCH", "FOOTE", "LEWES", "LIISA", "DARCY", "SIGER", "LUCAN", "IEDER", "ARMAS", "DONAL", "BRADY", - "WERKE", "ANNEX", "STATO", "HUNTS", "KAMER", "ALWAY", "PHILO", "SYNOD", "PROBE", "HORNY", "LEEDS", "FOUNT", - "NABOB", "ESTAR", "AVRIL", "DEFOE", "CAGED", "LOOPS", "NEVEU", "CLYDE", "GOEST", "MANES", "LEAUE", "TUNED", - "RISHI", "POSER", "STOIC", "NUEVO", "SAMOA", "SALIR", "SNORT", "ADIEU", "BOCHE", "LOGIS", "JAKOB", "LESSE", - "FILMY", "FEINT", "HOOGE", "LESEN", "TUTTE", "VEDIC", "BATON", "SCION", "SUBIR", "MASSY", "CLOVE", "WHOOP", - "TEILE", "DEBUT", "STOWE", "HELPE", "PAILS", "ADELE", "HERUM", "DESKS", "LIIAN", "HINZU", "SIDED", "TRITE", - "HORAS", "JINNY", "POPPY", "EQUIP", "NEERE", "URINE", "VIENE", "BERNE", "DUROC", "PERTH", "ARIEL", "GREGG", - "WOLLT", "MOREL", "DEEMS", "VIEIL", "MEZZO", "POETE", "HERON", "SHIRK", "SIETE", "HEMOS", "SORES", "EATON", - "ASHEN", "VERSO", "ALTON", "STORA", "SWOOP", "CZECH", "JOVEN", "SOFAS", "TIMED", "XXXVI", "KREIS", "WEILE", - "SCOUR", "JACKY", "ROLFE", "PIENI", "STIRN", "DREAR", "PIERO", "TRAYS", "SYLLA", "ERANO", "LURKS", "GOURD", - "MAAKT", "WANED", "LIBYA", "ALSOF", "FELON", "BREST", "ELENA", "WERDE", "JUNTO", "TENTE", "VERTE", "SILLE", - "VOLER", "ALBAN", "GALEN", "REDAN", "WALSH", "ARBRE", "ASTOR", "RAYON", "REEVE", "MUCHA", "NUORI", "LABAN", - "SIKSI", "LUCCA", "ALLUS", "PURTY", "BUDGE", "USURP", "MONTY", "YEMEN", "JOKED", "QUAYS", "LECOQ", "WYLIE", - "MACON", "UPTON", "NEALE", "SEENE", "OFFRE", "OTTER", "POING", "LEHRE", "ADOBE", "ABYSS", "SPURN", "PLATT", - "HUUSI", "PLUTO", "REMUS", "MOGUL", "DULLY", "BLOED", "SPICY", "GEEST", "FEIND", "KETCH", "LASSO", "ESTIS", - "BRYCE", "MENER", "PACHA", "ANTOI", "LEPER", "BAUER", "JOHNS", "FEREZ", "SWEDE", "HERAB", "VOTER", "SIREN", - "ELDON", "ROOKS", "EBERS", "NESTA", "YEERE", "ROLLE", "SLADE", "GLAND", "JOSEF", "BORES", "GALOP", "NELLE", - "HEGEL", "MEANE", "WIDEN", "DAWES", "PLUSH", "ERHOB", "FORSE", "SOLLE", "ZIEHT", "SUCHE", "BERGE", "HASAN", - "MANDY", "OEVER", "BAIRN", "ELGIN", "ZULKE", "AKBAR", "BUREN", "CRUMB", "BACKE", "LINGE", "WOHIN", "HACEN", - "BEENE", "LARVA", "REGEL", "BEVEL", "DUVAL", "KNAPP", "GEEFT", "REELS", "JUANA", "ESSER", "PLATA", "STIEG", - "RACEY", "KNELL", "LLAMA", "VOISI", "HINDI", "PASAR", "VENTS", "HOLDE", "PITIE", "PAGET", "SLUMS", "GIRTH", - "DROSS", "MAIRE", "LAGER", "FEHLT", "MAZES", "UNDID", "THYME", "WHORE", "WOGAN", "BLUES", "RIAIT", "PANZA", - "SALVE", "ZOUDE", "HERRE", "MIDAS", "PREIS", "SIRES", "GLUCK", "ROAST", "FABER", "ASIAN", "DEREK", "DIGBY", - "YARNS", "NUAGE", "SENCE", "HEWER", "SAWED", "SAVOR", "SWIMS", "CABOT", "TAFEL", "ANSON", "CAROL", "SNART", - "HUSKS", "SERRA", "LYMAN", "MARNE", "EDDIE", "GENII", "DAMEN", "RAMON", "FAUNA", "ARMEE", "CHUSE", "DEMIE", - "COMBS", "VENAL", "BLINK", "CYNIC", "JOLLA", "FUESE", "DUPES", "LOSER", "TERSE", "DUNES", "KENNE", "WINDE", - "GAYER", "CONAN", "MISSY", "BALLE", "RANGS", "REBUS", "FUSSY", "DRILY", "JUGES", "HABET", "IMACR", "CUZCO", - "FELLA", "HEWED", "AIENT", "SOULE", "CAPUA", "ULTRA", "EBBED", "VIPER", "CAFES", "KENNT", "CRAZE", "ELMER", - "DRONE", "SERAS", "SIENA", "WOLFF", "GILLS", "CRYPT", "GRAFT", "SANTE", "HALTS", "GAPED", "GELUK", "YOKED", - "FONDS", "FERNE", "NAOMI", "SECHS", "BUXOM", "TENEZ", "QUAKE", "TENUE", "ROUVA", "LOURD", "LYNDE", "REACT", - "SOUPS", "ODEUR", "KAZAN", "BOXED", "GRUEL", "ECLAT", "GLENS", "JOHON", "SOPRA", "COCKE", "RENEE", "SWISH", - "PFERD", "SCHIP", "KERRY", "ALDEN", "DANSE", "DELTA", "UNIDO", "SOORT", "EXULT", "ANDEN", "BLIVE", "HORNE", - "AMORE", "EORUM", "KINDE", "JEERS", "NAMUR", "ULICK", "SNAIL", "ALECK", "MEEST", "SUEUR", "DUMMY", "MARSA", - "CURRY", "ELTON", "NEDDA", "DOLOR", "GEVAL", "DEUIL", "THONG", "MULTA", "RESIN", "DIRGE", "LARGO", "INDUS", - "LEMMY", "BONUS", "PLEAS", "ALINE", "ABOUE", "CRAMP", "CORSE", "VERTY", "FALTA", "WOMIT", "EARNS", "PRATE", - "WEARE", "PHEBE", "OVERT", "BRACH", "CAMPS", "TRAUM", "TALTE", "MERGE", "SAUVE", "RIUER", "COALS", "FLOYD", - "ASSAI", "EMPOR", "NOTTE", "VINDT", "ENKEL", "SUCHT", "ZAGEN", "EPIST", "LYELL", "RILLS", "WOFUL", "MEKKA", - "ZULUS", "HUSSY", "GASPS", "JOTTA", "SOOTY", "ANNUL", "CAPES", "SHERE", "NENNT", "SORGE", "DUANE", "CALMS", - "DARBY", "TAHOE", "LIEST", "SAUTA", "SPURT", "VINCI", "ANNAT", "BOORD", "DROPT", "ZELVE", "DWARF", "ATTER", - "BLEUS", "DEANS", "JEWEL", "HEMEL", "AFIRE", "FLEAS", "ADAIR", "INSEL", "MUITA", "TALLY", "CABAL", "SAMME", - "MELON", "TEPID", "MIELI", "FOLGT", "POLLS", "ROSIE", "WALDO", "LEBTE", "HINGE", "SNOUT", "STURM", "BEALE", - "HAGAR", "SEWER", "SONIA", "TEXAN", "BEETS", "CHERS", "BURMA", "HOWEL", "EYING", "SAIDE", "PUNIC", "ANTIQ", - "UNTIE", "DILKE", "SOUPE", "WYNNE", "SKATE", "AMYAS", "AGUAS", "HESSE", "ADAGE", "ROOST", "WOLDE", "LEIPZ", - "TILED", "EAMES", "ASTUI", "SCOOP", "KNABE", "CLINK", "ELEVE", "RAZON", "VIEJO", "REYES", "ENDNU", "RIVEN", - "ORDEN", "PARRY", "GULFS", "EXTOL", "AUNTY", "TWEED", "RENDS", "STERK", "SIDON", "XXXIX", "SUDAN", "THANS", - "COMYN", "MATTI", "MONCK", "OHREN", "HADDE", "VREDE", "WAIVE", "LEJOS", "MITRE", "BEFEL", "PASTY", "WORKE", - "BLOTT", "LIEBT", "BARRE", "SOARS", "FISKE", "CERTO", "WRITS", "LENTE", "VAINE", "FALLE", "VERDE", "MOGEN", - "GRETA", "MOCKS", "TRANS", "CROAK", "DITTY", "VRAIS", "MINOS", "SALUA", "CHAOS", "PEKIN", "APPEL", "MAGIS", - "FINCH", "ELVES", "ISANG", "KOLME", "POESY", "DECOY", "STOPT", "LUCIE", "NETTA", "SOULT", "ALGUN", "IDIOT", - "DISKS", "CRISE", "EVOKE", "LIEVE", "KLEUR", "FERMA", "EENIG", "CIRCE", "SUELO", "MATER", "BATTU", "LINIE", - "MORNE", "SEGUN", "LARRY", "TOMAR", "GAGNE", "SCALY", "STAVE", "GLEAN", "KOVIN", "DORIC", "GORSE", "INDES", - "BEPPO", "GLANZ", "KUNTI", "OPFER", "DRIES", "TOTTA", "SHIRE", "SILVA", "OBRAS", "NAAST", "DETTO", "OMAHA", - "SHEBA", "TITAN", "BEAKS", "COWAN", "MARKT", "ADOWN", "GOUTY", "HAREN", "TOADS", "QUALI", "RENAN", "MANNE", - "SNORE", "COMMA", "ZYNDE", "LYGIA", "RUDER", "SCRIP", "WIERD", "ELEGY", "TWANG", "ANTTI", "SNIPE", "TABOO", - "MOHUN", "BRANT", "CAIRE", "DEMAS", "ARDAN", "ALTID", "COLLA", "BRAWL", "LEASH", "CHAFE", "PARED", "VOMIT", - "MYERS", "SOURD", "SANOO", "TAIRE", "ABDUL", "GNADE", "PALSY", "MIKSI", "JOHAN", "MELEE", "DRAWL", "HULOT", - "SOUCI", "WILLE", "AGATE", "NOMEN", "SEEKE", "GRIPS", "LEGEN", "SINEW", "JAMBE", "VRAAG", "EMACR", "FETID", - "LEWIN", "EDERS", "VIVES", "KALLE", "FILMS", "GUTES", "MARYA", "MYSIE", "JERKS", "PAARD", "NANNY", "EENER", - "ROVED", "CHICK", "BITCH", "HINAB", "IDEEN", "ESSET", "HOURE", "RHYME", "JULIO", "BELLO", "HAVIA", "DEARE", - "IDLER", "WOBEI", "JABEZ", "MIAMI", "ESTOY", "AULUS", "ANCOR", "MOVER", "WARRE", "IMPEL", "KRONE", "RACHE", - "FASTS", "NEPAL", "BANJO", "REINO", "DAWNS", "HETTA", "STAEL", "CHIRP", "PENTE", "SOLLT", "VOCES", "DEZER", - "LAINE", "MINDE", "ANENT", "ASSET", "PUITS", "SWAYS", "MOCHT", "WAHRE", "DEMUR", "BETTE", "LUZON", "TIESI", - "TONED", "FREDA", "TAINE", "DALLE", "SPITS", "DYKES", "TOLLS", "SOONE", "EURER", "ANDAR", "KITES", "KENBY", - "PROPS", "PUNKT", "TIEFE", "FLAPS", "NIMES", "ROHAN", "GUIDA", "GOODY", "LEROY", "BUCKS", "ONKEL", "ADLER", - "ALAIN", "SWIRL", "ZOORT", "GARRY", "AMAIN", "TEINT", "EERIE", "GUSTY", "AIDES", "ANIMO", "DOEST", "DREST", - "LAPSI", "FARBE", "RACKS", "VOEUX", "BELIE", "LEMON", "INCAS", "NECKS", "LOGIK", "RIVET", "DOTTY", "EWING", - "LENOX", "ZORZI", "TUNNE", "COLDS", "INNES", "CARON", "TULLY", "KULKI", "NIEUW", "RIPER", "HATER", "ROSEN", - "WAIFE", "FATTY", "VOGEL", "YPRES", "CASAS", "SINUT", "WINKS", "NOLAN", "BOARS", "BIRON", "MINAE", "KRAAL", - "STUHL", "PODIA", "MARLY", "MOROK", "DINES", "VENTE", "WHACK", "AMINE", "PALMA", "DANDO", "TAVIA", "QUILS", - "PATIO", "TRICE", "MARIN", "FOUET", "WAILS", "HAKON", "WALLY", "WINCE", "VOLGA", "TEKEE", "TURNE", "REFUS", - "SERRE", "LEAKY", "PROXY", "CAPPY", "BLARE", "BUSEN", "MAORI", "ALOES", "SLAVS", "BANCS", "VESTA", "METED", - "NEUES", "ALWYN", "GURTH", "JOSUE", "STOLZ", "JOZEF", "JOSIE", "MAYER", "STUBE", "ZADIG", "SLOEG", "STRUT", - "REARS", "MARTA", "BROIL", "CARTA", "DEGEN", "HURST", "MUNDE", "EPICS", "VILJA", "PESAR", "SPAWN", "MINUN", - "TALKE", "CROFT", "TESTA", "SEARS", "MYRRH", "CASED", "LISTE", "CHURL", "DICEN", "KURUS", "CORAL", "LIVIA", - "COLTS", "DRAYS", "ESTAN", "POETA", "CABUL", "JUDEN", "LATHE", "LITLE", "LEPIC", "EATER", "DIJON", "PAVIA", - "CLODS", "BYRNE", "JULEY", "GENTS", "VERNE", "ASSAY", "CHINS", "LIZZY", "PUHUI", "CANBY", "KNOBS", "KRING", - "BEZIG", "STINK", "STUND", "BUTTS", "DONOR", "SLICK", "WASPS", "HAMAN", "LIMES", "MEURT", "HAMEL", "CITIE", - "DIVIL", "SLAYS", "CHUMP", "DIKES", "PEEPS", "LISLE", "MOTTE", "FORET", "MACHE", "BREVE", "STROM", "BUENO", - "JEHAN", "BONTE", "GETAL", "HOBBS", "TAHDO", "VOLTE", "GIUEN", "TEXTE", "CLANK", "VITAE", "MAUVE", "LIDIA", - "SMEAR", "CIRCA", "JOUIR", "PRETE", "ARTEN", "PARKE", "CAVIL", "XLIII", "STUDS", "VALLE", "BOUTS", "KOMMO", - "NEITI", "GRIPE", "JOKES", "SOFIA", "ASPEN", "HEROS", "LOSSE", "BRAUT", "HOODS", "KRANK", "LINER", "TIDEN", - "FLAWS", "HAUTS", "UMHER", "MAATA", "HELAS", "SULLE", "JERKY", "OUTDO", "RABID", "CINNA", "MANIE", "TUSEN", - "BATED", "BELLY", "BORST", "MOLES", "YUKON", "STORK", "RAZED", "OMACR", "SLITS", "SERIE", "HAITI", "MORAN", - "RESTO", "WOVON", "BISON", "TULIP", "DARYA", "REEDY", "AILED", "DIESS", "RIRES", "SELBY", "ELITE", "FIXER", - "MIHIN", "NORMA", "MEADS", "TIERE", "OOZED", "POSES", "MIENE", "GEHAD", "DIENT", "RECIT", "AZTEC", "EILTE", - "GNATS", "JETTY", "JOIES", "KOHTI", "BARTO", "MCKEE", "SAUDI", "STAGS", "PARME", "SINGE", "ALVAN", "CLAMS", - "CRONE", "CORSO", "PUHUA", "SATIS", "DENRY", "FURRY", "FURZE", "PUEDO", "COUDE", "DIDNA", "SOIRS", "ACORN", - "KNOWE", "KREEG", "TOOTH", "SERBS", "DUNCE", "THEIL", "SANFT", "ASHER", "NOGEN", "TETES", "AFFIX", "CREER", - "PITHY", "MARIO", "WORDY", "SIMLA", "ROTEN", "VISOR", "ZAKEN", "BOGEN", "HERAN", "ILLUD", "JAHRH", "CUBIT", - "PRISM", "BAIRD", "DUPRE", "TORRE", "REGNE", "SUCRE", "WERRY", "MORIR", "SLASH", "MENEE", "SERUE", "WIENS", - "BUNKS", "SEERS", "ZOGEN", "CORKS", "HOGAN", "GARDA", "TARTS", "FROME", "POCOS", "SPRAY", "CAIRN", "BAITH", - "DAGAR", "DEJAH", "ICFTU", "ZUNGE", "CHARY", "CHURN", "FLOES", "SIENS", "VAPID", "GAVIN", "GRIER", "BLOTS", - "GRAPH", "CUBES", "BIGHT", "ARLES", "MICAH", "NEBEL", "DIVER", "STABS", "BESSY", "KUULI", "SITZT", "SENAT", - "PINKS", "SPANS", "VINER", "ALKAA", "HAELT", "BAYLE", "CREUX", "ABBAS", "CROCE", "JAFFA", "ABLER", "GULCH", - "KANSA", "LIENS", "TITEL", "MATED", "MEGAN", "ALACK", "COHEN", "HELGI", "SAMOS", "ALDER", "FORAN", "LANKY", - "GRISE", "OATES", "LAVER", "IONIC", "ALIBI", "OUVRE", "GERTY", "PONER", "KEENE", "FESTE", "SANAT", "ESSEN", - "ADDER", "KURZE", "REALS", "CRAYE", "SANON", "TULEN", "VIDES", "POATE", "WIPES", "ANITA", "GAMLA", "DELFT", - "CESSA", "FORDS", "SERIA", "ASAPH", "BUDDY", "GRAAF", "BENNY", "KREBS", "CLAPS", "AMIES", "DUPIN", "VEXES", - "COMUS", "ROUES", "IRATE", "MANSE", "TACKS", "BRACY", "TABOR", "STORT", "JINKS", "SHEIK", "PARVA", "SKUNK", - "ALPES", "VERUS", "BOGUS", "RIVES", "NINON", "FILLY", "JENKS", "DOLCE", "ZETTE", "EPSOM", "DENYS", "LOBES", - "SLUSH", "BONUM", "CANOT", "MYLES", "OTWAY", "TREUE", "DANKE", "FREIE", "FITCH", "SUMME", "CAPER", "BETTS", - "SPREE", "TEENS", "IRWIN", "STAHL", "MIXES", "TIARA", "MOTTO", "NOGHT", "STURT", "BLEUE", "DALES", "STING", - "SMOCK", "BLOCH", "FUROR", "SATED", "VENNE", "JAPON", "RIGBY", "DULCE", "HEIRS", "WISPS", "PROWL", "REGEN", - "PENIS", "SICUT", "TOSTO", "HULDA", "HOFFE", "SEEDY", "DRIER", "LILLA", "ZEVEN", "LADLE", "SAURA", "VESTE", - "AINOA", "ALICK", "RUGBY", "CORPO", "BLYTH", "JANUS", "DICTA", "VAUNT", "LAVAL", "DIGHT", "PHOTO", "ZUVOR", - "ELSJE", "LUNGE", "TOMBS", "STOFF", "SURAT", "BEVOR", "OUDEN", "TRESS", "WIRES", "TIMOR", "RUSSE", "TABAC", - "WIRFT", "BERYL", "CACAO", "HEALS", "ROODE", "SWARE", "LESER", "LIDDY", "ANIMA", "ILLIS", "LUONA", "PECHE", - "ROPER", "TORAH", "GOTTA", "SOMIT", "KOMIK", "ANNEE", "ERDEN", "BANGS", "PREST", "HOUDT", "ONZER", "RAKES", - "PAIRE", "SOLVE", "TYSON", "UTICA", "TROUS", "GAMUT", "PEPIN", "CHUNK", "MAMIE", "GUYON", "ORIEL", "MIEDO", - "BUTTE", "OMNIS", "QUISO", "GERDA", "DIETH", "SIRUP", "WHISK", "FAERY", "REGIS", "INANE", "MULLE", "SHINS", - "TAMAR", "ETTEN", "CIBOT", "BOGGY", "CAPUT", "ETEEN", "GRETE", "WADNA", "CRUST", "IPSUM", "BLIGH", "LOUER", - "ONKIN", "BOGGS", "HOLDT", "MEINT", "SKEIN", "TOCHT", "VIALS", "AIDER", "RECUT", "GRECS", "LOUGH", "SPOOR", - "SEEME", "KNOPF", "STACY", "FIEFS", "WEEDY", "BENIN", "CHINE", "BREDA", "BEKAM", "SPINS", "WARST", "BOXER", - "TROST", "STUNT", "ANIMI", "AURAS", "SUMMA", "YHDEN", "YOUSE", "GROOM", "PETTO", "POMPS", "MIRZA", "HALLA", - "DENNY", "JAILS", "PUFFY", "DELLS", "MUREN", "UNCUT", "LEACH", "LIKEN", "MURAL", "OSMAN", "TARDA", "DESEO", - "MODER", "MODUS", "MANON", "COLIC", "PRUNE", "VIVIR", "CLINT", "RIGGS", "CANNE", "SALVO", "AIRES", "PIKKU", - "SAMEN", "BASTE", "ARCIS", "EASIE", "SHAMS", "MATTY", "CANNY", "NEIGH", "HYMEN", "DAUNT", "BRUNE", "FORZA", - "YAZOO", "VENDU", "NICKY", "WINNA", "PESTS", "WARME", "BENET", "TOSIN", "VASCO", "BETES", "LIEUE", "PETAL", - "ANNOS", "COLLE", "TRIEB", "KLAUS", "PUNIR", "VODKA", "THRON", "DECIA", "BRAUN", "SHUNS", "UUDEN", "ALPHA", - "FLOUT", "SELLE", "STOOL", "NEUEM", "BASLE", "HERNE", "INFRA", "ERASE", "NESTS", "BERGS", "MEDAN", "NOITA", - "SUCKS", "IESUS", "CITER", "FRAGT", "VEDER", "DRUID", "SCALD", "ALIDA", "QUITO", "HOERT", "JUNOT", "PERDU", - "GEARY", "BOOKE", "KENDE", "CHICO", "MEATH", "ROGUE", "SMYTH", "DIVES", "FILET", "FLAKE", "PUTTY", "SIDAN", - "DANAE", "ILAND", "BORDE", "HERBS", "LIMBO", "PEINT", "ROLES", "BRINY", "ETRES", "FLANC", "BLATT", "DERRY", - "MUEHE", "RODDY", "DAMES", "INGER", "XLVII", "AIRED", "CHATS", "PAULO", "EJECT", "FUNGI", "SIKES", "CADRE", - "COOLS", "SNAPS", "BLENT", "BOOBY", "VIIME", "DALLY", "HIVES", "LEDDY", "BALDY", "PRANK", "NONCE", "OVENS", - "SCHAL", "AUGUR", "CALYX", "MOWED", "TOITS", "CIMON", "SWEYN", "EDLEN", "PALES", "SPERM", "THANE", "BEZIT", - "EILEN", "UUTTA", "NAMAN", "MACKO", "SHANG", "LITEN", "BAZIN", "THORD", "ARMES", "YEARE", "CALLY", "TRYST", - "CAPRI", "ETHAN", "HOEHE", "AOUDA", "HALEY", "BERTA", "KAREL", "CLUES", "CURST", "FORAY", "ITHER", "LASST", - "RICHT", "YAWNS", "WILKS", "GRIFF", "JOKER", "MOCHE", "ATLEE", "BAIZE", "SITAE", "TEMOR", "TENET", "HAYTI", - "MANGY", "LEVIS", "SHAKS", "CURIO", "ERICA", "FRETS", "TAKAA", "KLAAS", "FROCK", "SHIED", "TRUSS", "WACHT", - "LAMME", "SAMAA", "ARMER", "SLINK", "BLEEK", "BIBBS", "ARMIE", "BANDY", "HARDI", "MINNE", "MORIN", "COURU", - "CLUNY", "MOLTI", "PLOMB", "AHMAD", "ROCCO", "SLAKE", "HAARE", "ARIAN", "WOCHE", "ZILAH", "LURES", "KLEID", - "NUOVO", "TUMOR", "CLEON", "CORTE", "SUNNE", "ELFIN", "GLAZE", "HEDEN", "VREES", "BODED", "HVIDE", "TRIBU", - "ZACHT", "YUSUF", "FRUEH", "LEDEN", "SITIO", "LITET", "MAURY", "PRYOR", "ROBUR", "GRIST", "RUFEN", "VAERE", - "VILKA", "JORAM", "FONTE", "NOGLE", "BASEL", "FIRMA", "HUNCH", "STAVA", "ALLIE", "FIERI", "HOLEN", "TROTS", - "KNEAD", "MANGO", "OEUFS", "PONCE", "QUEED", "KLANG", "AMENI", "PONTE", "EIGNE", "BRILL", "LOCUM", "NERFS", - "CLEVE", "COPIE", "EHREN", "BOTHE", "SIKHS", "SHREW", "HODGE", "WILEY", "ALTRE", "SIGLO", "CORFU", "HAIRS", - "MALER", "OZONE", "IGUAL", "VORST", "ANGES", "INNAN", "NOMAD", "DOMIN", "FUTUR", "ROPED", "HALED", "NAVEL", - "PEASE", "SAMBO", "HABLA", "ALTYD", "BREIT", "RENTE", "BAGGY", "LANGT", "NOCES", "SHEAR", "BULGE", "SLEDS", - "TRURO", "ZWARE", "BANNS", "NAPPE", "PLASH", "TARES", "WHIRR", "SCOTS", "WANDA", "CREDO", "FUMED", "GABEN", - "POJAT", "SISTA", "VIVIE", "HALVE", "SICKE", "SIMUL", "WHELP", "BREEN", "EBENE", "HAWKE", "STAUB", "MOVIE", - "PECHO", "BUONA", "CULPA", "GAGNA", "HEELE", "FLITS", "HIESS", "SOLUM", "STEHN", "FIRME", "ESTER", "MACAO", - "LIBER", "MCKAY", "PETRA", "TOKYO", "SOLCH", "BAYOU", "HELGA", "LOCUS", "ZWART", "FABIO", "ROSNY", "CORNE", - "MESMO", "BASTA", "DISCS", "GRINS", "FLEES", "PIGMY", "MAUER", "FRISK", "HUNDE", "LOGOS", "TROMP", "HOHER", - "SOGEN", "BOEUF", "CUORE", "FACIE", "RANDY", "ALTRA", "ANKER", "DROLE", "LARGA", "POJAN", "ORTON", "DEJAR", - "GIBES", "MARIS", "SETTE", "SUBIT", "VARMA", "WISHT", "GUPPY", "THOTH", "MANUS", "TRILL", "FIRTH", "BANAL", - "METTE", "SNAKY", "ESIIN", "LUNGA", "BOOMS", "GRUBS", "NEEMT", "TASSE", "OHEIM", "ENCOR", "FELLS", "VERTS", - "GUNST", "SYKES", "VESTS", "COLON", "AGONE", "AIMEZ", "BAHIA", "LEDGE", "LICET", "BEGAB", "FOAMY", "HULKS", - "NEGER", "KEYED", "MAIST", "FABEL", "LORNA", "DICHA", "SACRE", "SITEN", "GODEN", "DARKY", "DRAVE", "NIXON", - "LAULU", "FIGUR", "GANGE", "KLAMM", "LACEY", "KORTE", "RUDES", "TUERE", "LUNGO", "VERRY", "VAURA", "CUANT", - "DRAUF", "OFFAL", "GORGO", "MENUS", "MINED", "OPTIC", "DRANG", "GRIME", "CORNY", "KETTE", "HEADY", "IRAIT", - "PAPEL", "BLITZ", "HUGHS", "DROEG", "TENDU", "WIERP", "DOANE", "ELATE", "LACHE", "PIEUX", "TULLE", "TONGA", - "NAEHE", "CLASP", "FANNS", "WAFER", "DRUCK", "SAUTE", "NEVIS", "PITTI", "FALDT", "USQUE", "EWELL", "GUNGA", - "MORAY", "FISHY", "FLAIL", "MEBBY", "DAGNY", "CLOGS", "ELEND", "MEAUX", "GOLFE", "HACHE", "SAILE", "GIRTY", - "NADIA", "PAVEL", "PAHAA", "SAEPE", "KIRKE", "WALDE", "EXITS", "DOBBS", "HAMAR", "MARCO", "NOYES", "HAILS", - "MALEN", "SHALE", "TAGER", "VELDT", "GREIF", "MASHA", "LOUIE", "DEDEN", "LEZEN", "NETTE", "CROWE", "MOYNE", - "GILDS", "CREON", "GOTHA", "KAREN", "BRUME", "VERIE", "BOSCH", "MEURS", "HYENA", "LLENO", "HOCHE", "CONDY", - "PYOTR", "CALMA", "SAMAN", "STEHE", "BERIC", "LIBBY", "MONTS", "TRONE", "JUSSI", "ALCUN", "BEGGE", "COVES", - "NYODA", "ALLZU", "DAIES", "MULTE", "JUNKS", "MUSST", "DRAPS", "HAAST", "JACKS", "FAYRE", "RUNDT", "FREUD", - "COBRA", "FUMEE", "SAGST", "PURDY", "BEELD", "TORTS", "VERVE", "BRAZO", "JOYED", "WAGTE", "JOURN", "MUNGO", - "PAPUA", "FUELS", "HALBE", "PALMY", "REGLE", "ELISA", "JUSTO", "TWIRL", "CONST", "PASSO", "AMANG", "LICKS", - "NEDER", "DEGRE", "DIREZ", "FRILL", "KORAK", "KREUZ", "TYCHO", "POVAS", "ASTON", "GOSSE", "ASILE", "BOOZE", - "BUMPS", "SOMOS", "WASNA", "LORCS", "CRATE", "GROEN", "WOOER", "HEBER", "JUNTA", "TURCS", "VOLTO", "WHILK", - "DEEPE", "NEBST", "CODEX", "JUIFS", "ASIAA", "FULCO", "DOMED", "OUTEN", "STEPT", "GALBA", "CHEAT", "COULE", - "MEETE", "PAWED", "BUNCE", "SANIN", "JOLIS", "PORED", "PYYSI", "HOWAT", "LAURE", "ONNEN", "PRIED", "SCHAR", - "BAZAR", "MITAD", "MOUND", "FUERE", "TRYON", "BAISA", "CROUP", "KUNNE", "DOONE", "FOLEY", "NEHME", "DUOMO", - "SONNA", "FACIT", "KENYA", "GIVIT", "BLEAT", "HURLS", "EIFER", "AILIE", "BASIL", "HARMS", "SUTRA", "LARME", - "BRITT", "GRADY", "MOROS", "TRIPE", "ADMIS", "HERTE", "VOIES", "LANKA", "FUSSE", "GLOAT", "GROAT", "CLEEK", - "MESES", "MOOIE", "HAMIL", "FORDI", "MENOR", "SATYR", "TODDY", "TANNO", "GAMIN", "SOLOS", "YOURN", "AHMET", - "FATTA", "ERWIN", "POWIS", "SEGEN", "AMENE", "BASAL", "MOSTE", "TOYED", "MAGDA", "DIEPE", "GENOU", "OCHRE", - "WEITE", "VOLKE", "POORT", "AIKEN", "JOPPA", "SLUYS", "FIERE", "JOUST", "LACHT", "SAYDE", "DELOS", "LETHE", - "TAGUS", "ALIIS", "HIVER", "TENGA", "ECOLE", "FUENF", "ROTHA", "VETCH", "GLANS", "LAIDE", "FLYNN", "KHASI", - "KELLS", "BOONS", "SLUGS", "ULCER", "VENTO", "BRAWN", "ENTRO", "VAIMO", "AGNEW", "CREES", "MEIGS", "GOUGH", - "PUSSY", "VERBA", "RICKS", "ROWAN", "FERRO", "LAGDE", "PRESO", "RUPEE", "FRANS", "ALDUS", "CLARO", "HANNO", - "VIEJA", "MARFA", "OSTEN", "GAUZY", "BOULE", "DRAFT", "GREVE", "SETON", "HEEDS", "SONNY", "HANSE", "LUISE", - "ALIUD", "AMASS", "EARES", "ROWDY", "STEWS", "KLARA", "ROSSI", "HENKI", "NOSTI", "ZOUDT", "GROTE", "DEALE", - "FREMD", "KLONK", "LIBRI", "CAPET", "FLOSI", "SONST", "CULTS", "DIKKE", "ALGER", "DIXIE", "NUNEZ", "QUAFF", - "QUALM", "TAFFY", "QUEDA", "SEDGE", "GUERT", "ETIEZ", "ICING", "PHIPS", "PICTS", "ABBIA", "THEER", "COREA", - "KABUL", "COMEN", "GOLDE", "VIXEN", "ZWAAR", "EADEM", "FREES", "MARGE", "MEREN", "SANAN", "EDLER", "LUOGO", - "PEDAL", "VAMOS", "OSTIA", "ALTES", "COUTE", "MANDO", "AESOP", "SAPOR", "PIEDI", "GIRLE", "EMORY", "MADDY", - "HULLS", "REVES", "BAITS", "MARTS", "PROUE", "RIFTS", "IFRCS", "SANER", "EISEN", "MORGA", "WISSE", "DANCY", - "HAZEN", "LASKI", "ESTAS", "AVOUE", "CACHA", "EWIGE", "LOOPT", "WAMBA", "ACASO", "MOLLE", "JUXON", "BOCCA", - "COLUI", "TRONC", "DAUER", "PARET", "CIEUX", "DOERS", "MICHT", "NADAT", "ROULE", "DURER", "GRADO", "SUFFI", - "WAYES", "HAWES", "VOREN", "MILCH", "HVERT", "COTTA", "ZAIRE", "ALLOT", "PANSY", "FOILS", "LEAKS", "SEKIN", - "SEUEN", "WHOME", "BERRI", "BIGGS", "PARAS", "PROSY", "RACER", "BOHUN", "OOTAH", "NIOBE", "COPRA", "PLAGE", - "SPEKE", "AUDIT", "FINIS", "ROMPU", "BANDA", "DAMPS", "QUARE", "TOQUE", "BRIER", "DOREN", "NANON", "BELLI", - "TYRAN", "HILFT", "PUEDA", "MOLDS", "OELIG", "ATHEN", "LARCH", "PUNTA", "CAMEO", "MALIN", "SONYA", "JAUNT", - "LAPIS", "FINIE", "POMPE", "SIMMS", "BEADY", "GUANO", "KEHRT", "RUIDO", "WRANG", "FAHRT", "URIAH", "BLEVO", - "DUQUE", "HACKS", "TASTY", "MAINZ", "COWER", "FUORI", "TEHTY", "ARRAN", "TOOTS", "UTHER", "CROCK", "HELLE", - "QUUNE", "LINGO", "CLLIA", "ELIHU", "APPUI", "BLUER", "DIRIS", "BYLES", "FEDER", "INJIN", "STENO", "AGORA", - "COMUN", "GILET", "GRAUE", "MISES", "WOHER", "MENKO", "AMBOS", "MUTES", "MOREA", "YONGE", "LAKOU", "TECLE", - "BRATS", "LAMED", "PENDU", "TOPAZ", "DRUSE", "LAPEL", "WANDS", "AINDA", "FOOLE", "FUSES", "DEBBY", "EUGEN", - "FABRE", "MALTE", "FEINE", "GANTS", "HETKI", "SENTE", "AMICI", "LOUPS", "OPDAT", "LAUNE", "TOULD", "DURAS", - "VERDI", "STUMM", "CHENG", "FLUSS", "SACRA", "SPADA", "CRONY", "LEGDE", "OIKEA", "ALVAR", "ABAJO", "CURDS", - "PRINS", "THEYR", "BOWIE", "REDET", "WRACK", "FLORE", "ABAFT", "ANDET", "CENCI", "CLAES", "GONGS", "OWENS", - "CUIUS", "EXIGE", "IGNIS", "SLAAP", "OWAIN", "MALLE", "SACHS", "SIGNE", "BOESE", "MUSTA", "TORNO", "GORKA", - "ERANT", "REGNO", "SWART", "DORMI", "MASAI", "FIDES", "KARMA", "RAIDE", "ILIUM", "MARTY", "ROLLA", "DEERE", - "TOMES", "KNOOP", "DOLCH", "PAIGE", "ISTUU", "WAIFS", "COLES", "ASKEW", "EUREN", "HELST", "CLAUD", "QUENU", - "ROJAS", "VYASA", "KULTA", "LEYES", "BOYER", "FUCHS", "NAWAB", "BIDED", "BUSCA", "LORNE", "AIKOI", "BEFAL", - "DUCAT", "ELOPE", "FERAS", "LLEVA", "BEINE", "EMERY", "BRIAR", "DIXIT", "MURAD", "TODES", "OLELO", "SALUE", - "TOTEM", "DILLY", "HAFEN", "NOEMI", "UNCAS", "JUGEA", "QUIVI", "ALIXE", "CASSE", "GAVEN", "GRAMS", "TORSO", - "DAMIS", "NILES", "NORTE", "OXLEY", "PASOS", "AGIAS", "ESSAI", "REFIT", "FAGIN", "HARUN", "AENNU", "DECRY", - "HOOSE", "PONTS", "RENDE", "UARDA", "DRUMS", "PLIES", "PRIUS", "USKOA", "GAHAN", "MIRAR", "MELAS", "ECHTE", - "DARIO", "RHEIN", "ROSAS", "FRUTO", "SLAPS", "BRAHE", "OGIER", "ROQUE", "BEFIT", "VIAJE", "ETATS", "FINOT", - "LINDE", "ZISKA", "LAHAT", "MITAE", "AUTOR", "FERRE", "LAMES", "LLENA", "HOLME", "MAHON", "MUDGE", "SAVVA", - "SEDER", "THUGS", "CERTE", "EILIG", "RAMPE", "GHANA", "TALER", "LEERE", "BURGH", "CLASE", "DIRAS", "MITES", - "PULPY", "CUFFE", "PLADS", "HALEN", "PAWNS", "FAKIR", "MANET", "BRUNG", "TEPEE", "CERTA", "LULLS", "BUTCH", - "ARSON", "DUMPS", "EMITS", "FORTO", "PESTE", "SHANK", "HARPE", "KAMES", "MEERE", "ENGEN", "SUING", "VITAM", - "LLOYD", "THEOL", "WITWE", "DOTED", "DUETS", "MONET", "AVERS", "CADDY", "SINFI", "CALOR", "CORNS", "GEBET", - "HAYNE", "TOTEN", "GUTEM", "TILLS", "JULES", "ROSSE", "LECKY", "STAMM", "WAUGH", "ALLEE", "COMED", "COVEY", - "LONGA", "ZATEN", "BEVIS", "MATTH", "AVERE", "VERUM", "MIRAH", "SOREL", "BOURG", "CREPE", "PRUDE", "MAGUS", - "TAPIO", "ZOCHT", "TOMAS", "TURAN", "BLOUD", "BOORS", "DRAGA", "HELLS", "DECKE", "GEMMA", "CONGE", "ETAIS", - "SANAA", "SLONE", "AXLES", "FAVRE", "ORTEN", "CAPON", "STODO", "POSTO", "DANBY", "SLIGO", "VACHE", "BOTHA", - "CANTY", "CONEY", "LAVES", "NEUVE", "TALAR", "CAKED", "POCHI", "CAULD", "DAAGS", "GNRAL", "RAVES", "OLSEN", - "AMICO", "CINCH", "TIGRE", "MIKKO", "SABIN", "FECIT", "GARNI", "ILLUM", "OOREN", "RUBAN", "BEGAF", "QUIRE", - "TALUS", "FINNA", "HARER", "MORTO", "BHAER", "KOPFE", "LEMME", "BOITE", "UUSIA", "VENIA", "ASAKO", "BOTTE", - "CLEFS", "FAWNS", "MUNDI", "NUBES", "SEGUR", "ANTIC", "JURER", "CRAIK", "BANGE", "KOUDE", "PARER", "VOBIS", - "YOKES", "SAKRA", "SAULT", "BODEM", "PASSI", "SMIRK", "WHAUR", "GYGES", "MONNY", "REIMS", "KOPJE", "POSSA", - "CHIOS", "VARUS", "CLOUT", "LOUSE", "PLAIE", "PROWS", "HOLTE", "DANEY", "WONNE", "FESTA", "SPOOL", "TOREN", - "MATEO", "NINNY", "JUPES", "PLEBE", "TRAHI", "DACRE", "LEIBE", "LUBIN", "AGAPE", "HIDES", "OPERE", "LAWES", - "GALLS", "ASHBY", "ASSAM", "SHIEL", "FREYA", "RODEN", "TYLOR", "CUIRE", "RAKAS", "SPAET", "THUIS", "MINGO", - "NILUS", "FIERS", "MOLTE", "SCAPE", "SKIPS", "ZAMEN", "FINNS", "JUMBO", "PAPST", "ZIELE", "ABASE", "GREEP", - "HONRA", "TESTY", "ZYNEN", "BRUIN", "FLUCH", "PRUDY", "DEPIT", "LUNDI", "TUULI", "UEBEL", "DAGON", "COUPA", - "LUULI", "MORES", "STATA", "KEBLE", "PEKKA", "KLAAR", "MOURN", "DRAXY", "CLAVE", "JOLLE", "VENGA", "OSAGE", - "POLEN", "DIGNO", "HELFT", "GABON", "KHOJA", "SIDEN", "DARKE", "LUMPY", "POPPA", "HECLA", "AGING", "FREUT", - "HALES", "TANIS", "DRAMS", "NEGEN", "STEER", "EULER", "ETOIT", "MAAND", "PHAON", "CREME", "FULLE", "SALUD", - "CHUNG", "FELDE", "RUBEN", "FALLA", "PELEG", "CUYAS", "FACTA", "KNITS", "NOEMT", "TUPIA", "URREA", "SYNES", - "TROUW", "JEWRY", "KAMAR", "PETRI", "STEEN", "HECHA", "WEAKE", "AVILA", "HAFIZ", "HARDE", "MOQUE", "THEIM", - "FARIA", "GRYCE", "PASSY", "OPHIR", "ROSEY", "WEIBE", "GANGA", "PALUS", "AGENS", "COOED", "TRINA", "BETEL", - "UNDAN", "BALLY", "DELLO", "HAIRE", "PEONS", "RAVIN", "WAXES", "VIDAL", "ELKEN", "MAGNO", "RINSE", "VERBO", - "RIPON", "ADORE", "FAGOT", "ROAMS", "BOTEN", "NOBLY", "ROULA", "THAIS", "LOGER", "VOLGT", "MOSCA", "SABES", - "DAMER", "SCALA", "BASSO", "GORED", "NOEUD", "PISTI", "SLATS", "SIXTE", "BUOYS", "PAYED", "REAPS", "RUSES", - "HEALY", "AILLE", "BLASE", "BRULE", "OASES", "PLUGS", "HEARN", "IYONG", "PRIMI", "GRAYS", "PALOS", "TICKS", - "ESTRE", "POUCE", "DAMME", "FLECK", "GENET", "HOUGH", "JEBEL", "CLAPP", "HYDRA", "TAMER", "BLUME", "AVIDE", - "DOWDY", "GIMME", "ESCAP", "POSEY", "CRASS", "EGARD", "NUDGE", "TURBA", "WARTS", "KLAGE", "LENNY", "ELLEI", - "GONNA", "LETTO", "IONIA", "SCHAM", "EPHOD", "MIENS", "TAGIT", "DOUAY", "NICOL", "STOKE", "FLICK", "HUOLI", - "REEKS", "FAROE", "FRUEN", "FICHU", "KERTA", "JARED", "WUNDE", "AYONS", "PAESE", "PATTE", "SINGT", "SCAND", - "ADHUC", "LYMPH", "PARVE", "SCHOB", "STRAX", "PERON", "BUSTE", "DARLE", "EODEM", "MOTES", "ORSON", "RAUME", - "BRINK", "GNOME", "LASTA", "CRUMP", "SKENE", "GRILL", "NIEVE", "BUBEN", "JOSUA", "CEDER", "INNEN", "MANGA", - "OLEVA", "SIXES", "ERICK", "GERRY", "STUFE", "CLAYS", "FACHE", "GELYK", "SALTY", "TEEMS", "AISNE", "RANCE", - "KAUAS", "VYING", "DOUAI", "CASUS", "CORES", "GENIO", "JEUDI", "RADIC", "RUNES", "CLARY", "BRISA", "MUCUS", - "NATUR", "COGIA", "MOXON", "LUTES", "MARKE", "POOTY", "SLAAN", "HAVAS", "MALOS", "SOWER", "DOMUS", "SEGNO", - "DONDE", "ETHIE", "CONCH", "MERES", "CHING", "KITTS", "MARCY", "REPAY", "COMBE", "PAOLI", "ARDEA", "ENCYC", - "KONGE", "MCCOY", "VESEY", "BURRO", "FURIE", "GEZAG", "JIFFY", "QUIPS", "COLLO", "MAGIE", "MYNEN", "COWES", - "OMEGA", "SKEAT", "BELGE", "PAEAN", "EMLYN", "CALDO", "KINDA", "AFTRE", "CITTA", "FETUS", "ONHAN", "TULTA", - "WINCH", "ADIGE", "EMMET", "EUREM", "NATTY", "TALMA", "WILNA", "FORBI", "BARTH", "MONRO", "ZADOK", "BARBA", - "BARCO", "GAZON", "VAART", "ANNAS", "MAHDI", "WERTH", "RAHAA", "SMAAK", "WIRKT", "YEELD", "YERBA", "FINGO", - "HEBEN", "VEINE", "LLEGA", "USKON", "SEMEN", "STOEL", "ZZZZZ", "HOWDY", "REGAN", "CLACK", "SELVE", "GLEGG", - "RAUCH", "DESTA", "FUMER", "KOMST", "BLOCS", "TRIAD", "VENIT", "BALIN", "UPANI", "VISST", "LOMAX", "RYMER", - "SOTTE", "ZOETE", "FOKER", "MILDE", "TATEN", "AKING", "DAVOR", "NITRE", "ERROL", "CLERC", "CONTA", "DEBAR", - "KREET", "MODEM", "CULCH", "FLERE", "OLIKO", "PREYS", "SLOPS", "WHIPT", "ADDIE", "BEULE", "BIJOU", "ECHAR", - "FUOCO", "HATHE", "QUAIS", "SOLDE", "FUZZY", "PISTE", "SAINE", "WEREN", "ZEBRA", "MASKE", "ANIME", "SAYST", - "LVIII", "MOIRA", "RAINA", "WOTAN", "ARMIS", "ASIAT", "BAGUE", "PRESE", "VOLSE", "NUOVA", "PARIA", "ALTOS", - "ENSAM", "TROLL", "ABBIE", "WILTS", "CESTE", "CUYOS", "VIGNE", "BEEBE", "GLIED", "RASHI", "DEVRA", "HOERE", - "LEZER", "NAHEN", "BERUF", "HIGGS", "LEEKS", "POLKA", "UMACR", "DOOMS", "FASTE", "GLEBE", "NOSED", "LOWTH", - "DOORE", "HIRES", "SKULK", "BILDE", "CHEBE", "WAITE", "ACEST", "DAMAS", "HOJAS", "MOMMA", "PERLE", "BALAK", - "LADIE", "MARCK", "YAQUI", "YEATS", "RECAL", "SINAE", "RUDRA", "AMENA", "CUJUS", "SIGNA", "SOLUS", "KISSE", - "NULLO", "PALJO", "SIEHE", "TOLDE", "VISCH", "HOVEY", "ROOME", "ETZEL", "SHURE", "FIFES", "BALTY", "FRITH", - "MICHU", "MOCHA", "TURKE", "FATTI", "GULES", "GALLO", "HANNE", "IZAAK", "MINTO", "ROYCE", "SARUM", "DEBIT", - "TENNE", "MANDI", "WELNU", "GOLLY", "JEUGD", "KELLO", "FULKE", "GUEUX", "VALSE", "WOEDE", "BORGO", "GALLE", - "HEMAN", "LODER", "SHANE", "FUGUE", "PESER", "CYRUS", "HOVED", "MACES", "NADER", "BUCKY", "HOFER", "KARIN", - "MELUN", "WAITZ", "ACTIF", "THEWS", "BOSIO", "MARYS", "CEOUS", "WOHNT", "OISIN", "ARTES", "ILLAM", "MASTE", - "LAMAR", "MULTO", "SABIA", "SAGAS", "SCANS", "VITRE", "ZUMAL", "KRIPA", "MORUS", "PINTO", "COCHE", "DEDIT", - "TALAT", "LEBEL", "SLOOT", "WUCHS", "BABET", "BEUTE", "FESCH", "NUBIA", "DELVE", "DEVAS", "NOHOW", "BAKOM", - "CIOUS", "MANDA", "OVALS", "PURES", "DYAKS", "ECTOR", "HERBE", "HUVUD", "LAIRS", "LIUES", "PLENA", "DOLED", - "MINST", "VELEN", "COROT", "OLNEY", "TELLO", "CARLE", "GNAWS", "HAAND", "ISSUS", "VINCY", "ARAGO", "BORAX", - "CENTO", "NAGOT", "SAUVA", "SOUSE", "VOUTE", "GRECE", "HEYST", "MILNE", "NANDA", "PRETS", "SLIPT", "STATU", - "WEICH", "ACTUS", "LOTYS", "HOORT", "PERES", "SOGGY", "BUSCH", "PUSEY", "MENEN", "SACAR", "GESTO", "TOTUM", - "AUBRY", "BEARN", "KAUAI", "BRAHM", "LAING", "BODIE", "LACKE", "OVARY", "SAYES", "SNAGS", "THATS", "ALPEN", - "MAYNE", "SELAH", "AUGER", "BETEN", "CORRE", "RUNNE", "TRANK", "TYPED", "MULEY", "SPOTT", "IGLOO", "KEELS", - "CASSY", "FAGON", "ACCES", "CXIUJ", "INTRA", "PITTY", "AHURA", "BAMBI", "BAYNE", "HIERO", "MAGOG", "MOONE", - "TODAS", "WENNA", "CAPAZ", "FONDE", "NAGON", "SLAGS", "RILLA", "GIVET", "POMME", "ANNAL", "BARTY", "CURIA", - "LEGGE", "PERSE", "SAALE", "THORA", "EDELE", "PERDS", "POULE", "BOGAN", "KAABA", "EGALE", "VEDEN", "PINKY", - "GERME", "MOULT", "VARIE", "DEWAN", "TEKLA", "FUDGE", "OPINE", "OSIER", "LAUFE", "LERAT", "PINUS", "THULE", - "EUERN", "PERRO", "SENDE", "WRENN", "DATUM", "AILSA", "GHITA", "WACHE", "ATOLL", "PELTS", "PLOWS", "POCAS", - "SHOON", "BEBER", "KATSO", "SOLEN", "HORRY", "PLASE", "EDIFY", "LADER", "TENUS", "JIMSY", "MAIJA", "SOLIS", - "AIMEE", "KUOLI", "ORTER", "OLAVI", "ORMUZ", "COSEY", "JAMAS", "PUOLI", "HEDDA", "TAMPA", "GRABS", "GRATA", - "LOUSY", "TRATO", "UDDER", "BEZUG", "BINET", "AMBLE", "HELMS", "LEVAI", "PAPPI", "COPIA", "SIRVE", "LIGUE", - "ORMUS", "GOUTS", "SINKT", "VRIJE", "LOTTA", "GENOT", "GRUPO", "MOEGE", "SLILY", "ARIES", "FRIST", "KYNGE", - "LLEGO", "MESSO", "NUEVE", "ALAMO", "OJEDA", "ROCCA", "THIER", "ALIOS", "BIDES", "DIEDE", "RUHEN", "TESTE", - "OZIAS", "QATAR", "PILES", "ALMAS", "BARBS", "BODDE", "BRIGS", "GWYNE", "SUELE", "GEOFF", "OAKUM", "ROCAS", - "GESTA", "HUBER", "VILNA", "CLAMP", "KUKIN", "NUQUE", "PELOS", "VIDEO", "WEKEN", "CRECY", "GRABE", "WESEL", - "ABORD", "AIMES", "HOHEM", "TULIN", "AUTUN", "BASHA", "JADIN", "CORAM", "LOOKT", "NEARS", "CLYST", "INIGO", - "NEPOS", "THERM", "FLUES", "FOCAL", "MUETS", "NOYER", "OCKSA", "SHOLD", "FRITS", "HITZE", "LAMAS", "MAHAL", - "MOMUS", "POWYS", "MAGST", "MENNE", "GOLPE", "WAFTS", "DORFE", "IRVIN", "RYDAL", "WYMAN", "CAVED", "CUEUR", - "FLIER", "SIGUE", "STRAF", "TRAJE", "DIRCK", "KANAG", "WESER", "WHYTE", "NKARA", "PECKS", "SAFER", "TRONO", - "ASURA", "MENTZ", "SASHA", "APPAL", "ETAGE", "LASTE", "USETH", "VOIMA", "VORAN", "LEMAN", "SHOOP", "AGITE", - "DABAN", "OMAAN", "BEATS", "TUBBS", "COLPA", "MATAR", "JORGE", "MCGEE", "AVOWS", "HUSET", "JOASH", "KARTE", - "AVISO", "MEATE", "MKHYA", "MUCHE", "ASCOT", "GALLA", "ISORA", "ROMER", "EPOUX", "FALSO", "BALBI", "SUNDA", - "LECON", "SPIJT", "SPUNK", "TRAMS", "VARAS", "LOTTE", "MAZDA", "MOXOS", "AMADO", "BEURT", "BOOST", "CATER", - "NEBER", "NEGRA", "BREDE", "MYRON", "ARTIG", "CIEGO", "FILER", "GAYON", "HABLO", "HINDS", "KUULU", "LIGGA", - "MARDI", "PRIVE", "VLOOG", "CRABB", "SOANE", "DALEN", "DIGNA", "IDLED", "INNIG", "LATHS", "SANZA", "TRAGE", - "WELKS", "BERKS", "BRECK", "ELIAB", "HAUCH", "HERAT", "AVARE", "HAFVA", "PRIJS", "GROIN", "NUIRE", "OBESE", - "OLTRE", "RIJKE", "WEEPE", "SNELL", "SYRIE", "CONTO", "FURIA", "ILLOS", "MEALY", "QUEDO", "ROSIN", "SNOBS", - "JUNIA", "LUKIN", "MESTY", "HELIX", "WALKE", "BUSSI", "TIBBY", "KURTZ", "DOTES", "FAUVE", "GAINE", "LEEFT", - "TAELS", "ACHAB", "EMIRS", "JEPPE", "KNIPP", "PILAR", "REUSS", "WEGES", "DROOM", "EYRIE", "JAGEN", "SLUMP", - "MAREN", "MODUM", "TREIN", "BEBEL", "KRUPP", "LAURI", "LYNNE", "VINET", "FANNO", "HADNA", "MENDS", "TIOUS", - "AMREI", "ARNOT", "FLOSS", "HYLDA", "DEBEN", "OUBLI", "TYLKO", "AVUTO", "BOATE", "LUKEA", "TRATA", "ECLAC", - "WIESE", "ZWEIG", "LINKE", "PRESA", "VIRTU", "BINES", "HERSE", "LUPUS", "MATTA", "FIVES", "INSTR", "KLEED", - "POLVO", "GWILT", "UNHCR", "DOSED", "JUROR", "RUNGS", "SILKE", "ISHAM", "KURDS", "SILLA", "SPINK", "ACTOS", - "DORST", "RIMES", "VIISI", "ZUCHT", "ATTEN", "BUCHE", "BYERS", "CISSY", "FIONN", "KATRI", "ASSIM", "HUIUS", - "MENTI", "PARSE", "VIDER", "KAYAN", "KRANZ", "NIELS", "FOIRE", "KIOSK", "OBERN", "RECTE", "LIPPO", "CABBY", - "IMBUE", "DOLPH", "RADHA", "FELIZ", "HOMEM", "TONTY", "YAKOV", "AEGIS", "PEDIR", "ROUTS", "UNICO", "VELUT", - "CROLL", "ALZOO", "FALSA", "KLARE", "PONEN", "AMEER", "BOSSU", "ORAGE", "POSEE", "PUTEA", "SELIG", "CHAKA", - "DISKO", "FALCO", "TEXEL", "ASCHE", "DONAU", "DUFFY", "LERMA", "QUINN", "SISSY", "TORCY", "ALTAS", "BEEST", - "BOUGE", "MONEN", "REGNA", "SNACK", "LADKO", "PIPPA", "WEALD", "ILLAN", "KALLA", "KOTIA", "BARCA", "CENIS", - "KATZE", "KORAH", "PRADO", "SANNA", "BURNE", "IDYLL", "SITTA", "VIRUM", "BEATA", "GUION", "RASSI", "SHAND", - "GAGES", "PUHUU", "RAHAT", "CERRO", "FARAO", "PAZZI", "RIZZO", "SCENA", "KALTE", "PATES", "TEUER", "CASOS", - "COLPO", "FLUME", "MAIOR", "SABIO", "VULGO", "ARIUS", "FLAMM", "MARET", "COTON", "DUCTS", "FORGO", "GRITO", - "POSSO", "ZULKS", "ETAIN", "POSEN", "TAMIL", "ZUEGE", "BIPED", "BLURT", "THEOS", "FLUKE", "MUNCH", "YOURE", - "BANTU", "BAREE", "BEGUM", "CHILO", "DINKS", "FADEN", "MAHAN", "PEAUX", "REGNI", "EINAR", "ISLAS", "RAYNE", - "TAAVI", "ARABE", "SCEAU", "DUCIE", "LORDY", "CHOUX", "GAMMA", "HAIES", "HORTE", "REMUE", "SCULL", "ANTAR", - "ENGLE", "NOVUM", "TONYA", "BAUEN", "IPSIS", "LOIKE", "LOUES", "SINGW", "STRID", "VILER", "SHAWN", "AURAI", - "GUISA", "KUULE", "SALEN", "WONEN", "CARBO", "MELKY", "DUMPY", "SPICK", "SPORE", "BARAK", "ONGAR", "VOLKS", - "DIETS", "LAVAS", "MUUAN", "ANMUT", "BEMIS", "BOLES", "GOOCH", "KAFIR", "LAGOS", "LUNDY", "BESOM", "JURIS", - "MESAS", "TYMES", "WORAN", "FOUAN", "HOOKE", "PUGET", "SIGEL", "DEARY", "LUMEN", "MEURE", "SERUM", "GREIS", - "PFEIL", "BONIS", "EIKAE", "ZWANG", "ASIEN", "CREAN", "LENIN", "CYTEE", "HALVT", "LOCIS", "NENNE", "SPINY", - "TOXIC", "FERDY", "MAROT", "WOVEN", "AEONS", "DEBIA", "DRAPE", "REAMS", "RISER", "RONDS", "TAGET", "AMASA", - "JANIE", "NIZZA", "ICILY", "TENTA", "FUNDY", "FANES", "GOERA", "VALDE", "PINTA", "PITTS", "SEIDE", "BILGE", - "DOMUM", "GEHST", "GNASH", "HETTE", "DORAN", "FABRY", "HEIGH", "SARTO", "SCHAU", "DIENS", "DOGME", "FAUNS", - "FROMM", "TAPES", "DREUX", "FAGAN", "HELLA", "SKYLD", "ARMUT", "JUBAL", "LUISA", "NOYON", "PIOTR", "PYLOS", - "BRAGT", "MEENT", "VAREN", "ILION", "NARES", "STUBB", "ASTER", "DANNO", "MOPED", "ONNEA", "PALAA", "WORDE", - "DONAT", "MACAN", "POVEY", "BODES", "DRIPS", "EGGED", "FEELE", "KLUGE", "MUROS", "ORBIS", "TABBY", "WICKS", - "JUMNA", "SYLVI", "SUNNI", "SUYOS", "JINGO", "MONAT", "PIPPO", "APING", "CHACE", "FLUFF", "POLIS", "AUBER", - "RAMEE", "SEOUL", "LYDER", "RICOS", "UNSAY", "ELSON", "SNOOP", "ANNIS", "FANGE", "FIJNE", "HUMUS", "LENTS", - "PUREE", "STOUP", "YERES", "JONDO", "LIGNY", "BLOKE", "PROPE", "ELLIE", "ERNIE", "OAKES", "DURES", "NEEDE", - "PLIER", "GONDI", "HAMET", "DUNKT", "FOAMS", "LOGES", "SERES", "STELT", "ARRAH", "GRATZ", "VEDIA", "DICIT", - "HARTS", "REINA", "SIELU", "KAPPA", "LIPPE", "OLINE", "PROUT", "INGOT", "LENTO", "NEGLI", "PARIE", "VEZES", - "RUSTY", "SUKEY", "SURRY", "ANIGH", "CIMES", "CLOUS", "FARSI", "GRAAG", "TINHA", "ARLEE", "DONGO", "HAGAN", - "LXIII", "RAMAH", "CLEPT", "MULTI", "NIAIS", "VAINS", "LEONI", "LECHE", "RAINE", "HYDER", "LARES", "HAVET", - "MINHA", "BATTY", "ERICH", "KUGEL", "BRAGS", "CRIBS", "FAINE", "IHANA", "KAHTA", "SAATU", "SAYLE", "SEINS", - "SLATY", "SQUIB", "VIZIR", "YELPS", "ENDEN", "HINDE", "CORTA", "CRICK", "FLERA", "SVARA", "HIPPO", "DRONG", - "FETED", "LEHRT", "TULIT", "ADERN", "DIDON", "QUILP", "TREAS", "CARRE", "MALUM", "SULKS", "LEARY", "LYDDY", - "PETYA", "SALIM", "TOPSY", "BOWEL", "CRESS", "DESTE", "ENROL", "FLAKY", "BARBY", "BEERS", "SOMAL", "TIMMY", - "YEGOR", "JONNE", "POILS", "SOLDI", "ZARTE", "KROOL", "MARKO", "DERER", "TIETO", "VOSSA", "BULOW", "ALENE", - "DIMES", "DOIVE", "HOPEN", "MOLDY", "TOIVO", "AVICE", "HERMY", "BAINS", "DAGUE", "FLINK", "FRASE", "HANGT", - "LEGUA", "TEMPI", "VAITI", "CUTTS", "VANEL", "ALTHO", "EINIG", "FIORD", "HARME", "MOWER", "PAGAR", "ROEPT", - "FULDA", "MOSUL", "VIRGO", "DINAR", "KATSE", "KURJA", "LINEA", "NEARE", "RUINA", "TUVAN", "ASSUR", "LANDA", - "EVERE", "LIVRA", "PENSO", "TILTS", "VSQUE", "MILER", "NERVA", "SHINT", "STICH", "BEDST", "BLAUW", "COLDE", - "GLAUB", "MOATS", "PRISA", "SIGMA", "MAHEU", "CHENE", "MUITO", "POEME", "PUDGY", "RILED", "SILLS", "VERTA", - "AMRAM", "BONAR", "SVAVA", "CURSO", "FIDEM", "CAPEL", "JAINS", "PRAED", "GAVEL", "KIVEN", "PEDIS", "WAARD", - "FIUME", "GLYNN", "PIGOT", "BICHE", "DOOTY", "LADYE", "VENGE", "JUKES", "PETRO", "BONIE", "FOWLE", "INURE", - "KONDE", "POKES", "SIMBA", "LITRE", "PAREA", "HOREB", "TIMUR", "HEATE", "NAGRA", "NOYSE", "PAURA", "PEECE", - "AGNUS", "HYNDS", "MOSER", "GAWKY", "HYDRO", "ISTUA", "JACHT", "SCUDO", "UUREN", "MOSCO", "BLAUE", "LIVET", - "SECHE", "MAYDE", "PIEDE", "RUFFS", "KAPOR", "MARTE", "QUADE", "GLATT", "INDIO", "OMNEM", "SALIO", "SADOC", - "VOBAN", "ZICCI", "BRAES", "COELO", "CORTO", "IRKED", "SABEN", "DWYER", "JOUIT", "OFFRA", "SCIRE", "SELVA", - "TULET", "FLAGG", "ETUDE", "GOUGE", "HUJUS", "OLHOS", "ASOKA", "HURRA", "KEVIN", "NELLO", "FICHE", "JETAI", - "LECHO", "LIENE", "MUMPS", "ORICE", "VIZOR", "HARAN", "LUCRE", "MAGEN", "MOORD", "WEEKE", "GOREE", "KIMON", - "AVASI", "FINNY", "POSTA", "REGTE", "REMET", "SILEX", "TELCO", "TERVE", "SOHNE", "TOBIN", "GEZET", "GRIDO", - "IVIED", "ODDER", "FLACK", "JESSY", "DIECI", "EPAIS", "THRUE", "DICES", "GREYS", "MORTI", "PERCE", "BASSA", - "FASTI", "MCTEE", "SOUSA", "VIGNY", "WOLKE", "DUROS", "GRAVI", "PROVA", "SWILL", "VEELE", "WANES", "BASAN", - "CUFFY", "XVIIE", "HUZZA", "VIDAS", "ALLIS", "BEUST", "BRESL", "SWASH", "PEPLE", "GOWDY", "HADJI", "NAHUM", - "THESS", "FROND", "HAINT", "AMOOR", "LARNE", "NADAB", "NEILL", "SAGET", "CREDE", "HUMPS", "MOSSE", "MUTED", - "OLLEN", "VERBE", "ALVIN", "CLERY", "JOVIS", "SEYNS", "SONEY", "SYLPH", "VITRY", "WIGAN", "HILTS", "LINTU", - "SINDS", "UMBRA", "VISER", "CASCA", "MERAN", "MONNA", "PINEY", "BRAUE", "LAPIN", "MUEDE", "POWRE", "STAAR", - "BONEY", "MAGUA", "MAULE", "COYLY", "GUMMY", "HAULS", "RUEGO", "ARCHY", "BAUME", "BEVAN", "DEGAS", "KAISA", - "LESTE", "MOULE", "REDLY", "VETUS", "AFRIC", "BINGO", "LXVII", "OLLIE", "SURYA", "ABRIR", "DEDOS", "EXCES", - "LAITA", "MINTS", "POTER", "VARDA", "BACHE", "SALIC", "HURLA", "MEWED", "NECKE", "NEITO", "PARKA", "VAPAA", - "WAPEN", "BIJAH", "GUETE", "ZANTE", "SCUDI", "IDUNA", "KONTO", "LUXOR", "DIEST", "REGEM", "SPANK", "TIEDE", - "URSEL", "GENTI", "NAVES", "ONDAS", "PRZED", "FOLCO", "NABAL", "NOSEY", "PAULI", "TOKIO", "ATING", "DICTE", - "GUDAR", "RHEUM", "CUTTY", "OSMIA", "MAGER", "SANOT", "TIBIA", "TRAER", "LIANE", "MATHO", "MUNDY", "PATTI", - "REESE", "SEELY", "HARPY", "HUTTE", "LEGES", "LIETO", "LOBEN", "RAVIE", "SOAPY", "SOUGH", "VUOLE", "BOGES", - "LUTHA", "MAPPO", "BONED", "FLYER", "VISTE", "ENDOR", "FAYAL", "HALSE", "KEANE", "SIHON", "ANCHO", "ATTAR", - "AVETE", "KROPP", "MIMES", "OPALS", "PLUMA", "UNLIT", "AKLIS", "ELSLI", "HERDE", "SKALE", "ANDAN", "JEANS", - "PREVU", "BAIAE", "GOETZ", "HYLAS", "JANIN", "CHYLE", "DAUBS", "HANDE", "KNAAP", "NATUS", "ZONEN", "MONTI", - "SUDRA", "LIKER", "LYCKA", "UKASE", "VIMOS", "VOYCE", "AMIEL", "PANGO", "RANDE", "SHIVA", "SPION", "TIECK", - "DECEM", "MOLAR", "WENTE", "IMLAC", "PONTO", "WASTL", "BORTA", "FANDT", "JOKIN", "LYFTE", "RESET", "RIENS", - "SEMEL", "TWIXT", "WISST", "BEORN", "CEUTA", "NABAB", "EVIGT", "LEGNO", "LERNT", "LILAS", "MYLEN", "PAPPA", - "PREUX", "SPOOK", "FOGER", "TILDA", "CIVET", "ERRER", "GEARS", "JUGEZ", "MAGNI", "SEZEE", "SPISE", "AYEAR", - "BYEAR", "DUNNE", "JEZUS", "LUCIO", "MATEY", "SKALA", "TIBUR", "BAADE", "HOOTS", "LOONS", "TUNGA", "BIORN", - "DAGLI", "OUTRO", "GAETA", "JANEY", "MENIN", "SITKA", "FOIST", "JEHER", "LAVED", "LINUS", "PAWLE", "VICKY", - "RIDES", "TREED", "ARIST", "AWEEL", "CELIE", "BURRS", "CLIPS", "DICEA", "MOLTA", "NAVVY", "OLIKA", "RAYOS", - "SEYDE", "VANES", "ALLYN", "GEBOT", "GROSE", "BAHAY", "DUREE", "FAZER", "GOADS", "GULPH", "HAVER", "NIGRA", - "NOCTE", "PARUM", "SAATA", "SKIMS", "WAART", "WRAAK", "MUZIO", "OLSON", "RABAT", "REGIN", "RUNIC", "MANDE", - "RAUHA", "AYMER", "NAURU", "DREEF", "GAAET", "IRREN", "LESKI", "MECUM", "SAVON", "ARIAS", "HANKS", "ROZEL", - "ZEUGE", "DEUGD", "ETHIC", "GOODE", "TROVA", "AETNA", "BENJY", "ORMES", "TUBAL", "BRIMS", "BULKS", "FILCH", - "MONAD", "OOZES", "REGIA", "SARVE", "VENTI", "VOERT", "ARION", "COOTE", "SNECK", "TESSA", "BASON", "GIENG", - "INTIL", "NABIJ", "PRZEZ", "RAMAS", "SITUE", "CATHY", "GREER", "LINUX", "TOBIT", "DIDNT", "DROGO", "NEHMT", - "TIGES", "EDRIC", "TROUP", "AYUDA", "BEVAT", "DAPAT", "OVATE", "BALCH", "COGAN", "AIRTH", "GOWNE", "JUEGO", - "REBUT", "SALSA", "WIJDE", "JUBEL", "NEDDY", "RONNY", "FLAIR", "PASEO", "BRONX", "EWALD", "STURZ", "WODEN", - "KUMMA", "LETTE", "VANTE", "CAERE", "MISHA", "AMBAS", "FAAET", "RUNDE", "EYLAU", "MENGS", "FEEST", "LOWED", - "VENDE", "CUMAE", "GASSE", "HILMA", "IZUMO", "PISAN", "SOFIE", "CARRO", "CONIC", "CRIMP", "MUSKY", "SAYTH", - "STANK", "BOWES", "FAHNE", "THOME", "VIION", "ASTUU", "DICKE", "FASST", "PAROI", "LAWRY", "PRATO", "TENCH", - "CERTI", "ISCHT", "NOONE", "SENTO", "TAULD", "VLOOT", "VOIRE", "VOLTS", "BELUS", "CAVOR", "JORIS", "SEPPI", - "ERGAB", "BABIE", "CATON", "ABAHT", "BEDRE", "OPPIA", "POUCO", "SCHEU", "SEALE", "SUMUS", "VASTY", "FRIDA", - "LIPPI", "LOUVE", "BANCO", "BUONO", "CLOMB", "EDELN", "HOULD", "PELAS", "BODIN", "FAUGH", "JUDAR", "SEITZ", - "TOTTY", "WERTE", "STANE", "WINNE", "COCOS", "RIATT", "WERFF", "FIERA", "HINAN", "MATKA", "OATEN", "PLACA", - "CONOR", "DARRY", "HERTZ", "LAMON", "BEYNG", "CONTR", "INEPT", "SAKEN", "SIAMO", "STEDE", "MORNA", "AGERE", - "LANCA", "OSASI", "SADAN", "WOONT", "EVERS", "TROVE", "FACIL", "GRENE", "KUUSI", "PAPAS", "RECUS", "UUDET", - "VENTA", "ALOIS", "GILLY", "GONDY", "NAIRN", "BAILE", "CLAPT", "CLUCK", "CULTO", "EETEN", "GAPES", "NIVEL", - "TREKT", "WARLD", "ABIES", "AUTOS", "BARKE", "BRDER", "EASES", "GIRDS", "HAILL", "HIJAS", "RETRO", "SOMMA", - "STEEG", "BAYER", "CAMUS", "DEVIN", "HAMID", "HUANG", "IMOLA", "DICED", "HOCHA", "KASTA", "MULGA", "MYNDE", - "PIENO", "YAONG", "FURST", "GATTE", "SPASS", "SVEIN", "THEMA", "ANSAH", "CHAUX", "HEVIG", "PRIES", "ZOMER", - "BAGGS", "DOURO", "HIPPY", "SAONE", "VAVEL", "WETTE", "ALNAR", "EUILL", "PEATY", "VENIS", "CRITO", "LYNDA", - "OGRAM", "TEMPE", "ABOON", "FACEA", "FINEM", "KNOUT", "LLAMO", "LOAMY", "MENAR", "TISSU", "VERAS", "VIRES", - "COLBY", "JAINA", "TEUSE", "TYNAN", "YANKS", "BEZAT", "KLOOF", "SEAMY", "SIDDE", "STEIL", "TYYNI", "CARIA", - "HENTY", "LOWRY", "SOUZA", "ALIUS", "FEETE", "GRANO", "HALKI", "KELPO", "PESKY", "PLEBS", "POVIS", "TOPER", - "VLOER", "VOULD", "EDMEE", "GAZEN", "GITON", "HOLLO", "KAROL", "REIZE", "ROACH", "ULLOA", "VERBY", "CLEWS", - "EVANG", "FERAL", "GLICH", "THROE", "ZIJNS", "EURIE", "FERIA", "JEEMS", "PAAVO", "SAUTI", "DOODE", "PIENA", - "RADII", "UNICA", "CROLY", "HADAD", "KEYES", "NIKKY", "SANDE", "SEGEL", "SOFYA", "TEXAR", "GAZER", "LEDIG", - "NOTAR", "OBEIR", "VIMES", "GENUA", "KANAL", "LIMON", "RAHAB", "SUOMI", "VEJEN", "BATEN", "FJORD", "LEVES", - "PHARE", "RECTO", "AAGOT", "GIZUR", "NADJA", "RUGGE", "SNEYD", "DECKT", "FAILE", "GAOLS", "MELER", "PACTO", - "PAHAN", "CALIF", "MENON", "SEPOY", "WADDY", "ZELLE", "AENDA", "ASTUA", "KROON", "LETRA", "MINIT", "NEEWA", - "PATNA", "URIEL", "HITTE", "HOMOJ", "JOUET", "KOSKI", "LYSTE", "MINAS", "RUHTE", "SETZE", "LASER", "SONIC", +possible_list = [ + "AAHED", "AALII", "AARGH", "AARTI", "ABACA", "ABACI", "ABACS", "ABAFT", "ABAKA", "ABAMP", "ABAND", "ABASH", + "ABASK", "ABAYA", "ABBAS", "ABBED", "ABBES", "ABCEE", "ABEAM", "ABEAR", "ABELE", "ABERS", "ABETS", "ABIES", + "ABLER", "ABLES", "ABLET", "ABLOW", "ABMHO", "ABOHM", "ABOIL", "ABOMA", "ABOON", "ABORD", "ABORE", "ABRAM", + "ABRAY", "ABRIM", "ABRIN", "ABRIS", "ABSEY", "ABSIT", "ABUNA", "ABUNE", "ABUTS", "ABUZZ", "ABYES", "ABYSM", + "ACAIS", "ACARI", "ACCAS", "ACCOY", "ACERB", "ACERS", "ACETA", "ACHAR", "ACHED", "ACHES", "ACHOO", "ACIDS", + "ACIDY", "ACING", "ACINI", "ACKEE", "ACKER", "ACMES", "ACMIC", "ACNED", "ACNES", "ACOCK", "ACOLD", "ACRED", + "ACRES", "ACROS", "ACTED", "ACTIN", "ACTON", "ACYLS", "ADAWS", "ADAYS", "ADBOT", "ADDAX", "ADDED", "ADDER", + "ADDIO", "ADDLE", "ADEEM", "ADHAN", "ADIEU", "ADIOS", "ADITS", "ADMAN", "ADMEN", "ADMIX", "ADOBO", "ADOWN", + "ADOZE", "ADRAD", "ADRED", "ADSUM", "ADUKI", "ADUNC", "ADUST", "ADVEW", "ADYTA", "ADZED", "ADZES", "AECIA", + "AEDES", "AEGIS", "AEONS", "AERIE", "AEROS", "AESIR", "AFALD", "AFARA", "AFARS", "AFEAR", "AFLAJ", "AFORE", + "AFRIT", "AFROS", "AGAMA", "AGAMI", "AGARS", "AGAST", "AGAVE", "AGAZE", "AGENE", "AGERS", "AGGER", "AGGIE", + "AGGRI", "AGGRO", "AGGRY", "AGHAS", "AGILA", "AGIOS", "AGISM", "AGIST", "AGITA", "AGLEE", "AGLET", "AGLEY", + "AGLOO", "AGLUS", "AGMAS", "AGOGE", "AGONE", "AGONS", "AGOOD", "AGORA", "AGRIA", "AGRIN", "AGROS", "AGUED", + "AGUES", "AGUNA", "AGUTI", "AHEAP", "AHENT", "AHIGH", "AHIND", "AHING", "AHINT", "AHOLD", "AHULL", "AHURU", + "AIDAS", "AIDED", "AIDES", "AIDOI", "AIDOS", "AIERY", "AIGAS", "AIGHT", "AILED", "AIMED", "AIMER", "AINEE", + "AINGA", "AIOLI", "AIRED", "AIRER", "AIRNS", "AIRTH", "AIRTS", "AITCH", "AITUS", "AIVER", "AIYEE", "AIZLE", + "AJIES", "AJIVA", "AJUGA", "AJWAN", "AKEES", "AKELA", "AKENE", "AKING", "AKITA", "AKKAS", "ALAAP", "ALACK", + "ALAMO", "ALAND", "ALANE", "ALANG", "ALANS", "ALANT", "ALAPA", "ALAPS", "ALARY", "ALATE", "ALAYS", "ALBAS", + "ALBEE", "ALCID", "ALCOS", "ALDEA", "ALDER", "ALDOL", "ALECK", "ALECS", "ALEFS", "ALEFT", "ALEPH", "ALEWS", + "ALEYE", "ALFAS", "ALGAL", "ALGAS", "ALGID", "ALGIN", "ALGOR", "ALGUM", "ALIAS", "ALIFS", "ALINE", "ALIST", + "ALIYA", "ALKIE", "ALKOS", "ALKYD", "ALKYL", "ALLEE", "ALLEL", "ALLIS", "ALLOD", "ALLYL", "ALMAH", "ALMAS", + "ALMEH", "ALMES", "ALMUD", "ALMUG", "ALODS", "ALOED", "ALOES", "ALOHA", "ALOIN", "ALOOS", "ALOWE", "ALTHO", + "ALTOS", "ALULA", "ALUMS", "ALURE", "ALVAR", "ALWAY", "AMAHS", "AMAIN", "AMATE", "AMAUT", "AMBAN", "AMBIT", + "AMBOS", "AMBRY", "AMEBA", "AMEER", "AMENE", "AMENS", "AMENT", "AMIAS", "AMICE", "AMICI", "AMIDE", "AMIDO", + "AMIDS", "AMIES", "AMIGA", "AMIGO", "AMINE", "AMINO", "AMINS", "AMIRS", "AMLAS", "AMMAN", "AMMON", "AMMOS", + "AMNIA", "AMNIC", "AMNIO", "AMOKS", "AMOLE", "AMORT", "AMOUR", "AMOVE", "AMOWT", "AMPED", "AMPUL", "AMRIT", + "AMUCK", "AMYLS", "ANANA", "ANATA", "ANCHO", "ANCLE", "ANCON", "ANDRO", "ANEAR", "ANELE", "ANENT", "ANGAS", + "ANGLO", "ANIGH", "ANILE", "ANILS", "ANIMA", "ANIMI", "ANION", "ANISE", "ANKER", "ANKHS", "ANKUS", "ANLAS", + "ANNAL", "ANNAS", "ANNAT", "ANOAS", "ANOLE", "ANOMY", "ANSAE", "ANTAE", "ANTAR", "ANTAS", "ANTED", "ANTES", + "ANTIS", "ANTRA", "ANTRE", "ANTSY", "ANURA", "ANYON", "APACE", "APAGE", "APAID", "APAYD", "APAYS", "APEAK", + "APEEK", "APERS", "APERT", "APERY", "APGAR", "APHIS", "APIAN", "APIOL", "APISH", "APISM", "APODE", "APODS", + "APOOP", "APORT", "APPAL", "APPAY", "APPEL", "APPRO", "APPUI", "APPUY", "APRES", "APSES", "APSIS", "APSOS", + "APTED", "APTER", "AQUAE", "AQUAS", "ARABA", "ARAKS", "ARAME", "ARARS", "ARBAS", "ARCED", "ARCHI", "ARCOS", + "ARCUS", "ARDEB", "ARDRI", "AREAD", "AREAE", "AREAL", "AREAR", "AREAS", "ARECA", "AREDD", "AREDE", "AREFY", + "AREIC", "ARENE", "AREPA", "ARERE", "ARETE", "ARETS", "ARETT", "ARGAL", "ARGAN", "ARGIL", "ARGLE", "ARGOL", + "ARGON", "ARGOT", "ARGUS", "ARHAT", "ARIAS", "ARIEL", "ARIKI", "ARILS", "ARIOT", "ARISH", "ARKED", "ARLED", + "ARLES", "ARMED", "ARMER", "ARMET", "ARMIL", "ARNAS", "ARNUT", "AROBA", "AROHA", "AROID", "ARPAS", "ARPEN", + "ARRAH", "ARRAS", "ARRET", "ARRIS", "ARROZ", "ARSED", "ARSES", "ARSEY", "ARSIS", "ARTAL", "ARTEL", "ARTIC", + "ARTIS", "ARUHE", "ARUMS", "ARVAL", "ARVEE", "ARVOS", "ARYLS", "ASANA", "ASCON", "ASCUS", "ASDIC", "ASHED", + "ASHES", "ASHET", "ASKED", "ASKER", "ASKOI", "ASKOS", "ASPEN", "ASPER", "ASPIC", "ASPIE", "ASPIS", "ASPRO", + "ASSAI", "ASSAM", "ASSES", "ASSEZ", "ASSOT", "ASTER", "ASTIR", "ASTUN", "ASURA", "ASWAY", "ASWIM", "ASYLA", + "ATAPS", "ATAXY", "ATIGI", "ATILT", "ATIMY", "ATLAS", "ATMAN", "ATMAS", "ATMOS", "ATOCS", "ATOKE", "ATOKS", + "ATOMS", "ATOMY", "ATONY", "ATOPY", "ATRIA", "ATRIP", "ATTAP", "ATTAR", "ATUAS", "AUDAD", "AUGER", "AUGHT", + "AULAS", "AULIC", "AULOI", "AULOS", "AUMIL", "AUNES", "AUNTS", "AURAE", "AURAL", "AURAR", "AURAS", "AUREI", + "AURES", "AURIC", "AURIS", "AURUM", "AUTOS", "AUXIN", "AVALE", "AVANT", "AVAST", "AVELS", "AVENS", "AVERS", + "AVGAS", "AVINE", "AVION", "AVISE", "AVISO", "AVIZE", "AVOWS", "AVYZE", "AWARN", "AWATO", "AWAVE", "AWAYS", + "AWDLS", "AWEEL", "AWETO", "AWING", "AWMRY", "AWNED", "AWNER", "AWOLS", "AWORK", "AXELS", "AXILE", "AXILS", + "AXING", "AXITE", "AXLED", "AXLES", "AXMAN", "AXMEN", "AXOID", "AXONE", "AXONS", "AYAHS", "AYAYA", "AYELP", + "AYGRE", "AYINS", "AYONT", "AYRES", "AYRIE", "AZANS", "AZIDE", "AZIDO", "AZINE", "AZLON", "AZOIC", "AZOLE", + "AZONS", "AZOTE", "AZOTH", "AZUKI", "AZURN", "AZURY", "AZYGY", "AZYME", "AZYMS", "BAAED", "BAALS", "BABAS", + "BABEL", "BABES", "BABKA", "BABOO", "BABUL", "BABUS", "BACCA", "BACCO", "BACCY", "BACHA", "BACHS", "BACKS", + "BADDY", "BAELS", "BAFFS", "BAFFY", "BAFTS", "BAGHS", "BAGIE", "BAHTS", "BAHUS", "BAHUT", "BAILS", "BAIRN", + "BAISA", "BAITH", "BAITS", "BAIZA", "BAIZE", "BAJAN", "BAJRA", "BAJRI", "BAJUS", "BAKED", "BAKEN", "BAKES", + "BAKRA", "BALAS", "BALDS", "BALDY", "BALED", "BALES", "BALKS", "BALKY", "BALLS", "BALLY", "BALMS", "BALOO", + "BALSA", "BALTI", "BALUN", "BALUS", "BAMBI", "BANAK", "BANCO", "BANCS", "BANDA", "BANDH", "BANDS", "BANDY", + "BANED", "BANES", "BANGS", "BANIA", "BANKS", "BANNS", "BANTS", "BANTU", "BANTY", "BANYA", "BAPUS", "BARBE", + "BARBS", "BARBY", "BARCA", "BARDE", "BARDO", "BARDS", "BARDY", "BARED", "BARER", "BARES", "BARFI", "BARFS", + "BARIC", "BARKS", "BARKY", "BARMS", "BARMY", "BARNS", "BARNY", "BARPS", "BARRA", "BARRE", "BARRO", "BARRY", + "BARYE", "BASAN", "BASED", "BASEN", "BASER", "BASES", "BASHO", "BASIJ", "BASKS", "BASON", "BASSE", "BASSI", + "BASSO", "BASSY", "BASTA", "BASTI", "BASTO", "BASTS", "BATED", "BATES", "BATHS", "BATIK", "BATTA", "BATTS", + "BATTU", "BAUDS", "BAUKS", "BAULK", "BAURS", "BAVIN", "BAWDS", "BAWKS", "BAWLS", "BAWNS", "BAWRS", "BAWTY", + "BAYED", "BAYER", "BAYES", "BAYLE", "BAYTS", "BAZAR", "BAZOO", "BEADS", "BEAKS", "BEAKY", "BEALS", "BEAMS", + "BEAMY", "BEANO", "BEANS", "BEANY", "BEARE", "BEARS", "BEATH", "BEATS", "BEATY", "BEAUS", "BEAUT", "BEAUX", + "BEBOP", "BECAP", "BECKE", "BECKS", "BEDAD", "BEDEL", "BEDES", "BEDEW", "BEDIM", "BEDYE", "BEEDI", "BEEFS", + "BEEPS", "BEERS", "BEERY", "BEETS", "BEFOG", "BEGAD", "BEGAR", "BEGEM", "BEGOT", "BEGUM", "BEIGE", "BEIGY", + "BEINS", "BEKAH", "BELAH", "BELAR", "BELAY", "BELEE", "BELGA", "BELLS", "BELON", "BELTS", "BEMAD", "BEMAS", + "BEMIX", "BEMUD", "BENDS", "BENDY", "BENES", "BENET", "BENGA", "BENIS", "BENNE", "BENNI", "BENNY", "BENTO", + "BENTS", "BENTY", "BEPAT", "BERAY", "BERES", "BERGS", "BERKO", "BERKS", "BERME", "BERMS", "BEROB", "BERYL", + "BESAT", "BESAW", "BESEE", "BESES", "BESIT", "BESOM", "BESOT", "BESTI", "BESTS", "BETAS", "BETED", "BETES", + "BETHS", "BETID", "BETON", "BETTA", "BETTY", "BEVER", "BEVOR", "BEVUE", "BEVVY", "BEWET", "BEWIG", "BEZES", + "BEZIL", "BEZZY", "BHAIS", "BHAJI", "BHANG", "BHATS", "BHELS", "BHOOT", "BHUNA", "BHUTS", "BIACH", "BIALI", + "BIALY", "BIBBS", "BIBES", "BICCY", "BICES", "BIDED", "BIDER", "BIDES", "BIDET", "BIDIS", "BIDON", "BIELD", + "BIERS", "BIFFO", "BIFFS", "BIFFY", "BIFID", "BIGAE", "BIGGS", "BIGGY", "BIGHA", "BIGHT", "BIGLY", "BIGOS", + "BIJOU", "BIKED", "BIKER", "BIKES", "BIKIE", "BILBO", "BILBY", "BILED", "BILES", "BILGY", "BILKS", "BILLS", + "BIMAH", "BIMAS", "BIMBO", "BINAL", "BINDI", "BINDS", "BINER", "BINES", "BINGS", "BINGY", "BINIT", "BINKS", + "BINTS", "BIOGS", "BIONT", "BIOTA", "BIPED", "BIPOD", "BIRDS", "BIRKS", "BIRLE", "BIRLS", "BIROS", "BIRRS", + "BIRSE", "BIRSY", "BISES", "BISKS", "BISOM", "BITCH", "BITER", "BITES", "BITOS", "BITOU", "BITSY", "BITTE", + "BITTS", "BIVIA", "BIVVY", "BIZES", "BIZZO", "BIZZY", "BLABS", "BLADS", "BLADY", "BLAER", "BLAES", "BLAFF", + "BLAGS", "BLAHS", "BLAIN", "BLAMS", "BLART", "BLASE", "BLASH", "BLATE", "BLATS", "BLATT", "BLAUD", "BLAWN", + "BLAWS", "BLAYS", "BLEAR", "BLEBS", "BLECH", "BLEES", "BLENT", "BLERT", "BLEST", "BLETS", "BLEYS", "BLIMY", + "BLING", "BLINI", "BLINS", "BLINY", "BLIPS", "BLIST", "BLITE", "BLITS", "BLIVE", "BLOBS", "BLOCS", "BLOGS", + "BLOOK", "BLOOP", "BLORE", "BLOTS", "BLOWS", "BLOWY", "BLUBS", "BLUDE", "BLUDS", "BLUDY", "BLUED", "BLUES", + "BLUET", "BLUEY", "BLUID", "BLUME", "BLUNK", "BLURS", "BLYPE", "BOABS", "BOAKS", "BOARS", "BOART", "BOATS", + "BOBAC", "BOBAK", "BOBAS", "BOBOL", "BOBOS", "BOCCA", "BOCCE", "BOCCI", "BOCHE", "BOCKS", "BODED", "BODES", + "BODGE", "BODHI", "BODLE", "BOEPS", "BOETS", "BOEUF", "BOFFO", "BOFFS", "BOGAN", "BOGEY", "BOGGY", "BOGIE", + "BOGLE", "BOGUE", "BOGUS", "BOHEA", "BOHOS", "BOILS", "BOING", "BOINK", "BOITE", "BOKED", "BOKEH", "BOKES", + "BOKOS", "BOLAR", "BOLAS", "BOLDS", "BOLES", "BOLIX", "BOLLS", "BOLOS", "BOLTS", "BOLUS", "BOMAS", "BOMBE", + "BOMBO", "BOMBS", "BONCE", "BONDS", "BONED", "BONER", "BONES", "BONGS", "BONIE", "BONKS", "BONNE", "BONNY", + "BONZA", "BONZE", "BOOAI", "BOOAY", "BOOBS", "BOODY", "BOOED", "BOOFY", "BOOGY", "BOOHS", "BOOKS", "BOOKY", + "BOOLS", "BOOMS", "BOOMY", "BOONG", "BOONS", "BOORD", "BOORS", "BOOSE", "BOOTS", "BOPPY", "BORAK", "BORAL", + "BORAS", "BORDE", "BORDS", "BORED", "BOREE", "BOREL", "BORER", "BORES", "BORGO", "BORIC", "BORKS", "BORMS", + "BORNA", "BORON", "BORTS", "BORTY", "BORTZ", "BOSIE", "BOSKS", "BOSKY", "BOSON", "BOSUN", "BOTAS", "BOTEL", + "BOTES", "BOTHY", "BOTTE", "BOTTS", "BOTTY", "BOUGE", "BOUKS", "BOULT", "BOUNS", "BOURD", "BOURG", "BOURN", + "BOUSE", "BOUSY", "BOUTS", "BOVID", "BOWAT", "BOWED", "BOWER", "BOWES", "BOWET", "BOWIE", "BOWLS", "BOWNE", + "BOWRS", "BOWSE", "BOXED", "BOXEN", "BOXES", "BOXLA", "BOXTY", "BOYAR", "BOYAU", "BOYED", "BOYFS", "BOYGS", + "BOYLA", "BOYOS", "BOYSY", "BOZOS", "BRAAI", "BRACH", "BRACK", "BRACT", "BRADS", "BRAES", "BRAGS", "BRAIL", + "BRAKS", "BRAKY", "BRAME", "BRANE", "BRANK", "BRANS", "BRANT", "BRAST", "BRATS", "BRAVA", "BRAVI", "BRAWS", + "BRAXY", "BRAYS", "BRAZA", "BRAZE", "BREAM", "BREDE", "BREDS", "BREEM", "BREER", "BREES", "BREID", "BREIS", + "BREME", "BRENS", "BRENT", "BRERE", "BRERS", "BREVE", "BREWS", "BREYS", "BRIER", "BRIES", "BRIGS", "BRIKI", + "BRIKS", "BRILL", "BRIMS", "BRINS", "BRIOS", "BRISE", "BRISS", "BRITH", "BRITS", "BRITT", "BRIZE", "BROCH", + "BROCK", "BRODS", "BROGH", "BROGS", "BROME", "BROMO", "BRONC", "BROND", "BROOL", "BROOS", "BROSE", "BROSY", + "BROWS", "BRUGH", "BRUIN", "BRUIT", "BRULE", "BRUME", "BRUNG", "BRUSK", "BRUST", "BRUTS", "BUATS", "BUAZE", + "BUBAL", "BUBAS", "BUBBA", "BUBBE", "BUBBY", "BUBUS", "BUCHU", "BUCKO", "BUCKS", "BUCKU", "BUDAS", "BUDIS", + "BUDOS", "BUFFA", "BUFFE", "BUFFI", "BUFFO", "BUFFS", "BUFFY", "BUFOS", "BUFTY", "BUHLS", "BUHRS", "BUIKS", + "BUIST", "BUKES", "BULBS", "BULGY", "BULKS", "BULLA", "BULLS", "BULSE", "BUMBO", "BUMFS", "BUMPH", "BUMPS", + "BUMPY", "BUNAS", "BUNCE", "BUNCO", "BUNDE", "BUNDH", "BUNDS", "BUNDT", "BUNDU", "BUNDY", "BUNGS", "BUNGY", + "BUNIA", "BUNJE", "BUNJY", "BUNKO", "BUNKS", "BUNNS", "BUNTS", "BUNTY", "BUNYA", "BUOYS", "BUPPY", "BURAN", + "BURAS", "BURBS", "BURDS", "BURET", "BURFI", "BURGH", "BURGS", "BURIN", "BURKA", "BURKE", "BURKS", "BURLS", + "BURNS", "BUROO", "BURPS", "BURQA", "BURRO", "BURRS", "BURRY", "BURSA", "BURSE", "BUSBY", "BUSES", "BUSKS", + "BUSKY", "BUSSU", "BUSTI", "BUSTS", "BUSTY", "BUTEO", "BUTES", "BUTLE", "BUTOH", "BUTTS", "BUTTY", "BUTUT", + "BUTYL", "BUZZY", "BWANA", "BWAZI", "BYDED", "BYDES", "BYKED", "BYKES", "BYRES", "BYRLS", "BYSSI", "BYTES", + "BYWAY", "CAAED", "CABAS", "CABER", "CABOB", "CABOC", "CABRE", "CACAS", "CACKS", "CACKY", "CADEE", "CADES", + "CADGE", "CADGY", "CADIE", "CADIS", "CADRE", "CAECA", "CAESE", "CAFES", "CAFFS", "CAGED", "CAGER", "CAGES", + "CAGOT", "CAHOW", "CAIDS", "CAINS", "CAIRD", "CAJON", "CAJUN", "CAKED", "CAKES", "CAKEY", "CALFS", "CALID", + "CALIF", "CALIX", "CALKS", "CALLA", "CALLS", "CALMS", "CALMY", "CALOS", "CALPA", "CALPS", "CALVE", "CALYX", + "CAMAN", "CAMAS", "CAMES", "CAMIS", "CAMOS", "CAMPI", "CAMPO", "CAMPS", "CAMPY", "CAMUS", "CANED", "CANEH", + "CANER", "CANES", "CANGS", "CANID", "CANNA", "CANNS", "CANSO", "CANST", "CANTO", "CANTS", "CANTY", "CAPAS", + "CAPED", "CAPES", "CAPEX", "CAPHS", "CAPIZ", "CAPLE", "CAPON", "CAPOS", "CAPOT", "CAPRI", "CAPUL", "CARAP", + "CARBO", "CARBS", "CARBY", "CARDI", "CARDS", "CARDY", "CARED", "CARER", "CARES", "CARET", "CAREX", "CARKS", + "CARLE", "CARLS", "CARNS", "CARNY", "CAROB", "CAROM", "CARON", "CARPI", "CARPS", "CARRS", "CARSE", "CARTA", + "CARTE", "CARTS", "CARVY", "CASAS", "CASCO", "CASED", "CASES", "CASKS", "CASKY", "CASTS", "CASUS", "CATES", + "CAUDA", "CAUKS", "CAULD", "CAULS", "CAUMS", "CAUPS", "CAURI", "CAUSA", "CAVAS", "CAVED", "CAVEL", "CAVER", + "CAVES", "CAVIE", "CAWED", "CAWKS", "CAXON", "CEAZE", "CEBID", "CECAL", "CECUM", "CEDED", "CEDER", "CEDES", + "CEDIS", "CEIBA", "CEILI", "CEILS", "CELEB", "CELLA", "CELLI", "CELLS", "CELOM", "CELTS", "CENSE", "CENTO", + "CENTS", "CENTU", "CEORL", "CEPES", "CERCI", "CERED", "CERES", "CERGE", "CERIA", "CERIC", "CERNE", "CEROC", + "CEROS", "CERTS", "CERTY", "CESSE", "CESTA", "CESTI", "CETES", "CETYL", "CEZVE", "CHACE", "CHACK", "CHACO", + "CHADO", "CHADS", "CHAFT", "CHAIS", "CHALS", "CHAMS", "CHANA", "CHANG", "CHANK", "CHAPE", "CHAPS", "CHAPT", + "CHARA", "CHARE", "CHARK", "CHARR", "CHARS", "CHARY", "CHATS", "CHAVE", "CHAVS", "CHAWK", "CHAWS", "CHAYA", + "CHAYS", "CHEEP", "CHEFS", "CHEKA", "CHELA", "CHELP", "CHEMO", "CHEMS", "CHERE", "CHERT", "CHETH", "CHEVY", + "CHEWS", "CHEWY", "CHIAO", "CHIAS", "CHIBS", "CHICA", "CHICH", "CHICO", "CHICS", "CHIEL", "CHIKS", "CHILE", + "CHIMB", "CHIMO", "CHIMP", "CHINE", "CHING", "CHINK", "CHINO", "CHINS", "CHIPS", "CHIRK", "CHIRL", "CHIRM", + "CHIRO", "CHIRR", "CHIRT", "CHIRU", "CHITS", "CHIVE", "CHIVS", "CHIVY", "CHIZZ", "CHOCO", "CHOCS", "CHODE", + "CHOGS", "CHOIL", "CHOKO", "CHOKY", "CHOLA", "CHOLI", "CHOLO", "CHOMP", "CHONS", "CHOOF", "CHOOK", "CHOOM", + "CHOON", "CHOPS", "CHOTA", "CHOTT", "CHOUT", "CHOUX", "CHOWK", "CHOWS", "CHUBS", "CHUFA", "CHUFF", "CHUGS", + "CHUMS", "CHURL", "CHURR", "CHUSE", "CHUTS", "CHYLE", "CHYME", "CHYND", "CIBOL", "CIDED", "CIDES", "CIELS", + "CIGGY", "CILIA", "CILLS", "CIMAR", "CIMEX", "CINCT", "CINES", "CINQS", "CIONS", "CIPPI", "CIRCS", "CIRES", + "CIRLS", "CIRRI", "CISCO", "CISSY", "CISTS", "CITAL", "CITED", "CITER", "CITES", "CIVES", "CIVET", "CIVIE", + "CIVVY", "CLACH", "CLADE", "CLADS", "CLAES", "CLAGS", "CLAME", "CLAMS", "CLANS", "CLAPS", "CLAPT", "CLARO", + "CLART", "CLARY", "CLAST", "CLATS", "CLAUT", "CLAVE", "CLAVI", "CLAWS", "CLAYS", "CLECK", "CLEEK", "CLEEP", + "CLEFS", "CLEGS", "CLEIK", "CLEMS", "CLEPE", "CLEPT", "CLEVE", "CLEWS", "CLIED", "CLIES", "CLIFT", "CLIME", + "CLINE", "CLINT", "CLIPE", "CLIPS", "CLIPT", "CLITS", "CLOAM", "CLODS", "CLOFF", "CLOGS", "CLOKE", "CLOMB", + "CLOMP", "CLONK", "CLONS", "CLOOP", "CLOOT", "CLOPS", "CLOTE", "CLOTS", "CLOUR", "CLOUS", "CLOWS", "CLOYE", + "CLOYS", "CLOZE", "CLUBS", "CLUES", "CLUEY", "CLUNK", "CLYPE", "CNIDA", "COACT", "COADY", "COALA", "COALS", + "COALY", "COAPT", "COARB", "COATE", "COATI", "COATS", "COBBS", "COBBY", "COBIA", "COBLE", "COBZA", "COCAS", + "COCCI", "COCCO", "COCKS", "COCKY", "COCOS", "CODAS", "CODEC", "CODED", "CODEN", "CODER", "CODES", "CODEX", + "CODON", "COEDS", "COFFS", "COGIE", "COGON", "COGUE", "COHAB", "COHEN", "COHOE", "COHOG", "COHOS", "COIFS", + "COIGN", "COILS", "COINS", "COIRS", "COITS", "COKED", "COKES", "COLAS", "COLBY", "COLDS", "COLED", "COLES", + "COLEY", "COLIC", "COLIN", "COLLS", "COLLY", "COLOG", "COLTS", "COLZA", "COMAE", "COMAL", "COMAS", "COMBE", + "COMBI", "COMBO", "COMBS", "COMBY", "COMER", "COMES", "COMIX", "COMMO", "COMMS", "COMMY", "COMPO", "COMPS", + "COMPT", "COMTE", "COMUS", "CONED", "CONES", "CONEY", "CONFS", "CONGA", "CONGE", "CONGO", "CONIA", "CONIN", + "CONKS", "CONKY", "CONNE", "CONNS", "CONTE", "CONTO", "CONUS", "CONVO", "COOCH", "COOED", "COOEE", "COOER", + "COOEY", "COOFS", "COOKS", "COOKY", "COOLS", "COOLY", "COOMB", "COOMS", "COOMY", "COONS", "COOPS", "COOPT", + "COOST", "COOTS", "COOZE", "COPAL", "COPAY", "COPED", "COPEN", "COPER", "COPES", "COPPY", "COPRA", "COPSY", + "COQUI", "CORAM", "CORBE", "CORBY", "CORDS", "CORED", "CORES", "COREY", "CORGI", "CORIA", "CORKS", "CORKY", + "CORMS", "CORNI", "CORNO", "CORNS", "CORNU", "CORPS", "CORSE", "CORSO", "COSEC", "COSED", "COSES", "COSET", + "COSEY", "COSIE", "COSTA", "COSTE", "COSTS", "COTAN", "COTED", "COTES", "COTHS", "COTTA", "COTTS", "COUDE", + "COUPS", "COURB", "COURD", "COURE", "COURS", "COUTA", "COUTH", "COVED", "COVES", "COVIN", "COWAL", "COWAN", + "COWED", "COWKS", "COWLS", "COWPS", "COWRY", "COXAE", "COXAL", "COXED", "COXES", "COXIB", "COYAU", "COYED", + "COYER", "COYPU", "COZED", "COZEN", "COZES", "COZEY", "COZIE", "CRAAL", "CRABS", "CRAGS", "CRAIC", "CRAIG", + "CRAKE", "CRAME", "CRAMS", "CRANS", "CRAPE", "CRAPS", "CRAPY", "CRARE", "CRAWS", "CRAYS", "CREDS", "CREEL", + "CREES", "CREMS", "CRENA", "CREPS", "CREPY", "CREWE", "CREWS", "CRIAS", "CRIBS", "CRIES", "CRIMS", "CRINE", + "CRIOS", "CRIPE", "CRIPS", "CRISE", "CRITH", "CRITS", "CROCI", "CROCS", "CROFT", "CROGS", "CROMB", "CROME", + "CRONK", "CRONS", "CROOL", "CROON", "CROPS", "CRORE", "CROST", "CROUT", "CROWS", "CROZE", "CRUCK", "CRUDO", + "CRUDS", "CRUDY", "CRUES", "CRUET", "CRUFT", "CRUNK", "CRUOR", "CRURA", "CRUSE", "CRUSY", "CRUVE", "CRWTH", + "CRYER", "CTENE", "CUBBY", "CUBEB", "CUBED", "CUBER", "CUBES", "CUBIT", "CUDDY", "CUFFO", "CUFFS", "CUIFS", + "CUING", "CUISH", "CUITS", "CUKES", "CULCH", "CULET", "CULEX", "CULLS", "CULLY", "CULMS", "CULPA", "CULTI", + "CULTS", "CULTY", "CUMEC", "CUNDY", "CUNEI", "CUNIT", "CUNTS", "CUPEL", "CUPID", "CUPPA", "CUPPY", "CURAT", + "CURBS", "CURCH", "CURDS", "CURDY", "CURED", "CURER", "CURES", "CURET", "CURFS", "CURIA", "CURIE", "CURLI", + "CURLS", "CURNS", "CURNY", "CURRS", "CURSI", "CURST", "CUSEC", "CUSHY", "CUSKS", "CUSPS", "CUSPY", "CUSSO", + "CUSUM", "CUTCH", "CUTER", "CUTES", "CUTEY", "CUTIN", "CUTIS", "CUTTO", "CUTTY", "CUTUP", "CUVEE", "CUZES", + "CWTCH", "CYANO", "CYANS", "CYCAD", "CYCAS", "CYCLO", "CYDER", "CYLIX", "CYMAE", "CYMAR", "CYMAS", "CYMES", + "CYMOL", "CYSTS", "CYTES", "CYTON", "CZARS", "DAALS", "DABBA", "DACES", "DACHA", "DACKS", "DADAH", "DADAS", + "DADOS", "DAFFS", "DAFFY", "DAGGA", "DAGGY", "DAGOS", "DAHLS", "DAIKO", "DAINE", "DAINT", "DAKER", "DALED", + "DALES", "DALIS", "DALLE", "DALTS", "DAMAN", "DAMAR", "DAMES", "DAMME", "DAMNS", "DAMPS", "DAMPY", "DANCY", + "DANGS", "DANIO", "DANKS", "DANNY", "DANTS", "DARAF", "DARBS", "DARCY", "DARED", "DARER", "DARES", "DARGA", + "DARGS", "DARIC", "DARIS", "DARKS", "DARKY", "DARNS", "DARRE", "DARTS", "DARZI", "DASHI", "DASHY", "DATAL", + "DATED", "DATER", "DATES", "DATOS", "DATTO", "DAUBE", "DAUBS", "DAUBY", "DAUDS", "DAULT", "DAURS", "DAUTS", + "DAVEN", "DAVIT", "DAWAH", "DAWDS", "DAWED", "DAWEN", "DAWKS", "DAWNS", "DAWTS", "DAYAN", "DAYCH", "DAYNT", + "DAZED", "DAZER", "DAZES", "DEADS", "DEAIR", "DEALS", "DEANS", "DEARE", "DEARN", "DEARS", "DEARY", "DEASH", + "DEAVE", "DEAWS", "DEAWY", "DEBAG", "DEBBY", "DEBEL", "DEBES", "DEBTS", "DEBUD", "DEBUR", "DEBUS", "DEBYE", + "DECAD", "DECAF", "DECAN", "DECKO", "DECKS", "DECOS", "DEDAL", "DEEDS", "DEEDY", "DEELY", "DEEMS", "DEENS", + "DEEPS", "DEERE", "DEERS", "DEETS", "DEEVE", "DEEVS", "DEFAT", "DEFFO", "DEFIS", "DEFOG", "DEGAS", "DEGUM", + "DEGUS", "DEICE", "DEIDS", "DEIFY", "DEILS", "DEISM", "DEIST", "DEKED", "DEKES", "DEKKO", "DELED", "DELES", + "DELFS", "DELFT", "DELIS", "DELLS", "DELLY", "DELOS", "DELPH", "DELTS", "DEMAN", "DEMES", "DEMIC", "DEMIT", + "DEMOB", "DEMOI", "DEMOS", "DEMPT", "DENAR", "DENAY", "DENCH", "DENES", "DENET", "DENIS", "DENTS", "DEOXY", + "DERAT", "DERAY", "DERED", "DERES", "DERIG", "DERMA", "DERMS", "DERNS", "DERNY", "DEROS", "DERRO", "DERRY", + "DERTH", "DERVS", "DESEX", "DESHI", "DESIS", "DESKS", "DESSE", "DEVAS", "DEVEL", "DEVIS", "DEVON", "DEVOS", + "DEVOT", "DEWAN", "DEWAR", "DEWAX", "DEWED", "DEXES", "DEXIE", "DHABA", "DHAKS", "DHALS", "DHIKR", "DHOBI", + "DHOLE", "DHOLL", "DHOLS", "DHOTI", "DHOWS", "DHUTI", "DIACT", "DIALS", "DIANE", "DIAZO", "DIBBS", "DICED", + "DICER", "DICES", "DICHT", "DICKS", "DICKY", "DICOT", "DICTA", "DICTS", "DICTY", "DIDDY", "DIDIE", "DIDOS", + "DIDST", "DIEBS", "DIELS", "DIENE", "DIETS", "DIFFS", "DIGHT", "DIKAS", "DIKED", "DIKER", "DIKES", "DIKEY", + "DILDO", "DILLI", "DILLS", "DIMBO", "DIMER", "DIMES", "DIMPS", "DINAR", "DINED", "DINES", "DINGE", "DINGS", + "DINIC", "DINKS", "DINKY", "DINNA", "DINOS", "DINTS", "DIOLS", "DIOTA", "DIPPY", "DIPSO", "DIRAM", "DIRER", + "DIRKE", "DIRKS", "DIRLS", "DIRTS", "DISAS", "DISCI", "DISCS", "DISHY", "DISKS", "DISME", "DITAL", "DITAS", + "DITED", "DITES", "DITSY", "DITTS", "DITZY", "DIVAN", "DIVAS", "DIVED", "DIVES", "DIVIS", "DIVNA", "DIVOS", + "DIVOT", "DIVVY", "DIWAN", "DIXIE", "DIXIT", "DIYAS", "DIZEN", "DJINN", "DJINS", "DOABS", "DOATS", "DOBBY", + "DOBES", "DOBIE", "DOBLA", "DOBRA", "DOBRO", "DOCHT", "DOCKS", "DOCOS", "DOCUS", "DODDY", "DODOS", "DOEKS", + "DOERS", "DOEST", "DOETH", "DOFFS", "DOGAN", "DOGES", "DOGEY", "DOGGO", "DOGGY", "DOGIE", "DOHYO", "DOILT", + "DOILY", "DOITS", "DOJOS", "DOLCE", "DOLCI", "DOLED", "DOLES", "DOLIA", "DOLLS", "DOLMA", "DOLOR", "DOLOS", + "DOLTS", "DOMAL", "DOMED", "DOMES", "DOMIC", "DONAH", "DONAS", "DONEE", "DONER", "DONGA", "DONGS", "DONKO", + "DONNA", "DONNE", "DONNY", "DONSY", "DOOBS", "DOOCE", "DOODY", "DOOKS", "DOOLE", "DOOLS", "DOOLY", "DOOMS", + "DOOMY", "DOONA", "DOORN", "DOORS", "DOOZY", "DOPAS", "DOPED", "DOPER", "DOPES", "DORAD", "DORBA", "DORBS", + "DOREE", "DORES", "DORIC", "DORIS", "DORKS", "DORKY", "DORMS", "DORMY", "DORPS", "DORRS", "DORSA", "DORSE", + "DORTS", "DORTY", "DOSAI", "DOSAS", "DOSED", "DOSEH", "DOSER", "DOSES", "DOSHA", "DOTAL", "DOTED", "DOTER", + "DOTES", "DOTTY", "DOUAR", "DOUCE", "DOUCS", "DOUKS", "DOULA", "DOUMA", "DOUMS", "DOUPS", "DOURA", "DOUSE", + "DOUTS", "DOVED", "DOVEN", "DOVER", "DOVES", "DOVIE", "DOWAR", "DOWDS", "DOWED", "DOWER", "DOWIE", "DOWLE", + "DOWLS", "DOWLY", "DOWNA", "DOWNS", "DOWPS", "DOWSE", "DOWTS", "DOXED", "DOXES", "DOXIE", "DOYEN", "DOYLY", + "DOZED", "DOZER", "DOZES", "DRABS", "DRACK", "DRACO", "DRAFF", "DRAGS", "DRAIL", "DRAMS", "DRANT", "DRAPS", + "DRATS", "DRAVE", "DRAWS", "DRAYS", "DREAR", "DRECK", "DREED", "DREER", "DREES", "DREGS", "DREKS", "DRENT", + "DRERE", "DREST", "DREYS", "DRIBS", "DRICE", "DRIES", "DRILY", "DRIPS", "DRIPT", "DROID", "DROIL", "DROKE", + "DROLE", "DROME", "DRONY", "DROOB", "DROOG", "DROOK", "DROPS", "DROPT", "DROUK", "DROWS", "DRUBS", "DRUGS", + "DRUMS", "DRUPE", "DRUSE", "DRUSY", "DRUXY", "DRYAD", "DRYAS", "DSOBO", "DSOMO", "DUADS", "DUALS", "DUANS", + "DUARS", "DUBBO", "DUCAL", "DUCAT", "DUCES", "DUCKS", "DUCKY", "DUCTS", "DUDDY", "DUDED", "DUDES", "DUELS", + "DUETS", "DUETT", "DUFFS", "DUFUS", "DUING", "DUITS", "DUKAS", "DUKED", "DUKES", "DUKKA", "DULCE", "DULES", + "DULIA", "DULLS", "DULSE", "DUMAS", "DUMBO", "DUMBS", "DUMKA", "DUMKY", "DUMPS", "DUNAM", "DUNCH", "DUNES", + "DUNGS", "DUNGY", "DUNKS", "DUNNO", "DUNNY", "DUNSH", "DUNTS", "DUOMI", "DUOMO", "DUPED", "DUPER", "DUPES", + "DUPLE", "DUPLY", "DUPPY", "DURAL", "DURAS", "DURED", "DURES", "DURGY", "DURNS", "DUROC", "DUROS", "DUROY", + "DURRA", "DURRS", "DURRY", "DURST", "DURUM", "DURZI", "DUSKS", "DUSTS", "DUXES", "DWAAL", "DWALE", "DWALM", + "DWAMS", "DWANG", "DWAUM", "DWEEB", "DWILE", "DWINE", "DYADS", "DYERS", "DYKED", "DYKES", "DYKEY", "DYKON", + "DYNEL", "DYNES", "DZHOS", "EAGRE", "EALED", "EALES", "EANED", "EARDS", "EARED", "EARLS", "EARNS", "EARNT", + "EARST", "EASED", "EASER", "EASES", "EASLE", "EASTS", "EATHE", "EAVED", "EAVES", "EBBED", "EBBET", "EBONS", + "EBOOK", "ECADS", "ECHED", "ECHES", "ECHOS", "ECRUS", "EDEMA", "EDGED", "EDGER", "EDGES", "EDILE", "EDITS", + "EDUCE", "EDUCT", "EEJIT", "EENSY", "EEVEN", "EEVNS", "EFFED", "EGADS", "EGERS", "EGEST", "EGGAR", "EGGED", + "EGGER", "EGMAS", "EHING", "EIDER", "EIDOS", "EIGNE", "EIKED", "EIKON", "EILDS", "EISEL", "EJIDO", "EKKAS", + "ELAIN", "ELAND", "ELANS", "ELCHI", "ELDIN", "ELEMI", "ELFED", "ELIAD", "ELINT", "ELMEN", "ELOGE", "ELOGY", + "ELOIN", "ELOPS", "ELPEE", "ELSIN", "ELUTE", "ELVAN", "ELVEN", "ELVER", "ELVES", "EMACS", "EMBAR", "EMBAY", + "EMBOG", "EMBOW", "EMBOX", "EMBUS", "EMEER", "EMEND", "EMERG", "EMERY", "EMEUS", "EMICS", "EMIRS", "EMITS", + "EMMAS", "EMMER", "EMMET", "EMMEW", "EMMYS", "EMOJI", "EMONG", "EMOTE", "EMOVE", "EMPTS", "EMULE", "EMURE", + "EMYDE", "EMYDS", "ENARM", "ENATE", "ENDED", "ENDER", "ENDEW", "ENDUE", "ENEWS", "ENFIX", "ENIAC", "ENLIT", + "ENMEW", "ENNOG", "ENOKI", "ENOLS", "ENORM", "ENOWS", "ENROL", "ENSEW", "ENSKY", "ENTIA", "ENURE", "ENURN", + "ENVOI", "ENZYM", "EORLS", "EOSIN", "EPACT", "EPEES", "EPHAH", "EPHAS", "EPHOD", "EPHOR", "EPICS", "EPODE", + "EPOPT", "EPRIS", "EQUES", "EQUID", "ERBIA", "EREVS", "ERGON", "ERGOS", "ERGOT", "ERHUS", "ERICA", "ERICK", + "ERICS", "ERING", "ERNED", "ERNES", "EROSE", "ERRED", "ERSES", "ERUCT", "ERUGO", "ERUVS", "ERVEN", "ERVIL", + "ESCAR", "ESCOT", "ESILE", "ESKAR", "ESKER", "ESNES", "ESSES", "ESTOC", "ESTOP", "ESTRO", "ETAGE", "ETAPE", + "ETATS", "ETENS", "ETHAL", "ETHNE", "ETHYL", "ETICS", "ETNAS", "ETTIN", "ETTLE", "ETUIS", "ETWEE", "ETYMA", + "EUGHS", "EUKED", "EUPAD", "EUROS", "EUSOL", "EVENS", "EVERT", "EVETS", "EVHOE", "EVILS", "EVITE", "EVOHE", + "EWERS", "EWEST", "EWHOW", "EWKED", "EXAMS", "EXEAT", "EXECS", "EXEEM", "EXEME", "EXFIL", "EXIES", "EXINE", + "EXING", "EXITS", "EXODE", "EXOME", "EXONS", "EXPAT", "EXPOS", "EXUDE", "EXULS", "EXURB", "EYASS", "EYERS", + "EYOTS", "EYRAS", "EYRES", "EYRIE", "EYRIR", "EZINE", "FABBY", "FACED", "FACER", "FACES", "FACIA", "FACTA", + "FACTS", "FADDY", "FADED", "FADER", "FADES", "FADGE", "FADOS", "FAENA", "FAERY", "FAFFS", "FAFFY", "FAGGY", + "FAGIN", "FAGOT", "FAIKS", "FAILS", "FAINE", "FAINS", "FAIRS", "FAKED", "FAKER", "FAKES", "FAKEY", "FAKIE", + "FAKIR", "FALAJ", "FALLS", "FAMED", "FAMES", "FANAL", "FANDS", "FANES", "FANGA", "FANGO", "FANGS", "FANKS", + "FANON", "FANOS", "FANUM", "FAQIR", "FARAD", "FARCI", "FARCY", "FARDS", "FARED", "FARER", "FARES", "FARLE", + "FARLS", "FARMS", "FAROS", "FARRO", "FARSE", "FARTS", "FASCI", "FASTI", "FASTS", "FATED", "FATES", "FATLY", + "FATSO", "FATWA", "FAUGH", "FAULD", "FAUNS", "FAURD", "FAUTS", "FAUVE", "FAVAS", "FAVEL", "FAVER", "FAVES", + "FAVUS", "FAWNS", "FAWNY", "FAXED", "FAXES", "FAYED", "FAYER", "FAYNE", "FAYRE", "FAZED", "FAZES", "FEALS", + "FEARE", "FEARS", "FEART", "FEASE", "FEATS", "FEAZE", "FECES", "FECHT", "FECIT", "FECKS", "FEDEX", "FEEBS", + "FEEDS", "FEELS", "FEENS", "FEERS", "FEESE", "FEEZE", "FEHME", "FEINT", "FEIST", "FELCH", "FELID", "FELLS", + "FELLY", "FELTS", "FELTY", "FEMAL", "FEMES", "FEMMY", "FENDS", "FENDY", "FENIS", "FENKS", "FENNY", "FENTS", + "FEODS", "FEOFF", "FERER", "FERES", "FERIA", "FERLY", "FERMI", "FERMS", "FERNS", "FERNY", "FESSE", "FESTA", + "FESTS", "FESTY", "FETAS", "FETED", "FETES", "FETOR", "FETTA", "FETTS", "FETWA", "FEUAR", "FEUDS", "FEUED", + "FEYED", "FEYER", "FEYLY", "FEZES", "FEZZY", "FIARS", "FIATS", "FIBRE", "FIBRO", "FICES", "FICHE", "FICHU", + "FICIN", "FICOS", "FIDES", "FIDGE", "FIDOS", "FIEFS", "FIENT", "FIERE", "FIERS", "FIEST", "FIFED", "FIFER", + "FIFES", "FIFIS", "FIGGY", "FIGOS", "FIKED", "FIKES", "FILAR", "FILCH", "FILED", "FILES", "FILII", "FILKS", + "FILLE", "FILLO", "FILLS", "FILMI", "FILMS", "FILOS", "FILUM", "FINCA", "FINDS", "FINED", "FINES", "FINIS", + "FINKS", "FINNY", "FINOS", "FIORD", "FIQHS", "FIQUE", "FIRED", "FIRER", "FIRES", "FIRIE", "FIRKS", "FIRMS", + "FIRNS", "FIRRY", "FIRTH", "FISCS", "FISKS", "FISTS", "FISTY", "FITCH", "FITLY", "FITNA", "FITTE", "FITTS", + "FIVER", "FIVES", "FIXED", "FIXES", "FIXIT", "FJELD", "FLABS", "FLAFF", "FLAGS", "FLAKS", "FLAMM", "FLAMS", + "FLAMY", "FLANE", "FLANS", "FLAPS", "FLARY", "FLATS", "FLAVA", "FLAWN", "FLAWS", "FLAWY", "FLAXY", "FLAYS", + "FLEAM", "FLEAS", "FLEEK", "FLEER", "FLEES", "FLEGS", "FLEME", "FLEUR", "FLEWS", "FLEXI", "FLEXO", "FLEYS", + "FLICS", "FLIED", "FLIES", "FLIMP", "FLIMS", "FLIPS", "FLIRS", "FLISK", "FLITE", "FLITS", "FLITT", "FLOBS", + "FLOCS", "FLOES", "FLOGS", "FLONG", "FLOPS", "FLORS", "FLORY", "FLOSH", "FLOTA", "FLOTE", "FLOWS", "FLUBS", + "FLUED", "FLUES", "FLUEY", "FLUKY", "FLUMP", "FLUOR", "FLURR", "FLUTY", "FLUYT", "FLYBY", "FLYPE", "FLYTE", + "FOALS", "FOAMS", "FOEHN", "FOGEY", "FOGIE", "FOGLE", "FOGOU", "FOHNS", "FOIDS", "FOILS", "FOINS", "FOLDS", + "FOLEY", "FOLIA", "FOLIC", "FOLIE", "FOLKS", "FOLKY", "FOMES", "FONDA", "FONDS", "FONDU", "FONES", "FONLY", + "FONTS", "FOODS", "FOODY", "FOOLS", "FOOTS", "FOOTY", "FORAM", "FORBS", "FORBY", "FORDO", "FORDS", "FOREL", + "FORES", "FOREX", "FORKS", "FORKY", "FORME", "FORMS", "FORTS", "FORZA", "FORZE", "FOSSA", "FOSSE", "FOUAT", + "FOUDS", "FOUER", "FOUET", "FOULE", "FOULS", "FOUNT", "FOURS", "FOUTH", "FOVEA", "FOWLS", "FOWTH", "FOXED", + "FOXES", "FOXIE", "FOYLE", "FOYNE", "FRABS", "FRACK", "FRACT", "FRAGS", "FRAIM", "FRANC", "FRAPE", "FRAPS", + "FRASS", "FRATE", "FRATI", "FRATS", "FRAUS", "FRAYS", "FREES", "FREET", "FREIT", "FREMD", "FRENA", "FREON", + "FRERE", "FRETS", "FRIBS", "FRIER", "FRIES", "FRIGS", "FRISE", "FRIST", "FRITH", "FRITS", "FRITT", "FRIZE", + "FRIZZ", "FROES", "FROGS", "FRONS", "FRORE", "FRORN", "FRORY", "FROSH", "FROWS", "FROWY", "FRUGS", "FRUMP", + "FRUSH", "FRUST", "FRYER", "FUBAR", "FUBBY", "FUBSY", "FUCKS", "FUCUS", "FUDDY", "FUDGY", "FUELS", "FUERO", + "FUFFS", "FUFFY", "FUGAL", "FUGGY", "FUGIE", "FUGIO", "FUGLE", "FUGLY", "FUGUS", "FUJIS", "FULLS", "FUMED", + "FUMER", "FUMES", "FUMET", "FUNDI", "FUNDS", "FUNDY", "FUNGO", "FUNGS", "FUNKS", "FURAL", "FURAN", "FURCA", + "FURLS", "FUROL", "FURRS", "FURTH", "FURZE", "FURZY", "FUSED", "FUSEE", "FUSEL", "FUSES", "FUSIL", "FUSKS", + "FUSTS", "FUSTY", "FUTON", "FUZED", "FUZEE", "FUZES", "FUZIL", "FYCES", "FYKED", "FYKES", "FYLES", "FYRDS", + "FYTTE", "GABBA", "GABBY", "GABLE", "GADDI", "GADES", "GADGE", "GADID", "GADIS", "GADJE", "GADJO", "GADSO", + "GAFFS", "GAGED", "GAGER", "GAGES", "GAIDS", "GAINS", "GAIRS", "GAITA", "GAITS", "GAITT", "GAJOS", "GALAH", + "GALAS", "GALAX", "GALEA", "GALED", "GALES", "GALLS", "GALLY", "GALOP", "GALUT", "GALVO", "GAMAS", "GAMAY", + "GAMBA", "GAMBE", "GAMBO", "GAMBS", "GAMED", "GAMES", "GAMEY", "GAMIC", "GAMIN", "GAMME", "GAMMY", "GAMPS", + "GANCH", "GANDY", "GANEF", "GANEV", "GANGS", "GANJA", "GANOF", "GANTS", "GAOLS", "GAPED", "GAPER", "GAPES", + "GAPOS", "GAPPY", "GARBE", "GARBO", "GARBS", "GARDA", "GARES", "GARIS", "GARMS", "GARNI", "GARRE", "GARTH", + "GARUM", "GASES", "GASPS", "GASPY", "GASTS", "GATCH", "GATED", "GATER", "GATES", "GATHS", "GATOR", "GAUCH", + "GAUCY", "GAUDS", "GAUJE", "GAULT", "GAUMS", "GAUMY", "GAUPS", "GAURS", "GAUSS", "GAUZY", "GAVOT", "GAWCY", + "GAWDS", "GAWKS", "GAWPS", "GAWSY", "GAYAL", "GAZAL", "GAZAR", "GAZED", "GAZES", "GAZON", "GAZOO", "GEALS", + "GEANS", "GEARE", "GEARS", "GEATS", "GEBUR", "GECKS", "GEEKS", "GEEPS", "GEEST", "GEIST", "GEITS", "GELDS", + "GELEE", "GELID", "GELLY", "GELTS", "GEMEL", "GEMMA", "GEMMY", "GEMOT", "GENAL", "GENAS", "GENES", "GENET", + "GENIC", "GENII", "GENIP", "GENNY", "GENOA", "GENOM", "GENRO", "GENTS", "GENTY", "GENUA", "GENUS", "GEODE", + "GEOID", "GERAH", "GERBE", "GERES", "GERLE", "GERMS", "GERMY", "GERNE", "GESSE", "GESSO", "GESTE", "GESTS", + "GETAS", "GETUP", "GEUMS", "GEYAN", "GEYER", "GHAST", "GHATS", "GHAUT", "GHAZI", "GHEES", "GHEST", "GHYLL", + "GIBED", "GIBEL", "GIBER", "GIBES", "GIBLI", "GIBUS", "GIFTS", "GIGAS", "GIGHE", "GIGOT", "GIGUE", "GILAS", + "GILDS", "GILET", "GILLS", "GILLY", "GILPY", "GILTS", "GIMEL", "GIMME", "GIMPS", "GIMPY", "GINCH", "GINGE", + "GINGS", "GINKS", "GINNY", "GINZO", "GIPON", "GIPPO", "GIPPY", "GIRDS", "GIRLS", "GIRNS", "GIRON", "GIROS", + "GIRRS", "GIRSH", "GIRTS", "GISMO", "GISMS", "GISTS", "GITCH", "GITES", "GIUST", "GIVED", "GIVES", "GIZMO", + "GLACE", "GLADS", "GLADY", "GLAIK", "GLAIR", "GLAMS", "GLANS", "GLARY", "GLAUM", "GLAUR", "GLAZY", "GLEBA", + "GLEBE", "GLEBY", "GLEDE", "GLEDS", "GLEED", "GLEEK", "GLEES", "GLEET", "GLEIS", "GLENS", "GLENT", "GLEYS", + "GLIAL", "GLIAS", "GLIBS", "GLIFF", "GLIFT", "GLIKE", "GLIME", "GLIMS", "GLISK", "GLITS", "GLITZ", "GLOAM", + "GLOBI", "GLOBS", "GLOBY", "GLODE", "GLOGG", "GLOMS", "GLOOP", "GLOPS", "GLOST", "GLOUT", "GLOWS", "GLOZE", + "GLUED", "GLUER", "GLUES", "GLUEY", "GLUGS", "GLUME", "GLUMS", "GLUON", "GLUTE", "GLUTS", "GNARL", "GNARR", + "GNARS", "GNATS", "GNAWN", "GNAWS", "GNOWS", "GOADS", "GOAFS", "GOALS", "GOARY", "GOATS", "GOATY", "GOBAN", + "GOBAR", "GOBBI", "GOBBO", "GOBBY", "GOBIS", "GOBOS", "GODET", "GODSO", "GOELS", "GOERS", "GOEST", "GOETH", + "GOETY", "GOFER", "GOFFS", "GOGGA", "GOGOS", "GOIER", "GOJIS", "GOLDS", "GOLDY", "GOLES", "GOLFS", "GOLPE", + "GOLPS", "GOMBO", "GOMER", "GOMPA", "GONCH", "GONEF", "GONGS", "GONIA", "GONIF", "GONKS", "GONNA", "GONOF", + "GONYS", "GONZO", "GOOBY", "GOODS", "GOOFS", "GOOGS", "GOOKS", "GOOKY", "GOOLD", "GOOLS", "GOOLY", "GOONS", + "GOONY", "GOOPS", "GOOPY", "GOORS", "GOORY", "GOOSY", "GOPAK", "GOPIK", "GORAL", "GORAS", "GORED", "GORES", + "GORIS", "GORMS", "GORMY", "GORPS", "GORSE", "GORSY", "GOSHT", "GOSSE", "GOTCH", "GOTHS", "GOTHY", "GOTTA", + "GOUCH", "GOUKS", "GOURA", "GOUTS", "GOUTY", "GOWAN", "GOWDS", "GOWFS", "GOWKS", "GOWLS", "GOWNS", "GOXES", + "GOYIM", "GOYLE", "GRAAL", "GRABS", "GRADS", "GRAFF", "GRAIP", "GRAMA", "GRAME", "GRAMP", "GRAMS", "GRANA", + "GRANS", "GRAPY", "GRAVS", "GRAYS", "GREBE", "GREBO", "GRECE", "GREEK", "GREES", "GREGE", "GREGO", "GREIN", + "GRENS", "GRESE", "GREVE", "GREWS", "GREYS", "GRICE", "GRIDE", "GRIDS", "GRIFF", "GRIFT", "GRIGS", "GRIKE", + "GRINS", "GRIOT", "GRIPS", "GRIPT", "GRIPY", "GRISE", "GRIST", "GRISY", "GRITH", "GRITS", "GRIZE", "GROAT", + "GRODY", "GROGS", "GROKS", "GROMA", "GRONE", "GROOF", "GROSZ", "GROTS", "GROUF", "GROVY", "GROWS", "GRRLS", + "GRRRL", "GRUBS", "GRUED", "GRUES", "GRUFE", "GRUME", "GRUMP", "GRUND", "GRYCE", "GRYDE", "GRYKE", "GRYPE", + "GRYPT", "GUACO", "GUANA", "GUANO", "GUANS", "GUARS", "GUCKS", "GUCKY", "GUDES", "GUFFS", "GUGAS", "GUIDS", + "GUIMP", "GUIRO", "GULAG", "GULAR", "GULAS", "GULES", "GULET", "GULFS", "GULFY", "GULLS", "GULPH", "GULPS", + "GULPY", "GUMMA", "GUMMI", "GUMPS", "GUNDY", "GUNGE", "GUNGY", "GUNKS", "GUNKY", "GUNNY", "GUQIN", "GURDY", + "GURGE", "GURLS", "GURLY", "GURNS", "GURRY", "GURSH", "GURUS", "GUSHY", "GUSLA", "GUSLE", "GUSLI", "GUSSY", + "GUSTS", "GUTSY", "GUTTA", "GUTTY", "GUYED", "GUYLE", "GUYOT", "GUYSE", "GWINE", "GYALS", "GYANS", "GYBED", + "GYBES", "GYELD", "GYMPS", "GYNAE", "GYNIE", "GYNNY", "GYNOS", "GYOZA", "GYPOS", "GYPPO", "GYPPY", "GYRAL", + "GYRED", "GYRES", "GYRON", "GYROS", "GYRUS", "GYTES", "GYVED", "GYVES", "HAAFS", "HAARS", "HABLE", "HABUS", + "HACEK", "HACKS", "HADAL", "HADED", "HADES", "HADJI", "HADST", "HAEMS", "HAETS", "HAFFS", "HAFIZ", "HAFTS", + "HAGGS", "HAHAS", "HAICK", "HAIKA", "HAIKS", "HAIKU", "HAILS", "HAILY", "HAINS", "HAINT", "HAIRS", "HAITH", + "HAJES", "HAJIS", "HAJJI", "HAKAM", "HAKAS", "HAKEA", "HAKES", "HAKIM", "HAKUS", "HALAL", "HALED", "HALER", + "HALES", "HALFA", "HALFS", "HALID", "HALLO", "HALLS", "HALMA", "HALMS", "HALON", "HALOS", "HALSE", "HALTS", + "HALVA", "HALWA", "HAMAL", "HAMBA", "HAMED", "HAMES", "HAMMY", "HAMZA", "HANAP", "HANCE", "HANCH", "HANDS", + "HANGI", "HANGS", "HANKS", "HANKY", "HANSA", "HANSE", "HANTS", "HAOLE", "HAOMA", "HAPAX", "HAPLY", "HAPPI", + "HAPUS", "HARAM", "HARDS", "HARED", "HARES", "HARIM", "HARKS", "HARLS", "HARMS", "HARNS", "HAROS", "HARPS", + "HARTS", "HASHY", "HASKS", "HASPS", "HASTA", "HATED", "HATES", "HATHA", "HAUDS", "HAUFS", "HAUGH", "HAULD", + "HAULM", "HAULS", "HAULT", "HAUNS", "HAUSE", "HAVER", "HAVES", "HAWED", "HAWKS", "HAWMS", "HAWSE", "HAYED", + "HAYER", "HAYEY", "HAYLE", "HAZAN", "HAZED", "HAZER", "HAZES", "HEADS", "HEALD", "HEALS", "HEAME", "HEAPS", + "HEAPY", "HEARE", "HEARS", "HEAST", "HEATS", "HEBEN", "HEBES", "HECHT", "HECKS", "HEDER", "HEDGY", "HEEDS", + "HEEDY", "HEELS", "HEEZE", "HEFTE", "HEFTS", "HEIDS", "HEIGH", "HEILS", "HEIRS", "HEJAB", "HEJRA", "HELED", + "HELES", "HELIO", "HELLS", "HELMS", "HELOS", "HELOT", "HELPS", "HELVE", "HEMAL", "HEMES", "HEMIC", "HEMIN", + "HEMPS", "HEMPY", "HENCH", "HENDS", "HENGE", "HENNA", "HENNY", "HENRY", "HENTS", "HEPAR", "HERBS", "HERBY", + "HERDS", "HERES", "HERLS", "HERMA", "HERMS", "HERNS", "HEROS", "HERRY", "HERSE", "HERTZ", "HERYE", "HESPS", + "HESTS", "HETES", "HETHS", "HEUCH", "HEUGH", "HEVEA", "HEWED", "HEWER", "HEWGH", "HEXAD", "HEXED", "HEXER", + "HEXES", "HEXYL", "HEYED", "HIANT", "HICKS", "HIDED", "HIDER", "HIDES", "HIEMS", "HIGHS", "HIGHT", "HIJAB", + "HIJRA", "HIKED", "HIKER", "HIKES", "HIKOI", "HILAR", "HILCH", "HILLO", "HILLS", "HILTS", "HILUM", "HILUS", + "HIMBO", "HINAU", "HINDS", "HINGS", "HINKY", "HINNY", "HINTS", "HIOIS", "HIPLY", "HIRED", "HIREE", "HIRER", + "HIRES", "HISSY", "HISTS", "HITHE", "HIVED", "HIVER", "HIVES", "HIZEN", "HOAED", "HOAGY", "HOARS", "HOARY", + "HOAST", "HOBOS", "HOCKS", "HOCUS", "HODAD", "HODJA", "HOERS", "HOGAN", "HOGEN", "HOGGS", "HOGHS", "HOHED", + "HOICK", "HOIED", "HOIKS", "HOING", "HOISE", "HOKAS", "HOKED", "HOKES", "HOKEY", "HOKIS", "HOKKU", "HOKUM", + "HOLDS", "HOLED", "HOLES", "HOLEY", "HOLKS", "HOLLA", "HOLLO", "HOLME", "HOLMS", "HOLON", "HOLOS", "HOLTS", + "HOMAS", "HOMED", "HOMES", "HOMEY", "HOMIE", "HOMME", "HOMOS", "HONAN", "HONDA", "HONDS", "HONED", "HONER", + "HONES", "HONGI", "HONGS", "HONKS", "HONKY", "HOOCH", "HOODS", "HOODY", "HOOEY", "HOOFS", "HOOKA", "HOOKS", + "HOOKY", "HOOLY", "HOONS", "HOOPS", "HOORD", "HOORS", "HOOSH", "HOOTS", "HOOTY", "HOOVE", "HOPAK", "HOPED", + "HOPER", "HOPES", "HOPPY", "HORAH", "HORAL", "HORAS", "HORIS", "HORKS", "HORME", "HORNS", "HORST", "HORSY", + "HOSED", "HOSEL", "HOSEN", "HOSER", "HOSES", "HOSEY", "HOSTA", "HOSTS", "HOTCH", "HOTEN", "HOTTY", "HOUFF", + "HOUFS", "HOUGH", "HOURI", "HOURS", "HOUTS", "HOVEA", "HOVED", "HOVEN", "HOVES", "HOWBE", "HOWES", "HOWFF", + "HOWFS", "HOWKS", "HOWLS", "HOWRE", "HOWSO", "HOXED", "HOXES", "HOYAS", "HOYED", "HOYLE", "HUBBY", "HUCKS", + "HUDNA", "HUDUD", "HUERS", "HUFFS", "HUFFY", "HUGER", "HUGGY", "HUHUS", "HUIAS", "HULAS", "HULES", "HULKS", + "HULKY", "HULLO", "HULLS", "HULLY", "HUMAS", "HUMFS", "HUMIC", "HUMPS", "HUMPY", "HUNKS", "HUNTS", "HURDS", + "HURLS", "HURLY", "HURRA", "HURST", "HURTS", "HUSHY", "HUSKS", "HUSOS", "HUTIA", "HUZZA", "HUZZY", "HWYLS", + "HYDRA", "HYENS", "HYGGE", "HYING", "HYKES", "HYLAS", "HYLEG", "HYLES", "HYLIC", "HYMNS", "HYNDE", "HYOID", + "HYPED", "HYPES", "HYPHA", "HYPHY", "HYPOS", "HYRAX", "HYSON", "HYTHE", "IAMBI", "IAMBS", "IBRIK", "ICERS", + "ICHED", "ICHES", "ICHOR", "ICIER", "ICKER", "ICKLE", "ICONS", "ICTAL", "ICTIC", "ICTUS", "IDANT", "IDEAS", + "IDEES", "IDENT", "IDLED", "IDLES", "IDOLA", "IDOLS", "IDYLS", "IFTAR", "IGAPO", "IGGED", "IGLUS", "IHRAM", + "IKANS", "IKATS", "IKONS", "ILEAC", "ILEAL", "ILEUM", "ILEUS", "ILIAD", "ILIAL", "ILIUM", "ILLER", "ILLTH", + "IMAGO", "IMAMS", "IMARI", "IMAUM", "IMBAR", "IMBED", "IMIDE", "IMIDO", "IMIDS", "IMINE", "IMINO", "IMMEW", + "IMMIT", "IMMIX", "IMPED", "IMPIS", "IMPOT", "IMPRO", "IMSHI", "IMSHY", "INAPT", "INARM", "INBYE", "INCEL", + "INCLE", "INCOG", "INCUS", "INCUT", "INDEW", "INDIA", "INDIE", "INDOL", "INDOW", "INDRI", "INDUE", "INERM", + "INFIX", "INFOS", "INFRA", "INGAN", "INGLE", "INION", "INKED", "INKER", "INKLE", "INNED", "INNIT", "INORB", + "INRUN", "INSET", "INSPO", "INTEL", "INTIL", "INTIS", "INTRA", "INULA", "INURE", "INURN", "INUST", "INVAR", + "INWIT", "IODIC", "IODID", "IODIN", "IOTAS", "IPPON", "IRADE", "IRIDS", "IRING", "IRKED", "IROKO", "IRONE", + "IRONS", "ISBAS", "ISHES", "ISLED", "ISLES", "ISNAE", "ISSEI", "ISTLE", "ITEMS", "ITHER", "IVIED", "IVIES", + "IXIAS", "IXNAY", "IXORA", "IXTLE", "IZARD", "IZARS", "IZZAT", "JAAPS", "JABOT", "JACAL", "JACKS", "JACKY", + "JADED", "JADES", "JAFAS", "JAFFA", "JAGAS", "JAGER", "JAGGS", "JAGGY", "JAGIR", "JAGRA", "JAILS", "JAKER", + "JAKES", "JAKEY", "JALAP", "JALOP", "JAMBE", "JAMBO", "JAMBS", "JAMBU", "JAMES", "JAMMY", "JAMON", "JANES", + "JANNS", "JANNY", "JANTY", "JAPAN", "JAPED", "JAPER", "JAPES", "JARKS", "JARLS", "JARPS", "JARTA", "JARUL", + "JASEY", "JASPE", "JASPS", "JATOS", "JAUKS", "JAUPS", "JAVAS", "JAVEL", "JAWAN", "JAWED", "JAXIE", "JEANS", + "JEATS", "JEBEL", "JEDIS", "JEELS", "JEELY", "JEEPS", "JEERS", "JEEZE", "JEFES", "JEFFS", "JEHAD", "JEHUS", + "JELAB", "JELLO", "JELLS", "JEMBE", "JEMMY", "JENNY", "JEONS", "JERID", "JERKS", "JERRY", "JESSE", "JESTS", + "JESUS", "JETES", "JETON", "JEUNE", "JEWED", "JEWIE", "JHALA", "JIAOS", "JIBBA", "JIBBS", "JIBED", "JIBER", + "JIBES", "JIFFS", "JIGGY", "JIGOT", "JIHAD", "JILLS", "JILTS", "JIMMY", "JIMPY", "JINGO", "JINKS", "JINNE", + "JINNI", "JINNS", "JIRDS", "JIRGA", "JIRRE", "JISMS", "JIVED", "JIVER", "JIVES", "JIVEY", "JNANA", "JOBED", + "JOBES", "JOCKO", "JOCKS", "JOCKY", "JOCOS", "JODEL", "JOEYS", "JOHNS", "JOINS", "JOKED", "JOKES", "JOKEY", + "JOKOL", "JOLED", "JOLES", "JOLLS", "JOLTS", "JOLTY", "JOMON", "JOMOS", "JONES", "JONGS", "JONTY", "JOOKS", + "JORAM", "JORUM", "JOTAS", "JOTTY", "JOTUN", "JOUAL", "JOUGS", "JOUKS", "JOULE", "JOURS", "JOWAR", "JOWED", + "JOWLS", "JOWLY", "JOYED", "JUBAS", "JUBES", "JUCOS", "JUDAS", "JUDGY", "JUDOS", "JUGAL", "JUGUM", "JUJUS", + "JUKED", "JUKES", "JUKUS", "JULEP", "JUMAR", "JUMBY", "JUMPS", "JUNCO", "JUNKS", "JUNKY", "JUPES", "JUPON", + "JURAL", "JURAT", "JUREL", "JURES", "JUSTS", "JUTES", "JUTTY", "JUVES", "JUVIE", "KAAMA", "KABAB", "KABAR", + "KABOB", "KACHA", "KACKS", "KADAI", "KADES", "KADIS", "KAFIR", "KAGOS", "KAGUS", "KAHAL", "KAIAK", "KAIDS", + "KAIES", "KAIFS", "KAIKA", "KAIKS", "KAILS", "KAIMS", "KAING", "KAINS", "KAKAS", "KAKIS", "KALAM", "KALES", + "KALIF", "KALIS", "KALPA", "KAMAS", "KAMES", "KAMIK", "KAMIS", "KAMME", "KANAE", "KANAS", "KANDY", "KANEH", + "KANES", "KANGA", "KANGS", "KANJI", "KANTS", "KANZU", "KAONS", "KAPAS", "KAPHS", "KAPOK", "KAPOW", "KAPUS", + "KAPUT", "KARAS", "KARAT", "KARKS", "KARNS", "KAROO", "KAROS", "KARRI", "KARST", "KARSY", "KARTS", "KARZY", + "KASHA", "KASME", "KATAL", "KATAS", "KATIS", "KATTI", "KAUGH", "KAURI", "KAURU", "KAURY", "KAVAL", "KAVAS", + "KAWAS", "KAWAU", "KAWED", "KAYLE", "KAYOS", "KAZIS", "KAZOO", "KBARS", "KEBAR", "KEBOB", "KECKS", "KEDGE", + "KEDGY", "KEECH", "KEEFS", "KEEKS", "KEELS", "KEEMA", "KEENO", "KEENS", "KEEPS", "KEETS", "KEEVE", "KEFIR", + "KEHUA", "KEIRS", "KELEP", "KELIM", "KELLS", "KELLY", "KELPS", "KELPY", "KELTS", "KELTY", "KEMBO", "KEMBS", + "KEMPS", "KEMPT", "KEMPY", "KENAF", "KENCH", "KENDO", "KENOS", "KENTE", "KENTS", "KEPIS", "KERBS", "KEREL", + "KERFS", "KERKY", "KERMA", "KERNE", "KERNS", "KEROS", "KERRY", "KERVE", "KESAR", "KESTS", "KETAS", "KETCH", + "KETES", "KETOL", "KEVEL", "KEVIL", "KEXES", "KEYED", "KEYER", "KHADI", "KHAFS", "KHANS", "KHAPH", "KHATS", + "KHAYA", "KHAZI", "KHEDA", "KHETH", "KHETS", "KHOJA", "KHORS", "KHOUM", "KHUDS", "KIAAT", "KIACK", "KIANG", + "KIBBE", "KIBBI", "KIBEI", "KIBES", "KIBLA", "KICKS", "KICKY", "KIDDO", "KIDDY", "KIDEL", "KIDGE", "KIEFS", + "KIERS", "KIEVE", "KIEVS", "KIGHT", "KIKES", "KIKOI", "KILEY", "KILIM", "KILLS", "KILNS", "KILOS", "KILPS", + "KILTS", "KILTY", "KIMBO", "KINAS", "KINDA", "KINDS", "KINDY", "KINES", "KINGS", "KININ", "KINKS", "KINOS", + "KIORE", "KIPES", "KIPPA", "KIPPS", "KIRBY", "KIRKS", "KIRNS", "KIRRI", "KISAN", "KISSY", "KISTS", "KITED", + "KITER", "KITES", "KITHE", "KITHS", "KITUL", "KIVAS", "KIWIS", "KLANG", "KLAPS", "KLETT", "KLICK", "KLIEG", + "KLIKS", "KLONG", "KLOOF", "KLUGE", "KLUTZ", "KNAGS", "KNAPS", "KNARL", "KNARS", "KNAUR", "KNAWE", "KNEES", + "KNELL", "KNISH", "KNITS", "KNIVE", "KNOBS", "KNOPS", "KNOSP", "KNOTS", "KNOUT", "KNOWE", "KNOWS", "KNUBS", + "KNURL", "KNURR", "KNURS", "KNUTS", "KOANS", "KOAPS", "KOBAN", "KOBOS", "KOELS", "KOFFS", "KOFTA", "KOGAL", + "KOHAS", "KOHEN", "KOHLS", "KOINE", "KOJIS", "KOKAM", "KOKAS", "KOKER", "KOKRA", "KOKUM", "KOLAS", "KOLOS", + "KOMBU", "KONBU", "KONDO", "KONKS", "KOOKS", "KOOKY", "KOORI", "KOPEK", "KOPHS", "KOPJE", "KOPPA", "KORAI", + "KORAN", "KORAS", "KORAT", "KORES", "KORMA", "KOROS", "KORUN", "KORUS", "KOSES", "KOTCH", "KOTOS", "KOTOW", + "KOURA", "KRAAL", "KRABS", "KRAFT", "KRAIS", "KRAIT", "KRANG", "KRANS", "KRANZ", "KRAUT", "KRAYS", "KREEP", + "KRENG", "KREWE", "KRONA", "KRONE", "KROON", "KRUBI", "KRUNK", "KSARS", "KUBIE", "KUDOS", "KUDUS", "KUDZU", + "KUFIS", "KUGEL", "KUIAS", "KUKRI", "KUKUS", "KULAK", "KULAN", "KULAS", "KULFI", "KUMIS", "KUMYS", "KURIS", + "KURRE", "KURTA", "KURUS", "KUSSO", "KUTAS", "KUTCH", "KUTIS", "KUTUS", "KUZUS", "KVASS", "KVELL", "KWELA", + "KYACK", "KYAKS", "KYANG", "KYARS", "KYATS", "KYBOS", "KYDST", "KYLES", "KYLIE", "KYLIN", "KYLIX", "KYLOE", + "KYNDE", "KYNDS", "KYPES", "KYRIE", "KYTES", "KYTHE", "LAARI", "LABDA", "LABIA", "LABIS", "LABRA", "LACED", + "LACER", "LACES", "LACET", "LACEY", "LACKS", "LADDY", "LADED", "LADER", "LADES", "LAERS", "LAEVO", "LAGAN", + "LAHAL", "LAHAR", "LAICH", "LAICS", "LAIDS", "LAIGH", "LAIKA", "LAIKS", "LAIRD", "LAIRS", "LAIRY", "LAITH", + "LAITY", "LAKED", "LAKER", "LAKES", "LAKHS", "LAKIN", "LAKSA", "LALDY", "LALLS", "LAMAS", "LAMBS", "LAMBY", + "LAMED", "LAMER", "LAMES", "LAMIA", "LAMMY", "LAMPS", "LANAI", "LANAS", "LANCH", "LANDE", "LANDS", "LANES", + "LANKS", "LANTS", "LAPIN", "LAPIS", "LAPJE", "LARCH", "LARDS", "LARDY", "LAREE", "LARES", "LARGO", "LARIS", + "LARKS", "LARKY", "LARNS", "LARNT", "LARUM", "LASED", "LASER", "LASES", "LASSI", "LASSU", "LASSY", "LASTS", + "LATAH", "LATED", "LATEN", "LATEX", "LATHI", "LATHS", "LATHY", "LATKE", "LATUS", "LAUAN", "LAUCH", "LAUDS", + "LAUFS", "LAUND", "LAURA", "LAVAL", "LAVAS", "LAVED", "LAVER", "LAVES", "LAVRA", "LAVVY", "LAWED", "LAWER", + "LAWIN", "LAWKS", "LAWNS", "LAWNY", "LAXED", "LAXER", "LAXES", "LAXLY", "LAYED", "LAYIN", "LAYUP", "LAZAR", + "LAZED", "LAZES", "LAZOS", "LAZZI", "LAZZO", "LEADS", "LEADY", "LEAFS", "LEAKS", "LEAMS", "LEANS", "LEANY", + "LEAPS", "LEARE", "LEARS", "LEARY", "LEATS", "LEAVY", "LEAZE", "LEBEN", "LECCY", "LEDES", "LEDGY", "LEDUM", + "LEEAR", "LEEKS", "LEEPS", "LEERS", "LEESE", "LEETS", "LEEZE", "LEFTE", "LEFTS", "LEGER", "LEGES", "LEGGE", + "LEGGO", "LEGIT", "LEHRS", "LEHUA", "LEIRS", "LEISH", "LEMAN", "LEMED", "LEMEL", "LEMES", "LEMMA", "LEMME", + "LENDS", "LENES", "LENGS", "LENIS", "LENOS", "LENSE", "LENTI", "LENTO", "LEONE", "LEPID", "LEPRA", "LEPTA", + "LERED", "LERES", "LERPS", "LESBO", "LESES", "LESTS", "LETCH", "LETHE", "LETUP", "LEUCH", "LEUCO", "LEUDS", + "LEUGH", "LEVAS", "LEVEE", "LEVES", "LEVIN", "LEVIS", "LEWIS", "LEXES", "LEXIS", "LEZES", "LEZZA", "LEZZY", + "LIANA", "LIANE", "LIANG", "LIARD", "LIARS", "LIART", "LIBER", "LIBRA", "LIBRI", "LICHI", "LICHT", "LICIT", + "LICKS", "LIDAR", "LIDOS", "LIEFS", "LIENS", "LIERS", "LIEUS", "LIEVE", "LIFER", "LIFES", "LIFTS", "LIGAN", + "LIGER", "LIGGE", "LIGNE", "LIKED", "LIKER", "LIKES", "LIKIN", "LILLS", "LILOS", "LILTS", "LIMAN", "LIMAS", + "LIMAX", "LIMBA", "LIMBI", "LIMBS", "LIMBY", "LIMED", "LIMEN", "LIMES", "LIMEY", "LIMMA", "LIMNS", "LIMOS", + "LIMPA", "LIMPS", "LINAC", "LINCH", "LINDS", "LINDY", "LINED", "LINES", "LINEY", "LINGA", "LINGS", "LINGY", + "LININ", "LINKS", "LINKY", "LINNS", "LINNY", "LINOS", "LINTS", "LINTY", "LINUM", "LINUX", "LIONS", "LIPAS", + "LIPES", "LIPIN", "LIPOS", "LIPPY", "LIRAS", "LIRKS", "LIROT", "LISKS", "LISLE", "LISPS", "LISTS", "LITAI", + "LITAS", "LITED", "LITER", "LITES", "LITHO", "LITHS", "LITRE", "LIVED", "LIVEN", "LIVES", "LIVOR", "LIVRE", + "LLANO", "LOACH", "LOADS", "LOAFS", "LOAMS", "LOANS", "LOAST", "LOAVE", "LOBAR", "LOBED", "LOBES", "LOBOS", + "LOBUS", "LOCHE", "LOCHS", "LOCIE", "LOCIS", "LOCKS", "LOCOS", "LOCUM", "LODEN", "LODES", "LOESS", "LOFTS", + "LOGAN", "LOGES", "LOGGY", "LOGIA", "LOGIE", "LOGOI", "LOGON", "LOGOS", "LOHAN", "LOIDS", "LOINS", "LOIPE", + "LOIRS", "LOKES", "LOLLS", "LOLLY", "LOLOG", "LOMAS", "LOMED", "LOMES", "LONER", "LONGA", "LONGE", "LONGS", + "LOOBY", "LOOED", "LOOEY", "LOOFA", "LOOFS", "LOOIE", "LOOKS", "LOOKY", "LOOMS", "LOONS", "LOONY", "LOOPS", + "LOORD", "LOOTS", "LOPED", "LOPER", "LOPES", "LOPPY", "LORAL", "LORAN", "LORDS", "LORDY", "LOREL", "LORES", + "LORIC", "LORIS", "LOSED", "LOSEL", "LOSEN", "LOSES", "LOSSY", "LOTAH", "LOTAS", "LOTES", "LOTIC", "LOTOS", + "LOTSA", "LOTTA", "LOTTE", "LOTTO", "LOTUS", "LOUED", "LOUGH", "LOUIE", "LOUIS", "LOUMA", "LOUND", "LOUNS", + "LOUPE", "LOUPS", "LOURE", "LOURS", "LOURY", "LOUTS", "LOVAT", "LOVED", "LOVES", "LOVEY", "LOVIE", "LOWAN", + "LOWED", "LOWES", "LOWND", "LOWNE", "LOWNS", "LOWPS", "LOWRY", "LOWSE", "LOWTS", "LOXED", "LOXES", "LOZEN", + "LUACH", "LUAUS", "LUBED", "LUBES", "LUBRA", "LUCES", "LUCKS", "LUCRE", "LUDES", "LUDIC", "LUDOS", "LUFFA", + "LUFFS", "LUGED", "LUGER", "LUGES", "LULLS", "LULUS", "LUMAS", "LUMBI", "LUMME", "LUMMY", "LUMPS", "LUNAS", + "LUNES", "LUNET", "LUNGI", "LUNGS", "LUNKS", "LUNTS", "LUPIN", "LURED", "LURER", "LURES", "LUREX", "LURGI", + "LURGY", "LURKS", "LURRY", "LURVE", "LUSER", "LUSHY", "LUSKS", "LUSTS", "LUSUS", "LUTEA", "LUTED", "LUTER", + "LUTES", "LUVVY", "LUXED", "LUXER", "LUXES", "LWEIS", "LYAMS", "LYARD", "LYART", "LYASE", "LYCEA", "LYCEE", + "LYCRA", "LYMES", "LYNCH", "LYNES", "LYRES", "LYSED", "LYSES", "LYSIN", "LYSIS", "LYSOL", "LYSSA", "LYTED", + "LYTES", "LYTHE", "LYTIC", "LYTTA", "MAAED", "MAARE", "MAARS", "MABES", "MACAS", "MACED", "MACER", "MACES", + "MACHE", "MACHI", "MACHS", "MACKS", "MACLE", "MACON", "MADGE", "MADID", "MADRE", "MAERL", "MAFIC", "MAGES", + "MAGGS", "MAGOT", "MAGUS", "MAHOE", "MAHUA", "MAHWA", "MAIDS", "MAIKO", "MAIKS", "MAILE", "MAILL", "MAILS", + "MAIMS", "MAINS", "MAIRE", "MAIRS", "MAISE", "MAIST", "MAKAR", "MAKES", "MAKIS", "MAKOS", "MALAM", "MALAR", + "MALAS", "MALAX", "MALES", "MALIC", "MALIK", "MALIS", "MALLS", "MALMS", "MALMY", "MALTS", "MALTY", "MALUS", + "MALVA", "MALWA", "MAMAS", "MAMBA", "MAMEE", "MAMEY", "MAMIE", "MANAS", "MANAT", "MANDI", "MANEB", "MANED", + "MANEH", "MANES", "MANET", "MANGS", "MANIS", "MANKY", "MANNA", "MANOS", "MANSE", "MANTA", "MANTO", "MANTY", + "MANUL", "MANUS", "MAPAU", "MAQUI", "MARAE", "MARAH", "MARAS", "MARCS", "MARDY", "MARES", "MARGE", "MARGS", + "MARIA", "MARID", "MARKA", "MARKS", "MARLE", "MARLS", "MARLY", "MARMS", "MARON", "MAROR", "MARRA", "MARRI", + "MARSE", "MARTS", "MARVY", "MASAS", "MASED", "MASER", "MASES", "MASHY", "MASKS", "MASSA", "MASSY", "MASTS", + "MASTY", "MASUS", "MATAI", "MATED", "MATER", "MATES", "MATHS", "MATIN", "MATLO", "MATTE", "MATTS", "MATZA", + "MATZO", "MAUBY", "MAUDS", "MAULS", "MAUND", "MAURI", "MAUSY", "MAUTS", "MAUZY", "MAVEN", "MAVIE", "MAVIN", + "MAVIS", "MAWED", "MAWKS", "MAWKY", "MAWNS", "MAWRS", "MAXED", "MAXES", "MAXIS", "MAYAN", "MAYAS", "MAYED", + "MAYOS", "MAYST", "MAZED", "MAZER", "MAZES", "MAZEY", "MAZUT", "MBIRA", "MEADS", "MEALS", "MEANE", "MEANS", + "MEANY", "MEARE", "MEASE", "MEATH", "MEATS", "MEBOS", "MECHS", "MECKS", "MEDII", "MEDLE", "MEEDS", "MEERS", + "MEETS", "MEFFS", "MEINS", "MEINT", "MEINY", "MEITH", "MEKKA", "MELAS", "MELBA", "MELDS", "MELIC", "MELIK", + "MELLS", "MELTS", "MELTY", "MEMES", "MEMOS", "MENAD", "MENDS", "MENED", "MENES", "MENGE", "MENGS", "MENSA", + "MENSE", "MENSH", "MENTA", "MENTO", "MENUS", "MEOUS", "MEOWS", "MERCH", "MERCS", "MERDE", "MERED", "MEREL", + "MERER", "MERES", "MERIL", "MERIS", "MERKS", "MERLE", "MERLS", "MERSE", "MESAL", "MESAS", "MESEL", "MESES", + "MESHY", "MESIC", "MESNE", "MESON", "MESSY", "MESTO", "METED", "METES", "METHO", "METHS", "METIC", "METIF", + "METIS", "METOL", "METRE", "MEUSE", "MEVED", "MEVES", "MEWED", "MEWLS", "MEYNT", "MEZES", "MEZZE", "MEZZO", + "MHORR", "MIAOU", "MIAOW", "MIASM", "MIAUL", "MICAS", "MICHE", "MICHT", "MICKS", "MICKY", "MICOS", "MICRA", + "MIDDY", "MIDGY", "MIDIS", "MIENS", "MIEVE", "MIFFS", "MIFFY", "MIFTY", "MIGGS", "MIHAS", "MIHIS", "MIKED", + "MIKES", "MIKRA", "MIKVA", "MILCH", "MILDS", "MILER", "MILES", "MILFS", "MILIA", "MILKO", "MILKS", "MILLE", + "MILLS", "MILOR", "MILOS", "MILPA", "MILTS", "MILTY", "MILTZ", "MIMED", "MIMEO", "MIMER", "MIMES", "MIMSY", + "MINAE", "MINAR", "MINAS", "MINCY", "MINDS", "MINED", "MINES", "MINGE", "MINGS", "MINGY", "MINIS", "MINKE", + "MINKS", "MINNY", "MINOS", "MINTS", "MIRED", "MIRES", "MIREX", "MIRID", "MIRIN", "MIRKS", "MIRKY", "MIRLY", + "MIROS", "MIRVS", "MIRZA", "MISCH", "MISDO", "MISES", "MISGO", "MISOS", "MISSA", "MISTS", "MISTY", "MITCH", + "MITER", "MITES", "MITIS", "MITRE", "MITTS", "MIXED", "MIXEN", "MIXER", "MIXES", "MIXTE", "MIXUP", "MIZEN", + "MIZZY", "MNEME", "MOANS", "MOATS", "MOBBY", "MOBES", "MOBEY", "MOBIE", "MOBLE", "MOCHI", "MOCHS", "MOCHY", + "MOCKS", "MODER", "MODES", "MODGE", "MODII", "MODUS", "MOERS", "MOFOS", "MOGGY", "MOHEL", "MOHOS", "MOHRS", + "MOHUA", "MOHUR", "MOILE", "MOILS", "MOIRA", "MOIRE", "MOITS", "MOJOS", "MOKES", "MOKIS", "MOKOS", "MOLAL", + "MOLAS", "MOLDS", "MOLED", "MOLES", "MOLLA", "MOLLS", "MOLLY", "MOLTO", "MOLTS", "MOLYS", "MOMES", "MOMMA", + "MOMMY", "MOMUS", "MONAD", "MONAL", "MONAS", "MONDE", "MONDO", "MONER", "MONGO", "MONGS", "MONIC", "MONIE", + "MONKS", "MONOS", "MONTE", "MONTY", "MOOBS", "MOOCH", "MOODS", "MOOED", "MOOKS", "MOOLA", "MOOLI", "MOOLS", + "MOOLY", "MOONG", "MOONS", "MOONY", "MOOPS", "MOORS", "MOORY", "MOOTS", "MOOVE", "MOPED", "MOPER", "MOPES", + "MOPEY", "MOPPY", "MOPSY", "MOPUS", "MORAE", "MORAS", "MORAT", "MORAY", "MOREL", "MORES", "MORIA", "MORNE", + "MORNS", "MORRA", "MORRO", "MORSE", "MORTS", "MOSED", "MOSES", "MOSEY", "MOSKS", "MOSSO", "MOSTE", "MOSTS", + "MOTED", "MOTEN", "MOTES", "MOTET", "MOTEY", "MOTHS", "MOTHY", "MOTIS", "MOTTE", "MOTTS", "MOTTY", "MOTUS", + "MOTZA", "MOUCH", "MOUES", "MOULD", "MOULS", "MOUPS", "MOUST", "MOUSY", "MOVED", "MOVES", "MOWAS", "MOWED", + "MOWRA", "MOXAS", "MOXIE", "MOYAS", "MOYLE", "MOYLS", "MOZED", "MOZES", "MOZOS", "MPRET", "MUCHO", "MUCIC", + "MUCID", "MUCIN", "MUCKS", "MUCOR", "MUCRO", "MUDGE", "MUDIR", "MUDRA", "MUFFS", "MUFTI", "MUGGA", "MUGGS", + "MUGGY", "MUHLY", "MUIDS", "MUILS", "MUIRS", "MUIST", "MUJIK", "MULCT", "MULED", "MULES", "MULEY", "MULGA", + "MULIE", "MULLA", "MULLS", "MULSE", "MULSH", "MUMMS", "MUMPS", "MUMSY", "MUMUS", "MUNGA", "MUNGE", "MUNGO", + "MUNGS", "MUNIS", "MUNTS", "MUNTU", "MUONS", "MURAS", "MURED", "MURES", "MUREX", "MURID", "MURKS", "MURLS", + "MURLY", "MURRA", "MURRE", "MURRI", "MURRS", "MURRY", "MURTI", "MURVA", "MUSAR", "MUSCA", "MUSED", "MUSER", + "MUSES", "MUSET", "MUSHA", "MUSIT", "MUSKS", "MUSOS", "MUSSE", "MUSSY", "MUSTH", "MUSTS", "MUTCH", "MUTED", + "MUTER", "MUTES", "MUTHA", "MUTIS", "MUTON", "MUTTS", "MUXED", "MUXES", "MUZAK", "MUZZY", "MVULE", "MYALL", + "MYLAR", "MYNAH", "MYNAS", "MYOID", "MYOMA", "MYOPE", "MYOPS", "MYOPY", "MYSID", "MYTHI", "MYTHS", "MYTHY", + "MYXOS", "MZEES", "NAAMS", "NAANS", "NABES", "NABIS", "NABKS", "NABLA", "NABOB", "NACHE", "NACHO", "NACRE", + "NADAS", "NAEVE", "NAEVI", "NAFFS", "NAGAS", "NAGGY", "NAGOR", "NAHAL", "NAIAD", "NAIFS", "NAIKS", "NAILS", + "NAIRA", "NAIRU", "NAKED", "NAKER", "NAKFA", "NALAS", "NALED", "NALLA", "NAMED", "NAMER", "NAMES", "NAMMA", + "NAMUS", "NANAS", "NANCE", "NANCY", "NANDU", "NANNA", "NANOS", "NANUA", "NAPAS", "NAPED", "NAPES", "NAPOO", + "NAPPA", "NAPPE", "NAPPY", "NARAS", "NARCO", "NARCS", "NARDS", "NARES", "NARIC", "NARIS", "NARKS", "NARKY", + "NARRE", "NASHI", "NATCH", "NATES", "NATIS", "NATTY", "NAUCH", "NAUNT", "NAVAR", "NAVES", "NAVEW", "NAVVY", + "NAWAB", "NAZES", "NAZIR", "NAZIS", "NDUJA", "NEAFE", "NEALS", "NEAPS", "NEARS", "NEATH", "NEATS", "NEBEK", + "NEBEL", "NECKS", "NEDDY", "NEEDS", "NEELD", "NEELE", "NEEMB", "NEEMS", "NEEPS", "NEESE", "NEEZE", "NEGRO", + "NEGUS", "NEIFS", "NEIST", "NEIVE", "NELIS", "NELLY", "NEMAS", "NEMNS", "NEMPT", "NENES", "NEONS", "NEPER", + "NEPIT", "NERAL", "NERDS", "NERKA", "NERKS", "NEROL", "NERTS", "NERTZ", "NERVY", "NESTS", "NETES", "NETOP", + "NETTS", "NETTY", "NEUKS", "NEUME", "NEUMS", "NEVEL", "NEVES", "NEVUS", "NEWBS", "NEWED", "NEWEL", "NEWIE", + "NEWSY", "NEWTS", "NEXTS", "NEXUS", "NGAIO", "NGANA", "NGATI", "NGOMA", "NGWEE", "NICAD", "NICHT", "NICKS", + "NICOL", "NIDAL", "NIDED", "NIDES", "NIDOR", "NIDUS", "NIEFS", "NIEVE", "NIFES", "NIFFS", "NIFFY", "NIFTY", + "NIGER", "NIGHS", "NIHIL", "NIKAB", "NIKAH", "NIKAU", "NILLS", "NIMBI", "NIMBS", "NIMPS", "NINER", "NINES", + "NINON", "NIPAS", "NIPPY", "NIQAB", "NIRLS", "NIRLY", "NISEI", "NISSE", "NISUS", "NITER", "NITES", "NITID", + "NITON", "NITRE", "NITRO", "NITRY", "NITTY", "NIVAL", "NIXED", "NIXER", "NIXES", "NIXIE", "NIZAM", "NKOSI", + "NOAHS", "NOBBY", "NOCKS", "NODAL", "NODDY", "NODES", "NODUS", "NOELS", "NOGGS", "NOHOW", "NOILS", "NOILY", + "NOINT", "NOIRS", "NOLES", "NOLLS", "NOLOS", "NOMAS", "NOMEN", "NOMES", "NOMIC", "NOMOI", "NOMOS", "NONAS", + "NONCE", "NONES", "NONET", "NONGS", "NONIS", "NONNY", "NONYL", "NOOBS", "NOOIT", "NOOKS", "NOOKY", "NOONS", + "NOOPS", "NOPAL", "NORIA", "NORIS", "NORKS", "NORMA", "NORMS", "NOSED", "NOSER", "NOSES", "NOTAL", "NOTED", + "NOTER", "NOTES", "NOTUM", "NOULD", "NOULE", "NOULS", "NOUNS", "NOUNY", "NOUPS", "NOVAE", "NOVAS", "NOVUM", + "NOWAY", "NOWED", "NOWLS", "NOWTS", "NOWTY", "NOXAL", "NOXES", "NOYAU", "NOYED", "NOYES", "NUBBY", "NUBIA", + "NUCHA", "NUDDY", "NUDER", "NUDES", "NUDIE", "NUDZH", "NUFFS", "NUGAE", "NUKED", "NUKES", "NULLA", "NULLS", + "NUMBS", "NUMEN", "NUMMY", "NUNNY", "NURDS", "NURDY", "NURLS", "NURRS", "NUTSO", "NUTSY", "NYAFF", "NYALA", + "NYING", "NYSSA", "OAKED", "OAKER", "OAKUM", "OARED", "OASES", "OASIS", "OASTS", "OATEN", "OATER", "OATHS", + "OAVES", "OBANG", "OBEAH", "OBELI", "OBEYS", "OBIAS", "OBIED", "OBIIT", "OBITS", "OBJET", "OBOES", "OBOLE", + "OBOLI", "OBOLS", "OCCAM", "OCHER", "OCHES", "OCHRE", "OCHRY", "OCKER", "OCREA", "OCTAD", "OCTAN", "OCTAS", + "OCTYL", "OCULI", "ODAHS", "ODALS", "ODEON", "ODEUM", "ODISM", "ODIST", "ODIUM", "ODORS", "ODOUR", "ODYLE", + "ODYLS", "OFAYS", "OFFED", "OFFIE", "OFLAG", "OFTER", "OGAMS", "OGEED", "OGEES", "OGGIN", "OGHAM", "OGIVE", + "OGLED", "OGLER", "OGLES", "OGMIC", "OGRES", "OHIAS", "OHING", "OHMIC", "OHONE", "OIDIA", "OILED", "OILER", + "OINKS", "OINTS", "OJIME", "OKAPI", "OKAYS", "OKEHS", "OKRAS", "OKTAS", "OLDIE", "OLEIC", "OLEIN", "OLENT", + "OLEOS", "OLEUM", "OLIOS", "OLLAS", "OLLAV", "OLLER", "OLLIE", "OLOGY", "OLPAE", "OLPES", "OMASA", "OMBER", + "OMBUS", "OMENS", "OMERS", "OMITS", "OMLAH", "OMOVS", "OMRAH", "ONCER", "ONCES", "ONCET", "ONCUS", "ONELY", + "ONERS", "ONERY", "ONIUM", "ONKUS", "ONLAY", "ONNED", "ONTIC", "OOBIT", "OOHED", "OOMPH", "OONTS", "OOPED", + "OORIE", "OOSES", "OOTID", "OOZED", "OOZES", "OPAHS", "OPALS", "OPENS", "OPEPE", "OPING", "OPPOS", "OPSIN", + "OPTED", "OPTER", "ORACH", "ORACY", "ORALS", "ORANG", "ORANT", "ORATE", "ORBED", "ORCAS", "ORCIN", "ORDOS", + "OREAD", "ORFES", "ORGIA", "ORGIC", "ORGUE", "ORIBI", "ORIEL", "ORIXA", "ORLES", "ORLON", "ORLOP", "ORMER", + "ORNIS", "ORPIN", "ORRIS", "ORTHO", "ORVAL", "ORZOS", "OSCAR", "OSHAC", "OSIER", "OSMIC", "OSMOL", "OSSIA", + "OSTIA", "OTAKU", "OTARY", "OTTAR", "OTTOS", "OUBIT", "OUCHT", "OUENS", "OUIJA", "OULKS", "OUMAS", "OUNDY", + "OUPAS", "OUPED", "OUPHE", "OUPHS", "OURIE", "OUSEL", "OUSTS", "OUTBY", "OUTED", "OUTRE", "OUTRO", "OUTTA", + "OUZEL", "OUZOS", "OVALS", "OVELS", "OVENS", "OVERS", "OVIST", "OVOLI", "OVOLO", "OVULE", "OWCHE", "OWIES", + "OWLED", "OWLER", "OWLET", "OWNED", "OWRES", "OWRIE", "OWSEN", "OXBOW", "OXERS", "OXEYE", "OXIDS", "OXIES", + "OXIME", "OXIMS", "OXLIP", "OXTER", "OYERS", "OZEKI", "OZZIE", "PAALS", "PAANS", "PACAS", "PACED", "PACER", + "PACES", "PACEY", "PACHA", "PACKS", "PACOS", "PACTA", "PACTS", "PADIS", "PADLE", "PADMA", "PADRE", "PADRI", + "PAEAN", "PAEDO", "PAEON", "PAGED", "PAGER", "PAGES", "PAGLE", "PAGOD", "PAGRI", "PAIKS", "PAILS", "PAINS", + "PAIRE", "PAIRS", "PAISA", "PAISE", "PAKKA", "PALAS", "PALAY", "PALEA", "PALED", "PALES", "PALET", "PALIS", + "PALKI", "PALLA", "PALLS", "PALLY", "PALMS", "PALMY", "PALPI", "PALPS", "PALSA", "PAMPA", "PANAX", "PANCE", + "PANDA", "PANDS", "PANDY", "PANED", "PANES", "PANGA", "PANGS", "PANIM", "PANKO", "PANNE", "PANNI", "PANTO", + "PANTS", "PANTY", "PAOLI", "PAOLO", "PAPAS", "PAPAW", "PAPES", "PAPPI", "PAPPY", "PARAE", "PARAS", "PARCH", + "PARDI", "PARDS", "PARDY", "PARED", "PAREN", "PAREO", "PARES", "PAREU", "PAREV", "PARGE", "PARGO", "PARIS", + "PARKI", "PARKS", "PARKY", "PARLE", "PARLY", "PARMA", "PAROL", "PARPS", "PARRA", "PARRS", "PARTI", "PARTS", + "PARVE", "PARVO", "PASEO", "PASES", "PASHA", "PASHM", "PASKA", "PASPY", "PASSE", "PASTS", "PATED", "PATEN", + "PATER", "PATES", "PATHS", "PATIN", "PATKA", "PATLY", "PATTE", "PATUS", "PAUAS", "PAULS", "PAVAN", "PAVED", + "PAVEN", "PAVER", "PAVES", "PAVID", "PAVIN", "PAVIS", "PAWAS", "PAWAW", "PAWED", "PAWER", "PAWKS", "PAWKY", + "PAWLS", "PAWNS", "PAXES", "PAYED", "PAYOR", "PAYSD", "PEAGE", "PEAGS", "PEAKS", "PEAKY", "PEALS", "PEANS", + "PEARE", "PEARS", "PEART", "PEASE", "PEATS", "PEATY", "PEAVY", "PEAZE", "PEBAS", "PECHS", "PECKE", "PECKS", + "PECKY", "PEDES", "PEDIS", "PEDRO", "PEECE", "PEEKS", "PEELS", "PEENS", "PEEOY", "PEEPE", "PEEPS", "PEERS", + "PEERY", "PEEVE", "PEGGY", "PEGHS", "PEINS", "PEISE", "PEIZE", "PEKAN", "PEKES", "PEKIN", "PEKOE", "PELAS", + "PELAU", "PELES", "PELFS", "PELLS", "PELMA", "PELON", "PELTA", "PELTS", "PENDS", "PENDU", "PENED", "PENES", + "PENGO", "PENIE", "PENIS", "PENKS", "PENNA", "PENNI", "PENTS", "PEONS", "PEONY", "PEPLA", "PEPOS", "PEPPY", + "PEPSI", "PERAI", "PERCE", "PERCS", "PERDU", "PERDY", "PEREA", "PERES", "PERIS", "PERKS", "PERMS", "PERNS", + "PEROG", "PERPS", "PERRY", "PERSE", "PERST", "PERTS", "PERVE", "PERVO", "PERVS", "PERVY", "PESOS", "PESTS", + "PESTY", "PETAR", "PETER", "PETIT", "PETRE", "PETRI", "PETTI", "PETTO", "PEWEE", "PEWIT", "PEYSE", "PHAGE", + "PHANG", "PHARE", "PHARM", "PHEER", "PHENE", "PHEON", "PHESE", "PHIAL", "PHISH", "PHIZZ", "PHLOX", "PHOCA", + "PHONO", "PHONS", "PHOTS", "PHPHT", "PHUTS", "PHYLA", "PHYLE", "PIANI", "PIANS", "PIBAL", "PICAL", "PICAS", + "PICCY", "PICKS", "PICOT", "PICRA", "PICUL", "PIEND", "PIERS", "PIERT", "PIETA", "PIETS", "PIEZO", "PIGHT", + "PIGMY", "PIING", "PIKAS", "PIKAU", "PIKED", "PIKER", "PIKES", "PIKEY", "PIKIS", "PIKUL", "PILAE", "PILAF", + "PILAO", "PILAR", "PILAU", "PILAW", "PILCH", "PILEA", "PILED", "PILEI", "PILER", "PILES", "PILIS", "PILLS", + "PILOW", "PILUM", "PILUS", "PIMAS", "PIMPS", "PINAS", "PINED", "PINES", "PINGO", "PINGS", "PINKO", "PINKS", + "PINNA", "PINNY", "PINON", "PINOT", "PINTA", "PINTS", "PINUP", "PIONS", "PIONY", "PIOUS", "PIOYE", "PIOYS", + "PIPAL", "PIPAS", "PIPED", "PIPES", "PIPET", "PIPIS", "PIPIT", "PIPPY", "PIPUL", "PIRAI", "PIRLS", "PIRNS", + "PIROG", "PISCO", "PISES", "PISKY", "PISOS", "PISSY", "PISTE", "PITAS", "PITHS", "PITON", "PITOT", "PITTA", + "PIUMS", "PIXES", "PIZED", "PIZES", "PLAAS", "PLACK", "PLAGE", "PLANS", "PLAPS", "PLASH", "PLASM", "PLAST", + "PLATS", "PLATT", "PLATY", "PLAYA", "PLAYS", "PLEAS", "PLEBE", "PLEBS", "PLENA", "PLEON", "PLESH", "PLEWS", + "PLICA", "PLIES", "PLIMS", "PLING", "PLINK", "PLOAT", "PLODS", "PLONG", "PLONK", "PLOOK", "PLOPS", "PLOTS", + "PLOTZ", "PLOUK", "PLOWS", "PLOYE", "PLOYS", "PLUES", "PLUFF", "PLUGS", "PLUMS", "PLUMY", "PLUOT", "PLUTO", + "PLYER", "POACH", "POAKA", "POAKE", "POBOY", "POCKS", "POCKY", "PODAL", "PODDY", "PODEX", "PODGE", "PODGY", + "PODIA", "POEMS", "POEPS", "POETS", "POGEY", "POGGE", "POGOS", "POHED", "POILU", "POIND", "POKAL", "POKED", + "POKES", "POKEY", "POKIE", "POLED", "POLER", "POLES", "POLEY", "POLIO", "POLIS", "POLJE", "POLKS", "POLLS", + "POLLY", "POLOS", "POLTS", "POLYS", "POMBE", "POMES", "POMMY", "POMOS", "POMPS", "PONCE", "PONCY", "PONDS", + "PONES", "PONEY", "PONGA", "PONGO", "PONGS", "PONGY", "PONKS", "PONTS", "PONTY", "PONZU", "POODS", "POOED", + "POOFS", "POOFY", "POOHS", "POOJA", "POOKA", "POOKS", "POOLS", "POONS", "POOPS", "POOPY", "POORI", "POORT", + "POOTS", "POOVE", "POOVY", "POPES", "POPPA", "POPSY", "PORAE", "PORAL", "PORED", "PORER", "PORES", "PORGE", + "PORGY", "PORIN", "PORKS", "PORKY", "PORNO", "PORNS", "PORNY", "PORTA", "PORTS", "PORTY", "POSED", "POSES", + "POSEY", "POSHO", "POSTS", "POTAE", "POTCH", "POTED", "POTES", "POTIN", "POTOO", "POTSY", "POTTO", "POTTS", + "POTTY", "POUFF", "POUFS", "POUKE", "POUKS", "POULE", "POULP", "POULT", "POUPE", "POUPT", "POURS", "POUTS", + "POWAN", "POWIN", "POWND", "POWNS", "POWNY", "POWRE", "POXED", "POXES", "POYNT", "POYOU", "POYSE", "POZZY", + "PRAAM", "PRADS", "PRAHU", "PRAMS", "PRANA", "PRANG", "PRAOS", "PRASE", "PRATE", "PRATS", "PRATT", "PRATY", + "PRAUS", "PRAYS", "PREDY", "PREED", "PREES", "PREIF", "PREMS", "PREMY", "PRENT", "PREON", "PREOP", "PREPS", + "PRESA", "PRESE", "PREST", "PREVE", "PREXY", "PREYS", "PRIAL", "PRICY", "PRIEF", "PRIER", "PRIES", "PRIGS", + "PRILL", "PRIMA", "PRIMI", "PRIMP", "PRIMS", "PRIMY", "PRINK", "PRION", "PRISE", "PRISS", "PROAS", "PROBS", + "PRODS", "PROEM", "PROFS", "PROGS", "PROIN", "PROKE", "PROLE", "PROLL", "PROMO", "PROMS", "PRONK", "PROPS", + "PRORE", "PROSO", "PROSS", "PROST", "PROSY", "PROTO", "PROUL", "PROWS", "PROYN", "PRUNT", "PRUTA", "PRYER", + "PRYSE", "PSEUD", "PSHAW", "PSION", "PSOAE", "PSOAI", "PSOAS", "PSORA", "PSYCH", "PSYOP", "PUBCO", "PUBES", + "PUBIS", "PUCAN", "PUCER", "PUCES", "PUCKA", "PUCKS", "PUDDY", "PUDGE", "PUDIC", "PUDOR", "PUDSY", "PUDUS", + "PUERS", "PUFFA", "PUFFS", "PUGGY", "PUGIL", "PUHAS", "PUJAH", "PUJAS", "PUKAS", "PUKED", "PUKER", "PUKES", + "PUKEY", "PUKKA", "PUKUS", "PULAO", "PULAS", "PULED", "PULER", "PULES", "PULIK", "PULIS", "PULKA", "PULKS", + "PULLI", "PULLS", "PULLY", "PULMO", "PULPS", "PULUS", "PUMAS", "PUMIE", "PUMPS", "PUNAS", "PUNCE", "PUNGA", + "PUNGS", "PUNJI", "PUNKA", "PUNKS", "PUNKY", "PUNNY", "PUNTO", "PUNTS", "PUNTY", "PUPAE", "PUPAL", "PUPAS", + "PUPUS", "PURDA", "PURED", "PURES", "PURIN", "PURIS", "PURLS", "PURPY", "PURRS", "PURSY", "PURTY", "PUSES", + "PUSLE", "PUSSY", "PUTID", "PUTON", "PUTTI", "PUTTO", "PUTTS", "PUZEL", "PWNED", "PYATS", "PYETS", "PYGAL", + "PYINS", "PYLON", "PYNED", "PYNES", "PYOID", "PYOTS", "PYRAL", "PYRAN", "PYRES", "PYREX", "PYRIC", "PYROS", + "PYXED", "PYXES", "PYXIE", "PYXIS", "PZAZZ", "QADIS", "QAIDS", "QAJAQ", "QANAT", "QAPIK", "QIBLA", "QOPHS", + "QORMA", "QUADS", "QUAFF", "QUAGS", "QUAIR", "QUAIS", "QUAKY", "QUALE", "QUANT", "QUARE", "QUASS", "QUATE", + "QUATS", "QUAYD", "QUAYS", "QUBIT", "QUEAN", "QUEME", "QUENA", "QUERN", "QUEYN", "QUEYS", "QUICH", "QUIDS", + "QUIFF", "QUIMS", "QUINA", "QUINE", "QUINO", "QUINS", "QUINT", "QUIPO", "QUIPS", "QUIPU", "QUIRE", "QUIRT", + "QUIST", "QUITS", "QUOAD", "QUODS", "QUOIF", "QUOIN", "QUOIT", "QUOLL", "QUONK", "QUOPS", "QURAN", "QURSH", + "QUYTE", "RABAT", "RABIC", "RABIS", "RACED", "RACES", "RACHE", "RACKS", "RACON", "RADGE", "RADIX", "RADON", + "RAFFS", "RAFTS", "RAGAS", "RAGDE", "RAGED", "RAGEE", "RAGER", "RAGES", "RAGGA", "RAGGS", "RAGGY", "RAGIS", + "RAGUS", "RAHED", "RAHUI", "RAIAS", "RAIDS", "RAIKS", "RAILE", "RAILS", "RAINE", "RAINS", "RAIRD", "RAITA", + "RAITS", "RAJAS", "RAJES", "RAKED", "RAKEE", "RAKER", "RAKES", "RAKIA", "RAKIS", "RAKUS", "RALES", "RAMAL", + "RAMEE", "RAMET", "RAMIE", "RAMIN", "RAMIS", "RAMMY", "RAMPS", "RAMUS", "RANAS", "RANCE", "RANDS", "RANEE", + "RANGA", "RANGI", "RANGS", "RANGY", "RANID", "RANIS", "RANKE", "RANKS", "RANTS", "RAPED", "RAPER", "RAPES", + "RAPHE", "RAPPE", "RARED", "RAREE", "RARES", "RARKS", "RASED", "RASER", "RASES", "RASPS", "RASSE", "RASTA", + "RATAL", "RATAN", "RATAS", "RATCH", "RATED", "RATEL", "RATER", "RATES", "RATHA", "RATHE", "RATHS", "RATOO", + "RATOS", "RATUS", "RAUNS", "RAUPO", "RAVED", "RAVEL", "RAVER", "RAVES", "RAVEY", "RAVIN", "RAWER", "RAWIN", + "RAWLY", "RAWNS", "RAXED", "RAXES", "RAYAH", "RAYAS", "RAYED", "RAYLE", "RAYNE", "RAZED", "RAZEE", "RAZER", + "RAZES", "RAZOO", "READD", "READS", "REAIS", "REAKS", "REALO", "REALS", "REAME", "REAMS", "REAMY", "REANS", + "REAPS", "REARS", "REAST", "REATA", "REATE", "REAVE", "REBBE", "REBEC", "REBID", "REBIT", "REBOP", "REBUY", + "RECAL", "RECCE", "RECCO", "RECCY", "RECIT", "RECKS", "RECON", "RECTA", "RECTI", "RECTO", "REDAN", "REDDS", + "REDDY", "REDED", "REDES", "REDIA", "REDID", "REDIP", "REDLY", "REDON", "REDOS", "REDOX", "REDRY", "REDUB", + "REDUX", "REDYE", "REECH", "REEDE", "REEDS", "REEFS", "REEFY", "REEKS", "REEKY", "REELS", "REENS", "REEST", + "REEVE", "REFED", "REFEL", "REFFO", "REFIS", "REFIX", "REFLY", "REFRY", "REGAR", "REGES", "REGGO", "REGIE", + "REGMA", "REGNA", "REGOS", "REGUR", "REHEM", "REIFS", "REIFY", "REIKI", "REIKS", "REINK", "REINS", "REIRD", + "REIST", "REIVE", "REJIG", "REJON", "REKED", "REKES", "REKEY", "RELET", "RELIE", "RELIT", "RELLO", "REMAN", + "REMAP", "REMEN", "REMET", "REMEX", "REMIX", "RENAY", "RENDS", "RENEY", "RENGA", "RENIG", "RENIN", "RENNE", + "RENOS", "RENTE", "RENTS", "REOIL", "REORG", "REPEG", "REPIN", "REPLA", "REPOS", "REPOT", "REPPS", "REPRO", + "RERAN", "RERIG", "RESAT", "RESAW", "RESAY", "RESEE", "RESES", "RESEW", "RESID", "RESIT", "RESOD", "RESOW", + "RESTO", "RESTS", "RESTY", "RESUS", "RETAG", "RETAX", "RETEM", "RETIA", "RETIE", "RETOX", "REVET", "REVIE", + "REWAN", "REWAX", "REWED", "REWET", "REWIN", "REWON", "REWTH", "REXES", "REZES", "RHEAS", "RHEME", "RHEUM", + "RHIES", "RHIME", "RHINE", "RHODY", "RHOMB", "RHONE", "RHUMB", "RHYNE", "RHYTA", "RIADS", "RIALS", "RIANT", + "RIATA", "RIBAS", "RIBBY", "RIBES", "RICED", "RICER", "RICES", "RICEY", "RICHT", "RICIN", "RICKS", "RIDES", + "RIDGY", "RIDIC", "RIELS", "RIEMS", "RIEVE", "RIFER", "RIFFS", "RIFTE", "RIFTS", "RIFTY", "RIGGS", "RIGOL", + "RILED", "RILES", "RILEY", "RILLE", "RILLS", "RIMAE", "RIMED", "RIMER", "RIMES", "RIMUS", "RINDS", "RINDY", + "RINES", "RINGS", "RINKS", "RIOJA", "RIOTS", "RIPED", "RIPES", "RIPPS", "RISES", "RISHI", "RISKS", "RISPS", + "RISUS", "RITES", "RITTS", "RITZY", "RIVAS", "RIVED", "RIVEL", "RIVEN", "RIVES", "RIYAL", "RIZAS", "ROADS", + "ROAMS", "ROANS", "ROARS", "ROARY", "ROATE", "ROBED", "ROBES", "ROBLE", "ROCKS", "RODED", "RODES", "ROGUY", + "ROHES", "ROIDS", "ROILS", "ROILY", "ROINS", "ROIST", "ROJAK", "ROJIS", "ROKED", "ROKER", "ROKES", "ROLAG", + "ROLES", "ROLFS", "ROLLS", "ROMAL", "ROMAN", "ROMEO", "ROMPS", "RONDE", "RONDO", "RONEO", "RONES", "RONIN", + "RONNE", "RONTE", "RONTS", "ROODS", "ROOFS", "ROOFY", "ROOKS", "ROOKY", "ROOMS", "ROONS", "ROOPS", "ROOPY", + "ROOSA", "ROOSE", "ROOTS", "ROOTY", "ROPED", "ROPER", "ROPES", "ROPEY", "ROQUE", "RORAL", "RORES", "RORIC", + "RORID", "RORIE", "RORTS", "RORTY", "ROSED", "ROSES", "ROSET", "ROSHI", "ROSIN", "ROSIT", "ROSTI", "ROSTS", + "ROTAL", "ROTAN", "ROTAS", "ROTCH", "ROTED", "ROTES", "ROTIS", "ROTLS", "ROTON", "ROTOS", "ROTTE", "ROUEN", + "ROUES", "ROULE", "ROULS", "ROUMS", "ROUPS", "ROUPY", "ROUST", "ROUTH", "ROUTS", "ROVED", "ROVEN", "ROVES", + "ROWAN", "ROWED", "ROWEL", "ROWEN", "ROWIE", "ROWME", "ROWND", "ROWTH", "ROWTS", "ROYNE", "ROYST", "ROZET", + "ROZIT", "RUANA", "RUBAI", "RUBBY", "RUBEL", "RUBES", "RUBIN", "RUBLE", "RUBLI", "RUBUS", "RUCHE", "RUCKS", + "RUDAS", "RUDDS", "RUDES", "RUDIE", "RUDIS", "RUEDA", "RUERS", "RUFFE", "RUFFS", "RUGAE", "RUGAL", "RUGGY", + "RUING", "RUINS", "RUKHS", "RULED", "RULES", "RUMAL", "RUMBO", "RUMEN", "RUMES", "RUMLY", "RUMMY", "RUMPO", + "RUMPS", "RUMPY", "RUNCH", "RUNDS", "RUNED", "RUNES", "RUNGS", "RUNIC", "RUNNY", "RUNTS", "RUNTY", "RUPIA", + "RURPS", "RURUS", "RUSAS", "RUSES", "RUSHY", "RUSKS", "RUSMA", "RUSSE", "RUSTS", "RUTHS", "RUTIN", "RUTTY", + "RYALS", "RYBAT", "RYKED", "RYKES", "RYMME", "RYNDS", "RYOTS", "RYPER", "SAAGS", "SABAL", "SABED", "SABER", + "SABES", "SABHA", "SABIN", "SABIR", "SABLE", "SABOT", "SABRA", "SABRE", "SACKS", "SACRA", "SADDO", "SADES", + "SADHE", "SADHU", "SADIS", "SADOS", "SADZA", "SAFED", "SAFES", "SAGAS", "SAGER", "SAGES", "SAGGY", "SAGOS", + "SAGUM", "SAHEB", "SAHIB", "SAICE", "SAICK", "SAICS", "SAIDS", "SAIGA", "SAILS", "SAIMS", "SAINE", "SAINS", + "SAIRS", "SAIST", "SAITH", "SAJOU", "SAKAI", "SAKER", "SAKES", "SAKIA", "SAKIS", "SAKTI", "SALAL", "SALAT", + "SALEP", "SALES", "SALET", "SALIC", "SALIX", "SALLE", "SALMI", "SALOL", "SALOP", "SALPA", "SALPS", "SALSE", + "SALTO", "SALTS", "SALUE", "SALUT", "SAMAN", "SAMAS", "SAMBA", "SAMBO", "SAMEK", "SAMEL", "SAMEN", "SAMES", + "SAMEY", "SAMFU", "SAMMY", "SAMPI", "SAMPS", "SANDS", "SANED", "SANES", "SANGA", "SANGH", "SANGO", "SANGS", + "SANKO", "SANSA", "SANTO", "SANTS", "SAOLA", "SAPAN", "SAPID", "SAPOR", "SARAN", "SARDS", "SARED", "SAREE", + "SARGE", "SARGO", "SARIN", "SARIS", "SARKS", "SARKY", "SAROD", "SAROS", "SARUS", "SASER", "SASIN", "SASSE", + "SATAI", "SATAY", "SATED", "SATEM", "SATES", "SATIS", "SAUBA", "SAUCH", "SAUGH", "SAULS", "SAULT", "SAUNT", + "SAURY", "SAUTS", "SAVED", "SAVER", "SAVES", "SAVEY", "SAVIN", "SAWAH", "SAWED", "SAWER", "SAXES", "SAYED", + "SAYER", "SAYID", "SAYNE", "SAYON", "SAYST", "SAZES", "SCABS", "SCADS", "SCAFF", "SCAGS", "SCAIL", "SCALA", + "SCALL", "SCAMS", "SCAND", "SCANS", "SCAPA", "SCAPE", "SCAPI", "SCARP", "SCARS", "SCART", "SCATH", "SCATS", + "SCATT", "SCAUD", "SCAUP", "SCAUR", "SCAWS", "SCEAT", "SCENA", "SCEND", "SCHAV", "SCHMO", "SCHUL", "SCHWA", + "SCLIM", "SCODY", "SCOGS", "SCOOG", "SCOOT", "SCOPA", "SCOPS", "SCOTS", "SCOUG", "SCOUP", "SCOWP", "SCOWS", + "SCRAB", "SCRAE", "SCRAG", "SCRAN", "SCRAT", "SCRAW", "SCRAY", "SCRIM", "SCRIP", "SCROB", "SCROD", "SCROG", + "SCROW", "SCUDI", "SCUDO", "SCUDS", "SCUFF", "SCUFT", "SCUGS", "SCULK", "SCULL", "SCULP", "SCULS", "SCUMS", + "SCUPS", "SCURF", "SCURS", "SCUSE", "SCUTA", "SCUTE", "SCUTS", "SCUZZ", "SCYES", "SDAYN", "SDEIN", "SEALS", + "SEAME", "SEAMS", "SEAMY", "SEANS", "SEARE", "SEARS", "SEASE", "SEATS", "SEAZE", "SEBUM", "SECCO", "SECHS", + "SECTS", "SEDER", "SEDES", "SEDGE", "SEDGY", "SEDUM", "SEEDS", "SEEKS", "SEELD", "SEELS", "SEELY", "SEEMS", + "SEEPS", "SEEPY", "SEERS", "SEFER", "SEGAR", "SEGNI", "SEGNO", "SEGOL", "SEGOS", "SEHRI", "SEIFS", "SEILS", + "SEINE", "SEIRS", "SEISE", "SEISM", "SEITY", "SEIZA", "SEKOS", "SEKTS", "SELAH", "SELES", "SELFS", "SELLA", + "SELLE", "SELLS", "SELVA", "SEMEE", "SEMES", "SEMIE", "SEMIS", "SENAS", "SENDS", "SENES", "SENGI", "SENNA", + "SENOR", "SENSA", "SENSI", "SENTE", "SENTI", "SENTS", "SENVY", "SENZA", "SEPAD", "SEPAL", "SEPIC", "SEPOY", + "SEPTA", "SEPTS", "SERAC", "SERAI", "SERAL", "SERED", "SERER", "SERES", "SERFS", "SERGE", "SERIC", "SERIN", + "SERKS", "SERON", "SEROW", "SERRA", "SERRE", "SERRS", "SERRY", "SERVO", "SESEY", "SESSA", "SETAE", "SETAL", + "SETON", "SETTS", "SEWAN", "SEWAR", "SEWED", "SEWEL", "SEWEN", "SEWIN", "SEXED", "SEXER", "SEXES", "SEXTO", + "SEXTS", "SEYEN", "SHADS", "SHAGS", "SHAHS", "SHAKO", "SHAKT", "SHALM", "SHALY", "SHAMA", "SHAMS", "SHAND", + "SHANS", "SHAPS", "SHARN", "SHASH", "SHAUL", "SHAWM", "SHAWN", "SHAWS", "SHAYA", "SHAYS", "SHCHI", "SHEAF", + "SHEAL", "SHEAS", "SHEDS", "SHEEL", "SHEND", "SHENT", "SHEOL", "SHERD", "SHERE", "SHERO", "SHETS", "SHEVA", + "SHEWN", "SHEWS", "SHIAI", "SHIEL", "SHIER", "SHIES", "SHILL", "SHILY", "SHIMS", "SHINS", "SHIPS", "SHIRR", + "SHIRS", "SHISH", "SHISO", "SHIST", "SHITE", "SHITS", "SHIUR", "SHIVA", "SHIVE", "SHIVS", "SHLEP", "SHLUB", + "SHMEK", "SHMOE", "SHOAT", "SHOED", "SHOER", "SHOES", "SHOGI", "SHOGS", "SHOJI", "SHOJO", "SHOLA", "SHOOL", + "SHOON", "SHOOS", "SHOPE", "SHOPS", "SHORL", "SHOTE", "SHOTS", "SHOTT", "SHOWD", "SHOWS", "SHOYU", "SHRED", + "SHRIS", "SHROW", "SHTIK", "SHTUM", "SHTUP", "SHULE", "SHULN", "SHULS", "SHUNS", "SHURA", "SHUTE", "SHUTS", + "SHWAS", "SHYER", "SIALS", "SIBBS", "SIBYL", "SICES", "SICHT", "SICKO", "SICKS", "SICKY", "SIDAS", "SIDED", + "SIDER", "SIDES", "SIDHA", "SIDHE", "SIDLE", "SIELD", "SIENS", "SIENT", "SIETH", "SIEUR", "SIFTS", "SIGHS", + "SIGIL", "SIGLA", "SIGNA", "SIGNS", "SIJOS", "SIKAS", "SIKER", "SIKES", "SILDS", "SILED", "SILEN", "SILER", + "SILES", "SILEX", "SILKS", "SILLS", "SILOS", "SILTS", "SILTY", "SILVA", "SIMAR", "SIMAS", "SIMBA", "SIMIS", + "SIMPS", "SIMUL", "SINDS", "SINED", "SINES", "SINGS", "SINHS", "SINKS", "SINKY", "SINUS", "SIPED", "SIPES", + "SIPPY", "SIRED", "SIREE", "SIRES", "SIRIH", "SIRIS", "SIROC", "SIRRA", "SIRUP", "SISAL", "SISES", "SISTA", + "SISTS", "SITAR", "SITED", "SITES", "SITHE", "SITKA", "SITUP", "SITUS", "SIVER", "SIXER", "SIXES", "SIXMO", + "SIXTE", "SIZAR", "SIZED", "SIZEL", "SIZER", "SIZES", "SKAGS", "SKAIL", "SKALD", "SKANK", "SKART", "SKATS", + "SKATT", "SKAWS", "SKEAN", "SKEAR", "SKEDS", "SKEED", "SKEEF", "SKEEN", "SKEER", "SKEES", "SKEET", "SKEGG", + "SKEGS", "SKEIN", "SKELF", "SKELL", "SKELM", "SKELP", "SKENE", "SKENS", "SKEOS", "SKEPS", "SKERS", "SKETS", + "SKEWS", "SKIDS", "SKIED", "SKIES", "SKIEY", "SKIMO", "SKIMS", "SKINK", "SKINS", "SKINT", "SKIOS", "SKIPS", + "SKIRL", "SKIRR", "SKITE", "SKITS", "SKIVE", "SKIVY", "SKLIM", "SKOAL", "SKODY", "SKOFF", "SKOGS", "SKOLS", + "SKOOL", "SKORT", "SKOSH", "SKRAN", "SKRIK", "SKUAS", "SKUGS", "SKYED", "SKYER", "SKYEY", "SKYFS", "SKYRE", + "SKYRS", "SKYTE", "SLABS", "SLADE", "SLAES", "SLAGS", "SLAID", "SLAKE", "SLAMS", "SLANE", "SLANK", "SLAPS", + "SLART", "SLATS", "SLATY", "SLAVE", "SLAWS", "SLAYS", "SLEBS", "SLEDS", "SLEER", "SLEWS", "SLEYS", "SLIER", + "SLILY", "SLIMS", "SLIPE", "SLIPS", "SLIPT", "SLISH", "SLITS", "SLIVE", "SLOAN", "SLOBS", "SLOES", "SLOGS", + "SLOID", "SLOJD", "SLOMO", "SLOOM", "SLOOT", "SLOPS", "SLOPY", "SLORM", "SLOTS", "SLOVE", "SLOWS", "SLOYD", + "SLUBB", "SLUBS", "SLUED", "SLUES", "SLUFF", "SLUGS", "SLUIT", "SLUMS", "SLURB", "SLURS", "SLUSE", "SLUTS", + "SLYER", "SLYPE", "SMAAK", "SMAIK", "SMALM", "SMALT", "SMARM", "SMAZE", "SMEEK", "SMEES", "SMEIK", "SMEKE", + "SMERK", "SMEWS", "SMIRR", "SMIRS", "SMITS", "SMOGS", "SMOKO", "SMOLT", "SMOOR", "SMOOT", "SMORE", "SMORG", + "SMOUT", "SMOWT", "SMUGS", "SMURS", "SMUSH", "SMUTS", "SNABS", "SNAFU", "SNAGS", "SNAPS", "SNARF", "SNARK", + "SNARS", "SNARY", "SNASH", "SNATH", "SNAWS", "SNEAD", "SNEAP", "SNEBS", "SNECK", "SNEDS", "SNEED", "SNEES", + "SNELL", "SNIBS", "SNICK", "SNIES", "SNIFT", "SNIGS", "SNIPS", "SNIPY", "SNIRT", "SNITS", "SNOBS", "SNODS", + "SNOEK", "SNOEP", "SNOGS", "SNOKE", "SNOOD", "SNOOK", "SNOOL", "SNOOT", "SNOTS", "SNOWK", "SNOWS", "SNUBS", + "SNUGS", "SNUSH", "SNYES", "SOAKS", "SOAPS", "SOARE", "SOARS", "SOAVE", "SOBAS", "SOCAS", "SOCES", "SOCKO", + "SOCKS", "SOCLE", "SODAS", "SODDY", "SODIC", "SODOM", "SOFAR", "SOFAS", "SOFTA", "SOFTS", "SOFTY", "SOGER", + "SOHUR", "SOILS", "SOILY", "SOJAS", "SOJUS", "SOKAH", "SOKEN", "SOKES", "SOKOL", "SOLAH", "SOLAN", "SOLAS", + "SOLDE", "SOLDI", "SOLDO", "SOLDS", "SOLED", "SOLEI", "SOLER", "SOLES", "SOLON", "SOLOS", "SOLUM", "SOLUS", + "SOMAN", "SOMAS", "SONCE", "SONDE", "SONES", "SONGS", "SONLY", "SONNE", "SONNY", "SONSE", "SONSY", "SOOEY", + "SOOKS", "SOOKY", "SOOLE", "SOOLS", "SOOMS", "SOOPS", "SOOTE", "SOOTS", "SOPHS", "SOPHY", "SOPOR", "SOPPY", + "SOPRA", "SORAL", "SORAS", "SORBO", "SORBS", "SORDA", "SORDO", "SORDS", "SORED", "SOREE", "SOREL", "SORER", + "SORES", "SOREX", "SORGO", "SORNS", "SORRA", "SORTA", "SORTS", "SORUS", "SOTHS", "SOTOL", "SOUCE", "SOUCT", + "SOUGH", "SOUKS", "SOULS", "SOUMS", "SOUPS", "SOUPY", "SOURS", "SOUSE", "SOUTS", "SOWAR", "SOWCE", "SOWED", + "SOWFF", "SOWFS", "SOWLE", "SOWLS", "SOWMS", "SOWND", "SOWNE", "SOWPS", "SOWSE", "SOWTH", "SOYAS", "SOYLE", + "SOYUZ", "SOZIN", "SPACY", "SPADO", "SPAED", "SPAER", "SPAES", "SPAGS", "SPAHI", "SPAIL", "SPAIN", "SPAIT", + "SPAKE", "SPALD", "SPALE", "SPALL", "SPALT", "SPAMS", "SPANE", "SPANG", "SPANS", "SPARD", "SPARS", "SPART", + "SPATE", "SPATS", "SPAUL", "SPAWL", "SPAWS", "SPAYD", "SPAYS", "SPAZA", "SPAZZ", "SPEAL", "SPEAN", "SPEAT", + "SPECS", "SPECT", "SPEEL", "SPEER", "SPEIL", "SPEIR", "SPEKS", "SPELD", "SPELK", "SPEOS", "SPETS", "SPEUG", + "SPEWS", "SPEWY", "SPIAL", "SPICA", "SPICK", "SPICS", "SPIDE", "SPIER", "SPIES", "SPIFF", "SPIFS", "SPIKS", + "SPILE", "SPIMS", "SPINA", "SPINK", "SPINS", "SPIRT", "SPIRY", "SPITS", "SPITZ", "SPIVS", "SPLAY", "SPLOG", + "SPODE", "SPODS", "SPOOM", "SPOOR", "SPOOT", "SPORK", "SPOSH", "SPOTS", "SPRAD", "SPRAG", "SPRAT", "SPRED", + "SPREW", "SPRIT", "SPROD", "SPROG", "SPRUE", "SPRUG", "SPUDS", "SPUED", "SPUER", "SPUES", "SPUGS", "SPULE", + "SPUME", "SPUMY", "SPURS", "SPUTA", "SPYAL", "SPYRE", "SQUAB", "SQUAW", "SQUEG", "SQUID", "SQUIT", "SQUIZ", + "STABS", "STADE", "STAGS", "STAGY", "STAIG", "STANE", "STANG", "STAPH", "STAPS", "STARN", "STARR", "STARS", + "STATS", "STAUN", "STAWS", "STAYS", "STEAN", "STEAR", "STEDD", "STEDE", "STEDS", "STEEK", "STEEM", "STEEN", + "STEIL", "STELA", "STELE", "STELL", "STEME", "STEMS", "STEND", "STENO", "STENS", "STENT", "STEPS", "STEPT", + "STERE", "STETS", "STEWS", "STEWY", "STEYS", "STICH", "STIED", "STIES", "STILB", "STILE", "STIME", "STIMS", + "STIMY", "STIPA", "STIPE", "STIRE", "STIRK", "STIRP", "STIRS", "STIVE", "STIVY", "STOAE", "STOAI", "STOAS", + "STOAT", "STOBS", "STOEP", "STOGY", "STOIT", "STOLN", "STOMA", "STOND", "STONG", "STONK", "STONN", "STOOK", + "STOOR", "STOPE", "STOPS", "STOPT", "STOSS", "STOTS", "STOTT", "STOUN", "STOUP", "STOUR", "STOWN", "STOWP", + "STOWS", "STRAD", "STRAE", "STRAG", "STRAK", "STREP", "STREW", "STRIA", "STRIG", "STRIM", "STROP", "STROW", + "STROY", "STRUM", "STUBS", "STUDE", "STUDS", "STULL", "STULM", "STUMM", "STUMS", "STUNS", "STUPA", "STUPE", + "STURE", "STURT", "STYED", "STYES", "STYLI", "STYLO", "STYME", "STYMY", "STYRE", "STYTE", "SUBAH", "SUBAS", + "SUBBY", "SUBER", "SUBHA", "SUCCI", "SUCKS", "SUCKY", "SUCRE", "SUDDS", "SUDOR", "SUDSY", "SUEDE", "SUENT", + "SUERS", "SUETE", "SUETS", "SUETY", "SUGAN", "SUGHS", "SUGOS", "SUHUR", "SUIDS", "SUINT", "SUITS", "SUJEE", + "SUKHS", "SUKUK", "SULCI", "SULFA", "SULFO", "SULKS", "SULPH", "SULUS", "SUMIS", "SUMMA", "SUMOS", "SUMPH", + "SUMPS", "SUNIS", "SUNKS", "SUNNA", "SUNNS", "SUNUP", "SUPES", "SUPRA", "SURAH", "SURAL", "SURAS", "SURAT", + "SURDS", "SURED", "SURES", "SURFS", "SURFY", "SURGY", "SURRA", "SUSED", "SUSES", "SUSUS", "SUTOR", "SUTRA", + "SUTTA", "SWABS", "SWACK", "SWADS", "SWAGE", "SWAGS", "SWAIL", "SWAIN", "SWALE", "SWALY", "SWAMY", "SWANG", + "SWANK", "SWANS", "SWAPS", "SWAPT", "SWARD", "SWARE", "SWARF", "SWART", "SWATS", "SWAYL", "SWAYS", "SWEAL", + "SWEDE", "SWEED", "SWEEL", "SWEER", "SWEES", "SWEIR", "SWELT", "SWERF", "SWEYS", "SWIES", "SWIGS", "SWILE", + "SWIMS", "SWINK", "SWIPE", "SWIRE", "SWISS", "SWITH", "SWITS", "SWIVE", "SWIZZ", "SWOBS", "SWOLE", "SWOLN", + "SWOPS", "SWOPT", "SWOTS", "SWOUN", "SYBBE", "SYBIL", "SYBOE", "SYBOW", "SYCEE", "SYCES", "SYCON", "SYENS", + "SYKER", "SYKES", "SYLIS", "SYLPH", "SYLVA", "SYMAR", "SYNCH", "SYNCS", "SYNDS", "SYNED", "SYNES", "SYNTH", + "SYPED", "SYPES", "SYPHS", "SYRAH", "SYREN", "SYSOP", "SYTHE", "SYVER", "TAALS", "TAATA", "TABER", "TABES", + "TABID", "TABIS", "TABLA", "TABOR", "TABUN", "TABUS", "TACAN", "TACES", "TACET", "TACHE", "TACHO", "TACHS", + "TACKS", "TACOS", "TACTS", "TAELS", "TAFIA", "TAGGY", "TAGMA", "TAHAS", "TAHRS", "TAIGA", "TAIGS", "TAIKO", + "TAILS", "TAINS", "TAIRA", "TAISH", "TAITS", "TAJES", "TAKAS", "TAKES", "TAKHI", "TAKIN", "TAKIS", "TAKKY", + "TALAK", "TALAQ", "TALAR", "TALAS", "TALCS", "TALCY", "TALEA", "TALER", "TALES", "TALKS", "TALKY", "TALLS", + "TALMA", "TALPA", "TALUK", "TALUS", "TAMAL", "TAMED", "TAMES", "TAMIN", "TAMIS", "TAMMY", "TAMPS", "TANAS", + "TANGA", "TANGI", "TANGS", "TANHS", "TANKA", "TANKS", "TANKY", "TANNA", "TANSY", "TANTI", "TANTO", "TANTY", + "TAPAS", "TAPED", "TAPEN", "TAPES", "TAPET", "TAPIS", "TAPPA", "TAPUS", "TARAS", "TARDO", "TARED", "TARES", + "TARGA", "TARGE", "TARNS", "TAROC", "TAROK", "TAROS", "TARPS", "TARRE", "TARRY", "TARSI", "TARTS", "TARTY", + "TASAR", "TASED", "TASER", "TASES", "TASKS", "TASSA", "TASSE", "TASSO", "TATAR", "TATER", "TATES", "TATHS", + "TATIE", "TATOU", "TATTS", "TATUS", "TAUBE", "TAULD", "TAUON", "TAUPE", "TAUTS", "TAVAH", "TAVAS", "TAVER", + "TAWAI", "TAWAS", "TAWED", "TAWER", "TAWIE", "TAWSE", "TAWTS", "TAXED", "TAXER", "TAXES", "TAXIS", "TAXOL", + "TAXON", "TAXOR", "TAXUS", "TAYRA", "TAZZA", "TAZZE", "TEADE", "TEADS", "TEAED", "TEAKS", "TEALS", "TEAMS", + "TEARS", "TEATS", "TEAZE", "TECHS", "TECHY", "TECTA", "TEELS", "TEEMS", "TEEND", "TEENE", "TEENS", "TEENY", + "TEERS", "TEFFS", "TEGGS", "TEGUA", "TEGUS", "TEHRS", "TEIID", "TEILS", "TEIND", "TEINS", "TELAE", "TELCO", + "TELES", "TELEX", "TELIA", "TELIC", "TELLS", "TELLY", "TELOI", "TELOS", "TEMED", "TEMES", "TEMPI", "TEMPS", + "TEMPT", "TEMSE", "TENCH", "TENDS", "TENDU", "TENES", "TENGE", "TENIA", "TENNE", "TENNO", "TENNY", "TENON", + "TENTS", "TENTY", "TENUE", "TEPAL", "TEPAS", "TEPOY", "TERAI", "TERAS", "TERCE", "TEREK", "TERES", "TERFE", + "TERFS", "TERGA", "TERMS", "TERNE", "TERNS", "TERRY", "TERTS", "TESLA", "TESTA", "TESTE", "TESTS", "TETES", + "TETHS", "TETRA", "TETRI", "TEUCH", "TEUGH", "TEWED", "TEWEL", "TEWIT", "TEXAS", "TEXES", "TEXTS", "THACK", + "THAGI", "THAIM", "THALE", "THALI", "THANA", "THANE", "THANG", "THANS", "THANX", "THARM", "THARS", "THAWS", + "THAWY", "THEBE", "THECA", "THEED", "THEEK", "THEES", "THEGN", "THEIC", "THEIN", "THELF", "THEMA", "THENS", + "THEOW", "THERM", "THESP", "THETE", "THEWS", "THEWY", "THIGS", "THILK", "THILL", "THINE", "THINS", "THIOL", + "THIRL", "THOFT", "THOLE", "THOLI", "THORO", "THORP", "THOUS", "THOWL", "THRAE", "THRAW", "THRID", "THRIP", + "THROE", "THUDS", "THUGS", "THUJA", "THUNK", "THURL", "THUYA", "THYMI", "THYMY", "TIANS", "TIARS", "TICAL", + "TICCA", "TICED", "TICES", "TICHY", "TICKS", "TICKY", "TIDDY", "TIDED", "TIDES", "TIERS", "TIFFS", "TIFOS", + "TIFTS", "TIGES", "TIGON", "TIKAS", "TIKES", "TIKIS", "TIKKA", "TILAK", "TILED", "TILER", "TILES", "TILLS", + "TILLY", "TILTH", "TILTS", "TIMBO", "TIMED", "TIMES", "TIMON", "TIMPS", "TINAS", "TINCT", "TINDS", "TINEA", + "TINED", "TINES", "TINGE", "TINGS", "TINKS", "TINNY", "TINTS", "TINTY", "TIPIS", "TIPPY", "TIRED", "TIRES", + "TIRLS", "TIROS", "TIRRS", "TITCH", "TITER", "TITIS", "TITRE", "TITTY", "TITUP", "TIYIN", "TIYNS", "TIZES", + "TIZZY", "TOADS", "TOADY", "TOAZE", "TOCKS", "TOCKY", "TOCOS", "TODDE", "TOEAS", "TOFFS", "TOFFY", "TOFTS", + "TOFUS", "TOGAE", "TOGAS", "TOGED", "TOGES", "TOGUE", "TOHOS", "TOILE", "TOILS", "TOING", "TOISE", "TOITS", + "TOKAY", "TOKED", "TOKER", "TOKES", "TOKOS", "TOLAN", "TOLAR", "TOLAS", "TOLED", "TOLES", "TOLLS", "TOLLY", + "TOLTS", "TOLUS", "TOLYL", "TOMAN", "TOMBS", "TOMES", "TOMIA", "TOMMY", "TOMOS", "TONDI", "TONDO", "TONED", + "TONER", "TONES", "TONEY", "TONGS", "TONKA", "TONKS", "TONNE", "TONUS", "TOOLS", "TOOMS", "TOONS", "TOOTS", + "TOPED", "TOPEE", "TOPEK", "TOPER", "TOPES", "TOPHE", "TOPHI", "TOPHS", "TOPIS", "TOPOI", "TOPOS", "TOPPY", + "TOQUE", "TORAH", "TORAN", "TORAS", "TORCS", "TORES", "TORIC", "TORII", "TOROS", "TOROT", "TORRS", "TORSE", + "TORSI", "TORSK", "TORTA", "TORTE", "TORTS", "TOSAS", "TOSED", "TOSES", "TOSHY", "TOSSY", "TOTED", "TOTER", + "TOTES", "TOTTY", "TOUKS", "TOUNS", "TOURS", "TOUSE", "TOUSY", "TOUTS", "TOUZE", "TOUZY", "TOWED", "TOWIE", + "TOWNS", "TOWNY", "TOWSE", "TOWSY", "TOWTS", "TOWZE", "TOWZY", "TOYED", "TOYER", "TOYON", "TOYOS", "TOZED", + "TOZES", "TOZIE", "TRABS", "TRADS", "TRAGI", "TRAIK", "TRAMS", "TRANK", "TRANQ", "TRANS", "TRANT", "TRAPE", + "TRAPS", "TRAPT", "TRASS", "TRATS", "TRATT", "TRAVE", "TRAYF", "TRAYS", "TRECK", "TREED", "TREEN", "TREES", + "TREFA", "TREIF", "TREKS", "TREMA", "TREMS", "TRESS", "TREST", "TRETS", "TREWS", "TREYF", "TREYS", "TRIAC", + "TRIDE", "TRIER", "TRIES", "TRIFF", "TRIGO", "TRIGS", "TRIKE", "TRILD", "TRILL", "TRIMS", "TRINE", "TRINS", + "TRIOL", "TRIOR", "TRIOS", "TRIPS", "TRIPY", "TRIST", "TROAD", "TROAK", "TROAT", "TROCK", "TRODE", "TRODS", + "TROGS", "TROIS", "TROKE", "TROMP", "TRONA", "TRONC", "TRONE", "TRONK", "TRONS", "TROOZ", "TROTH", "TROTS", + "TROWS", "TROYS", "TRUED", "TRUES", "TRUGO", "TRUGS", "TRULL", "TRYER", "TRYKE", "TRYMA", "TRYPS", "TSADE", + "TSADI", "TSARS", "TSKED", "TSUBA", "TSUBO", "TUANS", "TUART", "TUATH", "TUBAE", "TUBAR", "TUBAS", "TUBBY", + "TUBED", "TUBES", "TUCKS", "TUFAS", "TUFFE", "TUFFS", "TUFTS", "TUFTY", "TUGRA", "TUILE", "TUINA", "TUISM", + "TUKTU", "TULES", "TULPA", "TULSI", "TUMID", "TUMMY", "TUMPS", "TUMPY", "TUNAS", "TUNDS", "TUNED", "TUNER", + "TUNES", "TUNGS", "TUNNY", "TUPEK", "TUPIK", "TUPLE", "TUQUE", "TURDS", "TURFS", "TURFY", "TURKS", "TURME", + "TURMS", "TURNS", "TURNT", "TURPS", "TURRS", "TUSHY", "TUSKS", "TUSKY", "TUTEE", "TUTTI", "TUTTY", "TUTUS", + "TUXES", "TUYER", "TWAES", "TWAIN", "TWALS", "TWANK", "TWATS", "TWAYS", "TWEEL", "TWEEN", "TWEEP", "TWEER", + "TWERK", "TWERP", "TWIER", "TWIGS", "TWILL", "TWILT", "TWINK", "TWINS", "TWINY", "TWIRE", "TWIRP", "TWITE", + "TWITS", "TWOER", "TWYER", "TYEES", "TYERS", "TYIYN", "TYKES", "TYLER", "TYMPS", "TYNDE", "TYNED", "TYNES", + "TYPAL", "TYPED", "TYPES", "TYPEY", "TYPIC", "TYPOS", "TYPPS", "TYPTO", "TYRAN", "TYRED", "TYRES", "TYROS", + "TYTHE", "TZARS", "UDALS", "UDONS", "UGALI", "UGGED", "UHLAN", "UHURU", "UKASE", "ULAMA", "ULANS", "ULEMA", + "ULMIN", "ULNAD", "ULNAE", "ULNAR", "ULNAS", "ULPAN", "ULVAS", "ULYIE", "ULZIE", "UMAMI", "UMBEL", "UMBER", + "UMBLE", "UMBOS", "UMBRE", "UMIAC", "UMIAK", "UMIAQ", "UMMAH", "UMMAS", "UMMED", "UMPED", "UMPHS", "UMPIE", + "UMPTY", "UMRAH", "UMRAS", "UNAIS", "UNAPT", "UNARM", "UNARY", "UNAUS", "UNBAG", "UNBAN", "UNBAR", "UNBED", + "UNBID", "UNBOX", "UNCAP", "UNCES", "UNCIA", "UNCOS", "UNCOY", "UNCUS", "UNDAM", "UNDEE", "UNDOS", "UNDUG", + "UNETH", "UNFIX", "UNGAG", "UNGET", "UNGOD", "UNGOT", "UNGUM", "UNHAT", "UNHIP", "UNICA", "UNITS", "UNJAM", + "UNKED", "UNKET", "UNKID", "UNLAW", "UNLAY", "UNLED", "UNLET", "UNLID", "UNMAN", "UNMEW", "UNMIX", "UNPAY", + "UNPEG", "UNPEN", "UNPIN", "UNRED", "UNRID", "UNRIG", "UNRIP", "UNSAW", "UNSAY", "UNSEE", "UNSEW", "UNSEX", + "UNSOD", "UNTAX", "UNTIN", "UNWET", "UNWIT", "UNWON", "UPBOW", "UPBYE", "UPDOS", "UPDRY", "UPEND", "UPJET", + "UPLAY", "UPLED", "UPLIT", "UPPED", "UPRAN", "UPRUN", "UPSEE", "UPSEY", "UPTAK", "UPTER", "UPTIE", "URAEI", + "URALI", "URAOS", "URARE", "URARI", "URASE", "URATE", "URBEX", "URBIA", "URDEE", "UREAL", "UREAS", "UREDO", + "UREIC", "URENA", "URENT", "URGED", "URGER", "URGES", "URIAL", "URITE", "URMAN", "URNAL", "URNED", "URPED", + "URSAE", "URSID", "URSON", "URUBU", "URVAS", "USERS", "USNEA", "USQUE", "USURE", "USURY", "UTERI", "UVEAL", + "UVEAS", "UVULA", "VACUA", "VADED", "VADES", "VAGAL", "VAGUS", "VAILS", "VAIRE", "VAIRS", "VAIRY", "VAKAS", + "VAKIL", "VALES", "VALIS", "VALSE", "VAMPS", "VAMPY", "VANDA", "VANED", "VANES", "VANGS", "VANTS", "VAPED", + "VAPER", "VAPES", "VARAN", "VARAS", "VARDY", "VAREC", "VARES", "VARIA", "VARIX", "VARNA", "VARUS", "VARVE", + "VASAL", "VASES", "VASTS", "VASTY", "VATIC", "VATUS", "VAUCH", "VAUTE", "VAUTS", "VAWTE", "VAXES", "VEALE", + "VEALS", "VEALY", "VEENA", "VEEPS", "VEERS", "VEERY", "VEGAS", "VEGES", "VEGIE", "VEGOS", "VEHME", "VEILS", + "VEILY", "VEINS", "VEINY", "VELAR", "VELDS", "VELDT", "VELES", "VELLS", "VELUM", "VENAE", "VENAL", "VENDS", + "VENDU", "VENEY", "VENGE", "VENIN", "VENTS", "VENUS", "VERBS", "VERRA", "VERRY", "VERST", "VERTS", "VERTU", + "VESPA", "VESTA", "VESTS", "VETCH", "VEXED", "VEXER", "VEXES", "VEXIL", "VEZIR", "VIALS", "VIAND", "VIBES", + "VIBEX", "VIBEY", "VICED", "VICES", "VICHY", "VIERS", "VIEWS", "VIEWY", "VIFDA", "VIFFS", "VIGAS", "VIGIA", + "VILDE", "VILER", "VILLI", "VILLS", "VIMEN", "VINAL", "VINAS", "VINCA", "VINED", "VINER", "VINES", "VINEW", + "VINIC", "VINOS", "VINTS", "VIOLD", "VIOLS", "VIRED", "VIREO", "VIRES", "VIRGA", "VIRGE", "VIRID", "VIRLS", + "VIRTU", "VISAS", "VISED", "VISES", "VISIE", "VISNE", "VISON", "VISTO", "VITAE", "VITAS", "VITEX", "VITRO", + "VITTA", "VIVAS", "VIVAT", "VIVDA", "VIVER", "VIVES", "VIZIR", "VIZOR", "VLEIS", "VLIES", "VLOGS", "VOARS", + "VOCAB", "VOCES", "VODDY", "VODOU", "VODUN", "VOEMA", "VOGIE", "VOIDS", "VOILE", "VOIPS", "VOLAE", "VOLAR", + "VOLED", "VOLES", "VOLET", "VOLKS", "VOLTA", "VOLTE", "VOLTI", "VOLTS", "VOLVA", "VOLVE", "VOMER", "VOTED", + "VOTES", "VOUGE", "VOULU", "VOWED", "VOWER", "VOXEL", "VOZHD", "VRAIC", "VRILS", "VROOM", "VROUS", "VROUW", + "VROWS", "VUGGS", "VUGGY", "VUGHS", "VUGHY", "VULGO", "VULNS", "VULVA", "VUTTY", "WAACS", "WACKE", "WACKO", + "WACKS", "WADDS", "WADDY", "WADED", "WADER", "WADES", "WADGE", "WADIS", "WADTS", "WAFFS", "WAFTS", "WAGED", + "WAGES", "WAGGA", "WAGYU", "WAHOO", "WAIDE", "WAIFS", "WAIFT", "WAILS", "WAINS", "WAIRS", "WAITE", "WAITS", + "WAKAS", "WAKED", "WAKEN", "WAKER", "WAKES", "WAKFS", "WALDO", "WALDS", "WALED", "WALER", "WALES", "WALIE", + "WALIS", "WALKS", "WALLA", "WALLS", "WALLY", "WALTY", "WAMED", "WAMES", "WAMUS", "WANDS", "WANED", "WANES", + "WANEY", "WANGS", "WANKS", "WANKY", "WANLE", "WANLY", "WANNA", "WANTS", "WANTY", "WANZE", "WAQFS", "WARBS", + "WARBY", "WARDS", "WARED", "WARES", "WAREZ", "WARKS", "WARMS", "WARNS", "WARPS", "WARRE", "WARST", "WARTS", + "WASES", "WASHY", "WASMS", "WASPS", "WASPY", "WASTS", "WATAP", "WATTS", "WAUFF", "WAUGH", "WAUKS", "WAULK", + "WAULS", "WAURS", "WAVED", "WAVES", "WAVEY", "WAWAS", "WAWES", "WAWLS", "WAXED", "WAXER", "WAXES", "WAYED", + "WAZIR", "WAZOO", "WEALD", "WEALS", "WEAMB", "WEANS", "WEARS", "WEBBY", "WEBER", "WECHT", "WEDEL", "WEDGY", + "WEEDS", "WEEKE", "WEEKS", "WEELS", "WEEMS", "WEENS", "WEENY", "WEEPS", "WEEPY", "WEEST", "WEETE", "WEETS", + "WEFTE", "WEFTS", "WEIDS", "WEILS", "WEIRS", "WEISE", "WEIZE", "WEKAS", "WELDS", "WELKE", "WELKS", "WELKT", + "WELLS", "WELLY", "WELTS", "WEMBS", "WENCH", "WENDS", "WENGE", "WENNY", "WENTS", "WEROS", "WERSH", "WESTS", + "WETAS", "WETLY", "WEXED", "WEXES", "WHAMO", "WHAMS", "WHANG", "WHAPS", "WHARE", "WHATA", "WHATS", "WHAUP", + "WHAUR", "WHEAL", "WHEAR", "WHEEN", "WHEEP", "WHEFT", "WHELK", "WHELM", "WHENS", "WHETS", "WHEWS", "WHEYS", + "WHIDS", "WHIFT", "WHIGS", "WHILK", "WHIMS", "WHINS", "WHIOS", "WHIPS", "WHIPT", "WHIRR", "WHIRS", "WHISH", + "WHISS", "WHIST", "WHITS", "WHITY", "WHIZZ", "WHOMP", "WHOOF", "WHOOT", "WHOPS", "WHORE", "WHORL", "WHORT", + "WHOSO", "WHOWS", "WHUMP", "WHUPS", "WHYDA", "WICCA", "WICKS", "WICKY", "WIDDY", "WIDES", "WIELS", "WIFED", + "WIFES", "WIFEY", "WIFIE", "WIFTY", "WIGAN", "WIGGA", "WIGGY", "WIKIS", "WILCO", "WILDS", "WILED", "WILES", + "WILGA", "WILIS", "WILJA", "WILLS", "WILTS", "WIMPS", "WINDS", "WINED", "WINES", "WINEY", "WINGE", "WINGS", + "WINGY", "WINKS", "WINNA", "WINNS", "WINOS", "WINZE", "WIPED", "WIPER", "WIPES", "WIRED", "WIRER", "WIRES", + "WIRRA", "WISED", "WISES", "WISHA", "WISHT", "WISPS", "WISTS", "WITAN", "WITED", "WITES", "WITHE", "WITHS", + "WITHY", "WIVED", "WIVER", "WIVES", "WIZEN", "WIZES", "WOADS", "WOALD", "WOCKS", "WODGE", "WOFUL", "WOJUS", + "WOKER", "WOKKA", "WOLDS", "WOLFS", "WOLLY", "WOLVE", "WOMBS", "WOMBY", "WOMYN", "WONGA", "WONGI", "WONKS", + "WONKY", "WONTS", "WOODS", "WOOED", "WOOFS", "WOOFY", "WOOLD", "WOOLS", "WOONS", "WOOPS", "WOOPY", "WOOSE", + "WOOSH", "WOOTZ", "WORDS", "WORKS", "WORMS", "WORMY", "WORTS", "WOWED", "WOWEE", "WOXEN", "WRANG", "WRAPS", + "WRAPT", "WRAST", "WRATE", "WRAWL", "WRENS", "WRICK", "WRIED", "WRIER", "WRIES", "WRITS", "WROKE", "WROOT", + "WROTH", "WRYER", "WUDDY", "WUDUS", "WULLS", "WURST", "WUSES", "WUSHU", "WUSSY", "WUXIA", "WYLED", "WYLES", + "WYNDS", "WYNNS", "WYTED", "WYTES", "XEBEC", "XENIA", "XENIC", "XENON", "XERIC", "XEROX", "XERUS", "XOANA", + "XRAYS", "XYLAN", "XYLEM", "XYLIC", "XYLOL", "XYLYL", "XYSTI", "XYSTS", "YAARS", "YABAS", "YABBA", "YABBY", + "YACCA", "YACKA", "YACKS", "YAFFS", "YAGER", "YAGES", "YAGIS", "YAHOO", "YAIRD", "YAKKA", "YAKOW", "YALES", + "YAMEN", "YAMPY", "YAMUN", "YANGS", "YANKS", "YAPOK", "YAPON", "YAPPS", "YAPPY", "YARAK", "YARCO", "YARDS", + "YARER", "YARFA", "YARKS", "YARNS", "YARRS", "YARTA", "YARTO", "YATES", "YAUDS", "YAULD", "YAUPS", "YAWED", + "YAWEY", "YAWLS", "YAWNS", "YAWNY", "YAWPS", "YBORE", "YCLAD", "YCLED", "YCOND", "YDRAD", "YDRED", "YEADS", + "YEAHS", "YEALM", "YEANS", "YEARD", "YEARS", "YECCH", "YECHS", "YECHY", "YEDES", "YEEDS", "YEESH", "YEGGS", + "YELKS", "YELLS", "YELMS", "YELPS", "YELTS", "YENTA", "YENTE", "YERBA", "YERDS", "YERKS", "YESES", "YESKS", + "YESTS", "YESTY", "YETIS", "YETTS", "YEUKS", "YEUKY", "YEVEN", "YEVES", "YEWEN", "YEXED", "YEXES", "YFERE", + "YIKED", "YIKES", "YILLS", "YINCE", "YIPES", "YIPPY", "YIRDS", "YIRKS", "YIRRS", "YIRTH", "YITES", "YITIE", + "YLEMS", "YLIKE", "YLKES", "YMOLT", "YMPES", "YOBBO", "YOBBY", "YOCKS", "YODEL", "YODHS", "YODLE", "YOGAS", + "YOGEE", "YOGHS", "YOGIC", "YOGIN", "YOGIS", "YOICK", "YOJAN", "YOKED", "YOKEL", "YOKER", "YOKES", "YOKUL", + "YOLKS", "YOLKY", "YOMIM", "YOMPS", "YONIC", "YONIS", "YONKS", "YOOFS", "YOOPS", "YORES", "YORKS", "YORPS", + "YOUKS", "YOURN", "YOURS", "YOURT", "YOUSE", "YOWED", "YOWES", "YOWIE", "YOWLS", "YOWZA", "YRAPT", "YRENT", + "YRIVD", "YRNEH", "YSAME", "YTOST", "YUANS", "YUCAS", "YUCCA", "YUCCH", "YUCKO", "YUCKS", "YUCKY", "YUFTS", + "YUGAS", "YUKED", "YUKES", "YUKKY", "YUKOS", "YULAN", "YULES", "YUMMO", "YUMMY", "YUMPS", "YUPON", "YUPPY", + "YURTA", "YURTS", "YUZUS", "ZABRA", "ZACKS", "ZAIDA", "ZAIDY", "ZAIRE", "ZAKAT", "ZAMAN", "ZAMBO", "ZAMIA", + "ZANJA", "ZANTE", "ZANZA", "ZANZE", "ZAPPY", "ZARFS", "ZARIS", "ZATIS", "ZAXES", "ZAYIN", "ZAZEN", "ZEALS", + "ZEBEC", "ZEBUB", "ZEBUS", "ZEDAS", "ZEINS", "ZENDO", "ZERDA", "ZERKS", "ZEROS", "ZESTS", "ZETAS", "ZEXES", + "ZEZES", "ZHOMO", "ZIBET", "ZIFFS", "ZIGAN", "ZILAS", "ZILCH", "ZILLA", "ZILLS", "ZIMBI", "ZIMBS", "ZINCO", + "ZINCS", "ZINCY", "ZINEB", "ZINES", "ZINGS", "ZINGY", "ZINKE", "ZINKY", "ZIPPO", "ZIPPY", "ZIRAM", "ZITIS", + "ZIZEL", "ZIZIT", "ZLOTE", "ZLOTY", "ZOAEA", "ZOBOS", "ZOBUS", "ZOCCO", "ZOEAE", "ZOEAL", "ZOEAS", "ZOISM", + "ZOIST", "ZOMBI", "ZONAE", "ZONDA", "ZONED", "ZONER", "ZONES", "ZONKS", "ZOOEA", "ZOOEY", "ZOOID", "ZOOKS", + "ZOOMS", "ZOONS", "ZOOTY", "ZOPPA", "ZOPPO", "ZORIL", "ZORIS", "ZORRO", "ZOUKS", "ZOWEE", "ZOWIE", "ZULUS", + "ZUPAN", "ZUPAS", "ZUPPA", "ZURFS", "ZUZIM", "ZYGAL", "ZYGON", "ZYMES", "ZYMIC", ] alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] -def most_used_letters(words=legal_list): +def most_used_letters(words=valid_list): ''' Outputs how many times each letter is used in the words array. ''' - dicto = {} + use_each_letter = {} for i in alphabet: count = 0 for word in words: @@ -955,31 +1103,32 @@ def most_used_letters(words=legal_list): if i.upper() == letter.upper(): count+=1 break - dicto[i] = count - dicto = dict(sorted(dicto.items(), key=lambda item: item[1], reverse=True)) - print("Letter | Usage") - print("--------------") - for k in dicto: - print(f"{k.upper()} | {dicto[k]}") + use_each_letter[i] = count + use_each_letter = dict(sorted(use_each_letter.items(), key=lambda item: item[1], reverse=True)) + print("Letter | Usage | Percent") + print("----------------------------") + for k in use_each_letter: + print(f"{k.upper()} | {use_each_letter[k]:5} | {round((100 * use_each_letter[k]) / len(words)):2}%") + return use_each_letter -def list_of_valid_words(letters, words=legal_list): +def list_of_valid_words(letters, words=valid_list): ''' Outputs the array of valid words that can be made with the combination of letters. ''' letters = sorted(letters) for i, letter in enumerate(letters): # Force all letters to be capitalized letters[i] = letter.upper() - legal_words = [] + valid_words = [] for word in words: valid_word = True for letter in word: if letter.upper() not in letters: valid_word = False break - if valid_word and word not in legal_words: - legal_words.append(word) - return legal_words + if valid_word and word not in valid_words: + valid_words.append(word) + return valid_words def rearrange_words_by_uniqueness(words): @@ -997,52 +1146,56 @@ def capitalize_all_and_remove_duplicates(arr): return arr +def clean_chars(string): + # D looks bad on the watch when d does not. + string = string.replace('D', 'd') + return string + + def print_valid_words(letters=alphabet): ''' Prints the array of valid words that the wordle_face.c can use ''' items_per_row = 9 - legal_words = list_of_valid_words(letters, legal_list) - legal_words = capitalize_all_and_remove_duplicates(legal_words) - random.shuffle(legal_words) + valid_words = list_of_valid_words(letters, valid_list) + valid_words = capitalize_all_and_remove_duplicates(valid_words) + random.shuffle(valid_words) # Just in case the watch's random function is too pseudo, better to shuffle th elist so it's less likely to always have the same starting letter - legal_words, num_uniq = rearrange_words_by_uniqueness(legal_words) + valid_words, num_uniq = rearrange_words_by_uniqueness(valid_words) print("static const char _valid_letters[] = {", end='') + letters = sorted(letters) for letter in letters[:-1]: - print(f"'{letter}', ", end='') + print(f"'{clean_chars(letter)}', ", end='') print(f"'{letters[-1]}'" + "};") print("") - print("// From: https://gist.github.com/shmookey/b28e342e1b1756c4700f42f17102c2ff") - print(f"// Number of words found: {len(legal_words)}") + print(f"// From: {source_link}") + print(f"// Number of words found: {len(valid_words)}") i = 0 - print("static const char _legal_words[][WORDLE_LENGTH + 1] = {") - while i < len(legal_words): + print("static const char _valid_words[][WORDLE_LENGTH + 1] = {") + while i < len(valid_words): print(" ", end='') - for _ in range(min(items_per_row, len(legal_words)-i)): - print(f'"{legal_words[i]}", ', end='') + for _ in range(min(items_per_row, len(valid_words)-i)): + print(f'"{clean_chars(valid_words[i])}", ', end='') i+=1 print('') - print("};\n") - - expanded_words = list_of_valid_words(letters, expanded_list) - expanded_words = [word for word in expanded_words if word not in legal_list] - expanded_words = capitalize_all_and_remove_duplicates(expanded_words) - - print("// These are words that'll never be used, but still need to be in the dictionary for guesses.") - print("// Top 100K most common words from Wiktionary https://gist.github.com/h3xx/1976236") - print(f"// Number of words found: {len(expanded_words)}") + possible_words = list_of_valid_words(letters, possible_list) + possible_words = [word for word in possible_words if word not in valid_list] + possible_words = capitalize_all_and_remove_duplicates(possible_words) + print("};") + print("\n// These are words that'll never be used, but still need to be in the dictionary for guesses.") + print("static const char _possible_words[][WORDLE_LENGTH + 1] = {") i = 0 - print("static const char _expanded_words[][WORDLE_LENGTH + 1] = {") - while i < len(expanded_words): + while i < len(possible_words): print(" ", end='') - for j in range(min(items_per_row, len(expanded_words)-i)): - print(f'"{expanded_words[i]}", ', end='') + for j in range(min(items_per_row, len(possible_words)-i)): + print(f'"{clean_chars(possible_words[i])}", ', end='') i+=1 print('') print("};") + print('') - print(f"\nstatic const uint16_t _num_unique_words = {num_uniq}; // The _legal_words array begins with this many words where each letter is different.") + print(f"\nstatic const uint16_t _num_unique_words = {num_uniq}; // The _valid_words array begins with this many words where each letter is different.") def get_sec_val_and_units(seconds): @@ -1059,28 +1212,33 @@ def get_sec_val_and_units(seconds): return f"{secs} sec" -def txt_of_all_letter_combos(num_letters_in_set): +def txt_of_all_letter_combos(num_letters_in_set, words=valid_list, min_letter_occ_percent_to_consider=10, txt_out=True): ''' Creates a txt file that shows every combination of letters and how many words their combo can make. num_letters_in_set - How many letters should be in each combination - ''' + ''' num_status_prints = 100 dict_combos_counts = {} print_iter = 0 prev = time.time() start = prev - letters_to_ignore = ['D','T'] # Don't diplay well on the watch - legal_letters = [item for item in alphabet if item not in letters_to_ignore] - print(f"Finding all {num_letters_in_set} letter combinations with the following letters: {legal_letters}") - all_combos = list(itertools.combinations(legal_letters, num_letters_in_set)) + letters_to_ignore = ['D','M','T','Y'] # Don't diplay well on the watch + letter_usage = most_used_letters(words=words) + for letter in letter_usage: + if (100 * letter_usage[letter])/len(words) < min_letter_occ_percent_to_consider: + letters_to_ignore.append(letter) + valid_letters = [item for item in alphabet if item not in letters_to_ignore] + num_letters_in_set = min(num_letters_in_set, len(valid_letters)) + print(f"Finding all {num_letters_in_set} letter combinations with the following letters: {valid_letters}") + all_combos = list(itertools.combinations(valid_letters, num_letters_in_set)) len_all_combos = len(all_combos) to_print = max(1, int(len_all_combos/ num_status_prints)) print(f"Amount of Combos: {len_all_combos}") estimated_prints = round(len_all_combos / to_print) for i, letters in enumerate(all_combos): letters = sorted(letters) - dict_combos_counts[repr(letters)] = len(list_of_valid_words(letters)) + dict_combos_counts[repr(letters)] = len(list_of_valid_words(letters, words=words)) print_iter+=1 if print_iter >= to_print: curr = time.time() @@ -1100,15 +1258,53 @@ def txt_of_all_letter_combos(num_letters_in_set): dict_combos_counts = dict(sorted(dict_combos_counts.items(), key=lambda item: item[1], reverse=True)) most_common_key = next(iter(dict_combos_counts)) - print(f"The Most Common Combo is: {most_common_key}") - print_valid_words(ast.literal_eval(most_common_key)) + print(f"The Most Common Combo is: {most_common_key} with {dict_combos_counts[most_common_key]} words.") + # print_valid_words(ast.literal_eval(most_common_key)) # Uncomment to display the text it creates - with open('output.txt', 'w') as file: - for key, value in dict_combos_counts.items(): - file.write(f'{key}: {value}\n') + if txt_out: + with open('output.txt', 'w') as file: + for key, value in dict_combos_counts.items(): + file.write(f'{key}: {value}\n') + return dict_combos_counts + + +def txt_of_all_letter_combos_differing_sizes(min=9, max=15, num_combos_print=20, words=valid_list): + max_each_count = {} + with open('output.txt', 'w') as file: + for x in range(max, min-1, -1): + dict_combos_counts = txt_of_all_letter_combos(x, words=words, min_letter_occ_percent_to_consider=10, txt_out=False) + file.write(f'{x} Letter Options\n') + for i, key in enumerate(dict_combos_counts): + file.write(f'{key}: {dict_combos_counts[key]}\n') + if i == 0: + max_each_count[x] = dict_combos_counts[key] + if i >= num_combos_print-1: + break + file.write('\n') + print("num_letters, count of words") + for key, item in max_each_count.items(): + print(f"{key}, {item}") + + +def location_of_letters(letters=alphabet, list=valid_list): + print(" 1 2 3 4 5 ") + print("-----------------------------------------") + letters = sorted(letters) + for letter in letters: + location = [0, 0, 0, 0, 0] + for word in list: + for i, char in enumerate(word): + if char.upper() == letter.upper(): + location[i]+=1 + location = [f"{round((100 * x) / sum(location)):2}%" for x in location] + print(f"{letter} : {location}") if __name__ == "__main__": + my_letters = ['A', 'C', 'E', 'G', 'H', 'I', 'L', 'N', 'O', 'P', 'R', 'S'] + #print(f"{len(list_of_valid_words(my_letters, valid_list))} Words can be made with {my_letters}") #most_used_letters() - print_valid_words(['A', 'C', 'E', 'I', 'L', 'N', 'O', 'P', 'R', 'S']) - #txt_of_all_letter_combos(10) \ No newline at end of file + #location_of_letters(my_letters) + print_valid_words(my_letters) + #txt_of_all_letter_combos_differing_sizes(max = 16, min=10) + #txt_of_all_letter_combos(14) \ No newline at end of file From 099f78443e2397db4b5ec39d7dbd27c910eee2aa Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Fri, 23 Aug 2024 16:48:09 -0400 Subject: [PATCH 078/161] Added ability to use T in Wordle --- .../watch_faces/complication/wordle_face.c | 462 ++++++++++-------- utils/wordle_face/wordle_list.py | 4 +- 2 files changed, 262 insertions(+), 204 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index f27ae4a..260860b 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -30,216 +30,274 @@ #endif -static const char _valid_letters[] = {'A', 'C', 'E', 'G', 'H', 'I', 'L', 'N', 'O', 'P', 'R', 'S'}; +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: 298 +// Number of words found: 432 static const char _valid_words[][WORDLE_LENGTH + 1] = { - "PLACE", "SHONE", "POSER", "CHAIN", "CAPER", "POLAR", "LEARN", "SHORN", "PORCH", - "GRAPE", "GNASH", "CHAIR", "SCORE", "CIGAR", "GRASP", "SINCE", "SPIRE", "NEIGH", - "SHORE", "CHASE", "RAISE", "CAIRN", "PLIER", "LOSER", "GRACE", "LEASH", "PENAL", - "SLING", "RISEN", "LOGIC", "PRICE", "POISE", "SCALE", "SINGE", "SNARL", "LINER", - "ANGEL", "SNAIL", "PALER", "SCION", "ALONE", "AGILE", "APRON", "PERIL", "GRIPE", - "SPICE", "LOGIN", "REGAL", "CAROL", "SLICE", "CRONE", "LEACH", "COPSE", "SHEAR", - "ALIGN", "LARGE", "LAPSE", "AISLE", "NICER", "OCEAN", "OPERA", "ALIEN", "ACORN", - "ASHEN", "SHINE", "PANEL", "SPORE", "SCOPE", "SPACE", "PHASE", "AROSE", "CHOIR", - "SNIPE", "CHAOS", "RALPH", "EPOCH", "GRAIN", "SANER", "GROIN", "SLANG", "SLAIN", - "CRASH", "CLASP", "SHIRE", "SCONE", "ALONG", "APING", "NICHE", "CHEAP", "CHIRP", - "LAGER", "CHORE", "SNORE", "SHAPE", "RESIN", "PERCH", "PECAN", "GLARE", "GROAN", - "RHINO", "RENAL", "SALON", "GRAIL", "SEPIA", "LANCE", "PRONG", "RECAP", "CLONE", - "CLASH", "HORSE", "SOLAR", "HERON", "PEACH", "ARSON", "HINGE", "CLEAN", "CLING", - "PHONE", "CRANE", "CLANG", "SCORN", "SPEAR", "PLAIN", "PROSE", "SPOIL", "GONER", - "SHOAL", "REIGN", "CLEAR", "ANGER", "CHINA", "GRAPH", "PEARL", "CARGO", "CHOSE", - "SCALP", "CANOE", "RINSE", "RANGE", "LINGO", "RANCH", "PLANE", "SPINE", "REACH", - "CRISP", "PARSE", "RIPEN", "SNARE", "CLOSE", "SHARE", "CORAL", "NOISE", "SHARP", - "SPARE", "SONIC", "SCRAP", "SPIEL", "RELIC", "OPINE", "SCARE", "SPRIG", "SHALE", - "PANIC", "SONAR", "GROPE", "SLOPE", "ANGLE", "ORGAN", "PIANO", "PINCH", "GLEAN", - "PRONE", "ARISE", "ROACH", "SIREN", "CLASS", "POSSE", "INANE", "HENCE", "SNEER", - "PAGAN", "PREEN", "ROGER", "SPELL", "SHEEP", "SENSE", "INNER", "ALPHA", "SHEEN", - "SCREE", "CIRCA", "PRIOR", "RARER", "PEACE", "GENRE", "HELLO", "CACAO", "GORGE", - "GLOSS", "CRIER", "CROSS", "CREPE", "COLON", "CHILL", "ONION", "LINEN", "PIPER", - "SLOOP", "LEGAL", "SNOOP", "PAPER", "ALGAE", "LAPEL", "CHEER", "HIPPO", "PIECE", - "LILAC", "HONOR", "PAPAL", "ARENA", "APNEA", "RIPER", "SCENE", "SHALL", "NASAL", - "SPREE", "RIGOR", "EAGER", "LIEGE", "LEPER", "LEASE", "CORER", "SPOON", "GROSS", - "COACH", "CEASE", "GENIE", "HARSH", "PENCE", "CHILI", "SHELL", "CREEP", "RISER", - "ERASE", "CINCH", "SIEGE", "GOING", "SCOOP", "SPILL", "NOOSE", "EAGLE", "AGING", - "NIECE", "SPOOL", "APPLE", "SALSA", "LEECH", "GREEN", "IONIC", "LASSO", "CONCH", - "PENNE", "SLASH", "CANAL", "CRASS", "REPEL", "COCOA", "CRESS", "AGAPE", "EASEL", - "CELLO", "CONIC", "IGLOO", "RACER", "GOOSE", "ICING", "POOCH", "ILIAC", "GRASS", - "SHEER", "CANON", "ELOPE", "LOCAL", "EERIE", "COLOR", "AGREE", "PRESS", "GEESE", - "SLOSH", "SLEEP", "GRILL", "AGAIN", "GLASS", "PARER", "CHESS", "CACHE", "ERROR", - "LOOSE", + "STALE", "TRACE", "CLOSE", "ARISE", "SNIPE", "SHIRE", "LEASH", "SAINT", "CLEAN", + "RELIC", "CHORE", "CRONE", "REACH", "CHAOS", "TAPIR", "CAIRN", "TENOR", "STARE", + "HEART", "SCOPE", "SNARL", "SLEPT", "SINCE", "EPOCH", "SPACE", "SHARE", "SPOIL", + "LITER", "LEAPT", "LANCE", "RANCH", "HORSE", "LEACH", "LATER", "STEAL", "CHEAP", + "SHORT", "ETHIC", "CHANT", "ACTOR", "SPARE", "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", "SCARE", "ETHOS", "THEIR", "STONE", + "SLATE", "LATCH", "HASTE", "SNARE", "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", "RAISE", "POSIT", "ATONE", "SPIRE", + "COAST", "INEPT", "SHOAL", "CLASH", "THORN", "PHASE", "SCORE", "TRICE", "PERCH", + "PORCH", "SHEAR", "CHOIR", "RHINO", "PLANT", "SHONE", "SANER", "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", "CRATE", "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", "CRANE", "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. static const char _possible_words[][WORDLE_LENGTH + 1] = { - "AALII", "AARGH", "ACAIS", "ACARI", "ACCAS", "ACERS", "ACHAR", "ACHES", "ACHOO", - "ACING", "ACINI", "ACNES", "ACRES", "ACROS", "AECIA", "AEGIS", "AEONS", "AERIE", - "AEROS", "AESIR", "AGARS", "AGENE", "AGERS", "AGGER", "AGGIE", "AGGRI", "AGGRO", - "AGHAS", "AGILA", "AGIOS", "AGLEE", "AGLOO", "AGOGE", "AGONE", "AGONS", "AGORA", - "AGRIA", "AGRIN", "AGROS", "AHEAP", "AHIGH", "AHING", "AIGAS", "AINEE", "AINGA", - "AIOLI", "AIRER", "AIRNS", "ALAAP", "ALANE", "ALANG", "ALANS", "ALAPA", "ALAPS", - "ALCOS", "ALECS", "ALEPH", "ALGAL", "ALGAS", "ALGIN", "ALGOR", "ALIAS", "ALINE", - "ALLEE", "ALLEL", "ALLIS", "ALOES", "ALOHA", "ALOIN", "ALOOS", "ANANA", "ANCHO", - "ANCLE", "ANCON", "ANEAR", "ANELE", "ANGAS", "ANGLO", "ANIGH", "ANILE", "ANILS", - "ANION", "ANISE", "ANLAS", "ANNAL", "ANNAS", "ANOAS", "ANOLE", "ANSAE", "APACE", - "APAGE", "APERS", "APGAR", "APHIS", "APIAN", "APIOL", "APISH", "APOOP", "APPAL", - "APPEL", "APPRO", "APRES", "APSES", "APSIS", "APSOS", "ARARS", "ARCHI", "ARCOS", + "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", - "ARGAL", "ARGAN", "ARGIL", "ARGLE", "ARGOL", "ARGON", "ARIAS", "ARIEL", "ARILS", - "ARISH", "ARLES", "ARNAS", "AROHA", "ARPAS", "ARPEN", "ARRAH", "ARRAS", "ARRIS", - "ARSES", "ARSIS", "ASANA", "ASCON", "ASHES", "ASPEN", "ASPER", "ASPIC", "ASPIE", - "ASPIS", "ASPRO", "ASSAI", "ASSES", "CACAS", "CAECA", "CAESE", "CAGER", "CAGES", - "CAINS", "CALLA", "CALLS", "CALOS", "CALPA", "CALPS", "CANEH", "CANER", "CANES", - "CANGS", "CANNA", "CANNS", "CANSO", "CAPAS", "CAPES", "CAPHS", "CAPLE", "CAPON", - "CAPOS", "CAPRI", "CARAP", "CARER", "CARES", "CARLE", "CARLS", "CARNS", "CARON", - "CARPI", "CARPS", "CARRS", "CARSE", "CASAS", "CASCO", "CASES", "CECAL", "CEILI", - "CEILS", "CELLA", "CELLI", "CELLS", "CENSE", "CEORL", "CEPES", "CERCI", "CERES", - "CERGE", "CERIA", "CERIC", "CERNE", "CEROC", "CEROS", "CESSE", "CHACE", "CHACO", - "CHAIS", "CHALS", "CHANA", "CHANG", "CHAPE", "CHAPS", "CHARA", "CHARE", "CHARR", - "CHARS", "CHEEP", "CHELA", "CHELP", "CHERE", "CHIAO", "CHIAS", "CHICA", "CHICH", - "CHICO", "CHICS", "CHIEL", "CHILE", "CHINE", "CHING", "CHINO", "CHINS", "CHIPS", - "CHIRL", "CHIRO", "CHIRR", "CHOCO", "CHOCS", "CHOGS", "CHOIL", "CHOLA", "CHOLI", - "CHOLO", "CHONS", "CHOON", "CHOPS", "CIELS", "CILIA", "CILLS", "CINES", "CIONS", - "CIPPI", "CIRCS", "CIRES", "CIRLS", "CIRRI", "CISCO", "CLACH", "CLAES", "CLAGS", - "CLANS", "CLAPS", "CLARO", "CLEEP", "CLEGS", "CLEPE", "CLIES", "CLINE", "CLIPE", - "CLIPS", "CLOGS", "CLONS", "CLOOP", "CLOPS", "COALA", "COALS", "COCAS", "COCCI", - "COCCO", "COCOS", "COGIE", "COGON", "COHEN", "COHOE", "COHOG", "COHOS", "COIGN", - "COILS", "COINS", "COIRS", "COLAS", "COLES", "COLIC", "COLIN", "COLLS", "COLOG", - "CONES", "CONGA", "CONGE", "CONGO", "CONIA", "CONIN", "CONNE", "CONNS", "COOCH", - "COOEE", "COOER", "COOLS", "COONS", "COOPS", "COPAL", "COPEN", "COPER", "COPES", - "COPRA", "CORES", "CORGI", "CORIA", "CORNI", "CORNO", "CORNS", "CORPS", "CORSE", - "CORSO", "COSEC", "COSES", "COSIE", "CRAAL", "CRAGS", "CRAIC", "CRAIG", "CRANS", - "CRAPE", "CRAPS", "CRARE", "CREEL", "CREES", "CRENA", "CREPS", "CRIAS", "CRIES", - "CRINE", "CRIOS", "CRIPE", "CRIPS", "CRISE", "CROCI", "CROCS", "CROGS", "CRONS", - "CROOL", "CROON", "CROPS", "CRORE", "EAGRE", "EALES", "EARLS", "EARNS", "EASER", - "EASES", "EASLE", "ECHES", "ECHOS", "EGERS", "EGGAR", "EGGER", "EHING", "EIGNE", - "EISEL", "ELAIN", "ELANS", "ELCHI", "ELOGE", "ELOIN", "ELOPS", "ELPEE", "ELSIN", - "ENIAC", "ENNOG", "ENOLS", "ENROL", "EORLS", "EOSIN", "EPEES", "EPHAH", "EPHAS", - "EPHOR", "EPICS", "EPRIS", "ERGON", "ERGOS", "ERICA", "ERICS", "ERING", "ERNES", - "EROSE", "ERSES", "ESCAR", "ESILE", "ESNES", "ESSES", "GAGER", "GAGES", "GAINS", - "GAIRS", "GALAH", "GALAS", "GALEA", "GALES", "GALLS", "GALOP", "GANCH", "GANGS", - "GAOLS", "GAPER", "GAPES", "GAPOS", "GARES", "GARIS", "GARNI", "GARRE", "GASES", - "GASPS", "GEALS", "GEANS", "GEARE", "GEARS", "GEEPS", "GELEE", "GENAL", "GENAS", - "GENES", "GENIC", "GENII", "GENIP", "GENOA", "GENRO", "GERAH", "GERES", "GERLE", - "GERNE", "GESSE", "GESSO", "GHEES", "GIGAS", "GIGHE", "GILAS", "GILLS", "GINCH", - "GINGE", "GINGS", "GIPON", "GIPPO", "GIRLS", "GIRNS", "GIRON", "GIROS", "GIRRS", - "GIRSH", "GLACE", "GLAIR", "GLANS", "GLEES", "GLEIS", "GLENS", "GLIAL", "GLIAS", - "GLOGG", "GLOOP", "GLOPS", "GNARL", "GNARR", "GNARS", "GOALS", "GOELS", "GOERS", - "GOGGA", "GOGOS", "GOIER", "GOLES", "GOLPE", "GOLPS", "GONCH", "GONGS", "GONIA", - "GONNA", "GOOGS", "GOOLS", "GOONS", "GOOPS", "GOORS", "GORAL", "GORAS", "GORES", - "GORIS", "GORPS", "GORSE", "GOSSE", "GRAAL", "GRAIP", "GRANA", "GRANS", "GRECE", - "GREES", "GREGE", "GREGO", "GREIN", "GRENS", "GRESE", "GRICE", "GRIGS", "GRINS", - "GRIPS", "GRISE", "GROGS", "GRONE", "GRRLS", "GRRRL", "HAARS", "HAGGS", "HAHAS", - "HAILS", "HAINS", "HAIRS", "HALAL", "HALER", "HALES", "HALLO", "HALLS", "HALON", - "HALOS", "HALSE", "HANAP", "HANCE", "HANCH", "HANGI", "HANGS", "HANSA", "HANSE", - "HAOLE", "HAPPI", "HARES", "HARLS", "HARNS", "HAROS", "HARPS", "HASPS", "HEALS", - "HEAPS", "HEARE", "HEARS", "HEELS", "HEIGH", "HEILS", "HEIRS", "HELES", "HELIO", - "HELLS", "HELOS", "HELPS", "HENCH", "HENGE", "HENNA", "HEPAR", "HERES", "HERLS", - "HERNS", "HEROS", "HERSE", "HESPS", "HIGHS", "HILAR", "HILCH", "HILLO", "HILLS", - "HINGS", "HIOIS", "HIREE", "HIRER", "HIRES", "HOARS", "HOERS", "HOGAN", "HOGEN", - "HOGGS", "HOGHS", "HOING", "HOISE", "HOLES", "HOLLA", "HOLLO", "HOLON", "HOLOS", - "HONAN", "HONER", "HONES", "HONGI", "HONGS", "HOOCH", "HOONS", "HOOPS", "HOORS", - "HOOSH", "HOPER", "HOPES", "HORAH", "HORAL", "HORAS", "HORIS", "HORNS", "HOSEL", - "HOSEN", "HOSER", "HOSES", "ICERS", "ICHES", "ICHOR", "ICIER", "ICONS", "IGAPO", - "ILEAC", "ILEAL", "ILIAL", "ILLER", "INCEL", "INCLE", "INCOG", "INGAN", "INGLE", - "INION", "INSPO", "IPPON", "IRING", "IRONE", "IRONS", "ISHES", "ISLES", "ISNAE", - "ISSEI", "LAARI", "LACER", "LACES", "LAERS", "LAGAN", "LAHAL", "LAHAR", "LAICH", - "LAICS", "LAIGH", "LAIRS", "LALLS", "LANAI", "LANAS", "LANCH", "LANES", "LAPIN", - "LAPIS", "LARCH", "LAREE", "LARES", "LARGO", "LARIS", "LARNS", "LASER", "LASES", - "LASSI", "LEANS", "LEAPS", "LEARE", "LEARS", "LEEAR", "LEEPS", "LEERS", "LEESE", - "LEGER", "LEGES", "LEGGE", "LEGGO", "LEHRS", "LEIRS", "LEISH", "LENES", "LENGS", - "LENIS", "LENOS", "LENSE", "LEONE", "LEPRA", "LERES", "LERPS", "LESES", "LIANA", - "LIANE", "LIANG", "LIARS", "LICHI", "LIENS", "LIERS", "LIGAN", "LIGER", "LIGGE", - "LIGNE", "LILLS", "LILOS", "LINAC", "LINCH", "LINES", "LINGA", "LINGS", "LININ", - "LINNS", "LINOS", "LIONS", "LIPAS", "LIPES", "LIPIN", "LIPOS", "LIRAS", "LISLE", - "LISPS", "LLANO", "LOACH", "LOANS", "LOCHE", "LOCHS", "LOCIE", "LOCIS", "LOCOS", - "LOESS", "LOGAN", "LOGES", "LOGIA", "LOGIE", "LOGOI", "LOGON", "LOGOS", "LOHAN", - "LOINS", "LOIPE", "LOIRS", "LOLLS", "LOLOG", "LONER", "LONGA", "LONGE", "LONGS", - "LOOIE", "LOONS", "LOOPS", "LOPER", "LOPES", "LORAL", "LORAN", "LOREL", "LORES", - "LORIC", "LORIS", "LOSEL", "LOSEN", "LOSES", "NAANS", "NACHE", "NACHO", "NACRE", - "NAGAS", "NAGOR", "NAHAL", "NAILS", "NAIRA", "NALAS", "NALLA", "NANAS", "NANCE", - "NANNA", "NANOS", "NAPAS", "NAPES", "NAPOO", "NAPPA", "NAPPE", "NARAS", "NARCO", - "NARCS", "NARES", "NARIC", "NARIS", "NARRE", "NASHI", "NEALS", "NEAPS", "NEARS", - "NEELE", "NEEPS", "NEESE", "NEGRO", "NELIS", "NENES", "NEONS", "NEPER", "NERAL", - "NEROL", "NGAIO", "NGANA", "NICOL", "NIGER", "NIGHS", "NIHIL", "NILLS", "NINER", - "NINES", "NINON", "NIPAS", "NIRLS", "NISEI", "NISSE", "NOAHS", "NOELS", "NOGGS", - "NOILS", "NOIRS", "NOLES", "NOLLS", "NOLOS", "NONAS", "NONCE", "NONES", "NONGS", - "NONIS", "NOONS", "NOOPS", "NOPAL", "NORIA", "NORIS", "NOSER", "NOSES", "OASES", - "OASIS", "OCHER", "OCHES", "OCHRE", "OCREA", "OGEES", "OGGIN", "OGLER", "OGLES", - "OGRES", "OHIAS", "OHING", "OHONE", "OILER", "OLEIC", "OLEIN", "OLEOS", "OLIOS", - "OLLAS", "OLLER", "OLLIE", "OLPAE", "OLPES", "ONCER", "ONCES", "ONERS", "OORIE", - "OOSES", "OPAHS", "OPALS", "OPENS", "OPEPE", "OPING", "OPPOS", "OPSIN", "ORACH", - "ORALS", "ORANG", "ORCAS", "ORCIN", "ORGIA", "ORGIC", "ORIEL", "ORLES", "ORLON", - "ORLOP", "ORNIS", "ORPIN", "ORRIS", "OSCAR", "OSHAC", "OSIER", "OSSIA", "PAALS", - "PAANS", "PACAS", "PACER", "PACES", "PACHA", "PACOS", "PAEAN", "PAEON", "PAGER", - "PAGES", "PAGLE", "PAGRI", "PAILS", "PAINS", "PAIRE", "PAIRS", "PAISA", "PAISE", - "PALAS", "PALEA", "PALES", "PALIS", "PALLA", "PALLS", "PALPI", "PALPS", "PALSA", - "PANCE", "PANES", "PANGA", "PANGS", "PANNE", "PANNI", "PAOLI", "PAOLO", "PAPAS", - "PAPES", "PAPPI", "PARAE", "PARAS", "PARCH", "PAREN", "PAREO", "PARES", "PARGE", - "PARGO", "PARIS", "PARLE", "PAROL", "PARPS", "PARRA", "PARRS", "PASEO", "PASES", - "PASHA", "PASSE", "PEAGE", "PEAGS", "PEALS", "PEANS", "PEARE", "PEARS", "PEASE", - "PECHS", "PEECE", "PEELS", "PEENS", "PEEPE", "PEEPS", "PEERS", "PEGHS", "PEINS", - "PEISE", "PELAS", "PELES", "PELLS", "PELON", "PENES", "PENGO", "PENIE", "PENIS", - "PENNA", "PENNI", "PEONS", "PEPLA", "PEPOS", "PEPSI", "PERAI", "PERCE", "PERCS", - "PEREA", "PERES", "PERIS", "PERNS", "PEROG", "PERPS", "PERSE", "PESOS", "PHAGE", - "PHANG", "PHARE", "PHEER", "PHENE", "PHEON", "PHESE", "PHIAL", "PHISH", "PHOCA", - "PHONO", "PHONS", "PIANI", "PIANS", "PICAL", "PICAS", "PICRA", "PIERS", "PIING", + "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", "PINGO", "PINGS", "PINNA", "PINON", "PIONS", "PIPAL", - "PIPAS", "PIPES", "PIPIS", "PIRAI", "PIRLS", "PIRNS", "PIROG", "PISCO", "PISES", - "PISOS", "PLAAS", "PLAGE", "PLANS", "PLAPS", "PLASH", "PLEAS", "PLENA", "PLEON", - "PLESH", "PLICA", "PLIES", "PLING", "PLONG", "PLOPS", "POACH", "POEPS", "POGGE", - "POGOS", "POLER", "POLES", "POLIO", "POLIS", "POLLS", "POLOS", "PONCE", "PONES", - "PONGA", "PONGO", "PONGS", "POOHS", "POOLS", "POONS", "POOPS", "POORI", "POPES", - "POPPA", "PORAE", "PORAL", "PORER", "PORES", "PORGE", "PORIN", "PORNO", "PORNS", - "POSES", "POSHO", "PRANA", "PRANG", "PRAOS", "PRASE", "PREES", "PREON", "PREOP", - "PREPS", "PRESA", "PRESE", "PRIAL", "PRIER", "PRIES", "PRIGS", "PRILL", "PRION", - "PRISE", "PRISS", "PROAS", "PROGS", "PROIN", "PROLE", "PROLL", "PROPS", "PRORE", - "PROSO", "PROSS", "PSION", "PSOAE", "PSOAI", "PSOAS", "PSORA", "RACES", "RACHE", - "RACON", "RAGAS", "RAGEE", "RAGER", "RAGES", "RAGGA", "RAGGS", "RAGIS", "RAIAS", - "RAILE", "RAILS", "RAINE", "RAINS", "RALES", "RANAS", "RANCE", "RANEE", "RANGA", - "RANGI", "RANGS", "RANIS", "RAPER", "RAPES", "RAPHE", "RAPPE", "RAREE", "RARES", - "RASER", "RASES", "RASPS", "RASSE", "REAIS", "REALO", "REALS", "REANS", "REAPS", - "REARS", "RECAL", "RECCE", "RECCO", "RECON", "REECH", "REELS", "REENS", "REGAR", - "REGES", "REGGO", "REGIE", "REGNA", "REGOS", "REINS", "RELIE", "RELLO", "RENGA", - "RENIG", "RENIN", "RENNE", "RENOS", "REOIL", "REORG", "REPEG", "REPIN", "REPLA", - "REPOS", "REPPS", "REPRO", "RERAN", "RERIG", "RESEE", "RESES", "RHEAS", "RHIES", - "RHINE", "RHONE", "RIALS", "RICER", "RICES", "RICIN", "RIELS", "RIGGS", "RIGOL", - "RILES", "RILLE", "RILLS", "RINES", "RINGS", "RIPES", "RIPPS", "RISES", "RISHI", - "RISPS", "ROANS", "ROARS", "ROHES", "ROILS", "ROINS", "ROLAG", "ROLES", "ROLLS", - "RONEO", "RONES", "RONIN", "RONNE", "ROONS", "ROOPS", "ROOSA", "ROOSE", "ROPER", - "ROPES", "RORAL", "RORES", "RORIC", "RORIE", "ROSES", "ROSHI", "ROSIN", "SAAGS", - "SACRA", "SAGAS", "SAGER", "SAGES", "SAGOS", "SAICE", "SAICS", "SAIGA", "SAILS", - "SAINE", "SAINS", "SAIRS", "SALAL", "SALEP", "SALES", "SALIC", "SALLE", "SALOL", - "SALOP", "SALPA", "SALPS", "SALSE", "SANES", "SANGA", "SANGH", "SANGO", "SANGS", - "SANSA", "SAOLA", "SAPAN", "SAPOR", "SARAN", "SAREE", "SARGE", "SARGO", "SARIN", - "SARIS", "SAROS", "SASER", "SASIN", "SASSE", "SCAGS", "SCAIL", "SCALA", "SCALL", - "SCANS", "SCAPA", "SCAPE", "SCAPI", "SCARP", "SCARS", "SCENA", "SCOGS", "SCOOG", - "SCOPA", "SCOPS", "SCRAE", "SCRAG", "SCRAN", "SCRIP", "SCROG", "SEALS", "SEANS", - "SEARE", "SEARS", "SEASE", "SECCO", "SECHS", "SEELS", "SEEPS", "SEERS", "SEGAR", - "SEGNI", "SEGNO", "SEGOL", "SEGOS", "SEHRI", "SEILS", "SEINE", "SEIRS", "SEISE", - "SELAH", "SELES", "SELLA", "SELLE", "SELLS", "SENAS", "SENES", "SENGI", "SENNA", - "SENOR", "SENSA", "SENSI", "SEPAL", "SEPIC", "SERAC", "SERAI", "SERAL", "SERER", - "SERES", "SERGE", "SERIC", "SERIN", "SERON", "SERRA", "SERRE", "SERRS", "SESSA", - "SHAGS", "SHAHS", "SHANS", "SHAPS", "SHARN", "SHASH", "SHCHI", "SHEAL", "SHEAS", - "SHEEL", "SHEOL", "SHERE", "SHERO", "SHIAI", "SHIEL", "SHIER", "SHIES", "SHILL", - "SHINS", "SHIPS", "SHIRR", "SHIRS", "SHISH", "SHISO", "SHLEP", "SHOER", "SHOES", - "SHOGI", "SHOGS", "SHOLA", "SHOOL", "SHOON", "SHOOS", "SHOPE", "SHOPS", "SHORL", - "SHRIS", "SIALS", "SICES", "SIENS", "SIGHS", "SIGIL", "SIGLA", "SIGNA", "SIGNS", - "SILEN", "SILER", "SILES", "SILLS", "SILOS", "SINES", "SINGS", "SINHS", "SIPES", - "SIREE", "SIRES", "SIRIH", "SIRIS", "SIROC", "SIRRA", "SISAL", "SISES", "SLAES", - "SLAGS", "SLANE", "SLAPS", "SLEER", "SLIER", "SLIPE", "SLIPS", "SLISH", "SLOAN", - "SLOES", "SLOGS", "SLOPS", "SNAGS", "SNAPS", "SNARS", "SNASH", "SNEAP", "SNEES", - "SNELL", "SNIES", "SNIGS", "SNIPS", "SNOEP", "SNOGS", "SNOOL", "SOAPS", "SOARE", - "SOARS", "SOCAS", "SOCES", "SOCLE", "SOGER", "SOILS", "SOLAH", "SOLAN", "SOLAS", - "SOLEI", "SOLER", "SOLES", "SOLON", "SOLOS", "SONCE", "SONES", "SONGS", "SONNE", - "SONSE", "SOOLE", "SOOLS", "SOOPS", "SOPHS", "SOPOR", "SOPRA", "SORAL", "SORAS", - "SOREE", "SOREL", "SORER", "SORES", "SORGO", "SORNS", "SORRA", "SPAER", "SPAES", - "SPAGS", "SPAHI", "SPAIL", "SPAIN", "SPALE", "SPALL", "SPANE", "SPANG", "SPANS", - "SPARS", "SPEAL", "SPEAN", "SPECS", "SPEEL", "SPEER", "SPEIL", "SPEIR", "SPEOS", - "SPIAL", "SPICA", "SPICS", "SPIER", "SPIES", "SPILE", "SPINA", "SPINS", "SPLOG", - "SPOOR", "SPOSH", "SPRAG", "SPROG", + "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", }; static const uint16_t _num_words = (sizeof(_valid_words) / sizeof(_valid_words[0])); @@ -628,7 +686,7 @@ static void get_result(wordle_state_t *state) { } #if (USE_RANDOM_GUESS != 0) -static const uint16_t _num_unique_words = 157; // The _valid_words array begins with this many words where each letter is different. +static const uint16_t _num_unique_words = 257; // The _valid_words array begins with this many words where each letter is different. 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 diff --git a/utils/wordle_face/wordle_list.py b/utils/wordle_face/wordle_list.py index ed90988..fec404b 100644 --- a/utils/wordle_face/wordle_list.py +++ b/utils/wordle_face/wordle_list.py @@ -1223,7 +1223,7 @@ def txt_of_all_letter_combos(num_letters_in_set, words=valid_list, min_letter_oc print_iter = 0 prev = time.time() start = prev - letters_to_ignore = ['D','M','T','Y'] # Don't diplay well on the watch + letters_to_ignore = ['D','M'] # Don't diplay well on the watch letter_usage = most_used_letters(words=words) for letter in letter_usage: if (100 * letter_usage[letter])/len(words) < min_letter_occ_percent_to_consider: @@ -1301,7 +1301,7 @@ def location_of_letters(letters=alphabet, list=valid_list): if __name__ == "__main__": - my_letters = ['A', 'C', 'E', 'G', 'H', 'I', 'L', 'N', 'O', 'P', 'R', 'S'] + my_letters = ['A', 'C', 'E', 'H', 'I', 'L', 'N', 'O', 'P', 'R', 'S', 'T'] #print(f"{len(list_of_valid_words(my_letters, valid_list))} Words can be made with {my_letters}") #most_used_letters() #location_of_letters(my_letters) From 1d2fb20e994d297aa77c12aa29f333343a7ed262 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sat, 24 Aug 2024 10:36:38 -0400 Subject: [PATCH 079/161] Readding comment showing count of possible words. --- movement/watch_faces/complication/wordle_face.c | 1 + utils/wordle_face/wordle_list.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 260860b..7396a17 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -86,6 +86,7 @@ static const char _valid_words[][WORDLE_LENGTH + 1] = { }; // 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] = { "AALII", "AARTI", "ACAIS", "ACARI", "ACCAS", "ACERS", "ACETA", "ACHAR", "ACHES", "ACHOO", "ACINI", "ACNES", "ACRES", "ACROS", "ACTIN", "ACTON", "AECIA", "AEONS", diff --git a/utils/wordle_face/wordle_list.py b/utils/wordle_face/wordle_list.py index fec404b..d951bac 100644 --- a/utils/wordle_face/wordle_list.py +++ b/utils/wordle_face/wordle_list.py @@ -1171,8 +1171,8 @@ def print_valid_words(letters=alphabet): print("") print(f"// From: {source_link}") print(f"// Number of words found: {len(valid_words)}") - i = 0 print("static const char _valid_words[][WORDLE_LENGTH + 1] = {") + i = 0 while i < len(valid_words): print(" ", end='') for _ in range(min(items_per_row, len(valid_words)-i)): @@ -1184,6 +1184,7 @@ def print_valid_words(letters=alphabet): possible_words = capitalize_all_and_remove_duplicates(possible_words) print("};") print("\n// These are words that'll never be used, but still need to be in the dictionary for guesses.") + print(f"// Number of words found: {len(possible_words)}") print("static const char _possible_words[][WORDLE_LENGTH + 1] = {") i = 0 while i < len(possible_words): From 5149e7e1dd2941b7f9adc740280eb0f3fb4a3c62 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 25 Aug 2024 06:50:00 -0400 Subject: [PATCH 080/161] the dict of words to its own header --- .../watch_faces/complication/wordle_face.c | 279 +---------------- .../watch_faces/complication/wordle_face.h | 5 + .../complication/wordle_face_dict.h | 289 ++++++++++++++++++ 3 files changed, 295 insertions(+), 278 deletions(-) create mode 100644 movement/watch_faces/complication/wordle_face_dict.h diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 7396a17..9ce29bd 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -29,282 +29,6 @@ #include "watch_utility.h" #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] = { - "STALE", "TRACE", "CLOSE", "ARISE", "SNIPE", "SHIRE", "LEASH", "SAINT", "CLEAN", - "RELIC", "CHORE", "CRONE", "REACH", "CHAOS", "TAPIR", "CAIRN", "TENOR", "STARE", - "HEART", "SCOPE", "SNARL", "SLEPT", "SINCE", "EPOCH", "SPACE", "SHARE", "SPOIL", - "LITER", "LEAPT", "LANCE", "RANCH", "HORSE", "LEACH", "LATER", "STEAL", "CHEAP", - "SHORT", "ETHIC", "CHANT", "ACTOR", "SPARE", "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", "SCARE", "ETHOS", "THEIR", "STONE", - "SLATE", "LATCH", "HASTE", "SNARE", "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", "RAISE", "POSIT", "ATONE", "SPIRE", - "COAST", "INEPT", "SHOAL", "CLASH", "THORN", "PHASE", "SCORE", "TRICE", "PERCH", - "PORCH", "SHEAR", "CHOIR", "RHINO", "PLANT", "SHONE", "SANER", "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", "CRATE", "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", "CRANE", "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] = { - "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", -}; - -static const uint16_t _num_words = (sizeof(_valid_words) / sizeof(_valid_words[0])); -static const uint16_t _num_possible_words = (sizeof(_possible_words) / sizeof(_possible_words[0])); -static const uint8_t _num_valid_letters = (sizeof(_valid_letters) / sizeof(_valid_letters[0])); - static uint32_t get_random(uint32_t max) { #if __EMSCRIPTEN__ return rand() % max; @@ -687,11 +411,10 @@ static void get_result(wordle_state_t *state) { } #if (USE_RANDOM_GUESS != 0) -static const uint16_t _num_unique_words = 257; // The _valid_words array begins with this many words where each letter is different. 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(USE_RANDOM_GUESS == 2 ? _num_unique_words : _num_words); + 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 < _num_valid_letters; j++) diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index 62a4571..6ae1fbd 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -70,6 +70,11 @@ * 2 = Allow using a random guess of any value that can be an answer where all of its letters are unique */ #define USE_RANDOM_GUESS 2 +#include "wordle_face_dict.h" + +static const uint16_t _num_words = (sizeof(_valid_words) / sizeof(_valid_words[0])); +static const uint16_t _num_possible_words = (sizeof(_possible_words) / sizeof(_possible_words[0])); +static const uint8_t _num_valid_letters = (sizeof(_valid_letters) / sizeof(_valid_letters[0])); typedef enum { WORDLE_LETTER_WRONG = 0, diff --git a/movement/watch_faces/complication/wordle_face_dict.h b/movement/watch_faces/complication/wordle_face_dict.h new file mode 100644 index 0000000..1942b16 --- /dev/null +++ b/movement/watch_faces/complication/wordle_face_dict.h @@ -0,0 +1,289 @@ +#ifndef WORDLE_FACE_DICT_H_ +#define WORDLE_FACE_DICT_H_ + +#ifndef WORDLE_LENGTH +#define WORDLE_LENGTH 5 +#endif + +#ifndef USE_RANDOM_GUESS +#define 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] = { + "STALE", "TRACE", "CLOSE", "ARISE", "SNIPE", "SHIRE", "LEASH", "SAINT", "CLEAN", + "RELIC", "CHORE", "CRONE", "REACH", "CHAOS", "TAPIR", "CAIRN", "TENOR", "STARE", + "HEART", "SCOPE", "SNARL", "SLEPT", "SINCE", "EPOCH", "SPACE", "SHARE", "SPOIL", + "LITER", "LEAPT", "LANCE", "RANCH", "HORSE", "LEACH", "LATER", "STEAL", "CHEAP", + "SHORT", "ETHIC", "CHANT", "ACTOR", "SPARE", "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", "SCARE", "ETHOS", "THEIR", "STONE", + "SLATE", "LATCH", "HASTE", "SNARE", "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", "RAISE", "POSIT", "ATONE", "SPIRE", + "COAST", "INEPT", "SHOAL", "CLASH", "THORN", "PHASE", "SCORE", "TRICE", "PERCH", + "PORCH", "SHEAR", "CHOIR", "RHINO", "PLANT", "SHONE", "SANER", "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", "CRATE", "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", "CRANE", "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] = { + "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", +}; + +#if (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 (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 From 0d16d126cd2017382a1f260fbf3b63f717f4b4d6 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 25 Aug 2024 07:15:08 -0400 Subject: [PATCH 081/161] Added ability to guess non-dict words and repeats as that can save 11.5KB of watch memory --- .../watch_faces/complication/wordle_face.c | 28 ++++++++++++------- .../watch_faces/complication/wordle_face.h | 3 ++ .../complication/wordle_face_dict.h | 2 ++ utils/wordle_face/wordle_list.py | 23 +++++++++++++-- 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 9ce29bd..f44e4a6 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -95,6 +95,17 @@ static void display_all_letters(wordle_state_t *state) { state->position = prev_pos; } +#if !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 < _num_words; i++) { @@ -120,6 +131,9 @@ static uint32_t check_word_in_dict(uint8_t *word_elements) { return _num_words + _num_possible_words; } + +#endif + static bool check_word(wordle_state_t *state) { // Exact bool is_exact_match = true; @@ -167,9 +181,11 @@ static void reset_all_elements(wordle_state_t *state) { state->word_elements[i] = _num_valid_letters; state->word_elements_result[i] = WORDLE_LETTER_WRONG; } +#if !ALLOW_NON_WORD_AND_REPEAT_GUESSES for (size_t i = 0; i < WORDLE_MAX_ATTEMPTS; i++) { state->guessed_words[i] = _num_words + _num_possible_words; } +#endif state->using_random_guess = false; state->attempt = 0; } @@ -245,16 +261,6 @@ static uint32_t get_day_unix_time(void) { } #endif -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 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] : " "); @@ -373,6 +379,7 @@ static bool act_on_btn(wordle_state_t *state, const uint8_t pin) { } static void get_result(wordle_state_t *state) { +#if !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 == _num_words + _num_possible_words) { @@ -389,6 +396,7 @@ static void get_result(wordle_state_t *state) { } state->guessed_words[state->attempt] = in_dict; +#endif bool exact_match = check_word(state); if (exact_match) { reset_all_elements(state); diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index 6ae1fbd..52fd71f 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -63,6 +63,7 @@ #define WORDLE_LENGTH 5 #define WORDLE_MAX_ATTEMPTS 6 #define USE_DAILY_STREAK false +#define 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. /* USE_RANDOM_GUESS * 0 = Don't allow quickly choosing a random quess @@ -103,7 +104,9 @@ 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 !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; diff --git a/movement/watch_faces/complication/wordle_face_dict.h b/movement/watch_faces/complication/wordle_face_dict.h index 1942b16..00ae3ac 100644 --- a/movement/watch_faces/complication/wordle_face_dict.h +++ b/movement/watch_faces/complication/wordle_face_dict.h @@ -67,6 +67,7 @@ static const char _valid_words[][WORDLE_LENGTH + 1] = { // 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 !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", @@ -278,6 +279,7 @@ static const char _possible_words[][WORDLE_LENGTH + 1] = { "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 (USE_RANDOM_GUESS == 2) diff --git a/utils/wordle_face/wordle_list.py b/utils/wordle_face/wordle_list.py index d951bac..68481e0 100644 --- a/utils/wordle_face/wordle_list.py +++ b/utils/wordle_face/wordle_list.py @@ -1156,6 +1156,17 @@ def print_valid_words(letters=alphabet): ''' Prints the array of valid words that the wordle_face.c can use ''' + print("#ifndef WORDLE_FACE_DICT_H_") + print("#define WORDLE_FACE_DICT_H_") + + print("\n#ifndef WORDLE_LENGTH") + print("#define WORDLE_LENGTH 5") + print("#endif") + + print("\n#ifndef USE_RANDOM_GUESS") + print("#define USE_RANDOM_GUESS 2") + print("#endif\n") + items_per_row = 9 valid_words = list_of_valid_words(letters, valid_list) valid_words = capitalize_all_and_remove_duplicates(valid_words) @@ -1186,6 +1197,7 @@ def print_valid_words(letters=alphabet): print("\n// These are words that'll never be used, but still need to be in the dictionary for guesses.") print(f"// Number of words found: {len(possible_words)}") print("static const char _possible_words[][WORDLE_LENGTH + 1] = {") + print("#if !ALLOW_NON_WORD_AND_REPEAT_GUESSES") i = 0 while i < len(possible_words): print(" ", end='') @@ -1193,10 +1205,15 @@ def print_valid_words(letters=alphabet): print(f'"{clean_chars(possible_words[i])}", ', end='') i+=1 print('') - print("};") - print('') + print("#endif") + print("};\n") - print(f"\nstatic const uint16_t _num_unique_words = {num_uniq}; // The _valid_words array begins with this many words where each letter is different.") + print("#if (USE_RANDOM_GUESS == 2)") + print(f"static const uint16_t _num_random_guess_words = {num_uniq}; // The _valid_words array begins with this many words where each letter is different.") + print("#elif (USE_RANDOM_GUESS == 1)") + print("static const uint16_t _num_random_guess_words = _num_words;") + print("#endif") + print("\n#endif // WORDLE_FACE_DICT_H_") def get_sec_val_and_units(seconds): From 8342fef84fb4873aa4d258cf1046245ddbae51a1 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 25 Aug 2024 09:51:08 -0400 Subject: [PATCH 082/161] Added ability to skip already guessed letters that are wrong --- .../watch_faces/complication/wordle_face.c | 38 ++++++++++++++----- .../watch_faces/complication/wordle_face.h | 1 + 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index f44e4a6..77cd23d 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -62,14 +62,18 @@ static uint8_t get_prev_pos(uint8_t curr_pos, WordleLetterResult *word_elements_ return curr_pos; } -static void get_next_letter(uint8_t curr_pos, uint8_t *word_elements) { - if (word_elements[curr_pos] >= _num_valid_letters) word_elements[curr_pos] = 0; - else word_elements[curr_pos] = (word_elements[curr_pos] + 1) % _num_valid_letters; +static void get_next_letter(const uint8_t curr_pos, uint8_t *word_elements, const bool *known_wrong_letters) { + do { + if (word_elements[curr_pos] >= _num_valid_letters) word_elements[curr_pos] = 0; + else word_elements[curr_pos] = (word_elements[curr_pos] + 1) % _num_valid_letters; + } while (known_wrong_letters[word_elements[curr_pos]]); } -static void get_prev_letter(uint8_t curr_pos, uint8_t *word_elements) { - if (word_elements[curr_pos] >= _num_valid_letters) word_elements[curr_pos] = _num_valid_letters - 1; - else word_elements[curr_pos] = (word_elements[curr_pos] + _num_valid_letters - 1) % _num_valid_letters; +static void get_prev_letter(const uint8_t curr_pos, uint8_t *word_elements, const bool *known_wrong_letters) { + do { + if (word_elements[curr_pos] >= _num_valid_letters) word_elements[curr_pos] = _num_valid_letters - 1; + else word_elements[curr_pos] = (word_elements[curr_pos] + _num_valid_letters - 1) % _num_valid_letters; + } while (known_wrong_letters[word_elements[curr_pos]]); } static void display_letter(wordle_state_t *state, bool display_dash) { @@ -130,8 +134,6 @@ static uint32_t check_word_in_dict(uint8_t *word_elements) { } return _num_words + _num_possible_words; } - - #endif static bool check_word(wordle_state_t *state) { @@ -164,6 +166,18 @@ static bool check_word(wordle_state_t *state) { return false; } +static void update_known_wrong_letters(wordle_state_t *state) { + + for (size_t i = 0; i < WORDLE_LENGTH; i++) { + if (state->word_elements_result[i] == WORDLE_LETTER_WRONG) { + for (size_t j = 0; j < _num_valid_letters; j++) { + if (state->word_elements[i] == j) + state->known_wrong_letters[j] = true; + } + } + } +} + static void display_attempt(uint8_t attempt) { char buf[3]; sprintf(buf, "%d", attempt+1); @@ -181,6 +195,9 @@ static void reset_all_elements(wordle_state_t *state) { state->word_elements[i] = _num_valid_letters; state->word_elements_result[i] = WORDLE_LETTER_WRONG; } + for (size_t i = 0; i < _num_valid_letters; i++){ + state->known_wrong_letters[i] = false; + } #if !ALLOW_NON_WORD_AND_REPEAT_GUESSES for (size_t i = 0; i < WORDLE_MAX_ATTEMPTS; i++) { state->guessed_words[i] = _num_words + _num_possible_words; @@ -414,6 +431,7 @@ static void get_result(wordle_state_t *state) { state->streak = 0; return; } + update_known_wrong_letters(state); state->curr_screen = SCREEN_RESULT; return; } @@ -496,12 +514,12 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi break; case EVENT_LIGHT_BUTTON_UP: if (act_on_btn(state, BTN_LIGHT)) break; - get_next_letter(state->position, state->word_elements); + get_next_letter(state->position, state->word_elements, state->known_wrong_letters); display_letter(state, true); break; case EVENT_LIGHT_LONG_PRESS: if (state->curr_screen != SCREEN_PLAYING) break; - get_prev_letter(state->position, state->word_elements); + get_prev_letter(state->position, state->word_elements, state->known_wrong_letters); display_letter(state, true); break; case EVENT_ALARM_BUTTON_UP: diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index 52fd71f..a7b34dc 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -114,6 +114,7 @@ typedef struct { bool continuing : 1; uint8_t streak; WordleScreen curr_screen; + bool known_wrong_letters[_num_valid_letters]; #if USE_DAILY_STREAK uint32_t prev_day; uint32_t curr_day; From b58d6c0a2e7c77f360df748c407b9b5be53baab8 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 25 Aug 2024 09:59:04 -0400 Subject: [PATCH 083/161] Changed size of word lists from const int to #define to avoid folding constant array error --- .../watch_faces/complication/wordle_face.c | 66 +++++++++---------- .../watch_faces/complication/wordle_face.h | 26 ++++---- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 77cd23d..6957d5b 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -25,7 +25,7 @@ #include #include #include "wordle_face.h" -#if USE_DAILY_STREAK +#if WORDLE_USE_DAILY_STREAK #include "watch_utility.h" #endif @@ -64,21 +64,21 @@ static uint8_t get_prev_pos(uint8_t curr_pos, WordleLetterResult *word_elements_ static void get_next_letter(const uint8_t curr_pos, uint8_t *word_elements, const bool *known_wrong_letters) { do { - if (word_elements[curr_pos] >= _num_valid_letters) word_elements[curr_pos] = 0; - else word_elements[curr_pos] = (word_elements[curr_pos] + 1) % _num_valid_letters; + 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 (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) { do { - if (word_elements[curr_pos] >= _num_valid_letters) word_elements[curr_pos] = _num_valid_letters - 1; - else word_elements[curr_pos] = (word_elements[curr_pos] + _num_valid_letters - 1) % _num_valid_letters; + 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 (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] >= _num_valid_letters) { + if (state->word_elements[state->position] >= WORDLE_NUM_VALID_LETTERS) { if (display_dash) watch_display_string("-", state->position + 5); else @@ -99,7 +99,7 @@ static void display_all_letters(wordle_state_t *state) { state->position = prev_pos; } -#if !ALLOW_NON_WORD_AND_REPEAT_GUESSES +#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); @@ -112,7 +112,7 @@ static void display_already_guessed(wordle_state_t *state) { static uint32_t check_word_in_dict(uint8_t *word_elements) { bool is_exact_match; - for (uint16_t i = 0; i < _num_words; i++) { + 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]) { @@ -122,7 +122,7 @@ static uint32_t check_word_in_dict(uint8_t *word_elements) { } if (is_exact_match) return i; } - for (uint16_t i = 0; i < _num_possible_words; 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]) { @@ -130,9 +130,9 @@ static uint32_t check_word_in_dict(uint8_t *word_elements) { break; } } - if (is_exact_match) return _num_words + i; + if (is_exact_match) return WORDLE_NUM_WORDS + i; } - return _num_words + _num_possible_words; + return WORDLE_NUM_WORDS + WORDLE_NUM_POSSIBLE_WORDS; } #endif @@ -170,7 +170,7 @@ static void update_known_wrong_letters(wordle_state_t *state) { for (size_t i = 0; i < WORDLE_LENGTH; i++) { if (state->word_elements_result[i] == WORDLE_LETTER_WRONG) { - for (size_t j = 0; j < _num_valid_letters; j++) { + for (size_t j = 0; j < WORDLE_NUM_VALID_LETTERS; j++) { if (state->word_elements[i] == j) state->known_wrong_letters[j] = true; } @@ -192,15 +192,15 @@ static void display_playing(wordle_state_t *state) { static void reset_all_elements(wordle_state_t *state) { for (size_t i = 0; i < WORDLE_LENGTH; i++) { - state->word_elements[i] = _num_valid_letters; + state->word_elements[i] = WORDLE_NUM_VALID_LETTERS; state->word_elements_result[i] = WORDLE_LETTER_WRONG; } - for (size_t i = 0; i < _num_valid_letters; i++){ + for (size_t i = 0; i < WORDLE_NUM_VALID_LETTERS; i++){ state->known_wrong_letters[i] = false; } -#if !ALLOW_NON_WORD_AND_REPEAT_GUESSES +#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES for (size_t i = 0; i < WORDLE_MAX_ATTEMPTS; i++) { - state->guessed_words[i] = _num_words + _num_possible_words; + state->guessed_words[i] = WORDLE_NUM_WORDS + WORDLE_NUM_POSSIBLE_WORDS; } #endif state->using_random_guess = false; @@ -210,13 +210,13 @@ static void reset_all_elements(wordle_state_t *state) { 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] = _num_valid_letters; + 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(_num_words); + state->curr_answer = get_random(WORDLE_NUM_WORDS); watch_clear_colon(); state->position = get_first_pos(state->word_elements_result); display_playing(state); @@ -231,7 +231,7 @@ static void display_title(wordle_state_t *state) { watch_display_string("WO WordLE", 0); } -#if !USE_DAILY_STREAK +#if !WORDLE_USE_DAILY_STREAK static void display_continue_result(bool continuing) { watch_display_string(continuing ? "y" : "n", 9); } @@ -246,7 +246,7 @@ static void display_continue(wordle_state_t *state) { static void display_streak(wordle_state_t *state) { char buf[12]; state->curr_screen = SCREEN_STREAK; -#if USE_DAILY_STREAK +#if WORDLE_USE_DAILY_STREAK if (state->streak > 99) sprintf(buf, "WO St--dy"); else @@ -258,7 +258,7 @@ static void display_streak(wordle_state_t *state) { watch_set_colon(); } -#if USE_DAILY_STREAK +#if WORDLE_USE_DAILY_STREAK static void display_wait(wordle_state_t *state) { state->curr_screen = SCREEN_WAIT; if (state->streak < 40) { @@ -294,7 +294,7 @@ static void display_win(wordle_state_t *state, uint8_t subsecond) { 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] != _num_valid_letters) + if (state->word_elements[i] != WORDLE_NUM_VALID_LETTERS) return true; } return false; @@ -333,7 +333,7 @@ static bool act_on_btn(wordle_state_t *state, const uint8_t pin) { display_playing(state); return true; case SCREEN_TITLE: -#if USE_DAILY_STREAK +#if WORDLE_USE_DAILY_STREAK if (state->prev_day == get_day_unix_time()) { display_wait(state); } @@ -351,7 +351,7 @@ static bool act_on_btn(wordle_state_t *state, const uint8_t pin) { #endif return true; case SCREEN_STREAK: -#if USE_DAILY_STREAK +#if WORDLE_USE_DAILY_STREAK state->curr_day = get_day_unix_time(); #endif reset_board(state); @@ -365,7 +365,7 @@ static bool act_on_btn(wordle_state_t *state, const uint8_t pin) { state->position = get_first_pos(state->word_elements_result); display_playing(state); return true; -#if USE_DAILY_STREAK +#if WORDLE_USE_DAILY_STREAK case SCREEN_WAIT: (void) pin; display_title(state); @@ -396,10 +396,10 @@ static bool act_on_btn(wordle_state_t *state, const uint8_t pin) { } static void get_result(wordle_state_t *state) { -#if !ALLOW_NON_WORD_AND_REPEAT_GUESSES +#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 == _num_words + _num_possible_words) { + if (in_dict == WORDLE_NUM_WORDS + WORDLE_NUM_POSSIBLE_WORDS) { display_not_in_dict(state); return; } @@ -420,7 +420,7 @@ static void get_result(wordle_state_t *state) { state->curr_screen = SCREEN_WIN; if (state->streak < 0x7F) state->streak++; -#if USE_DAILY_STREAK +#if WORDLE_USE_DAILY_STREAK state->prev_day = get_day_unix_time(); #endif return; @@ -436,14 +436,14 @@ static void get_result(wordle_state_t *state) { return; } -#if (USE_RANDOM_GUESS != 0) +#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 < _num_valid_letters; j++) + 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; @@ -471,7 +471,7 @@ void wordle_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void wordle_face_activate(movement_settings_t *settings, void *context) { (void) settings; wordle_state_t *state = (wordle_state_t *)context; -#if USE_DAILY_STREAK +#if WORDLE_USE_DAILY_STREAK uint32_t now = get_day_unix_time() ; if (state->prev_day <= (now + (60 *60 * 24))) state->streak = 0; if (state->curr_day != now) reset_all_elements(state); @@ -525,8 +525,8 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi case EVENT_ALARM_BUTTON_UP: if (act_on_btn(state, BTN_ALARM)) break; display_letter(state, true); - if (state->word_elements[state->position] == _num_valid_letters) break; -#if (USE_RANDOM_GUESS != 0) + if (state->word_elements[state->position] == WORDLE_NUM_VALID_LETTERS) break; +#if (WORDLE_USE_RANDOM_GUESS != 0) if (watch_get_pin_level(BTN_LIGHT) && (state->using_random_guess || (state->attempt == 0 && state->position == 0))) { insert_random_guess(state); diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index a7b34dc..a29301e 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -37,7 +37,7 @@ * 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 USE_DAILY_STREAK is set to True, then the game can only be played once per day, + * 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: @@ -49,7 +49,7 @@ * Else: None * * Alarm Press - * If Playing: If USE_RANDOM_GUESS is set and Light btn held and + * 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 @@ -62,20 +62,20 @@ #define WORDLE_LENGTH 5 #define WORDLE_MAX_ATTEMPTS 6 -#define USE_DAILY_STREAK false -#define 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. +#define WORDLE_USE_DAILY_STREAK false +#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. -/* USE_RANDOM_GUESS +/* 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 */ -#define USE_RANDOM_GUESS 2 +#define WORDLE_USE_RANDOM_GUESS 2 #include "wordle_face_dict.h" -static const uint16_t _num_words = (sizeof(_valid_words) / sizeof(_valid_words[0])); -static const uint16_t _num_possible_words = (sizeof(_possible_words) / sizeof(_possible_words[0])); -static const uint8_t _num_valid_letters = (sizeof(_valid_letters) / sizeof(_valid_letters[0])); +#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, @@ -89,7 +89,7 @@ typedef enum { SCREEN_TITLE, SCREEN_STREAK, SCREEN_CONTINUE, -#if USE_DAILY_STREAK +#if WORDLE_USE_DAILY_STREAK SCREEN_WAIT, #endif SCREEN_RESULT, @@ -104,7 +104,7 @@ 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 !ALLOW_NON_WORD_AND_REPEAT_GUESSES +#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES uint16_t guessed_words[WORDLE_MAX_ATTEMPTS]; #endif uint8_t attempt : 4; @@ -114,8 +114,8 @@ typedef struct { bool continuing : 1; uint8_t streak; WordleScreen curr_screen; - bool known_wrong_letters[_num_valid_letters]; -#if USE_DAILY_STREAK + bool known_wrong_letters[WORDLE_NUM_VALID_LETTERS]; +#if WORDLE_USE_DAILY_STREAK uint32_t prev_day; uint32_t curr_day; #endif From 1b887aea2bc5b85be0184144a7eb89870ab06f92 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 25 Aug 2024 10:03:38 -0400 Subject: [PATCH 084/161] Made skipping a wrong letters a #define --- movement/watch_faces/complication/wordle_face.c | 3 ++- movement/watch_faces/complication/wordle_face.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 6957d5b..945bb76 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -167,7 +167,7 @@ static bool check_word(wordle_state_t *state) { } static void update_known_wrong_letters(wordle_state_t *state) { - +#if WORDLE_SKIP_WRONG_LETTERS 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++) { @@ -176,6 +176,7 @@ static void update_known_wrong_letters(wordle_state_t *state) { } } } +#endif } static void display_attempt(uint8_t attempt) { diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index a29301e..7071c79 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -64,7 +64,7 @@ #define WORDLE_MAX_ATTEMPTS 6 #define WORDLE_USE_DAILY_STREAK false #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. - +#define WORDLE_SKIP_WRONG_LETTERS true // If true, already guessed letters that are known to be wrong will be skipped when cycling /* 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 From fdff6f581a58af966b70acb260883547681a4dfe Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 25 Aug 2024 10:12:08 -0400 Subject: [PATCH 085/161] Name change in python script --- utils/wordle_face/wordle_list.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/utils/wordle_face/wordle_list.py b/utils/wordle_face/wordle_list.py index 68481e0..ea2154c 100644 --- a/utils/wordle_face/wordle_list.py +++ b/utils/wordle_face/wordle_list.py @@ -1163,8 +1163,8 @@ def print_valid_words(letters=alphabet): print("#define WORDLE_LENGTH 5") print("#endif") - print("\n#ifndef USE_RANDOM_GUESS") - print("#define USE_RANDOM_GUESS 2") + print("\n#ifndef WORDLE_USE_RANDOM_GUESS") + print("#define WORDLE_USE_RANDOM_GUESS 2") print("#endif\n") items_per_row = 9 @@ -1197,7 +1197,7 @@ def print_valid_words(letters=alphabet): print("\n// These are words that'll never be used, but still need to be in the dictionary for guesses.") print(f"// Number of words found: {len(possible_words)}") print("static const char _possible_words[][WORDLE_LENGTH + 1] = {") - print("#if !ALLOW_NON_WORD_AND_REPEAT_GUESSES") + print("#if !WORDLE_ALLOW_NON_WORD_AND_REPEAT_GUESSES") i = 0 while i < len(possible_words): print(" ", end='') @@ -1208,9 +1208,9 @@ def print_valid_words(letters=alphabet): print("#endif") print("};\n") - print("#if (USE_RANDOM_GUESS == 2)") + print("#if (WORDLE_USE_RANDOM_GUESS == 2)") print(f"static const uint16_t _num_random_guess_words = {num_uniq}; // The _valid_words array begins with this many words where each letter is different.") - print("#elif (USE_RANDOM_GUESS == 1)") + print("#elif (WORDLE_USE_RANDOM_GUESS == 1)") print("static const uint16_t _num_random_guess_words = _num_words;") print("#endif") print("\n#endif // WORDLE_FACE_DICT_H_") From d98f749f3b583b42659ebd294024daf27f61a376 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 25 Aug 2024 10:35:58 -0400 Subject: [PATCH 086/161] Changed variable names for dictionary header --- movement/watch_faces/complication/wordle_face_dict.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face_dict.h b/movement/watch_faces/complication/wordle_face_dict.h index 00ae3ac..11a89f7 100644 --- a/movement/watch_faces/complication/wordle_face_dict.h +++ b/movement/watch_faces/complication/wordle_face_dict.h @@ -5,8 +5,8 @@ #define WORDLE_LENGTH 5 #endif -#ifndef USE_RANDOM_GUESS -#define USE_RANDOM_GUESS 2 +#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'}; @@ -67,7 +67,7 @@ static const char _valid_words[][WORDLE_LENGTH + 1] = { // 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 !ALLOW_NON_WORD_AND_REPEAT_GUESSES +#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", @@ -282,9 +282,9 @@ static const char _possible_words[][WORDLE_LENGTH + 1] = { #endif }; -#if (USE_RANDOM_GUESS == 2) +#if (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 (USE_RANDOM_GUESS == 1) +#elif (WORDLE_USE_RANDOM_GUESS == 1) static const uint16_t _num_random_guess_words = _num_words; #endif From 733318c036147c989737dc489e1e0ad34a8d5f9b Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 25 Aug 2024 12:18:27 -0400 Subject: [PATCH 087/161] skip_wrong_letter is now a toggle --- .../watch_faces/complication/wordle_face.c | 36 ++++++++++++++----- .../watch_faces/complication/wordle_face.h | 8 ++--- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 945bb76..608fbe5 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -62,18 +62,18 @@ static uint8_t get_prev_pos(uint8_t curr_pos, WordleLetterResult *word_elements_ return curr_pos; } -static void get_next_letter(const uint8_t curr_pos, uint8_t *word_elements, const bool *known_wrong_letters) { +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 (known_wrong_letters[word_elements[curr_pos]]); + } 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) { +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 (known_wrong_letters[word_elements[curr_pos]]); + } while (skip_wrong_letter && known_wrong_letters[word_elements[curr_pos]]); } static void display_letter(wordle_state_t *state, bool display_dash) { @@ -166,8 +166,14 @@ static bool check_word(wordle_state_t *state) { return false; } +static void show_skip_wrong_letter_indicator(bool skipping, WordleScreen curr_screen) { + if (skipping && curr_screen <= SCREEN_CONTINUE) + watch_set_indicator(WATCH_INDICATOR_LAP); + else + watch_clear_indicator(WATCH_INDICATOR_LAP); +} + static void update_known_wrong_letters(wordle_state_t *state) { -#if WORDLE_SKIP_WRONG_LETTERS 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++) { @@ -176,7 +182,6 @@ static void update_known_wrong_letters(wordle_state_t *state) { } } } -#endif } static void display_attempt(uint8_t attempt) { @@ -186,9 +191,10 @@ static void display_attempt(uint8_t attempt) { } static void display_playing(wordle_state_t *state) { + state->curr_screen = SCREEN_PLAYING; + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); display_attempt(state->attempt); display_all_letters(state); - state->curr_screen = SCREEN_PLAYING; } static void reset_all_elements(wordle_state_t *state) { @@ -229,6 +235,7 @@ static void reset_board(wordle_state_t *state) { static void display_title(wordle_state_t *state) { state->curr_screen = SCREEN_TITLE; + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); watch_display_string("WO WordLE", 0); } @@ -239,6 +246,7 @@ static void display_continue_result(bool continuing) { static void display_continue(wordle_state_t *state) { state->curr_screen = SCREEN_CONTINUE; + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); watch_display_string("Cont ", 4); display_continue_result(state->continuing); } @@ -247,6 +255,7 @@ static void display_continue(wordle_state_t *state) { static void display_streak(wordle_state_t *state) { char buf[12]; state->curr_screen = SCREEN_STREAK; + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); #if WORDLE_USE_DAILY_STREAK if (state->streak > 99) sprintf(buf, "WO St--dy"); @@ -262,6 +271,7 @@ static void display_streak(wordle_state_t *state) { #if WORDLE_USE_DAILY_STREAK static void display_wait(wordle_state_t *state) { state->curr_screen = SCREEN_WAIT; + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); if (state->streak < 40) { char buf[13]; sprintf(buf,"WO%2d WaIt ", state->streak); @@ -281,6 +291,7 @@ static uint32_t get_day_unix_time(void) { static void display_lose(wordle_state_t *state, uint8_t subsecond) { char buf[WORDLE_LENGTH + 6]; + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); sprintf(buf," L %s", subsecond % 2 ? _valid_words[state->curr_answer] : " "); watch_display_string(buf, 0); } @@ -288,6 +299,7 @@ static void display_lose(wordle_state_t *state, uint8_t subsecond) { static void display_win(wordle_state_t *state, uint8_t subsecond) { (void) state; char buf[13]; + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); sprintf(buf," W %s ", subsecond % 2 ? "NICE" : "JOb "); watch_display_string(buf, 0); } @@ -322,6 +334,7 @@ static void display_result(wordle_state_t *state, uint8_t subsecond) { break; } } + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); watch_display_string(buf, 5); } @@ -464,6 +477,7 @@ void wordle_face_setup(movement_settings_t *settings, uint8_t watch_face_index, 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. @@ -515,12 +529,16 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi 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); + 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_CONTINUE) { + state->skip_wrong_letter = !state->skip_wrong_letter; + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); + } if (state->curr_screen != SCREEN_PLAYING) break; - get_prev_letter(state->position, state->word_elements, state->known_wrong_letters); + 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: diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index 7071c79..d1c1a54 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -64,7 +64,6 @@ #define WORDLE_MAX_ATTEMPTS 6 #define WORDLE_USE_DAILY_STREAK false #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. -#define WORDLE_SKIP_WRONG_LETTERS true // If true, already guessed letters that are known to be wrong will be skipped when cycling /* 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 @@ -85,13 +84,13 @@ typedef enum { } WordleLetterResult; typedef enum { - SCREEN_PLAYING = 0, - SCREEN_TITLE, + SCREEN_TITLE = 0, SCREEN_STREAK, SCREEN_CONTINUE, #if WORDLE_USE_DAILY_STREAK SCREEN_WAIT, #endif + SCREEN_PLAYING, SCREEN_RESULT, SCREEN_WIN, SCREEN_LOSE, @@ -110,8 +109,9 @@ typedef struct { uint8_t attempt : 4; uint8_t position : 3; bool using_random_guess : 1; - uint16_t curr_answer : 15; + 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]; From c87e814140fd27bf2743d6f1116bdd5b311f8758 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 25 Aug 2024 12:23:35 -0400 Subject: [PATCH 088/161] LAP indicator now dispalys on all screens --- movement/watch_faces/complication/wordle_face.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 608fbe5..ef002ae 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -166,8 +166,8 @@ static bool check_word(wordle_state_t *state) { return false; } -static void show_skip_wrong_letter_indicator(bool skipping, WordleScreen curr_screen) { - if (skipping && curr_screen <= SCREEN_CONTINUE) +static void show_skip_wrong_letter_indicator(bool skipping) { + if (skipping) watch_set_indicator(WATCH_INDICATOR_LAP); else watch_clear_indicator(WATCH_INDICATOR_LAP); @@ -192,7 +192,6 @@ static void display_attempt(uint8_t attempt) { static void display_playing(wordle_state_t *state) { state->curr_screen = SCREEN_PLAYING; - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); display_attempt(state->attempt); display_all_letters(state); } @@ -235,7 +234,6 @@ static void reset_board(wordle_state_t *state) { static void display_title(wordle_state_t *state) { state->curr_screen = SCREEN_TITLE; - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); watch_display_string("WO WordLE", 0); } @@ -246,7 +244,6 @@ static void display_continue_result(bool continuing) { static void display_continue(wordle_state_t *state) { state->curr_screen = SCREEN_CONTINUE; - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); watch_display_string("Cont ", 4); display_continue_result(state->continuing); } @@ -255,7 +252,6 @@ static void display_continue(wordle_state_t *state) { static void display_streak(wordle_state_t *state) { char buf[12]; state->curr_screen = SCREEN_STREAK; - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); #if WORDLE_USE_DAILY_STREAK if (state->streak > 99) sprintf(buf, "WO St--dy"); @@ -271,7 +267,6 @@ static void display_streak(wordle_state_t *state) { #if WORDLE_USE_DAILY_STREAK static void display_wait(wordle_state_t *state) { state->curr_screen = SCREEN_WAIT; - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); if (state->streak < 40) { char buf[13]; sprintf(buf,"WO%2d WaIt ", state->streak); @@ -291,7 +286,6 @@ static uint32_t get_day_unix_time(void) { static void display_lose(wordle_state_t *state, uint8_t subsecond) { char buf[WORDLE_LENGTH + 6]; - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); sprintf(buf," L %s", subsecond % 2 ? _valid_words[state->curr_answer] : " "); watch_display_string(buf, 0); } @@ -299,7 +293,6 @@ static void display_lose(wordle_state_t *state, uint8_t subsecond) { static void display_win(wordle_state_t *state, uint8_t subsecond) { (void) state; char buf[13]; - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); sprintf(buf," W %s ", subsecond % 2 ? "NICE" : "JOb "); watch_display_string(buf, 0); } @@ -334,7 +327,6 @@ static void display_result(wordle_state_t *state, uint8_t subsecond) { break; } } - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); watch_display_string(buf, 5); } @@ -497,6 +489,7 @@ void wordle_face_activate(movement_settings_t *settings, void *context) { state->position = get_first_pos(state->word_elements_result); } movement_request_tick_frequency(2); + show_skip_wrong_letter_indicator(state->skip_wrong_letter); display_title(state); } @@ -535,7 +528,7 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi case EVENT_LIGHT_LONG_PRESS: if (state->curr_screen <= SCREEN_CONTINUE) { state->skip_wrong_letter = !state->skip_wrong_letter; - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); + show_skip_wrong_letter_indicator(state->skip_wrong_letter); } if (state->curr_screen != SCREEN_PLAYING) break; get_prev_letter(state->position, state->word_elements, state->known_wrong_letters, state->skip_wrong_letter); From 4b8bd61408d3afb0d0b7e12fc81f1af40af8092b Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 25 Aug 2024 12:36:04 -0400 Subject: [PATCH 089/161] Added explanation on LAP icon --- movement/watch_faces/complication/wordle_face.c | 2 +- movement/watch_faces/complication/wordle_face.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index ef002ae..6de8cee 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -526,7 +526,7 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi display_letter(state, true); break; case EVENT_LIGHT_LONG_PRESS: - if (state->curr_screen <= SCREEN_CONTINUE) { + if (state->curr_screen < SCREEN_PLAYING) { state->skip_wrong_letter = !state->skip_wrong_letter; show_skip_wrong_letter_indicator(state->skip_wrong_letter); } diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index d1c1a54..a03f9ee 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -57,7 +57,7 @@ * Else: Next screen * Alarm Hold * If Playing: Previous position - * Else: None + * Else: Toggle skipping over letters that have been confirmed to not be in the word (indicated via the LAP icon) */ #define WORDLE_LENGTH 5 From 255ea97cc403aa70caa17619fee2927d201f6545 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 25 Aug 2024 12:38:08 -0400 Subject: [PATCH 090/161] Documentation fix --- movement/watch_faces/complication/wordle_face.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index a03f9ee..0d73a9b 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -46,7 +46,7 @@ * Else: Next screen * Light Hold * If Playing: Previous letter - * Else: None + * Else: Toggle skipping over letters that have been confirmed to not be in the word (indicated via the LAP icon) * * Alarm Press * If Playing: If WORDLE_USE_RANDOM_GUESS is set and Light btn held and @@ -57,7 +57,7 @@ * Else: Next screen * Alarm Hold * If Playing: Previous position - * Else: Toggle skipping over letters that have been confirmed to not be in the word (indicated via the LAP icon) + * Else: None */ #define WORDLE_LENGTH 5 From 8205abe5be65b15b60bb36ac32c59156f3133cd7 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Mon, 26 Aug 2024 19:52:15 -0400 Subject: [PATCH 091/161] Revert "LAP indicator now dispalys on all screens" This reverts commit 3bfa336b4d609668f6d8c71164f8f579f41240a5. --- movement/watch_faces/complication/wordle_face.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 6de8cee..ca62f30 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -166,8 +166,8 @@ static bool check_word(wordle_state_t *state) { return false; } -static void show_skip_wrong_letter_indicator(bool skipping) { - if (skipping) +static void show_skip_wrong_letter_indicator(bool skipping, WordleScreen curr_screen) { + if (skipping && curr_screen <= SCREEN_CONTINUE) watch_set_indicator(WATCH_INDICATOR_LAP); else watch_clear_indicator(WATCH_INDICATOR_LAP); @@ -192,6 +192,7 @@ static void display_attempt(uint8_t attempt) { static void display_playing(wordle_state_t *state) { state->curr_screen = SCREEN_PLAYING; + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); display_attempt(state->attempt); display_all_letters(state); } @@ -234,6 +235,7 @@ static void reset_board(wordle_state_t *state) { static void display_title(wordle_state_t *state) { state->curr_screen = SCREEN_TITLE; + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); watch_display_string("WO WordLE", 0); } @@ -244,6 +246,7 @@ static void display_continue_result(bool continuing) { static void display_continue(wordle_state_t *state) { state->curr_screen = SCREEN_CONTINUE; + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); watch_display_string("Cont ", 4); display_continue_result(state->continuing); } @@ -252,6 +255,7 @@ static void display_continue(wordle_state_t *state) { static void display_streak(wordle_state_t *state) { char buf[12]; state->curr_screen = SCREEN_STREAK; + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); #if WORDLE_USE_DAILY_STREAK if (state->streak > 99) sprintf(buf, "WO St--dy"); @@ -267,6 +271,7 @@ static void display_streak(wordle_state_t *state) { #if WORDLE_USE_DAILY_STREAK static void display_wait(wordle_state_t *state) { state->curr_screen = SCREEN_WAIT; + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); if (state->streak < 40) { char buf[13]; sprintf(buf,"WO%2d WaIt ", state->streak); @@ -286,6 +291,7 @@ static uint32_t get_day_unix_time(void) { static void display_lose(wordle_state_t *state, uint8_t subsecond) { char buf[WORDLE_LENGTH + 6]; + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); sprintf(buf," L %s", subsecond % 2 ? _valid_words[state->curr_answer] : " "); watch_display_string(buf, 0); } @@ -293,6 +299,7 @@ static void display_lose(wordle_state_t *state, uint8_t subsecond) { static void display_win(wordle_state_t *state, uint8_t subsecond) { (void) state; char buf[13]; + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); sprintf(buf," W %s ", subsecond % 2 ? "NICE" : "JOb "); watch_display_string(buf, 0); } @@ -327,6 +334,7 @@ static void display_result(wordle_state_t *state, uint8_t subsecond) { break; } } + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); watch_display_string(buf, 5); } @@ -489,7 +497,6 @@ void wordle_face_activate(movement_settings_t *settings, void *context) { state->position = get_first_pos(state->word_elements_result); } movement_request_tick_frequency(2); - show_skip_wrong_letter_indicator(state->skip_wrong_letter); display_title(state); } @@ -528,7 +535,7 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi 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); + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); } if (state->curr_screen != SCREEN_PLAYING) break; get_prev_letter(state->position, state->word_elements, state->known_wrong_letters, state->skip_wrong_letter); From b364a6cfabc78dd9469286c6ff87cfd04a9ce571 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Mon, 26 Aug 2024 21:10:00 -0400 Subject: [PATCH 092/161] Changed the lap to hard mode; fixed the ignore used letters --- .../watch_faces/complication/wordle_face.c | 41 +++++++++++-------- .../watch_faces/complication/wordle_face.h | 10 +++-- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index ca62f30..164c784 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -167,17 +167,29 @@ static bool check_word(wordle_state_t *state) { } static void show_skip_wrong_letter_indicator(bool skipping, WordleScreen curr_screen) { - if (skipping && curr_screen <= SCREEN_CONTINUE) - watch_set_indicator(WATCH_INDICATOR_LAP); + if (curr_screen >= SCREEN_PLAYING) return; + if (skipping) + watch_display_string("H", 3); else - watch_clear_indicator(WATCH_INDICATOR_LAP); + 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) + if (state->word_elements[i] == j && !wrong_loc[j]) state->known_wrong_letters[j] = true; } } @@ -192,7 +204,6 @@ static void display_attempt(uint8_t attempt) { static void display_playing(wordle_state_t *state) { state->curr_screen = SCREEN_PLAYING; - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); display_attempt(state->attempt); display_all_letters(state); } @@ -235,8 +246,8 @@ static void reset_board(wordle_state_t *state) { static void display_title(wordle_state_t *state) { state->curr_screen = SCREEN_TITLE; - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); watch_display_string("WO WordLE", 0); + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); } #if !WORDLE_USE_DAILY_STREAK @@ -246,8 +257,8 @@ static void display_continue_result(bool continuing) { static void display_continue(wordle_state_t *state) { state->curr_screen = SCREEN_CONTINUE; - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); watch_display_string("Cont ", 4); + show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); display_continue_result(state->continuing); } #endif @@ -255,7 +266,6 @@ static void display_continue(wordle_state_t *state) { static void display_streak(wordle_state_t *state) { char buf[12]; state->curr_screen = SCREEN_STREAK; - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); #if WORDLE_USE_DAILY_STREAK if (state->streak > 99) sprintf(buf, "WO St--dy"); @@ -266,12 +276,12 @@ static void display_streak(wordle_state_t *state) { #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 static void display_wait(wordle_state_t *state) { state->curr_screen = SCREEN_WAIT; - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); if (state->streak < 40) { char buf[13]; sprintf(buf,"WO%2d WaIt ", state->streak); @@ -280,6 +290,7 @@ static void display_wait(wordle_state_t *state) { 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); } static uint32_t get_day_unix_time(void) { @@ -291,7 +302,6 @@ static uint32_t get_day_unix_time(void) { static void display_lose(wordle_state_t *state, uint8_t subsecond) { char buf[WORDLE_LENGTH + 6]; - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); sprintf(buf," L %s", subsecond % 2 ? _valid_words[state->curr_answer] : " "); watch_display_string(buf, 0); } @@ -299,7 +309,6 @@ static void display_lose(wordle_state_t *state, uint8_t subsecond) { static void display_win(wordle_state_t *state, uint8_t subsecond) { (void) state; char buf[13]; - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); sprintf(buf," W %s ", subsecond % 2 ? "NICE" : "JOb "); watch_display_string(buf, 0); } @@ -334,7 +343,6 @@ static void display_result(wordle_state_t *state, uint8_t subsecond) { break; } } - show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); watch_display_string(buf, 5); } @@ -533,10 +541,6 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi 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); - } 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); @@ -559,6 +563,11 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi } break; case EVENT_ALARM_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; display_letter(state, true); state->position = get_prev_pos(state->position, state->word_elements_result); diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index 0d73a9b..2a3cc1d 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -46,18 +46,22 @@ * Else: Next screen * Light Hold * If Playing: Previous letter - * Else: Toggle skipping over letters that have been confirmed to not be in the word (indicated via the LAP icon) + * Else: None * * 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 - * Next position * Else: Next screen * Alarm Hold * If Playing: Previous position - * Else: None + * 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) + * + * 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 From 41df6c113f86fc6a5477f56f274ed1419e216d3e Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Mon, 26 Aug 2024 22:24:33 -0400 Subject: [PATCH 093/161] Reset streak if don't continue --- movement/watch_faces/complication/wordle_face.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 164c784..09b5dc2 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -401,6 +401,7 @@ static bool act_on_btn(wordle_state_t *state, const uint8_t pin) { display_playing(state); else { reset_board(state); + state->streak = 0; display_streak(state); } break; @@ -496,7 +497,7 @@ void wordle_face_activate(movement_settings_t *settings, void *context) { wordle_state_t *state = (wordle_state_t *)context; #if WORDLE_USE_DAILY_STREAK uint32_t now = get_day_unix_time() ; - if (state->prev_day <= (now + (60 *60 * 24))) state->streak = 0; + if (now >= (state->prev_day + (60 *60 * 24))) state->streak = 0; if (state->curr_day != now) reset_all_elements(state); #endif state->using_random_guess = false; From c43820e75d225d56093f132295dbfef45b77e1d9 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Tue, 27 Aug 2024 12:08:24 -0400 Subject: [PATCH 094/161] Wordle game resets after 24hrs of not playing when not using daily streak --- .../watch_faces/complication/wordle_face.c | 18 ++++++++++++------ .../watch_faces/complication/wordle_face.h | 5 +++-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 09b5dc2..1cf931b 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -25,9 +25,7 @@ #include #include #include "wordle_face.h" -#if WORDLE_USE_DAILY_STREAK #include "watch_utility.h" -#endif static uint32_t get_random(uint32_t max) { #if __EMSCRIPTEN__ @@ -298,6 +296,11 @@ static uint32_t get_day_unix_time(void) { now.unit.hour = now.unit.minute = now.unit.second = 0; return watch_utility_date_time_to_unix_time(now, 0); } +#else +static uint32_t get_day_unix_time(void) { + watch_date_time now = watch_rtc_get_date_time(); + return watch_utility_date_time_to_unix_time(now, 0); +} #endif static void display_lose(wordle_state_t *state, uint8_t subsecond) { @@ -373,9 +376,7 @@ static bool act_on_btn(wordle_state_t *state, const uint8_t pin) { #endif return true; case SCREEN_STREAK: -#if WORDLE_USE_DAILY_STREAK state->curr_day = get_day_unix_time(); -#endif reset_board(state); return true; case SCREEN_WIN: @@ -495,10 +496,15 @@ void wordle_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void wordle_face_activate(movement_settings_t *settings, void *context) { (void) settings; wordle_state_t *state = (wordle_state_t *)context; + uint32_t now = get_day_unix_time(); #if WORDLE_USE_DAILY_STREAK - uint32_t now = get_day_unix_time() ; - if (now >= (state->prev_day + (60 *60 * 24))) state->streak = 0; if (state->curr_day != now) reset_all_elements(state); + if (now >= (state->prev_day + (60 *60 * 24))) state->streak = 0; +#else + if (is_playing(state) && now >= (state->curr_day + (60 *60 * 24))) { + state->streak = 0; + reset_board(state); + } #endif state->using_random_guess = false; if (is_playing(state) && state->curr_screen >= SCREEN_RESULT) { diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index 2a3cc1d..ffb3782 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -66,7 +66,8 @@ #define WORDLE_LENGTH 5 #define WORDLE_MAX_ATTEMPTS 6 -#define WORDLE_USE_DAILY_STREAK false +#define WORDLE_USE_DAILY_STREAK false // If true, the board will reset daily and the streak will go to zero if the game isn't played for a day + // If false, then the streak will still reset if the game is not completed within 24 hours #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 @@ -119,9 +120,9 @@ typedef struct { uint8_t streak; WordleScreen curr_screen; bool known_wrong_letters[WORDLE_NUM_VALID_LETTERS]; + uint32_t curr_day; #if WORDLE_USE_DAILY_STREAK uint32_t prev_day; - uint32_t curr_day; #endif } wordle_state_t; From d1c19166a11d90cf9a08d8772ca6aebe28d4e958 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Tue, 27 Aug 2024 12:31:22 -0400 Subject: [PATCH 095/161] WORDLE_USE_DAILY_STREAK logic changed --- .../watch_faces/complication/wordle_face.c | 37 ++++++++----------- .../watch_faces/complication/wordle_face.h | 17 ++++++--- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index 1cf931b..bcab8f0 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -248,7 +248,7 @@ static void display_title(wordle_state_t *state) { show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); } -#if !WORDLE_USE_DAILY_STREAK +#if WORDLE_USE_DAILY_STREAK != 2 static void display_continue_result(bool continuing) { watch_display_string(continuing ? "y" : "n", 9); } @@ -264,7 +264,7 @@ static void display_continue(wordle_state_t *state) { static void display_streak(wordle_state_t *state) { char buf[12]; state->curr_screen = SCREEN_STREAK; -#if WORDLE_USE_DAILY_STREAK +#if WORDLE_USE_DAILY_STREAK != 2 if (state->streak > 99) sprintf(buf, "WO St--dy"); else @@ -277,7 +277,7 @@ static void display_streak(wordle_state_t *state) { show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); } -#if WORDLE_USE_DAILY_STREAK +#if WORDLE_USE_DAILY_STREAK == 2 static void display_wait(wordle_state_t *state) { state->curr_screen = SCREEN_WAIT; if (state->streak < 40) { @@ -290,18 +290,15 @@ static void display_wait(wordle_state_t *state) { } show_skip_wrong_letter_indicator(state->skip_wrong_letter, state->curr_screen); } +#endif static uint32_t get_day_unix_time(void) { watch_date_time now = watch_rtc_get_date_time(); +#if WORDLE_USE_DAILY_STREAK == 2 now.unit.hour = now.unit.minute = now.unit.second = 0; - return watch_utility_date_time_to_unix_time(now, 0); -} -#else -static uint32_t get_day_unix_time(void) { - watch_date_time now = watch_rtc_get_date_time(); - return watch_utility_date_time_to_unix_time(now, 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]; @@ -358,8 +355,8 @@ static bool act_on_btn(wordle_state_t *state, const uint8_t pin) { display_playing(state); return true; case SCREEN_TITLE: -#if WORDLE_USE_DAILY_STREAK - if (state->prev_day == get_day_unix_time()) { +#if WORDLE_USE_DAILY_STREAK == 2 + if (state->day_last_game_started == get_day_unix_time()) { display_wait(state); } else if (is_playing(state)) @@ -376,7 +373,7 @@ static bool act_on_btn(wordle_state_t *state, const uint8_t pin) { #endif return true; case SCREEN_STREAK: - state->curr_day = get_day_unix_time(); + state->day_last_game_started = get_day_unix_time(); reset_board(state); return true; case SCREEN_WIN: @@ -388,7 +385,7 @@ static bool act_on_btn(wordle_state_t *state, const uint8_t pin) { state->position = get_first_pos(state->word_elements_result); display_playing(state); return true; -#if WORDLE_USE_DAILY_STREAK +#if WORDLE_USE_DAILY_STREAK == 2 case SCREEN_WAIT: (void) pin; display_title(state); @@ -444,8 +441,8 @@ static void get_result(wordle_state_t *state) { state->curr_screen = SCREEN_WIN; if (state->streak < 0x7F) state->streak++; -#if WORDLE_USE_DAILY_STREAK - state->prev_day = get_day_unix_time(); +#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; } @@ -496,12 +493,10 @@ void wordle_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void wordle_face_activate(movement_settings_t *settings, void *context) { (void) settings; wordle_state_t *state = (wordle_state_t *)context; +#if WORDLE_USE_DAILY_STREAK != 0 uint32_t now = get_day_unix_time(); -#if WORDLE_USE_DAILY_STREAK - if (state->curr_day != now) reset_all_elements(state); - if (now >= (state->prev_day + (60 *60 * 24))) state->streak = 0; -#else - if (is_playing(state) && now >= (state->curr_day + (60 *60 * 24))) { + if (now >= (state->day_last_game_started + (60 *60 * 24)) && + (is_playing(state) || WORDLE_USE_DAILY_STREAK == 2)) { state->streak = 0; reset_board(state); } diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index ffb3782..b035f14 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -66,8 +66,16 @@ #define WORDLE_LENGTH 5 #define WORDLE_MAX_ATTEMPTS 6 -#define WORDLE_USE_DAILY_STREAK false // If true, the board will reset daily and the streak will go to zero if the game isn't played for a day - // If false, then the streak will still reset if the game is not completed within 24 hours +/* 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 @@ -120,10 +128,7 @@ typedef struct { uint8_t streak; WordleScreen curr_screen; bool known_wrong_letters[WORDLE_NUM_VALID_LETTERS]; - uint32_t curr_day; -#if WORDLE_USE_DAILY_STREAK - uint32_t prev_day; -#endif + uint32_t day_last_game_started; } wordle_state_t; void wordle_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); From 5435bc7f34350337fde476cbda2bba067ab29611 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Tue, 27 Aug 2024 13:08:13 -0400 Subject: [PATCH 096/161] Streak face fix --- movement/watch_faces/complication/wordle_face.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index bcab8f0..daaac20 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -264,7 +264,7 @@ static void display_continue(wordle_state_t *state) { static void display_streak(wordle_state_t *state) { char buf[12]; state->curr_screen = SCREEN_STREAK; -#if WORDLE_USE_DAILY_STREAK != 2 +#if WORDLE_USE_DAILY_STREAK == 2 if (state->streak > 99) sprintf(buf, "WO St--dy"); else From dd719183cf9af58e3edbf487575c7ee2b6649ac8 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Wed, 28 Aug 2024 21:38:09 -0400 Subject: [PATCH 097/161] hard mode btn changed; logic changed on daily streak so if puzzle wasn't started and completed the previous day, then drop the streak --- movement/watch_faces/complication/wordle_face.c | 15 ++++++++------- movement/watch_faces/complication/wordle_face.h | 6 +++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.c b/movement/watch_faces/complication/wordle_face.c index daaac20..8381cec 100644 --- a/movement/watch_faces/complication/wordle_face.c +++ b/movement/watch_faces/complication/wordle_face.c @@ -495,8 +495,9 @@ void wordle_face_activate(movement_settings_t *settings, void *context) { wordle_state_t *state = (wordle_state_t *)context; #if WORDLE_USE_DAILY_STREAK != 0 uint32_t now = get_day_unix_time(); - if (now >= (state->day_last_game_started + (60 *60 * 24)) && - (is_playing(state) || WORDLE_USE_DAILY_STREAK == 2)) { + 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); } @@ -543,6 +544,11 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi 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); @@ -565,11 +571,6 @@ bool wordle_face_loop(movement_event_t event, movement_settings_t *settings, voi } break; case EVENT_ALARM_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; display_letter(state, true); state->position = get_prev_pos(state->position, state->word_elements_result); diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index b035f14..1b9a7fb 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -46,7 +46,8 @@ * Else: Next screen * Light Hold * If Playing: Previous letter - * Else: None + * 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 @@ -56,8 +57,7 @@ * Else: Next screen * Alarm Hold * If Playing: Previous position - * 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) + * 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 From 4dedcb3a6d6914e7e9d5d20eb534b73df58c15b1 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Tue, 3 Sep 2024 10:15:48 -0400 Subject: [PATCH 098/161] Added ability to find best starting word --- utils/wordle_face/wordle_list.py | 84 +++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 17 deletions(-) diff --git a/utils/wordle_face/wordle_list.py b/utils/wordle_face/wordle_list.py index ea2154c..39e310c 100644 --- a/utils/wordle_face/wordle_list.py +++ b/utils/wordle_face/wordle_list.py @@ -1,4 +1,4 @@ -import random, itertools, time, ast +import random, itertools, time, numpy source_link = "https://matthewminer.name/projects/calculators/wordle-words-left/" valid_list = [ @@ -1091,12 +1091,12 @@ possible_list = [ alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] -def most_used_letters(words=valid_list): +def most_used_letters(words=valid_list, letters=alphabet, print_result=True): ''' Outputs how many times each letter is used in the words array. ''' use_each_letter = {} - for i in alphabet: + for i in letters: count = 0 for word in words: for letter in word: @@ -1105,10 +1105,11 @@ def most_used_letters(words=valid_list): break use_each_letter[i] = count use_each_letter = dict(sorted(use_each_letter.items(), key=lambda item: item[1], reverse=True)) - print("Letter | Usage | Percent") - print("----------------------------") - for k in use_each_letter: - print(f"{k.upper()} | {use_each_letter[k]:5} | {round((100 * use_each_letter[k]) / len(words)):2}%") + if print_result: + print("Letter | Usage | Percent") + print("----------------------------") + for k in use_each_letter: + print(f"{k.upper()} | {use_each_letter[k]:5} | {round((100 * use_each_letter[k]) / len(words)):2}%") return use_each_letter @@ -1242,7 +1243,7 @@ def txt_of_all_letter_combos(num_letters_in_set, words=valid_list, min_letter_oc prev = time.time() start = prev letters_to_ignore = ['D','M'] # Don't diplay well on the watch - letter_usage = most_used_letters(words=words) + letter_usage = most_used_letters(words=words, print_result=False) for letter in letter_usage: if (100 * letter_usage[letter])/len(words) < min_letter_occ_percent_to_consider: letters_to_ignore.append(letter) @@ -1276,7 +1277,7 @@ def txt_of_all_letter_combos(num_letters_in_set, words=valid_list, min_letter_oc dict_combos_counts = dict(sorted(dict_combos_counts.items(), key=lambda item: item[1], reverse=True)) most_common_key = next(iter(dict_combos_counts)) - print(f"The Most Common Combo is: {most_common_key} with {dict_combos_counts[most_common_key]} words.") + print(f"The Most Common Combo for {num_letters_in_set} letters is: {most_common_key} with {dict_combos_counts[most_common_key]} words.") # print_valid_words(ast.literal_eval(most_common_key)) # Uncomment to display the text it creates if txt_out: @@ -1304,9 +1305,11 @@ def txt_of_all_letter_combos_differing_sizes(min=9, max=15, num_combos_print=20, print(f"{key}, {item}") -def location_of_letters(letters=alphabet, list=valid_list): - print(" 1 2 3 4 5 ") - print("-----------------------------------------") +def location_of_letters(letters=alphabet, list=valid_list, print_result=True): + letter_location_percentages = {} + if print_result: + print(" 1 2 3 4 5 ") + print("-----------------------------------------") letters = sorted(letters) for letter in letters: location = [0, 0, 0, 0, 0] @@ -1314,15 +1317,62 @@ def location_of_letters(letters=alphabet, list=valid_list): for i, char in enumerate(word): if char.upper() == letter.upper(): location[i]+=1 - location = [f"{round((100 * x) / sum(location)):2}%" for x in location] - print(f"{letter} : {location}") + location = [((100 * x) / sum(location)) for x in location] + letter_location_percentages[letter] = location + location_txt = [f"{round(x):2}%" for x in location] + if print_result: + print(f"{letter} : {location_txt}") + return letter_location_percentages + + +def best_first_word(letters=alphabet, list=valid_list, print_result=True, words_to_print=None): + ''' + Word_good has every word with only unique letters as keys and that values are: + 1. Take the usage of every letter, normalize the max to 100 and the min to 0. + 2. Go through each letter in the word and see how often that letter appears in that exact location. + 3. Multiply that occurrance in location with the normalized total usage. + 4. Do this for each letter and add it all together. + + Ex: SLATE + Normalized usage: S=38, L=42, A=79, T=46, E=100 + S in position 1: 55% + L in position 2: 27% + A in position 3: 31% + T in position 4: 19% + E in position 5: 34% + Total = (38*55) + (42*27) + (79*31) + (46*19) + (100*35) = 10047 + ''' + valid_words = list_of_valid_words(letters, list) + letter_usage = most_used_letters(words=list, letters=letters, print_result=False) + a=[[max(letter_usage.values()),1],[min(letter_usage.values()),1]] + b=[100,0] + m,b = numpy.linalg.solve(a,b).tolist() + letter_usage_normalized = {key: ((m * value) + b) for key, value in letter_usage.items()} + loc_letters = location_of_letters(letters=letters, list=list, print_result=False) + word_good = {} + valid_words_unique = [word for word in valid_words if len(word) == len(set(word))] + for word in valid_words_unique: + usage = 0 + for i, char in enumerate(word): + usage += loc_letters[char][i] * letter_usage_normalized[char] + word_good[word] = round(usage) + word_good = {k: v for k, v in sorted(word_good.items(), key=lambda item: item[1], reverse=True)} + if print_result: + print("Word, Usage Value") + print("------------------") + for i,[k,v] in enumerate(word_good.items()): + if words_to_print is not None and i+1 > words_to_print: + break + print(f"{k}, {v}") + return word_good if __name__ == "__main__": my_letters = ['A', 'C', 'E', 'H', 'I', 'L', 'N', 'O', 'P', 'R', 'S', 'T'] #print(f"{len(list_of_valid_words(my_letters, valid_list))} Words can be made with {my_letters}") - #most_used_letters() - #location_of_letters(my_letters) + #most_used_letters(letters=my_letters) + #location_of_letters(letters=my_letters) print_valid_words(my_letters) #txt_of_all_letter_combos_differing_sizes(max = 16, min=10) - #txt_of_all_letter_combos(14) \ No newline at end of file + #txt_of_all_letter_combos(14) + #best_first_word(letters=my_letters, print_result=True, words_to_print=10) \ No newline at end of file From 73a975d0d9e3045102ce74c9d4489ff0a19876ef Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Tue, 16 Jul 2024 08:50:11 -0400 Subject: [PATCH 099/161] Added endless-runner face --- movement/make/Makefile | 1 + movement/movement_faces.h | 1 + .../complication/endless_runner_face.c | 408 ++++++++++++++++++ .../complication/endless_runner_face.h | 63 +++ 4 files changed, 473 insertions(+) create mode 100644 movement/watch_faces/complication/endless_runner_face.c create mode 100644 movement/watch_faces/complication/endless_runner_face.h diff --git a/movement/make/Makefile b/movement/make/Makefile index da5486b..7114856 100644 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -129,6 +129,7 @@ SRCS += \ ../watch_faces/clock/minute_repeater_decimal_face.c \ ../watch_faces/complication/tuning_tones_face.c \ ../watch_faces/complication/kitchen_conversions_face.c \ + ../watch_faces/complication/endless_runner_face.c \ # New watch faces go above this line. # Leave this line at the bottom of the file; it has all the targets for making your project. diff --git a/movement/movement_faces.h b/movement/movement_faces.h index 3557110..74856a6 100644 --- a/movement/movement_faces.h +++ b/movement/movement_faces.h @@ -104,6 +104,7 @@ #include "minute_repeater_decimal_face.h" #include "tuning_tones_face.h" #include "kitchen_conversions_face.h" +#include "endless_runner_face.h" // New includes go above this line. #endif // MOVEMENT_FACES_H_ diff --git a/movement/watch_faces/complication/endless_runner_face.c b/movement/watch_faces/complication/endless_runner_face.c new file mode 100644 index 0000000..6b2bb01 --- /dev/null +++ b/movement/watch_faces/complication/endless_runner_face.c @@ -0,0 +1,408 @@ +/* + * MIT License + * + * Copyright (c) 2024 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "endless_runner_face.h" + +typedef enum { + NOT_JUMPING = 0, + JUMP, + JUMPING_1, + JUMPING_2, + JUMP_COUNT +} ScrollingJumpState; + +typedef enum { + SCREEN_TITLE = 0, + SCREEN_PLAYING, + SCREEN_LOSE, + SCREEN_COUNT +} ScrollingCurrScreen; + +typedef enum { + DIFF_NORM = 0, // 8x speed; 4 0's min; + DIFF_HARD, // 8x speed; 3 0's min; 2 - Easy 4x speed; 4 0's min + DIFF_EASY, // 4x speed; 4 0's min + DIFF_COUNT +} ScrollingDifficulty; + +#define NUM_GRID 12 +#define FREQ 8 +#define FREQ_EASY 4 +#define MAX_DISP_SCORE 39 // The top-right digits can't properly display above 39 + +typedef struct { + uint32_t obst_pattern; + int16_t obst_indx : 8; + int16_t jump_state : 3; + int16_t sec_before_moves : 3; + bool loc_2_on; + bool loc_3_on; +} game_state_t; + +static game_state_t game_state; +static const uint8_t _num_bits_obst_pattern = sizeof(game_state.obst_pattern) * 8; + +static void print_binary(uint32_t value, int bits) { + for (int i = bits - 1; i >= 0; i--) { + // Print each bit + printf("%d", (value >> i) & 1); + // Optional: add a space every 4 bits for readability + if (i % 4 == 0 && i != 0) { + printf(" "); + } + } + printf("\r\n"); +} + +static uint32_t get_random(uint32_t max) { + #if __EMSCRIPTEN__ + return rand() % max; + #else + return arc4random_uniform(max); + #endif +} + +static uint32_t get_random_legal(uint32_t prev_val, uint16_t difficulty) { +/** @brief A legal random number starts with the previous number (which should be the 12 bits on the screen). + * @param prev_val The previous value to tack onto. The return will have its first NUM_GRID MSBs be the same as prev_val, and the rest be new + * @param difficulty To dictate how spread apart the obsticles must be + * @return the new random value, where it's first NUM_GRID MSBs are the same as prev_val + */ + uint8_t min_zeros = difficulty == DIFF_HARD ? 3 : 4; + uint32_t max = (1 << (_num_bits_obst_pattern - NUM_GRID)) - 1; + uint32_t rand = get_random(max); + uint32_t rand_legal = 0; + prev_val = prev_val & ~max; + + for (int i = (NUM_GRID + 1); i <= _num_bits_obst_pattern; i++) { + uint32_t mask = 1 << (_num_bits_obst_pattern - i); + bool msb = (rand & mask) >> (_num_bits_obst_pattern - i); + if (msb) { + rand_legal = rand_legal << min_zeros; + i+=min_zeros; + } + rand_legal |= msb; + rand_legal = rand_legal << 1; + } + + rand_legal = rand_legal & max; + for (int i = 0; i <= min_zeros; i++) { + if (prev_val & (1 << (i + _num_bits_obst_pattern - NUM_GRID))){ + rand_legal = rand_legal >> (min_zeros - i); + break; + } + } + rand_legal = prev_val | rand_legal; + print_binary(rand_legal, _num_bits_obst_pattern); + return rand_legal; +} + +static void display_ball(bool jumping) { + if (jumping == NOT_JUMPING) { + watch_set_pixel(0, 21); + watch_set_pixel(1, 21); + watch_set_pixel(0, 20); + watch_set_pixel(1, 20); + watch_clear_pixel(1, 17); + watch_clear_pixel(2, 20); + watch_clear_pixel(2, 21); + } + else { + watch_clear_pixel(0, 21); + watch_clear_pixel(1, 21); + watch_clear_pixel(0, 20); + watch_set_pixel(1, 20); + watch_set_pixel(1, 17); + watch_set_pixel(2, 20); + watch_set_pixel(2, 21); + } +} + +static void display_score(uint8_t score) { + char buf[3]; + if (score > MAX_DISP_SCORE) watch_display_string(" -", 2); + else{ + sprintf(buf, "%2d", score); + watch_display_string(buf, 2); + } +} + +static void display_difficulty(uint16_t difficulty) { + switch (difficulty) + { + case DIFF_EASY: + watch_display_string("E", 9); + break; + case DIFF_HARD: + watch_display_string("H", 9); + break; + case DIFF_NORM: + default: + watch_display_string("n", 9); + break; + } +} + +static void display_title(endless_runner_state_t *state) { + state -> curr_screen = SCREEN_TITLE; + memset(&game_state, 0, sizeof(game_state)); + game_state.sec_before_moves = 1; // The first obstacles will all be 0s, which is about an extra second of delay. + if (state -> soundOn) game_state.sec_before_moves--; // Start chime is about 1 second + watch_display_string("SC SEL ", 0); + display_score(state -> hi_score); + display_difficulty(state -> difficulty); +} + +static void display_lose_screen(endless_runner_state_t *state) { + movement_request_tick_frequency(1); + state -> curr_screen = SCREEN_LOSE; + state -> curr_score = 0; + watch_display_string(" LOSEr", 4); + if (state -> soundOn) + watch_buzzer_play_note(BUZZER_NOTE_A1, 600); + else + delay_ms(600); +} + +static bool display_obstacle(bool obstacle, int grid_loc, endless_runner_state_t *state) { + bool success_jump = false; + switch (grid_loc) + { + case 2: + game_state.loc_2_on = obstacle; + if (obstacle) + watch_set_pixel(0, 20); + else if (game_state.jump_state != NOT_JUMPING) + watch_clear_pixel(0, 20); + break; + case 3: + game_state.loc_3_on = obstacle; + if (obstacle) + watch_set_pixel(1, 21); + else if (game_state.jump_state != NOT_JUMPING) + watch_clear_pixel(1, 21); + break; + + case 1: + if (obstacle) { // If an obstacle is here, it means the ball cleared it + success_jump = true; + if (state -> curr_score < MAX_DISP_SCORE) { + state -> curr_score++; + if (state -> curr_score > state -> hi_score) + state -> hi_score = state -> curr_score; + display_score(state -> curr_score); + } + } + //fall through + case 0: + case 5: + if (obstacle) + watch_set_pixel(0, 18 + grid_loc); + else + watch_clear_pixel(0, 18 + grid_loc); + break; + case 4: + if (obstacle) + watch_set_pixel(1, 22); + else + watch_clear_pixel(1, 22); + break; + case 6: + if (obstacle) + watch_set_pixel(1, 0); + else + watch_clear_pixel(1, 0); + break; + case 7: + case 8: + if (obstacle) + watch_set_pixel(0, grid_loc - 6); + else + watch_clear_pixel(0, grid_loc - 6); + break; + case 9: + case 10: + if (obstacle) + watch_set_pixel(0, grid_loc - 5); + else + watch_clear_pixel(0, grid_loc - 5); + break; + case 11: + if (obstacle) + watch_set_pixel(1, 6); + else + watch_clear_pixel(1, 6); + break; + default: + break; + } + return success_jump; +} + +static bool display_obstacles(endless_runner_state_t *state) { + bool success_jump = false; + for (int i = 0; i < NUM_GRID; i++) { + // Use a bitmask to isolate each bit and shift it to the least significant position + uint32_t mask = 1 << ((_num_bits_obst_pattern - 1) - i); + bool obstacle = (game_state.obst_pattern & mask) >> ((_num_bits_obst_pattern - 1) - i); + if (display_obstacle(obstacle, i, state)) success_jump = true; + + } + game_state.obst_pattern = game_state.obst_pattern << 1; + game_state.obst_indx++; + if (game_state.obst_indx >= _num_bits_obst_pattern - NUM_GRID) { + game_state.obst_indx = 0; + game_state.obst_pattern = get_random_legal(game_state.obst_pattern, state -> difficulty); + } + return success_jump; +} + +void endless_runner_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { + (void) settings; + (void) watch_face_index; + if (*context_ptr == NULL) { + *context_ptr = malloc(sizeof(endless_runner_state_t)); + memset(*context_ptr, 0, sizeof(endless_runner_state_t)); + } +} + +void endless_runner_face_activate(movement_settings_t *settings, void *context) { + (void) settings; + (void) context; +} + +bool endless_runner_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { + endless_runner_state_t *state = (endless_runner_state_t *)context; + bool success_jump = false; + + switch (event.event_type) { + case EVENT_ACTIVATE: + state -> curr_screen = SCREEN_TITLE; + if (state -> soundOn) watch_set_indicator(WATCH_INDICATOR_BELL); + display_title(state); + state -> curr_score = 0; + break; + case EVENT_TICK: + switch (state -> curr_screen) + { + case SCREEN_TITLE: + case SCREEN_LOSE: + break; + default: + if (game_state.sec_before_moves == 0) + success_jump = display_obstacles(state); + else if (event.subsecond == 0) + if(--game_state.sec_before_moves == 0) game_state.obst_pattern = get_random_legal(0, state -> difficulty); + switch (game_state.jump_state) + { + case JUMP: + game_state.jump_state = JUMPING_1; + break; + case JUMPING_1: + game_state.jump_state = JUMPING_2; + break; + case JUMPING_2: + game_state.jump_state = NOT_JUMPING; + display_ball(game_state.jump_state); + if (state -> soundOn){ + if (success_jump) + watch_buzzer_play_note(BUZZER_NOTE_C5, 60); + else + watch_buzzer_play_note(BUZZER_NOTE_C3, 60); + } + break; + default: + break; + } + if (game_state.jump_state == NOT_JUMPING && (game_state.loc_2_on || game_state.loc_3_on)) + display_lose_screen(state); + break; + } + break; + case EVENT_LIGHT_BUTTON_UP: + case EVENT_ALARM_BUTTON_UP: + if (state -> curr_screen == SCREEN_TITLE) { + state -> curr_screen = SCREEN_PLAYING; + movement_request_tick_frequency(state -> difficulty == DIFF_EASY ? FREQ_EASY : FREQ); + watch_display_string(" ", 4); + display_ball(false); + display_score(state -> curr_score); + if (state -> soundOn){ + watch_buzzer_play_note(BUZZER_NOTE_C5, 200); + watch_buzzer_play_note(BUZZER_NOTE_E5, 200); + watch_buzzer_play_note(BUZZER_NOTE_G5, 200); + } + } + else if (state -> curr_screen == SCREEN_LOSE) { + display_title(state); + } + break; + case EVENT_LIGHT_LONG_PRESS: + if (state -> curr_screen == SCREEN_TITLE) { + state -> difficulty = (state -> difficulty + 1) % DIFF_COUNT; + display_difficulty(state -> difficulty); + if (state -> soundOn) { + if (state -> difficulty == DIFF_EASY) watch_buzzer_play_note(BUZZER_NOTE_B4, 30); + else watch_buzzer_play_note(BUZZER_NOTE_C5, 30); + } + } + break; + case EVENT_LIGHT_BUTTON_DOWN: + case EVENT_ALARM_BUTTON_DOWN: + if (state -> curr_screen == SCREEN_PLAYING && game_state.jump_state == NOT_JUMPING){ + game_state.jump_state = JUMP; + display_ball(game_state.jump_state); + } + break; + case EVENT_ALARM_LONG_PRESS: + if (state -> curr_screen != SCREEN_PLAYING) + { + state -> soundOn = !state -> soundOn; + if (state -> soundOn){ + watch_buzzer_play_note(BUZZER_NOTE_C5, 30); + watch_set_indicator(WATCH_INDICATOR_BELL); + } + else { + watch_clear_indicator(WATCH_INDICATOR_BELL); + } + } + break; + case EVENT_TIMEOUT: + movement_move_to_face(0); + break; + case EVENT_LOW_ENERGY_UPDATE: + break; + default: + return movement_default_loop_handler(event, settings); + } + return true; +} + +void endless_runner_face_resign(movement_settings_t *settings, void *context) { + (void) settings; + (void) context; +} + diff --git a/movement/watch_faces/complication/endless_runner_face.h b/movement/watch_faces/complication/endless_runner_face.h new file mode 100644 index 0000000..b588f35 --- /dev/null +++ b/movement/watch_faces/complication/endless_runner_face.h @@ -0,0 +1,63 @@ +/* + * MIT License + * + * Copyright (c) 2024 <#author_name#> + * + * 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 ENDLESS_RUNNER_FACE_H_ +#define ENDLESS_RUNNER_FACE_H_ + +#include "movement.h" + +/* + ENDLESS_RUNNER face + + This is a basic endless-runner, like the [Chrome Dino game](https://en.wikipedia.org/wiki/Dinosaur_Game). + On the title screen, you can select a difficulty by long-pressing LIGHT or toggle sound by long-pressing ALARM. + LED or ALARM are used to jump. + High-score is displayed on the top-right on the title screen. During a game, the current score is displayed. +*/ + +typedef struct { + // These are values that need saving between uses + uint16_t hi_score : 6; + uint16_t curr_score : 6; + uint16_t curr_screen : 2; + uint16_t difficulty : 2; + bool soundOn; + bool unused; +} endless_runner_state_t; + +void endless_runner_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); +void endless_runner_face_activate(movement_settings_t *settings, void *context); +bool endless_runner_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void endless_runner_face_resign(movement_settings_t *settings, void *context); + +#define endless_runner_face ((const watch_face_t){ \ + endless_runner_face_setup, \ + endless_runner_face_activate, \ + endless_runner_face_loop, \ + endless_runner_face_resign, \ + NULL, \ +}) + +#endif // ENDLESS_RUNNER_FACE_H_ + From e2870eb7aff72c05573d78d407a75651e23c7618 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Tue, 16 Jul 2024 08:51:45 -0400 Subject: [PATCH 100/161] Removed the binary print debug function --- .../watch_faces/complication/endless_runner_face.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/movement/watch_faces/complication/endless_runner_face.c b/movement/watch_faces/complication/endless_runner_face.c index 6b2bb01..2227d27 100644 --- a/movement/watch_faces/complication/endless_runner_face.c +++ b/movement/watch_faces/complication/endless_runner_face.c @@ -65,18 +65,6 @@ typedef struct { static game_state_t game_state; static const uint8_t _num_bits_obst_pattern = sizeof(game_state.obst_pattern) * 8; -static void print_binary(uint32_t value, int bits) { - for (int i = bits - 1; i >= 0; i--) { - // Print each bit - printf("%d", (value >> i) & 1); - // Optional: add a space every 4 bits for readability - if (i % 4 == 0 && i != 0) { - printf(" "); - } - } - printf("\r\n"); -} - static uint32_t get_random(uint32_t max) { #if __EMSCRIPTEN__ return rand() % max; @@ -116,7 +104,6 @@ static uint32_t get_random_legal(uint32_t prev_val, uint16_t difficulty) { } } rand_legal = prev_val | rand_legal; - print_binary(rand_legal, _num_bits_obst_pattern); return rand_legal; } From ed3c4d3c3034bdc301b4bdbf7629188b3539a40a Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Tue, 16 Jul 2024 09:29:19 -0400 Subject: [PATCH 101/161] Fixed the long delays when beginning a game --- .../complication/endless_runner_face.c | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/movement/watch_faces/complication/endless_runner_face.c b/movement/watch_faces/complication/endless_runner_face.c index 2227d27..44ba2cd 100644 --- a/movement/watch_faces/complication/endless_runner_face.c +++ b/movement/watch_faces/complication/endless_runner_face.c @@ -55,9 +55,9 @@ typedef enum { typedef struct { uint32_t obst_pattern; - int16_t obst_indx : 8; - int16_t jump_state : 3; - int16_t sec_before_moves : 3; + uint16_t obst_indx : 8; + uint16_t jump_state : 3; + uint16_t sec_before_moves : 3; bool loc_2_on; bool loc_3_on; } game_state_t; @@ -287,10 +287,8 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti switch (event.event_type) { case EVENT_ACTIVATE: - state -> curr_screen = SCREEN_TITLE; if (state -> soundOn) watch_set_indicator(WATCH_INDICATOR_BELL); display_title(state); - state -> curr_score = 0; break; case EVENT_TICK: switch (state -> curr_screen) @@ -299,10 +297,11 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti case SCREEN_LOSE: break; default: - if (game_state.sec_before_moves == 0) - success_jump = display_obstacles(state); - else if (event.subsecond == 0) - if(--game_state.sec_before_moves == 0) game_state.obst_pattern = get_random_legal(0, state -> difficulty); + if (game_state.sec_before_moves != 0) { + if (event.subsecond == 0) --game_state.sec_before_moves; + break; + } + success_jump = display_obstacles(state); switch (game_state.jump_state) { case JUMP: @@ -336,6 +335,10 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti movement_request_tick_frequency(state -> difficulty == DIFF_EASY ? FREQ_EASY : FREQ); watch_display_string(" ", 4); display_ball(false); + do // Avoid the first array of obstacles being a boring line of 0s + { + game_state.obst_pattern = get_random_legal(0, state -> difficulty); + } while (game_state.obst_pattern == 0); display_score(state -> curr_score); if (state -> soundOn){ watch_buzzer_play_note(BUZZER_NOTE_C5, 200); From abc0bedbde221f303b3960278f827b4a6ff1518a Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Tue, 16 Jul 2024 22:35:07 -0400 Subject: [PATCH 102/161] Gave an extra jumping frame for non-hard mode; Curr scroll now loops; Title changed to ER --- .../complication/endless_runner_face.c | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/movement/watch_faces/complication/endless_runner_face.c b/movement/watch_faces/complication/endless_runner_face.c index 44ba2cd..67a7b54 100644 --- a/movement/watch_faces/complication/endless_runner_face.c +++ b/movement/watch_faces/complication/endless_runner_face.c @@ -30,23 +30,24 @@ typedef enum { NOT_JUMPING = 0, JUMP, JUMPING_1, - JUMPING_2, + JUMPING_2, + JUMPING_3, JUMP_COUNT -} ScrollingJumpState; +} RunnerJumpState; typedef enum { SCREEN_TITLE = 0, SCREEN_PLAYING, SCREEN_LOSE, SCREEN_COUNT -} ScrollingCurrScreen; +} RunnerCurrScreen; typedef enum { - DIFF_NORM = 0, // 8x speed; 4 0's min; - DIFF_HARD, // 8x speed; 3 0's min; 2 - Easy 4x speed; 4 0's min - DIFF_EASY, // 4x speed; 4 0's min + DIFF_NORM = 0, // 8x speed; 4 0's min; jump is 3 frames + DIFF_HARD, // 8x speed; 3 0's min; jump is 2 frames + DIFF_EASY, // 4x speed; 4 0's min; jump is 3 frames DIFF_COUNT -} ScrollingDifficulty; +} RunnerDifficulty; #define NUM_GRID 12 #define FREQ 8 @@ -158,7 +159,7 @@ static void display_title(endless_runner_state_t *state) { memset(&game_state, 0, sizeof(game_state)); game_state.sec_before_moves = 1; // The first obstacles will all be 0s, which is about an extra second of delay. if (state -> soundOn) game_state.sec_before_moves--; // Start chime is about 1 second - watch_display_string("SC SEL ", 0); + watch_display_string("ER SEL ", 0); display_score(state -> hi_score); display_difficulty(state -> difficulty); } @@ -167,7 +168,7 @@ static void display_lose_screen(endless_runner_state_t *state) { movement_request_tick_frequency(1); state -> curr_screen = SCREEN_LOSE; state -> curr_score = 0; - watch_display_string(" LOSEr", 4); + watch_display_string(" U LOSE ", 0); if (state -> soundOn) watch_buzzer_play_note(BUZZER_NOTE_A1, 600); else @@ -195,16 +196,19 @@ static bool display_obstacle(bool obstacle, int grid_loc, endless_runner_state_t case 1: if (obstacle) { // If an obstacle is here, it means the ball cleared it - success_jump = true; - if (state -> curr_score < MAX_DISP_SCORE) { - state -> curr_score++; - if (state -> curr_score > state -> hi_score) - state -> hi_score = state -> curr_score; - display_score(state -> curr_score); - } + // Counter will continuously roll over, but high score will max out on the first roll-over + state -> curr_score = (state -> curr_score + 1) % (MAX_DISP_SCORE + 1); + if (state -> curr_score == 0) // This means the counter rolled over + state -> hi_score = MAX_DISP_SCORE + 1; + else if (state -> curr_score > state -> hi_score) + state -> hi_score = state -> curr_score; + display_score(state -> curr_score); } //fall through case 0: + if (obstacle) // If an obstacle is here, it means the ball cleared it + success_jump = true; + //fall through case 5: if (obstacle) watch_set_pixel(0, 18 + grid_loc); @@ -308,9 +312,12 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti game_state.jump_state = JUMPING_1; break; case JUMPING_1: - game_state.jump_state = JUMPING_2; + game_state.jump_state = (state -> difficulty == DIFF_HARD) ? JUMPING_3 : JUMPING_2; break; case JUMPING_2: + game_state.jump_state = JUMPING_3; + break; + case JUMPING_3: game_state.jump_state = NOT_JUMPING; display_ball(game_state.jump_state); if (state -> soundOn){ @@ -323,8 +330,10 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti default: break; } - if (game_state.jump_state == NOT_JUMPING && (game_state.loc_2_on || game_state.loc_3_on)) + if (game_state.jump_state == NOT_JUMPING && (game_state.loc_2_on || game_state.loc_3_on)) { + delay_ms(200); // To show the player jumping onto the obstacle before displaying the lose screen. display_lose_screen(state); + } break; } break; @@ -332,7 +341,7 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti case EVENT_ALARM_BUTTON_UP: if (state -> curr_screen == SCREEN_TITLE) { state -> curr_screen = SCREEN_PLAYING; - movement_request_tick_frequency(state -> difficulty == DIFF_EASY ? FREQ_EASY : FREQ); + movement_request_tick_frequency((state -> difficulty == DIFF_EASY) ? FREQ_EASY : FREQ); watch_display_string(" ", 4); display_ball(false); do // Avoid the first array of obstacles being a boring line of 0s From defd01f9f0c21e79e93bf1321aeca83756fdadf4 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Wed, 17 Jul 2024 17:05:48 -0400 Subject: [PATCH 103/161] Added baby mode which used to be easy mode; easy mode is now same speed as normal, but 3 frames to jump and normal is 2 frames. --- .../complication/endless_runner_face.c | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/movement/watch_faces/complication/endless_runner_face.c b/movement/watch_faces/complication/endless_runner_face.c index 67a7b54..1e6abae 100644 --- a/movement/watch_faces/complication/endless_runner_face.c +++ b/movement/watch_faces/complication/endless_runner_face.c @@ -43,15 +43,16 @@ typedef enum { } RunnerCurrScreen; typedef enum { - DIFF_NORM = 0, // 8x speed; 4 0's min; jump is 3 frames - DIFF_HARD, // 8x speed; 3 0's min; jump is 2 frames - DIFF_EASY, // 4x speed; 4 0's min; jump is 3 frames + DIFF_BABY = 0, // 0.5x speed; 4 0's min; jump is 3 frames + DIFF_EASY, // 1x speed; 4 0's min; jump is 3 frames + DIFF_NORM, // 1x speed; 4 0's min; jump is 2 frames + DIFF_HARD, // 1x speed; 3 0's min; jump is 2 frames DIFF_COUNT } RunnerDifficulty; #define NUM_GRID 12 #define FREQ 8 -#define FREQ_EASY 4 +#define FREQ_SLOW 4 #define MAX_DISP_SCORE 39 // The top-right digits can't properly display above 39 typedef struct { @@ -80,7 +81,7 @@ static uint32_t get_random_legal(uint32_t prev_val, uint16_t difficulty) { * @param difficulty To dictate how spread apart the obsticles must be * @return the new random value, where it's first NUM_GRID MSBs are the same as prev_val */ - uint8_t min_zeros = difficulty == DIFF_HARD ? 3 : 4; + uint8_t min_zeros = (difficulty == DIFF_HARD) ? 3 : 4; uint32_t max = (1 << (_num_bits_obst_pattern - NUM_GRID)) - 1; uint32_t rand = get_random(max); uint32_t rand_legal = 0; @@ -141,6 +142,9 @@ static void display_score(uint8_t score) { static void display_difficulty(uint16_t difficulty) { switch (difficulty) { + case DIFF_BABY: + watch_display_string("b", 9); + break; case DIFF_EASY: watch_display_string("E", 9); break; @@ -277,6 +281,8 @@ void endless_runner_face_setup(movement_settings_t *settings, uint8_t watch_face if (*context_ptr == NULL) { *context_ptr = malloc(sizeof(endless_runner_state_t)); memset(*context_ptr, 0, sizeof(endless_runner_state_t)); + endless_runner_state_t *state = (endless_runner_state_t *)*context_ptr; + state->difficulty = DIFF_NORM; } } @@ -312,7 +318,7 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti game_state.jump_state = JUMPING_1; break; case JUMPING_1: - game_state.jump_state = (state -> difficulty == DIFF_HARD) ? JUMPING_3 : JUMPING_2; + game_state.jump_state = (state -> difficulty >= DIFF_NORM) ? JUMPING_3 : JUMPING_2; break; case JUMPING_2: game_state.jump_state = JUMPING_3; @@ -341,7 +347,7 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti case EVENT_ALARM_BUTTON_UP: if (state -> curr_screen == SCREEN_TITLE) { state -> curr_screen = SCREEN_PLAYING; - movement_request_tick_frequency((state -> difficulty == DIFF_EASY) ? FREQ_EASY : FREQ); + movement_request_tick_frequency((state -> difficulty == DIFF_BABY) ? FREQ_SLOW : FREQ); watch_display_string(" ", 4); display_ball(false); do // Avoid the first array of obstacles being a boring line of 0s @@ -364,7 +370,7 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti state -> difficulty = (state -> difficulty + 1) % DIFF_COUNT; display_difficulty(state -> difficulty); if (state -> soundOn) { - if (state -> difficulty == DIFF_EASY) watch_buzzer_play_note(BUZZER_NOTE_B4, 30); + if (state -> difficulty == 0) watch_buzzer_play_note(BUZZER_NOTE_B4, 30); else watch_buzzer_play_note(BUZZER_NOTE_C5, 30); } } From 6f3f09c5babbabfa8471f6f070e442c40df73a5e Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Fri, 19 Jul 2024 08:43:15 -0400 Subject: [PATCH 104/161] Reformat to remove some hardocded variables --- .../complication/endless_runner_face.c | 84 ++++++++++--------- 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/movement/watch_faces/complication/endless_runner_face.c b/movement/watch_faces/complication/endless_runner_face.c index 1e6abae..cd672f6 100644 --- a/movement/watch_faces/complication/endless_runner_face.c +++ b/movement/watch_faces/complication/endless_runner_face.c @@ -27,12 +27,9 @@ #include "endless_runner_face.h" typedef enum { - NOT_JUMPING = 0, - JUMP, - JUMPING_1, - JUMPING_2, - JUMPING_3, - JUMP_COUNT + JUMPING_FINAL_FRAME = 0, + NOT_JUMPING, + JUMPING_START, } RunnerJumpState; typedef enum { @@ -53,12 +50,16 @@ typedef enum { #define NUM_GRID 12 #define FREQ 8 #define FREQ_SLOW 4 +#define JUMP_FRAMES 2 +#define JUMP_FRAMES_EASY 20 +#define MIN_ZEROES 4 +#define MIN_ZEROES_HARD 3 #define MAX_DISP_SCORE 39 // The top-right digits can't properly display above 39 typedef struct { uint32_t obst_pattern; uint16_t obst_indx : 8; - uint16_t jump_state : 3; + uint16_t jump_state : 5; uint16_t sec_before_moves : 3; bool loc_2_on; bool loc_3_on; @@ -81,7 +82,7 @@ static uint32_t get_random_legal(uint32_t prev_val, uint16_t difficulty) { * @param difficulty To dictate how spread apart the obsticles must be * @return the new random value, where it's first NUM_GRID MSBs are the same as prev_val */ - uint8_t min_zeros = (difficulty == DIFF_HARD) ? 3 : 4; + uint8_t min_zeros = (difficulty == DIFF_HARD) ? MIN_ZEROES_HARD : MIN_ZEROES; uint32_t max = (1 << (_num_bits_obst_pattern - NUM_GRID)) - 1; uint32_t rand = get_random(max); uint32_t rand_legal = 0; @@ -110,7 +111,7 @@ static uint32_t get_random_legal(uint32_t prev_val, uint16_t difficulty) { } static void display_ball(bool jumping) { - if (jumping == NOT_JUMPING) { + if (!jumping) { watch_set_pixel(0, 21); watch_set_pixel(1, 21); watch_set_pixel(0, 20); @@ -168,6 +169,24 @@ static void display_title(endless_runner_state_t *state) { display_difficulty(state -> difficulty); } +static void begin_playing(endless_runner_state_t *state) { + state -> curr_screen = SCREEN_PLAYING; + movement_request_tick_frequency((state -> difficulty == DIFF_BABY) ? FREQ_SLOW : FREQ); + watch_display_string("ER ", 0); + game_state.jump_state = NOT_JUMPING; + display_ball(game_state.jump_state != NOT_JUMPING); + do // Avoid the first array of obstacles being a boring line of 0s + { + game_state.obst_pattern = get_random_legal(0, state -> difficulty); + } while (game_state.obst_pattern == 0); + display_score(state -> curr_score); + if (state -> soundOn){ + watch_buzzer_play_note(BUZZER_NOTE_C5, 200); + watch_buzzer_play_note(BUZZER_NOTE_E5, 200); + watch_buzzer_play_note(BUZZER_NOTE_G5, 200); + } +} + static void display_lose_screen(endless_runner_state_t *state) { movement_request_tick_frequency(1); state -> curr_screen = SCREEN_LOSE; @@ -294,6 +313,7 @@ void endless_runner_face_activate(movement_settings_t *settings, void *context) bool endless_runner_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { endless_runner_state_t *state = (endless_runner_state_t *)context; bool success_jump = false; + uint8_t curr_jump_frame = 0; switch (event.event_type) { case EVENT_ACTIVATE: @@ -314,26 +334,25 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti success_jump = display_obstacles(state); switch (game_state.jump_state) { - case JUMP: - game_state.jump_state = JUMPING_1; + case NOT_JUMPING: break; - case JUMPING_1: - game_state.jump_state = (state -> difficulty >= DIFF_NORM) ? JUMPING_3 : JUMPING_2; - break; - case JUMPING_2: - game_state.jump_state = JUMPING_3; - break; - case JUMPING_3: + case JUMPING_FINAL_FRAME: game_state.jump_state = NOT_JUMPING; - display_ball(game_state.jump_state); + display_ball(game_state.jump_state != NOT_JUMPING); if (state -> soundOn){ if (success_jump) watch_buzzer_play_note(BUZZER_NOTE_C5, 60); else watch_buzzer_play_note(BUZZER_NOTE_C3, 60); } - break; + break; default: + curr_jump_frame = game_state.jump_state - NOT_JUMPING; + if (curr_jump_frame >= JUMP_FRAMES_EASY || (state -> difficulty >= DIFF_NORM && curr_jump_frame >= JUMP_FRAMES)) + game_state.jump_state = JUMPING_FINAL_FRAME; + else + game_state.jump_state++; + //if (!watch_get_pin_level(BTN_ALARM) && !watch_get_pin_level(BTN_LIGHT)) game_state.jump_state = JUMPING_FINAL_FRAME; // Uncomment to have depressing the buttons cause the ball to drop regardless of its current frame. break; } if (game_state.jump_state == NOT_JUMPING && (game_state.loc_2_on || game_state.loc_3_on)) { @@ -345,25 +364,10 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti break; case EVENT_LIGHT_BUTTON_UP: case EVENT_ALARM_BUTTON_UP: - if (state -> curr_screen == SCREEN_TITLE) { - state -> curr_screen = SCREEN_PLAYING; - movement_request_tick_frequency((state -> difficulty == DIFF_BABY) ? FREQ_SLOW : FREQ); - watch_display_string(" ", 4); - display_ball(false); - do // Avoid the first array of obstacles being a boring line of 0s - { - game_state.obst_pattern = get_random_legal(0, state -> difficulty); - } while (game_state.obst_pattern == 0); - display_score(state -> curr_score); - if (state -> soundOn){ - watch_buzzer_play_note(BUZZER_NOTE_C5, 200); - watch_buzzer_play_note(BUZZER_NOTE_E5, 200); - watch_buzzer_play_note(BUZZER_NOTE_G5, 200); - } - } - else if (state -> curr_screen == SCREEN_LOSE) { + if (state -> curr_screen == SCREEN_TITLE) + begin_playing(state); + else if (state -> curr_screen == SCREEN_LOSE) display_title(state); - } break; case EVENT_LIGHT_LONG_PRESS: if (state -> curr_screen == SCREEN_TITLE) { @@ -378,8 +382,8 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti case EVENT_LIGHT_BUTTON_DOWN: case EVENT_ALARM_BUTTON_DOWN: if (state -> curr_screen == SCREEN_PLAYING && game_state.jump_state == NOT_JUMPING){ - game_state.jump_state = JUMP; - display_ball(game_state.jump_state); + game_state.jump_state = JUMPING_START; + display_ball(game_state.jump_state != NOT_JUMPING); } break; case EVENT_ALARM_LONG_PRESS: From 2d7aaceff7c73ae2438a5c6a11a1749bf0191203 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sat, 20 Jul 2024 06:46:08 -0400 Subject: [PATCH 105/161] hi score resets weekly --- .../watch_faces/complication/endless_runner_face.c | 13 +++++++++++++ .../watch_faces/complication/endless_runner_face.h | 2 ++ 2 files changed, 15 insertions(+) diff --git a/movement/watch_faces/complication/endless_runner_face.c b/movement/watch_faces/complication/endless_runner_face.c index cd672f6..6c761df 100644 --- a/movement/watch_faces/complication/endless_runner_face.c +++ b/movement/watch_faces/complication/endless_runner_face.c @@ -25,6 +25,7 @@ #include #include #include "endless_runner_face.h" +#include "watch_utility.h" typedef enum { JUMPING_FINAL_FRAME = 0, @@ -314,9 +315,21 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti endless_runner_state_t *state = (endless_runner_state_t *)context; bool success_jump = false; uint8_t curr_jump_frame = 0; + watch_date_time date_time; + uint32_t weeknumber; switch (event.event_type) { case EVENT_ACTIVATE: + date_time = watch_rtc_get_date_time(); + weeknumber = watch_utility_get_weeknumber(date_time.unit.year, date_time.unit.month, date_time.unit.day); + if ((state -> weeknumber_prev_hi_score != weeknumber) || + (state -> year_prev_hi_score != date_time.unit.year)) + { + // The high score resets itself every new week. + state -> hi_score = 0; + state -> weeknumber_prev_hi_score = weeknumber; + state -> year_prev_hi_score = date_time.unit.year; + } if (state -> soundOn) watch_set_indicator(WATCH_INDICATOR_BELL); display_title(state); break; diff --git a/movement/watch_faces/complication/endless_runner_face.h b/movement/watch_faces/complication/endless_runner_face.h index b588f35..03ec418 100644 --- a/movement/watch_faces/complication/endless_runner_face.h +++ b/movement/watch_faces/complication/endless_runner_face.h @@ -44,6 +44,8 @@ typedef struct { uint16_t difficulty : 2; bool soundOn; bool unused; + uint8_t weeknumber_prev_hi_score; + uint8_t year_prev_hi_score; } endless_runner_state_t; void endless_runner_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); From 07d2bc91a54b4a817f3deaba92b5218e92e97bf8 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sat, 20 Jul 2024 07:48:50 -0400 Subject: [PATCH 106/161] Modified hi score display to allow for 3 digits in hi-score, it now resets at the beginning of each month --- .../complication/endless_runner_face.c | 88 ++++++++++--------- .../complication/endless_runner_face.h | 12 ++- 2 files changed, 52 insertions(+), 48 deletions(-) diff --git a/movement/watch_faces/complication/endless_runner_face.c b/movement/watch_faces/complication/endless_runner_face.c index 6c761df..3016f88 100644 --- a/movement/watch_faces/complication/endless_runner_face.c +++ b/movement/watch_faces/complication/endless_runner_face.c @@ -25,7 +25,6 @@ #include #include #include "endless_runner_face.h" -#include "watch_utility.h" typedef enum { JUMPING_FINAL_FRAME = 0, @@ -52,9 +51,10 @@ typedef enum { #define FREQ 8 #define FREQ_SLOW 4 #define JUMP_FRAMES 2 -#define JUMP_FRAMES_EASY 20 +#define JUMP_FRAMES_EASY 3 #define MIN_ZEROES 4 #define MIN_ZEROES_HARD 3 +#define MAX_HI_SCORE 999 #define MAX_DISP_SCORE 39 // The top-right digits can't properly display above 39 typedef struct { @@ -62,6 +62,8 @@ typedef struct { uint16_t obst_indx : 8; uint16_t jump_state : 5; uint16_t sec_before_moves : 3; + uint16_t curr_score : 10; + uint16_t curr_screen : 4; bool loc_2_on; bool loc_3_on; } game_state_t; @@ -134,44 +136,52 @@ static void display_ball(bool jumping) { static void display_score(uint8_t score) { char buf[3]; - if (score > MAX_DISP_SCORE) watch_display_string(" -", 2); - else{ - sprintf(buf, "%2d", score); - watch_display_string(buf, 2); - } + score %= (MAX_DISP_SCORE + 1); + sprintf(buf, "%2d", score); + watch_display_string(buf, 2); } static void display_difficulty(uint16_t difficulty) { switch (difficulty) { case DIFF_BABY: - watch_display_string("b", 9); + watch_display_string("b", 3); break; case DIFF_EASY: - watch_display_string("E", 9); + watch_display_string("E", 3); break; case DIFF_HARD: - watch_display_string("H", 9); + watch_display_string("H", 3); break; case DIFF_NORM: default: - watch_display_string("n", 9); + watch_display_string("N", 3); break; } } static void display_title(endless_runner_state_t *state) { - state -> curr_screen = SCREEN_TITLE; + char buf[10]; + uint16_t hi_score = state -> hi_score; + uint8_t difficulty = state -> difficulty; + bool sound_on = state -> soundOn; memset(&game_state, 0, sizeof(game_state)); game_state.sec_before_moves = 1; // The first obstacles will all be 0s, which is about an extra second of delay. - if (state -> soundOn) game_state.sec_before_moves--; // Start chime is about 1 second - watch_display_string("ER SEL ", 0); - display_score(state -> hi_score); - display_difficulty(state -> difficulty); + if (sound_on) game_state.sec_before_moves--; // Start chime is about 1 second + watch_set_colon(); + if (hi_score <= 99) + sprintf(buf, "ER HS%2d ", hi_score); + else if (hi_score <= MAX_HI_SCORE) + sprintf(buf, "ER HS%3d ", hi_score); + else + sprintf(buf, "ER HS-- ", hi_score); + watch_display_string(buf, 0); + display_difficulty(difficulty); } static void begin_playing(endless_runner_state_t *state) { - state -> curr_screen = SCREEN_PLAYING; + game_state.curr_screen = SCREEN_PLAYING; + watch_clear_colon(); movement_request_tick_frequency((state -> difficulty == DIFF_BABY) ? FREQ_SLOW : FREQ); watch_display_string("ER ", 0); game_state.jump_state = NOT_JUMPING; @@ -180,7 +190,7 @@ static void begin_playing(endless_runner_state_t *state) { { game_state.obst_pattern = get_random_legal(0, state -> difficulty); } while (game_state.obst_pattern == 0); - display_score(state -> curr_score); + display_score( game_state.curr_score); if (state -> soundOn){ watch_buzzer_play_note(BUZZER_NOTE_C5, 200); watch_buzzer_play_note(BUZZER_NOTE_E5, 200); @@ -189,9 +199,8 @@ static void begin_playing(endless_runner_state_t *state) { } static void display_lose_screen(endless_runner_state_t *state) { - movement_request_tick_frequency(1); - state -> curr_screen = SCREEN_LOSE; - state -> curr_score = 0; + game_state.curr_screen = SCREEN_LOSE; + game_state.curr_score = 0; watch_display_string(" U LOSE ", 0); if (state -> soundOn) watch_buzzer_play_note(BUZZER_NOTE_A1, 600); @@ -220,13 +229,12 @@ static bool display_obstacle(bool obstacle, int grid_loc, endless_runner_state_t case 1: if (obstacle) { // If an obstacle is here, it means the ball cleared it - // Counter will continuously roll over, but high score will max out on the first roll-over - state -> curr_score = (state -> curr_score + 1) % (MAX_DISP_SCORE + 1); - if (state -> curr_score == 0) // This means the counter rolled over - state -> hi_score = MAX_DISP_SCORE + 1; - else if (state -> curr_score > state -> hi_score) - state -> hi_score = state -> curr_score; - display_score(state -> curr_score); + if (game_state.curr_score <= MAX_HI_SCORE) { + game_state.curr_score++; + if (game_state.curr_score > state -> hi_score) + state -> hi_score = game_state.curr_score; + } + display_score(game_state.curr_score); } //fall through case 0: @@ -316,25 +324,23 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti bool success_jump = false; uint8_t curr_jump_frame = 0; watch_date_time date_time; - uint32_t weeknumber; switch (event.event_type) { case EVENT_ACTIVATE: date_time = watch_rtc_get_date_time(); - weeknumber = watch_utility_get_weeknumber(date_time.unit.year, date_time.unit.month, date_time.unit.day); - if ((state -> weeknumber_prev_hi_score != weeknumber) || - (state -> year_prev_hi_score != date_time.unit.year)) + if ((state -> year_last_hi_score != date_time.unit.year) || + (state -> month_last_hi_score != date_time.unit.month)) { - // The high score resets itself every new week. + // The high score resets itself every new month. state -> hi_score = 0; - state -> weeknumber_prev_hi_score = weeknumber; - state -> year_prev_hi_score = date_time.unit.year; + state -> year_last_hi_score = date_time.unit.year; + state -> month_last_hi_score = date_time.unit.month; } if (state -> soundOn) watch_set_indicator(WATCH_INDICATOR_BELL); display_title(state); break; case EVENT_TICK: - switch (state -> curr_screen) + switch (game_state.curr_screen) { case SCREEN_TITLE: case SCREEN_LOSE: @@ -377,13 +383,13 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti break; case EVENT_LIGHT_BUTTON_UP: case EVENT_ALARM_BUTTON_UP: - if (state -> curr_screen == SCREEN_TITLE) + if (game_state.curr_screen == SCREEN_TITLE) begin_playing(state); - else if (state -> curr_screen == SCREEN_LOSE) + else if (game_state.curr_screen == SCREEN_LOSE) display_title(state); break; case EVENT_LIGHT_LONG_PRESS: - if (state -> curr_screen == SCREEN_TITLE) { + if (game_state.curr_screen == SCREEN_TITLE) { state -> difficulty = (state -> difficulty + 1) % DIFF_COUNT; display_difficulty(state -> difficulty); if (state -> soundOn) { @@ -394,13 +400,13 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti break; case EVENT_LIGHT_BUTTON_DOWN: case EVENT_ALARM_BUTTON_DOWN: - if (state -> curr_screen == SCREEN_PLAYING && game_state.jump_state == NOT_JUMPING){ + if (game_state.curr_screen == SCREEN_PLAYING && game_state.jump_state == NOT_JUMPING){ game_state.jump_state = JUMPING_START; display_ball(game_state.jump_state != NOT_JUMPING); } break; case EVENT_ALARM_LONG_PRESS: - if (state -> curr_screen != SCREEN_PLAYING) + if (game_state.curr_screen != SCREEN_PLAYING) { state -> soundOn = !state -> soundOn; if (state -> soundOn){ diff --git a/movement/watch_faces/complication/endless_runner_face.h b/movement/watch_faces/complication/endless_runner_face.h index 03ec418..56d22a0 100644 --- a/movement/watch_faces/complication/endless_runner_face.h +++ b/movement/watch_faces/complication/endless_runner_face.h @@ -38,14 +38,12 @@ typedef struct { // These are values that need saving between uses - uint16_t hi_score : 6; - uint16_t curr_score : 6; - uint16_t curr_screen : 2; - uint16_t difficulty : 2; + uint32_t hi_score : 10; + uint32_t difficulty : 2; bool soundOn; - bool unused; - uint8_t weeknumber_prev_hi_score; - uint8_t year_prev_hi_score; + uint32_t month_last_hi_score : 4; + uint32_t year_last_hi_score : 6; + uint32_t unused : 9; } endless_runner_state_t; void endless_runner_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); From 6ec6476d0f6a27137b2955aec82f8979d4c98dbf Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sat, 20 Jul 2024 09:43:49 -0400 Subject: [PATCH 107/161] Refectored the state machine --- .../complication/endless_runner_face.c | 181 ++++++++++-------- 1 file changed, 98 insertions(+), 83 deletions(-) diff --git a/movement/watch_faces/complication/endless_runner_face.c b/movement/watch_faces/complication/endless_runner_face.c index 3016f88..31507c8 100644 --- a/movement/watch_faces/complication/endless_runner_face.c +++ b/movement/watch_faces/complication/endless_runner_face.c @@ -40,21 +40,21 @@ typedef enum { } RunnerCurrScreen; typedef enum { - DIFF_BABY = 0, // 0.5x speed; 4 0's min; jump is 3 frames - DIFF_EASY, // 1x speed; 4 0's min; jump is 3 frames - DIFF_NORM, // 1x speed; 4 0's min; jump is 2 frames - DIFF_HARD, // 1x speed; 3 0's min; jump is 2 frames + DIFF_BABY = 0, // FREQ_SLOW FPS; MIN_ZEROES 0's min; Jump is JUMP_FRAMES_EASY frames + DIFF_EASY, // FREQ FPS; MIN_ZEROES 0's min; Jump is JUMP_FRAMES_EASY frames + DIFF_NORM, // FREQ FPS; MIN_ZEROES 0's min; Jump is JUMP_FRAMES frames + DIFF_HARD, // FREQ FPS; MIN_ZEROES_HARD 0's min; jump is JUMP_FRAMES frames DIFF_COUNT } RunnerDifficulty; -#define NUM_GRID 12 -#define FREQ 8 -#define FREQ_SLOW 4 -#define JUMP_FRAMES 2 -#define JUMP_FRAMES_EASY 3 -#define MIN_ZEROES 4 -#define MIN_ZEROES_HARD 3 -#define MAX_HI_SCORE 999 +#define NUM_GRID 12 // This the length that the obstacle track can be on +#define FREQ 8 // Frequency request for the game +#define FREQ_SLOW 4 // Frequency request for baby mode +#define JUMP_FRAMES 2 // Wait this many frames on difficulties above EASY before coming down from the jump button pressed +#define JUMP_FRAMES_EASY 3 // Wait this many frames on difficulties at or below EASY before coming down from the jump button pressed +#define MIN_ZEROES 4 // At minimum, we'll have this many spaces between obstacles +#define MIN_ZEROES_HARD 3 // At minimum, we'll have this many spaces between obstacles on hard mode +#define MAX_HI_SCORE 999 // Max hi score to store and display on the title screen. #define MAX_DISP_SCORE 39 // The top-right digits can't properly display above 39 typedef struct { @@ -141,6 +141,19 @@ static void display_score(uint8_t score) { watch_display_string(buf, 2); } +static void check_and_reset_hi_score(endless_runner_state_t *state) { + // Resets the hi scroe at the beginning of each month. + watch_date_time date_time = watch_rtc_get_date_time(); + if ((state -> year_last_hi_score != date_time.unit.year) || + (state -> month_last_hi_score != date_time.unit.month)) + { + // The high score resets itself every new month. + state -> hi_score = 0; + state -> year_last_hi_score = date_time.unit.year; + state -> month_last_hi_score = date_time.unit.month; + } +} + static void display_difficulty(uint16_t difficulty) { switch (difficulty) { @@ -160,8 +173,27 @@ static void display_difficulty(uint16_t difficulty) { } } +static void change_difficulty(endless_runner_state_t *state) { + state -> difficulty = (state -> difficulty + 1) % DIFF_COUNT; + display_difficulty(state -> difficulty); + if (state -> soundOn) { + if (state -> difficulty == 0) watch_buzzer_play_note(BUZZER_NOTE_B4, 30); + else watch_buzzer_play_note(BUZZER_NOTE_C5, 30); + } +} + +static void toggle_sound(endless_runner_state_t *state) { + state -> soundOn = !state -> soundOn; + if (state -> soundOn){ + watch_buzzer_play_note(BUZZER_NOTE_C5, 30); + watch_set_indicator(WATCH_INDICATOR_BELL); + } + else { + watch_clear_indicator(WATCH_INDICATOR_BELL); + } +} + static void display_title(endless_runner_state_t *state) { - char buf[10]; uint16_t hi_score = state -> hi_score; uint8_t difficulty = state -> difficulty; bool sound_on = state -> soundOn; @@ -169,13 +201,17 @@ static void display_title(endless_runner_state_t *state) { game_state.sec_before_moves = 1; // The first obstacles will all be 0s, which is about an extra second of delay. if (sound_on) game_state.sec_before_moves--; // Start chime is about 1 second watch_set_colon(); - if (hi_score <= 99) - sprintf(buf, "ER HS%2d ", hi_score); - else if (hi_score <= MAX_HI_SCORE) - sprintf(buf, "ER HS%3d ", hi_score); - else - sprintf(buf, "ER HS-- ", hi_score); - watch_display_string(buf, 0); + if (hi_score > MAX_HI_SCORE) { + watch_display_string("ER HS-- ", 0); + } + else { + char buf[14]; + if (hi_score <= 99) + sprintf(buf, "ER HS%2d ", hi_score); + else if (hi_score <= MAX_HI_SCORE) + sprintf(buf, "ER HS%3d ", hi_score); + watch_display_string(buf, 0); + } display_difficulty(difficulty); } @@ -303,6 +339,43 @@ static bool display_obstacles(endless_runner_state_t *state) { return success_jump; } +static void update_game(endless_runner_state_t *state, uint8_t subsecond) { + bool success_jump = false; + uint8_t curr_jump_frame = 0; + if (game_state.sec_before_moves != 0) { + if (subsecond == 0) --game_state.sec_before_moves; + return; + } + success_jump = display_obstacles(state); + switch (game_state.jump_state) + { + case NOT_JUMPING: + break; + case JUMPING_FINAL_FRAME: + game_state.jump_state = NOT_JUMPING; + display_ball(game_state.jump_state != NOT_JUMPING); + if (state -> soundOn){ + if (success_jump) + watch_buzzer_play_note(BUZZER_NOTE_C5, 60); + else + watch_buzzer_play_note(BUZZER_NOTE_C3, 60); + } + break; + default: + curr_jump_frame = game_state.jump_state - NOT_JUMPING; + if (curr_jump_frame >= JUMP_FRAMES_EASY || (state -> difficulty >= DIFF_NORM && curr_jump_frame >= JUMP_FRAMES)) + game_state.jump_state = JUMPING_FINAL_FRAME; + else + game_state.jump_state++; + //if (!watch_get_pin_level(BTN_ALARM) && !watch_get_pin_level(BTN_LIGHT)) game_state.jump_state = JUMPING_FINAL_FRAME; // Uncomment to have depressing the buttons cause the ball to drop regardless of its current frame. + break; + } + if (game_state.jump_state == NOT_JUMPING && (game_state.loc_2_on || game_state.loc_3_on)) { + delay_ms(200); // To show the player jumping onto the obstacle before displaying the lose screen. + display_lose_screen(state); + } +} + void endless_runner_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { (void) settings; (void) watch_face_index; @@ -321,21 +394,9 @@ void endless_runner_face_activate(movement_settings_t *settings, void *context) bool endless_runner_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { endless_runner_state_t *state = (endless_runner_state_t *)context; - bool success_jump = false; - uint8_t curr_jump_frame = 0; - watch_date_time date_time; - switch (event.event_type) { case EVENT_ACTIVATE: - date_time = watch_rtc_get_date_time(); - if ((state -> year_last_hi_score != date_time.unit.year) || - (state -> month_last_hi_score != date_time.unit.month)) - { - // The high score resets itself every new month. - state -> hi_score = 0; - state -> year_last_hi_score = date_time.unit.year; - state -> month_last_hi_score = date_time.unit.month; - } + check_and_reset_hi_score(state); if (state -> soundOn) watch_set_indicator(WATCH_INDICATOR_BELL); display_title(state); break; @@ -346,38 +407,7 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti case SCREEN_LOSE: break; default: - if (game_state.sec_before_moves != 0) { - if (event.subsecond == 0) --game_state.sec_before_moves; - break; - } - success_jump = display_obstacles(state); - switch (game_state.jump_state) - { - case NOT_JUMPING: - break; - case JUMPING_FINAL_FRAME: - game_state.jump_state = NOT_JUMPING; - display_ball(game_state.jump_state != NOT_JUMPING); - if (state -> soundOn){ - if (success_jump) - watch_buzzer_play_note(BUZZER_NOTE_C5, 60); - else - watch_buzzer_play_note(BUZZER_NOTE_C3, 60); - } - break; - default: - curr_jump_frame = game_state.jump_state - NOT_JUMPING; - if (curr_jump_frame >= JUMP_FRAMES_EASY || (state -> difficulty >= DIFF_NORM && curr_jump_frame >= JUMP_FRAMES)) - game_state.jump_state = JUMPING_FINAL_FRAME; - else - game_state.jump_state++; - //if (!watch_get_pin_level(BTN_ALARM) && !watch_get_pin_level(BTN_LIGHT)) game_state.jump_state = JUMPING_FINAL_FRAME; // Uncomment to have depressing the buttons cause the ball to drop regardless of its current frame. - break; - } - if (game_state.jump_state == NOT_JUMPING && (game_state.loc_2_on || game_state.loc_3_on)) { - delay_ms(200); // To show the player jumping onto the obstacle before displaying the lose screen. - display_lose_screen(state); - } + update_game(state, event.subsecond); break; } break; @@ -389,14 +419,8 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti display_title(state); break; case EVENT_LIGHT_LONG_PRESS: - if (game_state.curr_screen == SCREEN_TITLE) { - state -> difficulty = (state -> difficulty + 1) % DIFF_COUNT; - display_difficulty(state -> difficulty); - if (state -> soundOn) { - if (state -> difficulty == 0) watch_buzzer_play_note(BUZZER_NOTE_B4, 30); - else watch_buzzer_play_note(BUZZER_NOTE_C5, 30); - } - } + if (game_state.curr_screen == SCREEN_TITLE) + change_difficulty(state); break; case EVENT_LIGHT_BUTTON_DOWN: case EVENT_ALARM_BUTTON_DOWN: @@ -407,16 +431,7 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti break; case EVENT_ALARM_LONG_PRESS: if (game_state.curr_screen != SCREEN_PLAYING) - { - state -> soundOn = !state -> soundOn; - if (state -> soundOn){ - watch_buzzer_play_note(BUZZER_NOTE_C5, 30); - watch_set_indicator(WATCH_INDICATOR_BELL); - } - else { - watch_clear_indicator(WATCH_INDICATOR_BELL); - } - } + toggle_sound(state); break; case EVENT_TIMEOUT: movement_move_to_face(0); From 503fcd6ebcbc9b215c7fe87a10594b92e9e61387 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Tue, 23 Jul 2024 17:52:50 -0400 Subject: [PATCH 108/161] Added author in header --- movement/watch_faces/complication/endless_runner_face.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/movement/watch_faces/complication/endless_runner_face.h b/movement/watch_faces/complication/endless_runner_face.h index 56d22a0..b9484dc 100644 --- a/movement/watch_faces/complication/endless_runner_face.h +++ b/movement/watch_faces/complication/endless_runner_face.h @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2024 <#author_name#> + * Copyright (c) 2024 * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal From c027b247b22cf720abe88bd88620542f5e31d813 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Tue, 23 Jul 2024 23:31:13 -0400 Subject: [PATCH 109/161] Changed hi score number offset and refactored some code --- .../complication/endless_runner_face.c | 71 ++++++++++--------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/movement/watch_faces/complication/endless_runner_face.c b/movement/watch_faces/complication/endless_runner_face.c index 31507c8..1f35be9 100644 --- a/movement/watch_faces/complication/endless_runner_face.c +++ b/movement/watch_faces/complication/endless_runner_face.c @@ -54,16 +54,17 @@ typedef enum { #define JUMP_FRAMES_EASY 3 // Wait this many frames on difficulties at or below EASY before coming down from the jump button pressed #define MIN_ZEROES 4 // At minimum, we'll have this many spaces between obstacles #define MIN_ZEROES_HARD 3 // At minimum, we'll have this many spaces between obstacles on hard mode -#define MAX_HI_SCORE 999 // Max hi score to store and display on the title screen. +#define MAX_HI_SCORE 9999 // Max hi score to store and display on the title screen. #define MAX_DISP_SCORE 39 // The top-right digits can't properly display above 39 typedef struct { uint32_t obst_pattern; + uint16_t curr_score; uint16_t obst_indx : 8; uint16_t jump_state : 5; uint16_t sec_before_moves : 3; - uint16_t curr_score : 10; - uint16_t curr_screen : 4; + uint8_t curr_screen : 4; + bool success_jump; // Flag used for making a successful jumping sound. bool loc_2_on; bool loc_3_on; } game_state_t; @@ -141,6 +142,16 @@ static void display_score(uint8_t score) { watch_display_string(buf, 2); } +static void add_to_score(endless_runner_state_t *state) { + if (game_state.curr_score <= MAX_HI_SCORE) { + game_state.curr_score++; + if (game_state.curr_score > state -> hi_score) + state -> hi_score = game_state.curr_score; + } + game_state.success_jump = true; + display_score(game_state.curr_score); +} + static void check_and_reset_hi_score(endless_runner_state_t *state) { // Resets the hi scroe at the beginning of each month. watch_date_time date_time = watch_rtc_get_date_time(); @@ -202,14 +213,11 @@ static void display_title(endless_runner_state_t *state) { if (sound_on) game_state.sec_before_moves--; // Start chime is about 1 second watch_set_colon(); if (hi_score > MAX_HI_SCORE) { - watch_display_string("ER HS-- ", 0); + watch_display_string("ER HS --", 0); } else { char buf[14]; - if (hi_score <= 99) - sprintf(buf, "ER HS%2d ", hi_score); - else if (hi_score <= MAX_HI_SCORE) - sprintf(buf, "ER HS%3d ", hi_score); + sprintf(buf, "ER HS%4d ", hi_score); watch_display_string(buf, 0); } display_difficulty(difficulty); @@ -244,8 +252,19 @@ static void display_lose_screen(endless_runner_state_t *state) { delay_ms(600); } -static bool display_obstacle(bool obstacle, int grid_loc, endless_runner_state_t *state) { - bool success_jump = false; +static void stop_jumping(endless_runner_state_t *state) { + game_state.jump_state = NOT_JUMPING; + display_ball(game_state.jump_state != NOT_JUMPING); + if (state -> soundOn){ + if (game_state.success_jump) + watch_buzzer_play_note(BUZZER_NOTE_C5, 60); + else + watch_buzzer_play_note(BUZZER_NOTE_C3, 60); + } + game_state.success_jump = false; +} + +static void display_obstacle(bool obstacle, int grid_loc, endless_runner_state_t *state) { switch (grid_loc) { case 2: @@ -265,18 +284,10 @@ static bool display_obstacle(bool obstacle, int grid_loc, endless_runner_state_t case 1: if (obstacle) { // If an obstacle is here, it means the ball cleared it - if (game_state.curr_score <= MAX_HI_SCORE) { - game_state.curr_score++; - if (game_state.curr_score > state -> hi_score) - state -> hi_score = game_state.curr_score; - } - display_score(game_state.curr_score); + add_to_score(state); } //fall through case 0: - if (obstacle) // If an obstacle is here, it means the ball cleared it - success_jump = true; - //fall through case 5: if (obstacle) watch_set_pixel(0, 18 + grid_loc); @@ -318,16 +329,14 @@ static bool display_obstacle(bool obstacle, int grid_loc, endless_runner_state_t default: break; } - return success_jump; } -static bool display_obstacles(endless_runner_state_t *state) { - bool success_jump = false; +static void display_obstacles(endless_runner_state_t *state) { for (int i = 0; i < NUM_GRID; i++) { // Use a bitmask to isolate each bit and shift it to the least significant position uint32_t mask = 1 << ((_num_bits_obst_pattern - 1) - i); bool obstacle = (game_state.obst_pattern & mask) >> ((_num_bits_obst_pattern - 1) - i); - if (display_obstacle(obstacle, i, state)) success_jump = true; + display_obstacle(obstacle, i, state); } game_state.obst_pattern = game_state.obst_pattern << 1; @@ -336,30 +345,21 @@ static bool display_obstacles(endless_runner_state_t *state) { game_state.obst_indx = 0; game_state.obst_pattern = get_random_legal(game_state.obst_pattern, state -> difficulty); } - return success_jump; } static void update_game(endless_runner_state_t *state, uint8_t subsecond) { - bool success_jump = false; uint8_t curr_jump_frame = 0; if (game_state.sec_before_moves != 0) { if (subsecond == 0) --game_state.sec_before_moves; return; } - success_jump = display_obstacles(state); + display_obstacles(state); switch (game_state.jump_state) { case NOT_JUMPING: break; case JUMPING_FINAL_FRAME: - game_state.jump_state = NOT_JUMPING; - display_ball(game_state.jump_state != NOT_JUMPING); - if (state -> soundOn){ - if (success_jump) - watch_buzzer_play_note(BUZZER_NOTE_C5, 60); - else - watch_buzzer_play_note(BUZZER_NOTE_C3, 60); - } + stop_jumping(state); break; default: curr_jump_frame = game_state.jump_state - NOT_JUMPING; @@ -434,7 +434,8 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti toggle_sound(state); break; case EVENT_TIMEOUT: - movement_move_to_face(0); + if (game_state.curr_screen != SCREEN_TITLE) + display_title(state); break; case EVENT_LOW_ENERGY_UPDATE: break; From 30363d408e211d8cd1781ad5e8bc4632c9cb66f3 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Wed, 24 Jul 2024 21:03:54 -0400 Subject: [PATCH 110/161] Added fuel mode --- .../complication/endless_runner_face.c | 205 ++++++++++++++---- .../complication/endless_runner_face.h | 4 +- 2 files changed, 166 insertions(+), 43 deletions(-) diff --git a/movement/watch_faces/complication/endless_runner_face.c b/movement/watch_faces/complication/endless_runner_face.c index 1f35be9..bf8e3ac 100644 --- a/movement/watch_faces/complication/endless_runner_face.c +++ b/movement/watch_faces/complication/endless_runner_face.c @@ -36,6 +36,7 @@ typedef enum { SCREEN_TITLE = 0, SCREEN_PLAYING, SCREEN_LOSE, + SCREEN_TIME, SCREEN_COUNT } RunnerCurrScreen; @@ -44,6 +45,7 @@ typedef enum { DIFF_EASY, // FREQ FPS; MIN_ZEROES 0's min; Jump is JUMP_FRAMES_EASY frames DIFF_NORM, // FREQ FPS; MIN_ZEROES 0's min; Jump is JUMP_FRAMES frames DIFF_HARD, // FREQ FPS; MIN_ZEROES_HARD 0's min; jump is JUMP_FRAMES frames + DIFF_FUEL, // Mode where the top-right displays the amoount of fuel that you can be above the ground for, dodging obstacles. When on the ground, your fuel recharges. DIFF_COUNT } RunnerDifficulty; @@ -56,22 +58,45 @@ typedef enum { #define MIN_ZEROES_HARD 3 // At minimum, we'll have this many spaces between obstacles on hard mode #define MAX_HI_SCORE 9999 // Max hi score to store and display on the title screen. #define MAX_DISP_SCORE 39 // The top-right digits can't properly display above 39 +#define JUMP_FRAMES_FUEL 30 // The max fuel that fuel that the fuel mode game will hold +#define JUMP_FRAMES_FUEL_RECHARGE 3 // How much fuel each frame on the ground adds +#define MAX_DISP_SCORE_FUEL 9 // Since the fuel mode displays the score in the weekday slot, two digits will display wonky data typedef struct { uint32_t obst_pattern; - uint16_t curr_score; uint16_t obst_indx : 8; uint16_t jump_state : 5; uint16_t sec_before_moves : 3; - uint8_t curr_screen : 4; - bool success_jump; // Flag used for making a successful jumping sound. + uint16_t curr_score : 10; + uint16_t curr_screen : 4; bool loc_2_on; bool loc_3_on; + bool success_jump; + bool fuel_mode; + uint8_t fuel; } game_state_t; static game_state_t game_state; static const uint8_t _num_bits_obst_pattern = sizeof(game_state.obst_pattern) * 8; +static void print_binary(uint32_t value, int bits) { +#if __EMSCRIPTEN__ + for (int i = bits - 1; i >= 0; i--) { + // Print each bit + printf("%lu", (value >> i) & 1); + // Optional: add a space every 4 bits for readability + if (i % 4 == 0 && i != 0) { + printf(" "); + } + } + printf("\r\n"); +#else + (void) value; + (void) bits; +#endif + return; +} + static uint32_t get_random(uint32_t max) { #if __EMSCRIPTEN__ return rand() % max; @@ -80,6 +105,51 @@ static uint32_t get_random(uint32_t max) { #endif } +static uint32_t get_random_nonzero(uint32_t max) { + uint32_t random; + do + { + random = get_random(max); + } while (random == 0); + return random; +} + +static uint32_t get_random_kinda_nonzero(uint32_t max) { + // Returns a number that's between 1 and max, unless max is 0 or 1, then it returns 0 to max. + if (max == 0) return 0; + else if (max == 1) return get_random(max); + return get_random_nonzero(max); +} + +static uint32_t get_random_fuel(uint32_t prev_val) { + static uint8_t prev_rand_subset = 0; + uint32_t rand; + uint8_t max_ones, subset; + uint32_t rand_legal = 0; + prev_val = prev_val & ~0xFFFF; + + for (int i = 0; i < 2; i++) { + subset = 0; + max_ones = 8; + if (prev_rand_subset > 4) + max_ones -= prev_rand_subset; + rand = get_random_kinda_nonzero(max_ones); + if (rand > 5 && prev_rand_subset) rand = 5; // The gap of one or two is awkward + for (uint32_t j = 0; j < rand; j++) { + subset |= (1 << j); + } + if (prev_rand_subset >= 7) + subset = subset << 1; + subset &= 0xFF; + rand_legal |= subset << (8 * i); + prev_rand_subset = rand; + } + + rand_legal = prev_val | rand_legal; + print_binary(rand_legal, 32); + return rand_legal; +} + static uint32_t get_random_legal(uint32_t prev_val, uint16_t difficulty) { /** @brief A legal random number starts with the previous number (which should be the 12 bits on the screen). * @param prev_val The previous value to tack onto. The return will have its first NUM_GRID MSBs be the same as prev_val, and the rest be new @@ -88,7 +158,7 @@ static uint32_t get_random_legal(uint32_t prev_val, uint16_t difficulty) { */ uint8_t min_zeros = (difficulty == DIFF_HARD) ? MIN_ZEROES_HARD : MIN_ZEROES; uint32_t max = (1 << (_num_bits_obst_pattern - NUM_GRID)) - 1; - uint32_t rand = get_random(max); + uint32_t rand = get_random_nonzero(max); uint32_t rand_legal = 0; prev_val = prev_val & ~max; @@ -111,6 +181,7 @@ static uint32_t get_random_legal(uint32_t prev_val, uint16_t difficulty) { } } rand_legal = prev_val | rand_legal; + print_binary(rand_legal, 32); return rand_legal; } @@ -137,9 +208,16 @@ static void display_ball(bool jumping) { static void display_score(uint8_t score) { char buf[3]; - score %= (MAX_DISP_SCORE + 1); - sprintf(buf, "%2d", score); - watch_display_string(buf, 2); + if (game_state.fuel_mode) { + score %= (MAX_DISP_SCORE_FUEL + 1); + sprintf(buf, "%1d", score); + watch_display_string(buf, 0); + } + else { + score %= (MAX_DISP_SCORE + 1); + sprintf(buf, "%2d", score); + watch_display_string(buf, 2); + } } static void add_to_score(endless_runner_state_t *state) { @@ -152,8 +230,14 @@ static void add_to_score(endless_runner_state_t *state) { display_score(game_state.curr_score); } +static void display_fuel(uint8_t subsecond, uint8_t difficulty) { + char buf[4]; + sprintf(buf, "%2d", game_state.fuel); + watch_display_string(buf, 2); +} + static void check_and_reset_hi_score(endless_runner_state_t *state) { - // Resets the hi scroe at the beginning of each month. + // Resets the hi score at the beginning of each month. watch_date_time date_time = watch_rtc_get_date_time(); if ((state -> year_last_hi_score != date_time.unit.year) || (state -> month_last_hi_score != date_time.unit.month)) @@ -169,19 +253,23 @@ static void display_difficulty(uint16_t difficulty) { switch (difficulty) { case DIFF_BABY: - watch_display_string("b", 3); + watch_display_string(" b", 2); break; case DIFF_EASY: - watch_display_string("E", 3); + watch_display_string(" E", 2); break; case DIFF_HARD: - watch_display_string("H", 3); + watch_display_string(" H", 2); + break; + case DIFF_FUEL: + watch_display_string(" F", 2); break; case DIFF_NORM: default: - watch_display_string("N", 3); + watch_display_string(" N", 2); break; } + game_state.fuel_mode = difficulty == DIFF_FUEL; } static void change_difficulty(endless_runner_state_t *state) { @@ -217,23 +305,30 @@ static void display_title(endless_runner_state_t *state) { } else { char buf[14]; - sprintf(buf, "ER HS%4d ", hi_score); + sprintf(buf, "ER HS%4d", hi_score); watch_display_string(buf, 0); } display_difficulty(difficulty); } static void begin_playing(endless_runner_state_t *state) { + uint8_t difficulty = state -> difficulty; game_state.curr_screen = SCREEN_PLAYING; watch_clear_colon(); movement_request_tick_frequency((state -> difficulty == DIFF_BABY) ? FREQ_SLOW : FREQ); - watch_display_string("ER ", 0); + if (game_state.fuel_mode) { + watch_display_string(" ", 0); + game_state.obst_pattern = get_random_fuel(0); + if ((16 * JUMP_FRAMES_FUEL_RECHARGE) < JUMP_FRAMES_FUEL) // 16 frames of zeros at the start of a level + game_state.fuel = JUMP_FRAMES_FUEL - (16 * JUMP_FRAMES_FUEL_RECHARGE); // Have it below its max to show it counting up when starting. + if (game_state.fuel < JUMP_FRAMES_FUEL_RECHARGE) game_state.fuel = JUMP_FRAMES_FUEL_RECHARGE; + } + else { + watch_display_string(" ", 2); + game_state.obst_pattern = get_random_legal(0, difficulty); + } game_state.jump_state = NOT_JUMPING; display_ball(game_state.jump_state != NOT_JUMPING); - do // Avoid the first array of obstacles being a boring line of 0s - { - game_state.obst_pattern = get_random_legal(0, state -> difficulty); - } while (game_state.obst_pattern == 0); display_score( game_state.curr_score); if (state -> soundOn){ watch_buzzer_play_note(BUZZER_NOTE_C5, 200); @@ -252,27 +347,20 @@ static void display_lose_screen(endless_runner_state_t *state) { delay_ms(600); } -static void stop_jumping(endless_runner_state_t *state) { - game_state.jump_state = NOT_JUMPING; - display_ball(game_state.jump_state != NOT_JUMPING); - if (state -> soundOn){ - if (game_state.success_jump) - watch_buzzer_play_note(BUZZER_NOTE_C5, 60); - else - watch_buzzer_play_note(BUZZER_NOTE_C3, 60); - } - game_state.success_jump = false; -} - static void display_obstacle(bool obstacle, int grid_loc, endless_runner_state_t *state) { + static bool prev_obst_pos_two = 0; switch (grid_loc) { case 2: game_state.loc_2_on = obstacle; if (obstacle) watch_set_pixel(0, 20); - else if (game_state.jump_state != NOT_JUMPING) + else if (game_state.jump_state != NOT_JUMPING) { watch_clear_pixel(0, 20); + if (game_state.fuel_mode && prev_obst_pos_two) + add_to_score(state); + } + prev_obst_pos_two = obstacle; break; case 3: game_state.loc_3_on = obstacle; @@ -283,9 +371,8 @@ static void display_obstacle(bool obstacle, int grid_loc, endless_runner_state_t break; case 1: - if (obstacle) { // If an obstacle is here, it means the ball cleared it + if (!game_state.fuel_mode && obstacle) // If an obstacle is here, it means the ball cleared it add_to_score(state); - } //fall through case 0: case 5: @@ -331,17 +418,34 @@ static void display_obstacle(bool obstacle, int grid_loc, endless_runner_state_t } } +static void stop_jumping(endless_runner_state_t *state) { + game_state.jump_state = NOT_JUMPING; + display_ball(game_state.jump_state != NOT_JUMPING); + if (state -> soundOn){ + if (game_state.success_jump) + watch_buzzer_play_note(BUZZER_NOTE_C5, 60); + else + watch_buzzer_play_note(BUZZER_NOTE_C3, 60); + } + game_state.success_jump = false; +} + static void display_obstacles(endless_runner_state_t *state) { for (int i = 0; i < NUM_GRID; i++) { // Use a bitmask to isolate each bit and shift it to the least significant position uint32_t mask = 1 << ((_num_bits_obst_pattern - 1) - i); bool obstacle = (game_state.obst_pattern & mask) >> ((_num_bits_obst_pattern - 1) - i); display_obstacle(obstacle, i, state); - } game_state.obst_pattern = game_state.obst_pattern << 1; game_state.obst_indx++; - if (game_state.obst_indx >= _num_bits_obst_pattern - NUM_GRID) { + if (game_state.fuel_mode) { + if (game_state.obst_indx >= (_num_bits_obst_pattern / 2)) { + game_state.obst_indx = 0; + game_state.obst_pattern = get_random_fuel(game_state.obst_pattern); + } + } + else if (game_state.obst_indx >= _num_bits_obst_pattern - NUM_GRID) { game_state.obst_indx = 0; game_state.obst_pattern = get_random_legal(game_state.obst_pattern, state -> difficulty); } @@ -357,23 +461,41 @@ static void update_game(endless_runner_state_t *state, uint8_t subsecond) { switch (game_state.jump_state) { case NOT_JUMPING: + if (game_state.fuel_mode) { + for (int i = 0; i < JUMP_FRAMES_FUEL_RECHARGE; i++) + { + if(game_state.fuel >= JUMP_FRAMES_FUEL) + break; + game_state.fuel++; + } + } break; case JUMPING_FINAL_FRAME: stop_jumping(state); break; default: - curr_jump_frame = game_state.jump_state - NOT_JUMPING; - if (curr_jump_frame >= JUMP_FRAMES_EASY || (state -> difficulty >= DIFF_NORM && curr_jump_frame >= JUMP_FRAMES)) - game_state.jump_state = JUMPING_FINAL_FRAME; - else - game_state.jump_state++; - //if (!watch_get_pin_level(BTN_ALARM) && !watch_get_pin_level(BTN_LIGHT)) game_state.jump_state = JUMPING_FINAL_FRAME; // Uncomment to have depressing the buttons cause the ball to drop regardless of its current frame. + if (game_state.fuel_mode) { + if (!game_state.fuel) + game_state.jump_state = JUMPING_FINAL_FRAME; + else + game_state.fuel--; + if (!watch_get_pin_level(BTN_ALARM) && !watch_get_pin_level(BTN_LIGHT)) stop_jumping(state); + } + else { + curr_jump_frame = game_state.jump_state - NOT_JUMPING; + if (curr_jump_frame >= JUMP_FRAMES_EASY || (state -> difficulty >= DIFF_NORM && curr_jump_frame >= JUMP_FRAMES)) + game_state.jump_state = JUMPING_FINAL_FRAME; + else + game_state.jump_state++; + } break; } if (game_state.jump_state == NOT_JUMPING && (game_state.loc_2_on || game_state.loc_3_on)) { delay_ms(200); // To show the player jumping onto the obstacle before displaying the lose screen. display_lose_screen(state); } + else if (game_state.fuel_mode) + display_fuel(subsecond, state -> difficulty); } void endless_runner_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { @@ -425,6 +547,7 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti case EVENT_LIGHT_BUTTON_DOWN: case EVENT_ALARM_BUTTON_DOWN: if (game_state.curr_screen == SCREEN_PLAYING && game_state.jump_state == NOT_JUMPING){ + if (game_state.fuel_mode && !game_state.fuel) break; game_state.jump_state = JUMPING_START; display_ball(game_state.jump_state != NOT_JUMPING); } diff --git a/movement/watch_faces/complication/endless_runner_face.h b/movement/watch_faces/complication/endless_runner_face.h index b9484dc..f9d6409 100644 --- a/movement/watch_faces/complication/endless_runner_face.h +++ b/movement/watch_faces/complication/endless_runner_face.h @@ -39,11 +39,11 @@ typedef struct { // These are values that need saving between uses uint32_t hi_score : 10; - uint32_t difficulty : 2; + uint32_t difficulty : 3; bool soundOn; uint32_t month_last_hi_score : 4; uint32_t year_last_hi_score : 6; - uint32_t unused : 9; + uint32_t unused : 8; } endless_runner_state_t; void endless_runner_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); From 324942009ef8adaa7d3e3669b8420a3d1b9e35b2 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Wed, 24 Jul 2024 21:08:35 -0400 Subject: [PATCH 111/161] Added second fuel mode where we don't recharge the fuel if it hits zero. --- .../watch_faces/complication/endless_runner_face.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/movement/watch_faces/complication/endless_runner_face.c b/movement/watch_faces/complication/endless_runner_face.c index bf8e3ac..6009015 100644 --- a/movement/watch_faces/complication/endless_runner_face.c +++ b/movement/watch_faces/complication/endless_runner_face.c @@ -46,6 +46,7 @@ typedef enum { DIFF_NORM, // FREQ FPS; MIN_ZEROES 0's min; Jump is JUMP_FRAMES frames DIFF_HARD, // FREQ FPS; MIN_ZEROES_HARD 0's min; jump is JUMP_FRAMES frames DIFF_FUEL, // Mode where the top-right displays the amoount of fuel that you can be above the ground for, dodging obstacles. When on the ground, your fuel recharges. + DIFF_FUEL_1, // Same as DIFF_FUEL, but if your fuel is 0, then you won't recharge DIFF_COUNT } RunnerDifficulty; @@ -232,6 +233,10 @@ static void add_to_score(endless_runner_state_t *state) { static void display_fuel(uint8_t subsecond, uint8_t difficulty) { char buf[4]; + if (difficulty == DIFF_FUEL_1 && game_state.fuel == 0 && subsecond % 4 == 0) { + watch_display_string(" ", 2); // Blink the 0 fuel to show it cannot be refilled. + return; + } sprintf(buf, "%2d", game_state.fuel); watch_display_string(buf, 2); } @@ -264,12 +269,15 @@ static void display_difficulty(uint16_t difficulty) { case DIFF_FUEL: watch_display_string(" F", 2); break; + case DIFF_FUEL_1: + watch_display_string("1F", 2); + break; case DIFF_NORM: default: watch_display_string(" N", 2); break; } - game_state.fuel_mode = difficulty == DIFF_FUEL; + game_state.fuel_mode = difficulty >= DIFF_FUEL && difficulty <= DIFF_FUEL_1; } static void change_difficulty(endless_runner_state_t *state) { @@ -464,7 +472,7 @@ static void update_game(endless_runner_state_t *state, uint8_t subsecond) { if (game_state.fuel_mode) { for (int i = 0; i < JUMP_FRAMES_FUEL_RECHARGE; i++) { - if(game_state.fuel >= JUMP_FRAMES_FUEL) + if(game_state.fuel >= JUMP_FRAMES_FUEL || (state -> difficulty == DIFF_FUEL_1 && !game_state.fuel)) break; game_state.fuel++; } From 28b14d36654998963a75bab687f4e02b645f1203 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Wed, 24 Jul 2024 21:12:17 -0400 Subject: [PATCH 112/161] LE mode in the endless runner now displays the current time. --- .../complication/endless_runner_face.c | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/movement/watch_faces/complication/endless_runner_face.c b/movement/watch_faces/complication/endless_runner_face.c index 6009015..96dfa89 100644 --- a/movement/watch_faces/complication/endless_runner_face.c +++ b/movement/watch_faces/complication/endless_runner_face.c @@ -319,6 +319,37 @@ static void display_title(endless_runner_state_t *state) { display_difficulty(difficulty); } +static void display_time(watch_date_time date_time, bool clock_mode_24h) { + static watch_date_time previous_date_time; + char buf[6 + 1]; + + // If the hour needs updating + if (date_time.unit.hour != previous_date_time.unit.hour) { + uint8_t hour = date_time.unit.hour; + + if (clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H); + else { + if (hour >= 12) watch_set_indicator(WATCH_INDICATOR_PM); + hour %= 12; + if (hour == 0) hour = 12; + } + watch_set_colon(); + sprintf( buf, "%2d%02d ", hour, date_time.unit.minute); + watch_display_string(buf, 4); + } + // If both digits of the minute need updating + else if ((date_time.unit.minute / 10) != (previous_date_time.unit.minute / 10)) { + sprintf( buf, "%02d ", date_time.unit.minute); + watch_display_string(buf, 6); + } + // If only the ones-place of the minute needs updating. + else if (date_time.unit.minute != previous_date_time.unit.minute) { + sprintf( buf, "%d ", date_time.unit.minute % 10); + watch_display_string(buf, 7); + } + previous_date_time.reg = date_time.reg; +} + static void begin_playing(endless_runner_state_t *state) { uint8_t difficulty = state -> difficulty; game_state.curr_screen = SCREEN_PLAYING; @@ -569,6 +600,7 @@ bool endless_runner_face_loop(movement_event_t event, movement_settings_t *setti display_title(state); break; case EVENT_LOW_ENERGY_UPDATE: + display_time(watch_rtc_get_date_time(), settings->bit.clock_mode_24h); break; default: return movement_default_loop_handler(event, settings); From e4a5121303c70d46773af8f99174386047ce86dc Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Fri, 26 Jul 2024 06:10:26 -0400 Subject: [PATCH 113/161] bug fix on displaying time in LE mode --- movement/watch_faces/complication/endless_runner_face.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/movement/watch_faces/complication/endless_runner_face.c b/movement/watch_faces/complication/endless_runner_face.c index 96dfa89..f24003f 100644 --- a/movement/watch_faces/complication/endless_runner_face.c +++ b/movement/watch_faces/complication/endless_runner_face.c @@ -304,6 +304,7 @@ static void display_title(endless_runner_state_t *state) { uint16_t hi_score = state -> hi_score; uint8_t difficulty = state -> difficulty; bool sound_on = state -> soundOn; + game_state.curr_screen = SCREEN_TITLE; memset(&game_state, 0, sizeof(game_state)); game_state.sec_before_moves = 1; // The first obstacles will all be 0s, which is about an extra second of delay. if (sound_on) game_state.sec_before_moves--; // Start chime is about 1 second @@ -323,9 +324,10 @@ static void display_time(watch_date_time date_time, bool clock_mode_24h) { static watch_date_time previous_date_time; char buf[6 + 1]; - // If the hour needs updating - if (date_time.unit.hour != previous_date_time.unit.hour) { + // If the hour needs updating or it's the first time displaying the time + if ((game_state.curr_screen != SCREEN_TIME) || (date_time.unit.hour != previous_date_time.unit.hour)) { uint8_t hour = date_time.unit.hour; + game_state.curr_screen = SCREEN_TIME; if (clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H); else { From c74ed78d72c98433c5b57f2573da984b2d0a4149 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Tue, 27 Aug 2024 13:09:44 -0400 Subject: [PATCH 114/161] Changed U LOSE to LOSE --- movement/watch_faces/complication/endless_runner_face.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/movement/watch_faces/complication/endless_runner_face.c b/movement/watch_faces/complication/endless_runner_face.c index f24003f..54cf31e 100644 --- a/movement/watch_faces/complication/endless_runner_face.c +++ b/movement/watch_faces/complication/endless_runner_face.c @@ -381,7 +381,7 @@ static void begin_playing(endless_runner_state_t *state) { static void display_lose_screen(endless_runner_state_t *state) { game_state.curr_screen = SCREEN_LOSE; game_state.curr_score = 0; - watch_display_string(" U LOSE ", 0); + watch_display_string(" LOSE ", 0); if (state -> soundOn) watch_buzzer_play_note(BUZZER_NOTE_A1, 600); else From 2e46aa0e2c5c3e406e90890e5a581b0257303fa9 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Tue, 3 Sep 2024 16:25:05 -0400 Subject: [PATCH 115/161] got rid of hardcoding of half-second zero blink --- movement/watch_faces/complication/endless_runner_face.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/movement/watch_faces/complication/endless_runner_face.c b/movement/watch_faces/complication/endless_runner_face.c index 54cf31e..3509fc2 100644 --- a/movement/watch_faces/complication/endless_runner_face.c +++ b/movement/watch_faces/complication/endless_runner_face.c @@ -233,7 +233,7 @@ static void add_to_score(endless_runner_state_t *state) { static void display_fuel(uint8_t subsecond, uint8_t difficulty) { char buf[4]; - if (difficulty == DIFF_FUEL_1 && game_state.fuel == 0 && subsecond % 4 == 0) { + if (difficulty == DIFF_FUEL_1 && game_state.fuel == 0 && subsecond % (FREQ/2) == 0) { watch_display_string(" ", 2); // Blink the 0 fuel to show it cannot be refilled. return; } From faec45ce245fa45a24a6ef3c67b6e8b1d089583b Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Tue, 3 Sep 2024 17:09:52 -0400 Subject: [PATCH 116/161] Include ability to select only the best words as the first choice --- .../watch_faces/complication/wordle_face.h | 1 + .../complication/wordle_face_dict.h | 26 ++++++++++--------- utils/wordle_face/wordle_list.py | 11 ++++++-- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/movement/watch_faces/complication/wordle_face.h b/movement/watch_faces/complication/wordle_face.h index 1b9a7fb..f390e73 100644 --- a/movement/watch_faces/complication/wordle_face.h +++ b/movement/watch_faces/complication/wordle_face.h @@ -81,6 +81,7 @@ * 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" diff --git a/movement/watch_faces/complication/wordle_face_dict.h b/movement/watch_faces/complication/wordle_face_dict.h index 11a89f7..6b82976 100644 --- a/movement/watch_faces/complication/wordle_face_dict.h +++ b/movement/watch_faces/complication/wordle_face_dict.h @@ -14,25 +14,25 @@ static const char _valid_letters[] = {'A', 'C', 'E', 'H', 'I', 'L', 'N', 'O', 'P // From: https://matthewminer.name/projects/calculators/wordle-words-left/ // Number of words found: 432 static const char _valid_words[][WORDLE_LENGTH + 1] = { - "STALE", "TRACE", "CLOSE", "ARISE", "SNIPE", "SHIRE", "LEASH", "SAINT", "CLEAN", - "RELIC", "CHORE", "CRONE", "REACH", "CHAOS", "TAPIR", "CAIRN", "TENOR", "STARE", - "HEART", "SCOPE", "SNARL", "SLEPT", "SINCE", "EPOCH", "SPACE", "SHARE", "SPOIL", + "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", "SPARE", "SEPIA", "ONSET", "SPLAT", "LEANT", + "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", "SCARE", "ETHOS", "THEIR", "STONE", - "SLATE", "LATCH", "HASTE", "SNARE", "SPINE", "SLANT", "SPEAR", "SCALE", "CAPER", + "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", "RAISE", "POSIT", "ATONE", "SPIRE", + "SPELT", "TRIAL", "PERIL", "SLICE", "SCANT", "SAINT", "POSIT", "ATONE", "SPIRE", "COAST", "INEPT", "SHOAL", "CLASH", "THORN", "PHASE", "SCORE", "TRICE", "PERCH", - "PORCH", "SHEAR", "CHOIR", "RHINO", "PLANT", "SHONE", "SANER", "LEARN", "ALTER", + "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", "CRATE", "ISLET", "PRINT", "SHINE", "NORTH", "CLEAT", "PLAIT", + "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", @@ -41,7 +41,7 @@ static const char _valid_words[][WORDLE_LENGTH + 1] = { "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", "CRANE", "CRISP", "TRASH", "PATIO", "PLATE", "HOTEL", "LEAST", + "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", @@ -282,8 +282,10 @@ static const char _possible_words[][WORDLE_LENGTH + 1] = { #endif }; -#if (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. +#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 diff --git a/utils/wordle_face/wordle_list.py b/utils/wordle_face/wordle_list.py index 39e310c..dac5ad6 100644 --- a/utils/wordle_face/wordle_list.py +++ b/utils/wordle_face/wordle_list.py @@ -1169,11 +1169,16 @@ def print_valid_words(letters=alphabet): print("#endif\n") items_per_row = 9 + top_words_percent = 3 valid_words = list_of_valid_words(letters, valid_list) valid_words = capitalize_all_and_remove_duplicates(valid_words) random.shuffle(valid_words) # Just in case the watch's random function is too pseudo, better to shuffle th elist so it's less likely to always have the same starting letter valid_words, num_uniq = rearrange_words_by_uniqueness(valid_words) + best_words = list(best_first_word(letters=letters, print_result=False).keys()) + num_best_words = round(len(valid_words) * top_words_percent / 100) + for i in range(num_best_words, 0, -1): + valid_words.insert(0, valid_words.pop(valid_words.index(best_words[i-1]))) print("static const char _valid_letters[] = {", end='') letters = sorted(letters) @@ -1209,8 +1214,10 @@ def print_valid_words(letters=alphabet): print("#endif") print("};\n") - print("#if (WORDLE_USE_RANDOM_GUESS == 2)") - print(f"static const uint16_t _num_random_guess_words = {num_uniq}; // The _valid_words array begins with this many words where each letter is different.") + print("#if (WORDLE_USE_RANDOM_GUESS == 3)") + print(f"static const uint16_t _num_random_guess_words = {num_best_words}; // The valid_words array begins with this many words that are considered the top {top_words_percent}% best options.") + print("#elif (WORDLE_USE_RANDOM_GUESS == 2)") + print(f"static const uint16_t _num_random_guess_words = {num_uniq}; // The valid_words array begins with this many words where each letter is different.") print("#elif (WORDLE_USE_RANDOM_GUESS == 1)") print("static const uint16_t _num_random_guess_words = _num_words;") print("#endif") From 118c07a3b63392fc5fb9c5b4b0dbfa052b254c9a Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Tue, 3 Sep 2024 17:13:59 -0400 Subject: [PATCH 117/161] Reduced struct memory per code review --- .../watch_faces/complication/endless_runner_face.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/movement/watch_faces/complication/endless_runner_face.h b/movement/watch_faces/complication/endless_runner_face.h index f9d6409..8c8fa21 100644 --- a/movement/watch_faces/complication/endless_runner_face.h +++ b/movement/watch_faces/complication/endless_runner_face.h @@ -37,13 +37,12 @@ */ typedef struct { - // These are values that need saving between uses - uint32_t hi_score : 10; - uint32_t difficulty : 3; - bool soundOn; - uint32_t month_last_hi_score : 4; - uint32_t year_last_hi_score : 6; - uint32_t unused : 8; + uint16_t hi_score : 10; + uint8_t difficulty : 3; + uint8_t month_last_hi_score : 4; + uint8_t year_last_hi_score : 6; + uint8_t soundOn : 1; + /* 24 bits, likely aligned to 32 bits = 4 bytes */ } endless_runner_state_t; void endless_runner_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); From b37be89bae9874ca70967580ba55a70f8a6f3dd4 Mon Sep 17 00:00:00 2001 From: mcguirepr89 Date: Wed, 4 Sep 2024 11:46:33 -0400 Subject: [PATCH 118/161] adds a short and long harry potter signal --- movement/movement_custom_signal_tunes.h | 56 +++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/movement/movement_custom_signal_tunes.h b/movement/movement_custom_signal_tunes.h index 0ac527a..2db10fc 100644 --- a/movement/movement_custom_signal_tunes.h +++ b/movement/movement_custom_signal_tunes.h @@ -119,4 +119,60 @@ int8_t signal_tune[] = { }; #endif // SIGNAL_TUNE_LAYLA +#ifdef SIGNAL_TUNE_HARRY_POTTER_SHORT +int8_t signal_tune[] = { + BUZZER_NOTE_B5, 24, + BUZZER_NOTE_REST, 1, + BUZZER_NOTE_E6, 12, + BUZZER_NOTE_REST, 1, + BUZZER_NOTE_G6, 6, + BUZZER_NOTE_REST, 1, + BUZZER_NOTE_F6SHARP_G6FLAT, 6, + BUZZER_NOTE_REST, 1, + BUZZER_NOTE_E6, 16, + BUZZER_NOTE_REST, 1, + BUZZER_NOTE_B6, 8, + BUZZER_NOTE_REST, 1, + BUZZER_NOTE_A6, 24, + BUZZER_NOTE_REST, 1, + BUZZER_NOTE_F6SHARP_G6FLAT, 24, + 0 +}; +#endif // SIGNAL_TUNE_HARRY_POTTER_SHORT + +#ifdef SIGNAL_TUNE_HARRY_POTTER_LONG +int8_t signal_tune[] = { + BUZZER_NOTE_B5, 24, + BUZZER_NOTE_REST, 1, + BUZZER_NOTE_E6, 12, + BUZZER_NOTE_REST, 1, + BUZZER_NOTE_G6, 6, + BUZZER_NOTE_REST, 1, + BUZZER_NOTE_F6SHARP_G6FLAT, 6, + BUZZER_NOTE_REST, 1, + BUZZER_NOTE_E6, 16, + BUZZER_NOTE_REST, 1, + BUZZER_NOTE_B6, 8, + BUZZER_NOTE_REST, 1, + BUZZER_NOTE_A6, 24, + BUZZER_NOTE_REST, 1, + BUZZER_NOTE_F6SHARP_G6FLAT, 24, + BUZZER_NOTE_REST, 1, + + BUZZER_NOTE_E6, 12, + BUZZER_NOTE_REST, 1, + BUZZER_NOTE_G6, 6, + BUZZER_NOTE_REST, 1, + BUZZER_NOTE_F6SHARP_G6FLAT, 6, + BUZZER_NOTE_REST, 1, + BUZZER_NOTE_D6SHARP_E6FLAT, 16, + BUZZER_NOTE_REST, 1, + BUZZER_NOTE_F6, 8, + BUZZER_NOTE_REST, 1, + BUZZER_NOTE_B5, 24, + + 0 +}; +#endif // SIGNAL_TUNE_HARRY_POTTER_LONG + #endif // MOVEMENT_CUSTOM_SIGNAL_TUNES_H_ From 48fd4ee90314ed44b0e6680f3e476533b4975c07 Mon Sep 17 00:00:00 2001 From: mcguirepr89 Date: Fri, 6 Sep 2024 10:19:17 -0400 Subject: [PATCH 119/161] slightly shorter first note --- movement/movement_custom_signal_tunes.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/movement/movement_custom_signal_tunes.h b/movement/movement_custom_signal_tunes.h index 2db10fc..5bf0f27 100644 --- a/movement/movement_custom_signal_tunes.h +++ b/movement/movement_custom_signal_tunes.h @@ -121,7 +121,7 @@ int8_t signal_tune[] = { #ifdef SIGNAL_TUNE_HARRY_POTTER_SHORT int8_t signal_tune[] = { - BUZZER_NOTE_B5, 24, + BUZZER_NOTE_B5, 12, BUZZER_NOTE_REST, 1, BUZZER_NOTE_E6, 12, BUZZER_NOTE_REST, 1, @@ -142,7 +142,7 @@ int8_t signal_tune[] = { #ifdef SIGNAL_TUNE_HARRY_POTTER_LONG int8_t signal_tune[] = { - BUZZER_NOTE_B5, 24, + BUZZER_NOTE_B5, 12, BUZZER_NOTE_REST, 1, BUZZER_NOTE_E6, 12, BUZZER_NOTE_REST, 1, From a79bb46d39ac32add2e0447ce2a100c8dd94dbf1 Mon Sep 17 00:00:00 2001 From: Jeremy O'Brien Date: Tue, 3 Sep 2024 21:43:25 -0400 Subject: [PATCH 120/161] add metal gear solid codec tune --- movement/movement_custom_signal_tunes.h | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/movement/movement_custom_signal_tunes.h b/movement/movement_custom_signal_tunes.h index 0ac527a..596df33 100644 --- a/movement/movement_custom_signal_tunes.h +++ b/movement/movement_custom_signal_tunes.h @@ -119,4 +119,31 @@ int8_t signal_tune[] = { }; #endif // SIGNAL_TUNE_LAYLA +#ifdef SIGNAL_TUNE_MGS_CODEC +int8_t signal_tune[] = { + BUZZER_NOTE_G5SHARP_A5FLAT, 1, + BUZZER_NOTE_C6, 1, + BUZZER_NOTE_G5SHARP_A5FLAT, 1, + BUZZER_NOTE_C6, 1, + BUZZER_NOTE_G5SHARP_A5FLAT, 1, + BUZZER_NOTE_C6, 1, + BUZZER_NOTE_G5SHARP_A5FLAT, 1, + BUZZER_NOTE_C6, 1, + BUZZER_NOTE_G5SHARP_A5FLAT, 1, + BUZZER_NOTE_C6, 1, + BUZZER_NOTE_REST, 6, + BUZZER_NOTE_G5SHARP_A5FLAT, 1, + BUZZER_NOTE_C6, 1, + BUZZER_NOTE_G5SHARP_A5FLAT, 1, + BUZZER_NOTE_C6, 1, + BUZZER_NOTE_G5SHARP_A5FLAT, 1, + BUZZER_NOTE_C6, 1, + BUZZER_NOTE_G5SHARP_A5FLAT, 1, + BUZZER_NOTE_C6, 1, + BUZZER_NOTE_G5SHARP_A5FLAT, 1, + BUZZER_NOTE_C6, 1, + 0 +}; +#endif // SIGNAL_TUNE_MGS_CODEC + #endif // MOVEMENT_CUSTOM_SIGNAL_TUNES_H_ From ddaf3a8324239b4da112ebbc7aaf0c8d2ed7952e Mon Sep 17 00:00:00 2001 From: Joseph Bryant Date: Sat, 24 Aug 2024 12:02:50 +0100 Subject: [PATCH 121/161] Add auto-repeat feature to Countdown watch face --- .../watch_faces/complication/countdown_face.c | 69 ++++++++++++++----- .../watch_faces/complication/countdown_face.h | 2 + 2 files changed, 55 insertions(+), 16 deletions(-) diff --git a/movement/watch_faces/complication/countdown_face.c b/movement/watch_faces/complication/countdown_face.c index b48ef59..f585ebc 100644 --- a/movement/watch_faces/complication/countdown_face.c +++ b/movement/watch_faces/complication/countdown_face.c @@ -1,6 +1,7 @@ /* * MIT License * + * Copyright (c) 2024 Joseph Bryant * Copyright (c) 2023 Konrad Rieck * Copyright (c) 2022 Wesley Ellis * @@ -68,17 +69,30 @@ static inline void button_beep(movement_settings_t *settings) { watch_buzzer_play_note(BUZZER_NOTE_C7, 50); } -static void start(countdown_state_t *state, movement_settings_t *settings) { - watch_date_time now = watch_rtc_get_date_time(); +static void schedule_countdown(countdown_state_t *state, movement_settings_t *settings) { - state->mode = cd_running; - state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); - state->target_ts = watch_utility_offset_timestamp(state->now_ts, state->hours, state->minutes, state->seconds); + // Calculate the new state->now_ts but don't update it until we've updated the target - + // avoid possible race where the old target is compared to the new time and immediately triggers + uint32_t new_now = watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), get_tz_offset(settings)); + state->target_ts = watch_utility_offset_timestamp(new_now, state->hours, state->minutes, state->seconds); + state->now_ts = new_now; watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, get_tz_offset(settings)); - movement_schedule_background_task(target_dt); - watch_set_indicator(WATCH_INDICATOR_BELL); + movement_schedule_background_task_for_face(state->watch_face_index, target_dt); } +static void auto_repeat(countdown_state_t *state, movement_settings_t *settings) { + movement_play_alarm(); + load_countdown(state); + schedule_countdown(state, settings); +} + +static void start(countdown_state_t *state, movement_settings_t *settings) { + state->mode = cd_running; + schedule_countdown(state, settings); +} + + + static void draw(countdown_state_t *state, uint8_t subsecond) { char buf[16]; @@ -100,7 +114,7 @@ static void draw(countdown_state_t *state, uint8_t subsecond) { break; case cd_reset: case cd_paused: - watch_clear_indicator(WATCH_INDICATOR_BELL); + watch_clear_indicator(WATCH_INDICATOR_SIGNAL); sprintf(buf, "CD %2d%02d%02d", state->hours, state->minutes, state->seconds); break; case cd_setting: @@ -127,13 +141,13 @@ static void draw(countdown_state_t *state, uint8_t subsecond) { static void pause(countdown_state_t *state) { state->mode = cd_paused; - movement_cancel_background_task(); - watch_clear_indicator(WATCH_INDICATOR_BELL); + movement_cancel_background_task_for_face(state->watch_face_index); + watch_clear_indicator(WATCH_INDICATOR_SIGNAL); } static void reset(countdown_state_t *state) { state->mode = cd_reset; - movement_cancel_background_task(); + movement_cancel_background_task_for_face(state->watch_face_index); load_countdown(state); } @@ -142,6 +156,15 @@ static void ring(countdown_state_t *state) { reset(state); } +static void times_up(movement_settings_t *settings, countdown_state_t *state) { + if(state->repeat) { + auto_repeat(state, settings); + } + else { + ring(state); + } +} + static void settings_increment(countdown_state_t *state) { switch(state->selection) { case 0: @@ -170,6 +193,7 @@ void countdown_face_setup(movement_settings_t *settings, uint8_t watch_face_inde memset(*context_ptr, 0, sizeof(countdown_state_t)); state->minutes = DEFAULT_MINUTES; state->mode = cd_reset; + state->watch_face_index = watch_face_index; store_countdown(state); } } @@ -180,9 +204,11 @@ void countdown_face_activate(movement_settings_t *settings, void *context) { if(state->mode == cd_running) { watch_date_time now = watch_rtc_get_date_time(); state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); - watch_set_indicator(WATCH_INDICATOR_BELL); + watch_set_indicator(WATCH_INDICATOR_SIGNAL); } watch_set_colon(); + if(state->repeat) + watch_set_indicator(WATCH_INDICATOR_BELL); movement_request_tick_frequency(1); quick_ticks_running = false; @@ -252,6 +278,7 @@ bool countdown_face_loop(movement_event_t event, movement_settings_t *settings, // Only start the timer if we have a valid time. start(state, settings); button_beep(settings); + watch_set_indicator(WATCH_INDICATOR_SIGNAL); } break; case cd_setting: @@ -261,9 +288,19 @@ bool countdown_face_loop(movement_event_t event, movement_settings_t *settings, draw(state, event.subsecond); break; case EVENT_ALARM_LONG_PRESS: - if (state->mode == cd_setting) { - quick_ticks_running = true; - movement_request_tick_frequency(8); + switch(state->mode) { + case cd_setting: + quick_ticks_running = true; + movement_request_tick_frequency(8); + break; + default: + // Toggle auto-repeat + button_beep(settings); + state->repeat = !state->repeat; + if(state->repeat) + watch_set_indicator(WATCH_INDICATOR_BELL); + else + watch_clear_indicator(WATCH_INDICATOR_BELL); } break; case EVENT_LIGHT_LONG_PRESS: @@ -285,7 +322,7 @@ bool countdown_face_loop(movement_event_t event, movement_settings_t *settings, abort_quick_ticks(state); break; case EVENT_BACKGROUND_TASK: - ring(state); + times_up(settings, state); break; case EVENT_TIMEOUT: abort_quick_ticks(state); diff --git a/movement/watch_faces/complication/countdown_face.h b/movement/watch_faces/complication/countdown_face.h index 1fe7c37..0f9cd8d 100644 --- a/movement/watch_faces/complication/countdown_face.h +++ b/movement/watch_faces/complication/countdown_face.h @@ -62,6 +62,8 @@ typedef struct { uint8_t set_seconds; uint8_t selection; countdown_mode_t mode; + bool repeat; + uint8_t watch_face_index; } countdown_state_t; From 07a2a49e72b43d56f570536b08e16a11358221ba Mon Sep 17 00:00:00 2001 From: CarpeNoctem Date: Wed, 17 Jan 2024 22:47:16 +1100 Subject: [PATCH 122/161] french_revolutionary face: fix compiler warning and uninitialized date_time variable --- movement/watch_faces/clock/french_revolutionary_face.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/movement/watch_faces/clock/french_revolutionary_face.c b/movement/watch_faces/clock/french_revolutionary_face.c index 519cfc3..da94fc9 100644 --- a/movement/watch_faces/clock/french_revolutionary_face.c +++ b/movement/watch_faces/clock/french_revolutionary_face.c @@ -28,6 +28,7 @@ void french_revolutionary_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { (void) settings; + (void) watch_face_index; if (*context_ptr == NULL) { *context_ptr = malloc(sizeof(french_revolutionary_state_t)); memset(*context_ptr, 0, sizeof(french_revolutionary_state_t)); @@ -64,7 +65,7 @@ bool french_revolutionary_face_loop(movement_event_t event, movement_settings_t break; case EVENT_TICK: case EVENT_LOW_ENERGY_UPDATE: - + date_time = watch_rtc_get_date_time(); decimal_time = get_decimal_time(&date_time); @@ -107,6 +108,7 @@ bool french_revolutionary_face_loop(movement_event_t event, movement_settings_t state->use_am_pm = !state->use_am_pm; if (state->use_am_pm) { watch_clear_indicator(WATCH_INDICATOR_24H); + date_time = watch_rtc_get_date_time(); if (date_time.unit.hour < 12) { watch_clear_indicator(WATCH_INDICATOR_PM); } else { watch_set_indicator(WATCH_INDICATOR_PM); } } else { @@ -240,4 +242,4 @@ char fix_character_one(char digit) { break; } return return_char; -} \ No newline at end of file +} From fea60e615cd1eeffb0b9ff791cb0fd82b67d8801 Mon Sep 17 00:00:00 2001 From: Konrad Rieck Date: Sat, 9 Sep 2023 15:40:57 +0200 Subject: [PATCH 123/161] display closest deadline on activation --- movement/watch_faces/complication/deadline_face.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/movement/watch_faces/complication/deadline_face.c b/movement/watch_faces/complication/deadline_face.c index 01ddc12..0796bd4 100644 --- a/movement/watch_faces/complication/deadline_face.c +++ b/movement/watch_faces/complication/deadline_face.c @@ -503,6 +503,18 @@ void deadline_face_activate(movement_settings_t *settings, void *context) /* Set display options */ _running_init(settings, state); state->mode = DEADLINE_FACE_RUNNING; + + /* Determine closest deadline */ + watch_date_time now = watch_rtc_get_date_time(); + uint32_t now_ts = watch_utility_date_time_to_unix_time(now, _get_tz_offset(settings)); + uint32_t min_ts = UINT32_MAX; + + for (uint8_t i = 0; i < DEADLINE_FACE_DATES; i++) { + if (state->deadlines[i] < now_ts || state->deadlines[i] > min_ts) + continue; + min_ts = state->deadlines[i]; + state->current_index = i; + } } /* Loop face */ From 4cb00ebb4e1cb186b5c67a198255f550cddcc676 Mon Sep 17 00:00:00 2001 From: Konrad Rieck Date: Sat, 9 Sep 2023 16:26:11 +0200 Subject: [PATCH 124/161] support for alarm --- .../watch_faces/complication/deadline_face.c | 81 +++++++++++++++---- .../watch_faces/complication/deadline_face.h | 1 + 2 files changed, 66 insertions(+), 16 deletions(-) diff --git a/movement/watch_faces/complication/deadline_face.c b/movement/watch_faces/complication/deadline_face.c index 0796bd4..09e3c34 100644 --- a/movement/watch_faces/complication/deadline_face.c +++ b/movement/watch_faces/complication/deadline_face.c @@ -196,6 +196,43 @@ static inline void _change_tick_freq(uint8_t freq) } } +/* Determine index of closest deadline */ +static uint8_t _closest_deadline(movement_settings_t *settings, deadline_state_t *state) +{ + watch_date_time now = watch_rtc_get_date_time(); + uint32_t now_ts = watch_utility_date_time_to_unix_time(now, _get_tz_offset(settings)); + uint32_t min_ts = UINT32_MAX; + uint8_t min_index = 0; + + for (uint8_t i = 0; i < DEADLINE_FACE_DATES; i++) { + if (state->deadlines[i] < now_ts || state->deadlines[i] > min_ts) + continue; + min_ts = state->deadlines[i]; + min_index = i; + } + + return min_index; +} + +/* Schedule alarm for next deadline */ +static void _schedule_alarm(movement_settings_t *settings, deadline_state_t *state) +{ + /* Cancel current alarm */ + movement_cancel_background_task(); + + /* Determine closest deadline */ + watch_date_time now = watch_rtc_get_date_time(); + uint32_t now_ts = watch_utility_date_time_to_unix_time(now, _get_tz_offset(settings)); + uint32_t next_ts = state->deadlines[_closest_deadline(settings, state)]; + watch_date_time next = watch_utility_date_time_from_unix_time(next_ts, _get_tz_offset(settings)); + + /* No suitable deadline */ + if (next_ts < now_ts) + return; + + movement_schedule_background_task(next); +} + /* Reset deadline to tomorrow */ static inline void _reset_deadline(movement_settings_t *settings, deadline_state_t *state) { @@ -259,6 +296,12 @@ static void _running_display(movement_event_t event, movement_settings_t *settin uint8_t i, range[] = { 60, 60, 24, 30, 12, 0 }; char buf[16]; + /* Display indicators */ + if (state->alarm_enabled) + watch_set_indicator(WATCH_INDICATOR_BELL); + else + watch_clear_indicator(WATCH_INDICATOR_BELL); + watch_date_time now = watch_rtc_get_date_time(); uint32_t now_ts = watch_utility_date_time_to_unix_time(now, _get_tz_offset(settings)); @@ -275,8 +318,9 @@ static void _running_display(movement_event_t event, movement_settings_t *settin } /* Get date time structs */ - watch_date_time deadline = watch_utility_date_time_from_unix_time(state->deadlines[state->current_index], _get_tz_offset(settings) - ); + watch_date_time deadline = watch_utility_date_time_from_unix_time( + state->deadlines[state->current_index], _get_tz_offset(settings) + ); /* Calculate naive difference of dates */ unit[0] = deadline.unit.second - now.unit.second; @@ -316,8 +360,6 @@ static void _running_display(movement_event_t event, movement_settings_t *settin /* hours:minutes:seconds */ sprintf(buf, "DL%2d%02d%02d%02d", i, unit[2] % 24, unit[1] % 60, unit[0] % 60); } - - //watch_set_indicator(WATCH_INDICATOR_BELL); watch_display_string(buf, 0); } @@ -352,9 +394,20 @@ static bool _running_loop(movement_event_t event, movement_settings_t *settings, case EVENT_MODE_BUTTON_UP: movement_move_to_next_face(); return false; + case EVENT_LIGHT_BUTTON_DOWN: + break; + case EVENT_LIGHT_LONG_PRESS: + state->alarm_enabled = !state->alarm_enabled; + _running_display(event, settings, state); + break; case EVENT_TIMEOUT: movement_move_to_face(0); break; + case EVENT_BACKGROUND_TASK: + if (state->alarm_enabled) + movement_play_alarm(); + _schedule_alarm(settings, state); + break; case EVENT_LOW_ENERGY_UPDATE: break; default: @@ -467,14 +520,21 @@ static bool _setting_loop(movement_event_t event, movement_settings_t *settings, break; case EVENT_TIMEOUT: _beep_button(settings); + _schedule_alarm(settings, state); _change_tick_freq(1); movement_move_to_face(0); break; case EVENT_MODE_BUTTON_UP: _beep_disable(settings); + _schedule_alarm(settings, state); _running_init(settings, state); state->mode = DEADLINE_FACE_RUNNING; break; + case EVENT_BACKGROUND_TASK: + if (state->alarm_enabled) + movement_play_alarm(); + _schedule_alarm(settings, state); + break; default: return movement_default_loop_handler(event, settings); } @@ -503,18 +563,7 @@ void deadline_face_activate(movement_settings_t *settings, void *context) /* Set display options */ _running_init(settings, state); state->mode = DEADLINE_FACE_RUNNING; - - /* Determine closest deadline */ - watch_date_time now = watch_rtc_get_date_time(); - uint32_t now_ts = watch_utility_date_time_to_unix_time(now, _get_tz_offset(settings)); - uint32_t min_ts = UINT32_MAX; - - for (uint8_t i = 0; i < DEADLINE_FACE_DATES; i++) { - if (state->deadlines[i] < now_ts || state->deadlines[i] > min_ts) - continue; - min_ts = state->deadlines[i]; - state->current_index = i; - } + state->current_index = _closest_deadline(settings, state); } /* Loop face */ diff --git a/movement/watch_faces/complication/deadline_face.h b/movement/watch_faces/complication/deadline_face.h index b560ab2..977ddf3 100644 --- a/movement/watch_faces/complication/deadline_face.h +++ b/movement/watch_faces/complication/deadline_face.h @@ -42,6 +42,7 @@ typedef struct { deadline_mode_t mode:1; uint8_t current_page:3; uint8_t current_index:2; + uint8_t alarm_enabled:1; uint32_t deadlines[DEADLINE_FACE_DATES]; } deadline_state_t; From c102a1016510388e37c55250153cc8dd4b93750e Mon Sep 17 00:00:00 2001 From: Konrad Rieck Date: Sat, 9 Sep 2023 16:35:30 +0200 Subject: [PATCH 125/161] beep on button --- movement/watch_faces/complication/deadline_face.c | 1 + 1 file changed, 1 insertion(+) diff --git a/movement/watch_faces/complication/deadline_face.c b/movement/watch_faces/complication/deadline_face.c index 09e3c34..21f4062 100644 --- a/movement/watch_faces/complication/deadline_face.c +++ b/movement/watch_faces/complication/deadline_face.c @@ -397,6 +397,7 @@ static bool _running_loop(movement_event_t event, movement_settings_t *settings, case EVENT_LIGHT_BUTTON_DOWN: break; case EVENT_LIGHT_LONG_PRESS: + _beep_button(settings); state->alarm_enabled = !state->alarm_enabled; _running_display(event, settings, state); break; From fab8c94428e1ea812b77121595e0c2fa08f3cfb3 Mon Sep 17 00:00:00 2001 From: Konrad Rieck Date: Sat, 2 Mar 2024 21:11:17 +0100 Subject: [PATCH 126/161] moved tick_freq to deadline_state_t structure. --- .../watch_faces/complication/deadline_face.c | 26 +++++++++---------- .../watch_faces/complication/deadline_face.h | 1 + 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/movement/watch_faces/complication/deadline_face.c b/movement/watch_faces/complication/deadline_face.c index 21f4062..2eeb0f7 100644 --- a/movement/watch_faces/complication/deadline_face.c +++ b/movement/watch_faces/complication/deadline_face.c @@ -100,8 +100,6 @@ #define SETTINGS_NUM (5) const char settings_titles[SETTINGS_NUM][3] = { "YR", "MO", "DA", "HR", "M1" }; -static uint8_t tick_freq; - /* Local functions */ static void _running_init(movement_settings_t *settings, deadline_state_t * state); static bool _running_loop(movement_event_t event, movement_settings_t *settings, void *context); @@ -113,7 +111,7 @@ static void _setting_display(movement_event_t event, movement_settings_t *settin /* Utility functions */ static void _increment_date(movement_settings_t *settings, deadline_state_t * state, watch_date_time date_time); static inline int32_t _get_tz_offset(movement_settings_t *settings); -static inline void _change_tick_freq(uint8_t freq); +static inline void _change_tick_freq(uint8_t freq, deadline_state_t * state); static inline bool _is_leap(int16_t y); static inline int _days_in_month(int16_t mpnth, int16_t y); static inline unsigned int _mod(int a, int b); @@ -188,11 +186,11 @@ static inline void _beep_disable(movement_settings_t *settings) } /* Change tick frequency */ -static inline void _change_tick_freq(uint8_t freq) +static inline void _change_tick_freq(uint8_t freq, deadline_state_t *state) { - if (tick_freq != freq) { + if (state->tick_freq != freq) { movement_request_tick_frequency(freq); - tick_freq = freq; + state->tick_freq = freq; } } @@ -416,7 +414,7 @@ static bool _running_loop(movement_event_t event, movement_settings_t *settings, } /* Slow down frequency after first loop for snappiness */ - _change_tick_freq(1); + _change_tick_freq(1, state); return true; } @@ -469,7 +467,7 @@ static void _setting_display(movement_event_t event, movement_settings_t *settin /* Init setting mode */ static void _setting_init(movement_settings_t *settings, deadline_state_t *state) { - _change_tick_freq(4); + _change_tick_freq(4, state); state->current_page = 0; /* Init fresh deadline to next day */ @@ -489,20 +487,20 @@ static bool _setting_loop(movement_event_t event, movement_settings_t *settings, switch (event.event_type) { case EVENT_TICK: - if (tick_freq == 8) { + if (state->tick_freq == 8) { if (watch_get_pin_level(BTN_ALARM)) { _increment_date(settings, state, date_time); _setting_display(event, settings, state, date_time); } else { - _change_tick_freq(4); + _change_tick_freq(4, state); } } break; case EVENT_ALARM_LONG_PRESS: - _change_tick_freq(8); + _change_tick_freq(8, state); break; case EVENT_ALARM_LONG_UP: - _change_tick_freq(4); + _change_tick_freq(4, state); break; case EVENT_LIGHT_LONG_PRESS: _beep_button(settings); @@ -515,14 +513,14 @@ static bool _setting_loop(movement_event_t event, movement_settings_t *settings, _setting_display(event, settings, state, date_time); break; case EVENT_ALARM_BUTTON_UP: - _change_tick_freq(4); + _change_tick_freq(4, state); _increment_date(settings, state, date_time); _setting_display(event, settings, state, date_time); break; case EVENT_TIMEOUT: _beep_button(settings); _schedule_alarm(settings, state); - _change_tick_freq(1); + _change_tick_freq(1, state); movement_move_to_face(0); break; case EVENT_MODE_BUTTON_UP: diff --git a/movement/watch_faces/complication/deadline_face.h b/movement/watch_faces/complication/deadline_face.h index 977ddf3..3b554b6 100644 --- a/movement/watch_faces/complication/deadline_face.h +++ b/movement/watch_faces/complication/deadline_face.h @@ -43,6 +43,7 @@ typedef struct { uint8_t current_page:3; uint8_t current_index:2; uint8_t alarm_enabled:1; + uint8_t tick_freq; uint32_t deadlines[DEADLINE_FACE_DATES]; } deadline_state_t; From c89316b3ecc11d91d32ec3a0c2b36e8260aa6873 Mon Sep 17 00:00:00 2001 From: Konrad Rieck Date: Sat, 2 Mar 2024 21:17:37 +0100 Subject: [PATCH 127/161] removed higher frequency in first loop run. --- movement/watch_faces/complication/deadline_face.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/movement/watch_faces/complication/deadline_face.c b/movement/watch_faces/complication/deadline_face.c index 2eeb0f7..c6b074d 100644 --- a/movement/watch_faces/complication/deadline_face.c +++ b/movement/watch_faces/complication/deadline_face.c @@ -413,8 +413,6 @@ static bool _running_loop(movement_event_t event, movement_settings_t *settings, return movement_default_loop_handler(event, settings); } - /* Slow down frequency after first loop for snappiness */ - _change_tick_freq(1, state); return true; } @@ -467,7 +465,7 @@ static void _setting_display(movement_event_t event, movement_settings_t *settin /* Init setting mode */ static void _setting_init(movement_settings_t *settings, deadline_state_t *state) { - _change_tick_freq(4, state); + _change_tick_freq(1, state); state->current_page = 0; /* Init fresh deadline to next day */ From 27ab799e858ff98e66c4d163dc7a2da869e678e8 Mon Sep 17 00:00:00 2001 From: Konrad Rieck Date: Fri, 8 Mar 2024 14:50:32 +0100 Subject: [PATCH 128/161] Fixed bugs and improved watchface - A background task is only scheduled if the alarm option is activated. If the option is enabled, an alarm sounds when the next deadline is reached. If the option is disabled, no alarm sounds and the watch can enter low energy sleep. - Fixed tick frequency error. During running mode, the clock ticks at 1Hz. This is set in the init function `_running_init` and thus ensures that we do not run too fast when returning from the setting mode. - Minor corrections to comments and indentations. --- .../watch_faces/complication/deadline_face.c | 63 +++++++++++++------ .../watch_faces/complication/deadline_face.h | 2 +- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/movement/watch_faces/complication/deadline_face.c b/movement/watch_faces/complication/deadline_face.c index c6b074d..3267940 100644 --- a/movement/watch_faces/complication/deadline_face.c +++ b/movement/watch_faces/complication/deadline_face.c @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2023 Konrad Rieck + * Copyright (c) 2023-2024 Konrad Rieck * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -65,6 +65,12 @@ * * - A *long press* on the *alarm button* activates settings mode and * enables configuring the currently selected deadline. + * + * - A *long press* on the *light button* activates a deadline alarm. The + * bell icon is displayed, and the alarm will ring upon reaching any of + * the deadlines set. It is important to note that the watch will not + * enter low-energy sleep mode while the alarm is enabled. + * * * ## Settings Mode * @@ -101,24 +107,24 @@ const char settings_titles[SETTINGS_NUM][3] = { "YR", "MO", "DA", "HR", "M1" }; /* Local functions */ -static void _running_init(movement_settings_t *settings, deadline_state_t * state); +static void _running_init(movement_settings_t *settings, deadline_state_t *state); static bool _running_loop(movement_event_t event, movement_settings_t *settings, void *context); -static void _running_display(movement_event_t event, movement_settings_t *settings, deadline_state_t * state); -static void _setting_init(movement_settings_t *settings, deadline_state_t * state); +static void _running_display(movement_event_t event, movement_settings_t *settings, deadline_state_t *state); +static void _setting_init(movement_settings_t *settings, deadline_state_t *state); static bool _setting_loop(movement_event_t event, movement_settings_t *settings, void *context); -static void _setting_display(movement_event_t event, movement_settings_t *settings, deadline_state_t * state, watch_date_time date); +static void _setting_display(movement_event_t event, movement_settings_t *settings, deadline_state_t *state, watch_date_time date); /* Utility functions */ -static void _increment_date(movement_settings_t *settings, deadline_state_t * state, watch_date_time date_time); +static void _increment_date(movement_settings_t *settings, deadline_state_t *state, watch_date_time date_time); static inline int32_t _get_tz_offset(movement_settings_t *settings); -static inline void _change_tick_freq(uint8_t freq, deadline_state_t * state); +static inline void _change_tick_freq(uint8_t freq, deadline_state_t *state); static inline bool _is_leap(int16_t y); static inline int _days_in_month(int16_t mpnth, int16_t y); static inline unsigned int _mod(int a, int b); static inline void _beep_button(movement_settings_t *settings); static inline void _beep_enable(movement_settings_t *settings); static inline void _beep_disable(movement_settings_t *settings); -static inline void _reset_deadline(movement_settings_t *settings, deadline_state_t * state); +static inline void _reset_deadline(movement_settings_t *settings, deadline_state_t *state); /* Check for leap year */ static inline bool _is_leap(int16_t y) @@ -147,7 +153,7 @@ static inline int _days_in_month(int16_t month, int16_t year) } } -/* Calculate time zone offset */ +/* Return time zone offset */ static inline int32_t _get_tz_offset(movement_settings_t *settings) { return movement_timezone_offsets[settings->bit.time_zone] * 60; @@ -156,7 +162,7 @@ static inline int32_t _get_tz_offset(movement_settings_t *settings) /* Beep for a button press*/ static inline void _beep_button(movement_settings_t *settings) { - // play a beep as confirmation for a button press (if applicable) + // Play a beep as confirmation for a button press (if applicable) if (!settings->bit.button_should_sound) return; @@ -289,7 +295,7 @@ static void _running_display(movement_event_t event, movement_settings_t *settin (void) event; (void) settings; - /* seconds, minutes, hours, days, months, years */ + /* Seconds, minutes, hours, days, months, years */ int16_t unit[] = { 0, 0, 0, 0, 0, 0 }; uint8_t i, range[] = { 60, 60, 24, 30, 12, 0 }; char buf[16]; @@ -370,6 +376,9 @@ static void _running_init(movement_settings_t *settings, deadline_state_t *state watch_clear_indicator(WATCH_INDICATOR_24H); watch_clear_indicator(WATCH_INDICATOR_PM); watch_set_colon(); + + /* Ensure 1Hz updates only */ + _change_tick_freq(1, state); } /* Loop of running mode */ @@ -397,15 +406,21 @@ static bool _running_loop(movement_event_t event, movement_settings_t *settings, case EVENT_LIGHT_LONG_PRESS: _beep_button(settings); state->alarm_enabled = !state->alarm_enabled; + if (state->alarm_enabled) { + _schedule_alarm(settings, state); + } else { + movement_cancel_background_task(); + } _running_display(event, settings, state); break; case EVENT_TIMEOUT: movement_move_to_face(0); break; case EVENT_BACKGROUND_TASK: - if (state->alarm_enabled) + if (state->alarm_enabled) { movement_play_alarm(); - _schedule_alarm(settings, state); + _schedule_alarm(settings, state); + } break; case EVENT_LOW_ENERGY_UPDATE: break; @@ -465,13 +480,15 @@ static void _setting_display(movement_event_t event, movement_settings_t *settin /* Init setting mode */ static void _setting_init(movement_settings_t *settings, deadline_state_t *state) { - _change_tick_freq(1, state); state->current_page = 0; /* Init fresh deadline to next day */ if (state->deadlines[state->current_index] == 0) { _reset_deadline(settings, state); } + + /* Ensure 1Hz updates only */ + _change_tick_freq(1, state); } /* Loop of setting mode */ @@ -517,20 +534,30 @@ static bool _setting_loop(movement_event_t event, movement_settings_t *settings, break; case EVENT_TIMEOUT: _beep_button(settings); - _schedule_alarm(settings, state); _change_tick_freq(1, state); + + /* Update alarm as deadlines may have changed */ + if (state->alarm_enabled) { + _schedule_alarm(settings, state); + } movement_move_to_face(0); break; case EVENT_MODE_BUTTON_UP: _beep_disable(settings); - _schedule_alarm(settings, state); _running_init(settings, state); + _running_display(event, settings, state); + + /* Update alarm as deadlines may have changed */ + if (state->alarm_enabled) { + _schedule_alarm(settings, state); + } state->mode = DEADLINE_FACE_RUNNING; break; case EVENT_BACKGROUND_TASK: - if (state->alarm_enabled) + if (state->alarm_enabled) { movement_play_alarm(); - _schedule_alarm(settings, state); + _schedule_alarm(settings, state); + } break; default: return movement_default_loop_handler(event, settings); diff --git a/movement/watch_faces/complication/deadline_face.h b/movement/watch_faces/complication/deadline_face.h index 3b554b6..9f175b0 100644 --- a/movement/watch_faces/complication/deadline_face.h +++ b/movement/watch_faces/complication/deadline_face.h @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2023 Konrad Rieck + * Copyright (c) 2023-2024 Konrad Rieck * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the From 93b9ca634168ba2c5bf1c8e16f11f58b51b4e4bf Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Thu, 8 Aug 2024 22:45:07 -0400 Subject: [PATCH 129/161] Made cards go through a deck format. --- .../complication/higher_lower_game_face.c | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/movement/watch_faces/complication/higher_lower_game_face.c b/movement/watch_faces/complication/higher_lower_game_face.c index cf5205c..bbf456b 100755 --- a/movement/watch_faces/complication/higher_lower_game_face.c +++ b/movement/watch_faces/complication/higher_lower_game_face.c @@ -43,6 +43,8 @@ #define BOARD_DISPLAY_END 9 #define MIN_CARD_VALUE 2 #define MAX_CARD_VALUE 14 +#define DUPLICATES_OF_CARD 4 +#define DECK_COUNT (DUPLICATES_OF_CARD * (MAX_CARD_VALUE - MIN_CARD_VALUE + 1)) #define FLIP_BOARD_DIRECTION false typedef struct card_t { @@ -73,6 +75,8 @@ static card_t game_board[GAME_BOARD_SIZE] = {0}; static uint8_t guess_position = 0; static uint8_t score = 0; static uint8_t completed_board_count = 0; +static uint8_t _deck[DECK_COUNT]; +static uint8_t _curr_card; static uint8_t generate_random_number(uint8_t num_values) { // Emulator: use rand. Hardware: use arc4random. @@ -83,10 +87,41 @@ static uint8_t generate_random_number(uint8_t num_values) { #endif } +static void stack_deck(uint8_t *array) { + const uint8_t unique_cards = MAX_CARD_VALUE - MIN_CARD_VALUE + 1; + for (uint8_t i = 0; i < unique_cards; i++) + { + for (uint8_t j = 0; j < DUPLICATES_OF_CARD; j++) + array[(i * DUPLICATES_OF_CARD) + j] = MIN_CARD_VALUE + i; + } +} + +static void shuffle_deck(uint8_t *array, uint8_t n) { + // Randomize shuffle with Fisher Yates + uint8_t i, j, tmp; + for (i = n - 1; i > 0; i--) { + j = generate_random_number(0xFF) % (i + 1); + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + } +} + +static void reset_deck(void) { + _curr_card = 0; + stack_deck(_deck); + shuffle_deck(_deck, DECK_COUNT); +} + +static uint8_t get_next_card(void) { + if (_curr_card >= DECK_COUNT) reset_deck(); + return _deck[_curr_card++]; +} + static void reset_board(bool first_round) { // First card is random on the first board, and carried over from the last position on subsequent boards const uint8_t first_card_value = first_round - ? generate_random_number(MAX_CARD_VALUE - MIN_CARD_VALUE + 1) + MIN_CARD_VALUE + ? get_next_card() : game_board[GAME_BOARD_SIZE - 1].value; game_board[0].value = first_card_value; @@ -95,7 +130,7 @@ static void reset_board(bool first_round) { // Fill remainder of board for (size_t i = 1; i < GAME_BOARD_SIZE; ++i) { game_board[i] = (card_t) { - .value = generate_random_number(MAX_CARD_VALUE - MIN_CARD_VALUE + 1) + MIN_CARD_VALUE, + .value = get_next_card(), .revealed = false }; } @@ -105,6 +140,7 @@ static void init_game(void) { watch_clear_display(); watch_display_string(TITLE_TEXT, BOARD_DISPLAY_START); watch_display_string("GA", STATUS_DISPLAY_START); + reset_deck(); reset_board(true); score = 0; completed_board_count = 0; From 6db0a62bbf6e6de686ea8879f503671064d1840a Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 29 Jul 2023 17:44:54 +0100 Subject: [PATCH 130/161] Hi-lo: Use alternate card faces --- .../complication/higher_lower_game_face.c | 14 ++++---------- .../complication/higher_lower_game_face.h | 7 ++++--- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/movement/watch_faces/complication/higher_lower_game_face.c b/movement/watch_faces/complication/higher_lower_game_face.c index aed6eee..cf5205c 100755 --- a/movement/watch_faces/complication/higher_lower_game_face.c +++ b/movement/watch_faces/complication/higher_lower_game_face.c @@ -139,28 +139,22 @@ static void render_board_position(size_t board_position) { const uint8_t value = game_board[board_position].value; switch (value) { - case 14: // A - watch_display_character('H', display_position); - break; - case 13: // K (≡) + case 14: // A (≡) watch_display_character(' ', display_position); set_segment_at_position(A, display_position); set_segment_at_position(D, display_position); set_segment_at_position(G, display_position); break; - case 12: // Q (=) + case 13: // K (=) watch_display_character(' ', display_position); set_segment_at_position(A, display_position); set_segment_at_position(D, display_position); break; - case 11: // J (-) + case 12: // Q (-) watch_display_character('-', display_position); break; - case 10: // 10 (0) - watch_display_character('0', display_position); - break; default: { - const char display_char = value + '0'; + const char display_char = (value - MIN_CARD_VALUE) + '0'; watch_display_character(display_char, display_position); } } diff --git a/movement/watch_faces/complication/higher_lower_game_face.h b/movement/watch_faces/complication/higher_lower_game_face.h index d093680..efbd394 100755 --- a/movement/watch_faces/complication/higher_lower_game_face.h +++ b/movement/watch_faces/complication/higher_lower_game_face.h @@ -68,13 +68,14 @@ * | Cards | | * |---------|--------------------------| * | Value |2|3|4|5|6|7|8|9|10|J|Q|K|A| - * | Display |2|3|4|5|6|7|8|9| 0|-|=|≡|H| + * | Display |0|1|2|3|4|5|6|7|8 |9|-|=|≡| * - * The following may more legible choice: + * A previous alternative can be found in the git history: * | Cards | | * |---------|--------------------------| * | Value |2|3|4|5|6|7|8|9|10|J|Q|K|A| - * | Display |0|1|2|3|4|5|6|7|8 |9|-|=|≡| + * | Display |2|3|4|5|6|7|8|9| 0|-|=|≡|H| + * * * Future Ideas: * - Add sounds From e2ec468754b5d1b43a4debfc7761041d0b9e0e5b Mon Sep 17 00:00:00 2001 From: Konrad Rieck Date: Sat, 9 Mar 2024 20:48:42 +0100 Subject: [PATCH 131/161] Alarm handled using background task. The alarm for deadlines is now handled via a background task. Instead of a scheduled task that prevents sleep mode, the face checks in the background every minute whether an deadline is due. If this is the case, the face wakes up from sleep mode and starts a scheduled task for the remaining seconds. --- .../watch_faces/complication/deadline_face.c | 107 +++++++++++------- .../watch_faces/complication/deadline_face.h | 4 +- 2 files changed, 72 insertions(+), 39 deletions(-) diff --git a/movement/watch_faces/complication/deadline_face.c b/movement/watch_faces/complication/deadline_face.c index 3267940..521c9bb 100644 --- a/movement/watch_faces/complication/deadline_face.c +++ b/movement/watch_faces/complication/deadline_face.c @@ -115,6 +115,9 @@ static bool _setting_loop(movement_event_t event, movement_settings_t *settings, static void _setting_display(movement_event_t event, movement_settings_t *settings, deadline_state_t *state, watch_date_time date); /* Utility functions */ +static void _background_alarm_play(movement_settings_t *settings, deadline_state_t *state); +static void _background_alarm_schedule(movement_settings_t *settings, deadline_state_t *state); +static void _background_alarm_cancel(movement_settings_t *settings, deadline_state_t *state); static void _increment_date(movement_settings_t *settings, deadline_state_t *state, watch_date_time date_time); static inline int32_t _get_tz_offset(movement_settings_t *settings); static inline void _change_tick_freq(uint8_t freq, deadline_state_t *state); @@ -218,23 +221,29 @@ static uint8_t _closest_deadline(movement_settings_t *settings, deadline_state_t return min_index; } -/* Schedule alarm for next deadline */ -static void _schedule_alarm(movement_settings_t *settings, deadline_state_t *state) +/* Play background alarm */ +static void _background_alarm_play(movement_settings_t *settings, deadline_state_t *state) { - /* Cancel current alarm */ - movement_cancel_background_task(); + (void) settings; - /* Determine closest deadline */ - watch_date_time now = watch_rtc_get_date_time(); - uint32_t now_ts = watch_utility_date_time_to_unix_time(now, _get_tz_offset(settings)); - uint32_t next_ts = state->deadlines[_closest_deadline(settings, state)]; - watch_date_time next = watch_utility_date_time_from_unix_time(next_ts, _get_tz_offset(settings)); + /* Use the default alarm from movement */ + if (state->alarm_enabled) + movement_play_alarm(); +} - /* No suitable deadline */ - if (next_ts < now_ts) - return; +/* Schedule background alarm */ +static void _background_alarm_schedule(movement_settings_t *settings, deadline_state_t *state) +{ + /* We simply re-use the scheduling in the background task */ + deadline_face_wants_background_task(settings, state); +} - movement_schedule_background_task(next); +/* Cancel background alarm */ +static void _background_alarm_cancel(movement_settings_t *settings, deadline_state_t *state) +{ + (void) settings; + + movement_cancel_background_task_for_face(state->face_idx); } /* Reset deadline to tomorrow */ @@ -322,9 +331,8 @@ static void _running_display(movement_event_t event, movement_settings_t *settin } /* Get date time structs */ - watch_date_time deadline = watch_utility_date_time_from_unix_time( - state->deadlines[state->current_index], _get_tz_offset(settings) - ); + watch_date_time deadline = watch_utility_date_time_from_unix_time(state->deadlines[state->current_index], _get_tz_offset(settings) + ); /* Calculate naive difference of dates */ unit[0] = deadline.unit.second - now.unit.second; @@ -385,7 +393,9 @@ static void _running_init(movement_settings_t *settings, deadline_state_t *state static bool _running_loop(movement_event_t event, movement_settings_t *settings, void *context) { deadline_state_t *state = (deadline_state_t *) context; - _running_display(event, settings, state); + + if (event.event_type != EVENT_BACKGROUND_TASK) + _running_display(event, settings, state); switch (event.event_type) { case EVENT_ALARM_BUTTON_UP: @@ -407,9 +417,9 @@ static bool _running_loop(movement_event_t event, movement_settings_t *settings, _beep_button(settings); state->alarm_enabled = !state->alarm_enabled; if (state->alarm_enabled) { - _schedule_alarm(settings, state); + _background_alarm_schedule(settings, context); } else { - movement_cancel_background_task(); + _background_alarm_cancel(settings, context); } _running_display(event, settings, state); break; @@ -417,10 +427,7 @@ static bool _running_loop(movement_event_t event, movement_settings_t *settings, movement_move_to_face(0); break; case EVENT_BACKGROUND_TASK: - if (state->alarm_enabled) { - movement_play_alarm(); - _schedule_alarm(settings, state); - } + _background_alarm_play(settings, state); break; case EVENT_LOW_ENERGY_UPDATE: break; @@ -498,7 +505,8 @@ static bool _setting_loop(movement_event_t event, movement_settings_t *settings, watch_date_time date_time; date_time = watch_utility_date_time_from_unix_time(state->deadlines[state->current_index], _get_tz_offset(settings)); - _setting_display(event, settings, state, date_time); + if (event.event_type != EVENT_BACKGROUND_TASK) + _setting_display(event, settings, state, date_time); switch (event.event_type) { case EVENT_TICK: @@ -534,30 +542,20 @@ static bool _setting_loop(movement_event_t event, movement_settings_t *settings, break; case EVENT_TIMEOUT: _beep_button(settings); + _background_alarm_schedule(settings, context); _change_tick_freq(1, state); - - /* Update alarm as deadlines may have changed */ - if (state->alarm_enabled) { - _schedule_alarm(settings, state); - } + state->mode = DEADLINE_FACE_RUNNING; movement_move_to_face(0); break; case EVENT_MODE_BUTTON_UP: _beep_disable(settings); + _background_alarm_schedule(settings, context); _running_init(settings, state); _running_display(event, settings, state); - - /* Update alarm as deadlines may have changed */ - if (state->alarm_enabled) { - _schedule_alarm(settings, state); - } state->mode = DEADLINE_FACE_RUNNING; break; case EVENT_BACKGROUND_TASK: - if (state->alarm_enabled) { - movement_play_alarm(); - _schedule_alarm(settings, state); - } + _background_alarm_play(settings, state); break; default: return movement_default_loop_handler(event, settings); @@ -574,8 +572,13 @@ void deadline_face_setup(movement_settings_t *settings, uint8_t watch_face_index if (*context_ptr != NULL) return; /* Skip setup if context available */ + /* Allocate state */ *context_ptr = malloc(sizeof(deadline_state_t)); memset(*context_ptr, 0, sizeof(deadline_state_t)); + + /* Store face index for background tasks */ + deadline_state_t *state = (deadline_state_t *) *context_ptr; + state->face_idx = watch_face_index; } /* Activate face */ @@ -614,3 +617,31 @@ void deadline_face_resign(movement_settings_t *settings, void *context) (void) settings; (void) context; } + +/* Want background task */ +bool deadline_face_wants_background_task(movement_settings_t *settings, void *context) +{ + deadline_state_t *state = (deadline_state_t *) context; + + if (!state->alarm_enabled) + return false; + + /* Determine closest deadline */ + watch_date_time now = watch_rtc_get_date_time(); + uint32_t now_ts = watch_utility_date_time_to_unix_time(now, _get_tz_offset(settings)); + uint32_t next_ts = state->deadlines[_closest_deadline(settings, state)]; + + /* No active deadline */ + if (next_ts < now_ts) + return false; + + /* No deadline within next 60 seconds */ + if (next_ts >= now_ts + 60) + return false; + + /* Deadline within next minute. Let's set up an alarm */ + watch_date_time next = watch_utility_date_time_from_unix_time(next_ts, _get_tz_offset(settings)); + movement_request_wake(); + movement_schedule_background_task_for_face(state->face_idx, next); + return false; +} diff --git a/movement/watch_faces/complication/deadline_face.h b/movement/watch_faces/complication/deadline_face.h index 9f175b0..4e4efe0 100644 --- a/movement/watch_faces/complication/deadline_face.h +++ b/movement/watch_faces/complication/deadline_face.h @@ -44,6 +44,7 @@ typedef struct { uint8_t current_index:2; uint8_t alarm_enabled:1; uint8_t tick_freq; + uint8_t face_idx; uint32_t deadlines[DEADLINE_FACE_DATES]; } deadline_state_t; @@ -51,13 +52,14 @@ void deadline_face_setup(movement_settings_t *settings, uint8_t watch_face_index void deadline_face_activate(movement_settings_t *settings, void *context); bool deadline_face_loop(movement_event_t event, movement_settings_t *settings, void *context); void deadline_face_resign(movement_settings_t *settings, void *context); +bool deadline_face_wants_background_task(movement_settings_t *settings, void *context); #define deadline_face ((const watch_face_t){ \ deadline_face_setup, \ deadline_face_activate, \ deadline_face_loop, \ deadline_face_resign, \ - NULL, \ + deadline_face_wants_background_task \ }) #endif // DEADLINE_FACE_H_ From 50cff5483377ac7ff4eae3601ba68207e4533e8f Mon Sep 17 00:00:00 2001 From: Konrad Rieck Date: Sat, 9 Mar 2024 23:50:47 +0100 Subject: [PATCH 132/161] move to deadline face on alarm --- movement/watch_faces/complication/deadline_face.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/movement/watch_faces/complication/deadline_face.c b/movement/watch_faces/complication/deadline_face.c index 521c9bb..ae9cab8 100644 --- a/movement/watch_faces/complication/deadline_face.c +++ b/movement/watch_faces/complication/deadline_face.c @@ -226,9 +226,11 @@ static void _background_alarm_play(movement_settings_t *settings, deadline_state { (void) settings; - /* Use the default alarm from movement */ - if (state->alarm_enabled) + /* Use the default alarm from movement and move to foreground */ + if (state->alarm_enabled) { movement_play_alarm(); + movement_move_to_face(state->face_idx); + } } /* Schedule background alarm */ From 9e1e692511011e9bb105dbab0c2d5426dd7cbb94 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 1 Sep 2024 19:30:31 +0100 Subject: [PATCH 133/161] Hi-lo: Additional code tweaks --- .../complication/higher_lower_game_face.c | 59 ++++++++++--------- .../complication/higher_lower_game_face.h | 2 + 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/movement/watch_faces/complication/higher_lower_game_face.c b/movement/watch_faces/complication/higher_lower_game_face.c index bbf456b..f9dbcd0 100755 --- a/movement/watch_faces/complication/higher_lower_game_face.c +++ b/movement/watch_faces/complication/higher_lower_game_face.c @@ -36,15 +36,16 @@ #define GAME_BOARD_SIZE 6 #define MAX_BOARDS 40 #define GUESSES_PER_SCREEN 5 -#define WIN_SCORE MAX_BOARDS * GUESSES_PER_SCREEN +#define WIN_SCORE (MAX_BOARDS * GUESSES_PER_SCREEN) #define STATUS_DISPLAY_START 0 #define BOARD_SCORE_DISPLAY_START 2 #define BOARD_DISPLAY_START 4 #define BOARD_DISPLAY_END 9 #define MIN_CARD_VALUE 2 #define MAX_CARD_VALUE 14 -#define DUPLICATES_OF_CARD 4 -#define DECK_COUNT (DUPLICATES_OF_CARD * (MAX_CARD_VALUE - MIN_CARD_VALUE + 1)) +#define CARD_RANK_COUNT (MAX_CARD_VALUE - MIN_CARD_VALUE + 1) +#define CARD_SUIT_COUNT 4 +#define DECK_SIZE (CARD_SUIT_COUNT * CARD_RANK_COUNT) #define FLIP_BOARD_DIRECTION false typedef struct card_t { @@ -75,8 +76,8 @@ static card_t game_board[GAME_BOARD_SIZE] = {0}; static uint8_t guess_position = 0; static uint8_t score = 0; static uint8_t completed_board_count = 0; -static uint8_t _deck[DECK_COUNT]; -static uint8_t _curr_card; +static uint8_t deck[DECK_SIZE] = {0}; +static uint8_t current_card = 0; static uint8_t generate_random_number(uint8_t num_values) { // Emulator: use rand. Hardware: use arc4random. @@ -87,35 +88,37 @@ static uint8_t generate_random_number(uint8_t num_values) { #endif } -static void stack_deck(uint8_t *array) { - const uint8_t unique_cards = MAX_CARD_VALUE - MIN_CARD_VALUE + 1; - for (uint8_t i = 0; i < unique_cards; i++) - { - for (uint8_t j = 0; j < DUPLICATES_OF_CARD; j++) - array[(i * DUPLICATES_OF_CARD) + j] = MIN_CARD_VALUE + i; +static void stack_deck(void) { + for (size_t i = 0; i < CARD_RANK_COUNT; i++) { + for (size_t j = 0; j < CARD_SUIT_COUNT; j++) + deck[(i * CARD_SUIT_COUNT) + j] = MIN_CARD_VALUE + i; } } -static void shuffle_deck(uint8_t *array, uint8_t n) { +static void shuffle_deck(void) { // Randomize shuffle with Fisher Yates - uint8_t i, j, tmp; - for (i = n - 1; i > 0; i--) { - j = generate_random_number(0xFF) % (i + 1); - tmp = array[j]; - array[j] = array[i]; - array[i] = tmp; - } + size_t i; + size_t j; + uint8_t tmp; + + for (i = DECK_SIZE - 1; i > 0; i--) { + j = generate_random_number(0xFF) % (i + 1); + tmp = deck[j]; + deck[j] = deck[i]; + deck[i] = tmp; + } } static void reset_deck(void) { - _curr_card = 0; - stack_deck(_deck); - shuffle_deck(_deck, DECK_COUNT); + current_card = 0; + stack_deck(); + shuffle_deck(); } static uint8_t get_next_card(void) { - if (_curr_card >= DECK_COUNT) reset_deck(); - return _deck[_curr_card++]; + if (current_card >= DECK_SIZE) + reset_deck(); + return deck[current_card++]; } static void reset_board(bool first_round) { @@ -156,8 +159,8 @@ static void set_segment_at_position(segment_t segment, uint8_t position) { static void render_board_position(size_t board_position) { const size_t display_position = FLIP_BOARD_DIRECTION - ? BOARD_DISPLAY_START + board_position - : BOARD_DISPLAY_END - board_position; + ? BOARD_DISPLAY_START + board_position + : BOARD_DISPLAY_END - board_position; const bool revealed = game_board[board_position].revealed; //// Current position indicator spot @@ -196,7 +199,7 @@ static void render_board_position(size_t board_position) { } } -static void render_board() { +static void render_board(void) { for (size_t i = 0; i < GAME_BOARD_SIZE; ++i) { render_board_position(i); } @@ -218,7 +221,7 @@ static void render_final_score(void) { watch_display_string(buf, BOARD_DISPLAY_START); } -static guess_t get_answer() { +static guess_t get_answer(void) { if (guess_position < 1 || guess_position > GAME_BOARD_SIZE) return HL_GUESS_EQUAL; // Maybe add an error state, shouldn't ever hit this. diff --git a/movement/watch_faces/complication/higher_lower_game_face.h b/movement/watch_faces/complication/higher_lower_game_face.h index efbd394..13da586 100755 --- a/movement/watch_faces/complication/higher_lower_game_face.h +++ b/movement/watch_faces/complication/higher_lower_game_face.h @@ -65,6 +65,8 @@ * The face tries to remain true to the spirit of using "cards"; to cope with the display limitations I've arrived at * the following mapping of card values to screen display, but am open to better suggestions: * + * Thanks to voloved for adding deck shuffling and drawing! + * * | Cards | | * |---------|--------------------------| * | Value |2|3|4|5|6|7|8|9|10|J|Q|K|A| From 52f9500666a10dfa3d45288749d5edcae1d5b211 Mon Sep 17 00:00:00 2001 From: "R. Alex Barbieri" <> Date: Sat, 27 Apr 2024 13:33:32 -0500 Subject: [PATCH 134/161] add blinking to DST toggle in settings page --- movement/watch_faces/settings/set_time_face.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/movement/watch_faces/settings/set_time_face.c b/movement/watch_faces/settings/set_time_face.c index 16011e3..fb0c806 100644 --- a/movement/watch_faces/settings/set_time_face.c +++ b/movement/watch_faces/settings/set_time_face.c @@ -185,6 +185,9 @@ bool set_time_face_loop(movement_event_t event, movement_settings_t *settings, v case 5: buf[8] = buf[9] = ' '; break; + case 7: + buf[9] = ' '; + break; } } From 5ae88e438d79bcdb6e18f58aa165d4cf9ef6f87f Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 8 Sep 2024 10:50:09 -0400 Subject: [PATCH 135/161] Minot cleanup --- movement/movement.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/movement/movement.c b/movement/movement.c index ebd831f..60e9106 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -457,8 +457,7 @@ uint8_t movement_claim_backup_register(void) { } int16_t get_timezone_offset(uint8_t timezone_idx, watch_date_time date_time) { - if (!movement_state.settings.bit.dst_active) return movement_timezone_offsets[timezone_idx]; - if (dst_occurring(date_time)) + if (movement_state.settings.bit.dst_active && dst_occurring(date_time)) return movement_timezone_dst_offsets[timezone_idx]; return movement_timezone_offsets[timezone_idx]; } From 002f5bc242a7d8a4a2aa371b685994ecb86aa2b7 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Tue, 6 Aug 2024 07:05:19 -0400 Subject: [PATCH 136/161] Updated the tally couter face to allow for presets, negative numbers, fast scrolling, and toggling sound --- .../watch_faces/complication/tally_face.c | 145 ++++++++++++++++-- .../watch_faces/complication/tally_face.h | 21 ++- 2 files changed, 145 insertions(+), 21 deletions(-) diff --git a/movement/watch_faces/complication/tally_face.c b/movement/watch_faces/complication/tally_face.c index 896a54f..ca3ed32 100644 --- a/movement/watch_faces/complication/tally_face.c +++ b/movement/watch_faces/complication/tally_face.c @@ -27,44 +27,148 @@ #include "tally_face.h" #include "watch.h" +#define TALLY_FACE_MAX 9999 +#define TALLY_FACE_MIN -99 + +static bool _init_val; +static bool _quick_ticks_running; +static const int16_t _tally_default[] = {0, 40, 20}; +static const uint8_t _tally_default_size = sizeof(_tally_default) / sizeof(int16_t); + void tally_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { (void) settings; (void) watch_face_index; if (*context_ptr == NULL) { *context_ptr = malloc(sizeof(tally_state_t)); memset(*context_ptr, 0, sizeof(tally_state_t)); + tally_state_t *state = (tally_state_t *)*context_ptr; + state->tally_default_idx = 0; + state->soundOff = true; + state->tally_idx = _tally_default[state->tally_default_idx]; + _init_val = true; } } void tally_face_activate(movement_settings_t *settings, void *context) { (void) settings; (void) context; + _quick_ticks_running = false; +} + +static void start_quick_cyc(void){ + _quick_ticks_running = true; + movement_request_tick_frequency(8); +} + +static void stop_quick_cyc(void){ + _quick_ticks_running = false; + movement_request_tick_frequency(1); +} + +static void tally_face_increment(tally_state_t *state) { + bool soundOn = !_quick_ticks_running && !state->soundOff; + _init_val = false; + if (state->tally_idx >= TALLY_FACE_MAX){ + if (soundOn) watch_buzzer_play_note(BUZZER_NOTE_E7, 30); + } + else { + state->tally_idx++; + print_tally(state); + if (soundOn) watch_buzzer_play_note(BUZZER_NOTE_E6, 30); + } +} + +static void tally_face_decrement(tally_state_t *state) { + bool soundOn = !_quick_ticks_running && !state->soundOff; + _init_val = false; + if (state->tally_idx <= TALLY_FACE_MIN){ + if (soundOn) watch_buzzer_play_note(BUZZER_NOTE_C5SHARP_D5FLAT, 30); + } + else { + state->tally_idx--; + print_tally(state); + if (soundOn) watch_buzzer_play_note(BUZZER_NOTE_C6SHARP_D6FLAT, 30); + } } bool tally_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { (void) settings; tally_state_t *state = (tally_state_t *)context; + static bool using_led = false; + + if (using_led) { + if(!watch_get_pin_level(BTN_MODE) && !watch_get_pin_level(BTN_LIGHT) && !watch_get_pin_level(BTN_ALARM)) + using_led = false; + else { + if (event.event_type == EVENT_LIGHT_BUTTON_DOWN || event.event_type == EVENT_ALARM_BUTTON_DOWN) + movement_illuminate_led(); + return true; + } + } switch (event.event_type) { - case EVENT_ALARM_BUTTON_UP: - // increment tally index - state->tally_idx++; - if (state->tally_idx > 999999) { //0-999,999 - //reset tally index and play a reset tune - state->tally_idx = 0; - watch_buzzer_play_note(BUZZER_NOTE_G6, 30); - watch_buzzer_play_note(BUZZER_NOTE_REST, 30); + case EVENT_TICK: + if (_quick_ticks_running) { + bool light_pressed = watch_get_pin_level(BTN_LIGHT); + bool alarm_pressed = watch_get_pin_level(BTN_ALARM); + if (light_pressed && alarm_pressed) stop_quick_cyc(); + else if (light_pressed) tally_face_increment(state); + else if (alarm_pressed) tally_face_decrement(state); + else stop_quick_cyc(); } - print_tally(state); - watch_buzzer_play_note(BUZZER_NOTE_E6, 30); + break; + case EVENT_ALARM_BUTTON_UP: + tally_face_decrement(state); break; case EVENT_ALARM_LONG_PRESS: - state->tally_idx = 0; // reset tally index - //play a reset tune - watch_buzzer_play_note(BUZZER_NOTE_G6, 30); - watch_buzzer_play_note(BUZZER_NOTE_REST, 30); - watch_buzzer_play_note(BUZZER_NOTE_E6, 30); - print_tally(state); + if (_init_val) { + state->soundOff = !state->soundOff; + if (!state->soundOff) watch_buzzer_play_note(BUZZER_NOTE_E6, 30); + print_tally(state); + } + else{ + tally_face_decrement(state); + start_quick_cyc(); + } + break; + case EVENT_MODE_LONG_PRESS: + if (state->tally_idx == _tally_default[state->tally_default_idx]) { + _init_val = true; + movement_move_to_face(0); + } + else { + state->tally_idx = _tally_default[state->tally_default_idx]; // reset tally index + _init_val = true; + //play a reset tune + if (!state->soundOff) watch_buzzer_play_note(BUZZER_NOTE_G6, 30); + if (!state->soundOff) watch_buzzer_play_note(BUZZER_NOTE_REST, 30); + if (!state->soundOff) watch_buzzer_play_note(BUZZER_NOTE_E6, 30); + print_tally(state); + } + break; + case EVENT_LIGHT_BUTTON_UP: + tally_face_increment(state); + break; + case EVENT_LIGHT_BUTTON_DOWN: + case EVENT_ALARM_BUTTON_DOWN: + if (watch_get_pin_level(BTN_MODE)) { + movement_illuminate_led(); + using_led = true; + } + break; + case EVENT_LIGHT_LONG_PRESS: + if (_init_val){ + state->tally_default_idx = (state->tally_default_idx + 1) % _tally_default_size; + state->tally_idx = _tally_default[state->tally_default_idx]; + if (!state->soundOff) watch_buzzer_play_note(BUZZER_NOTE_E6, 30); + if (!state->soundOff) watch_buzzer_play_note(BUZZER_NOTE_REST, 30); + if (!state->soundOff) watch_buzzer_play_note(BUZZER_NOTE_G6, 30); + print_tally(state); + } + else{ + tally_face_increment(state); + start_quick_cyc(); + } break; case EVENT_ACTIVATE: print_tally(state); @@ -83,7 +187,14 @@ bool tally_face_loop(movement_event_t event, movement_settings_t *settings, void // print tally index at the center of display. void print_tally(tally_state_t *state) { char buf[14]; - sprintf(buf, "TA %06d", (int)(state->tally_idx)); // center of LCD display + if (!state->soundOff) + watch_set_indicator(WATCH_INDICATOR_BELL); + else + watch_clear_indicator(WATCH_INDICATOR_BELL); + if (state->tally_idx >= 0) + sprintf(buf, "TA %4d ", (int)(state->tally_idx)); // center of LCD display + else + sprintf(buf, "TA %-3d", (int)(state->tally_idx)); // center of LCD display watch_display_string(buf, 0); } diff --git a/movement/watch_faces/complication/tally_face.h b/movement/watch_faces/complication/tally_face.h index 8096592..727d218 100644 --- a/movement/watch_faces/complication/tally_face.h +++ b/movement/watch_faces/complication/tally_face.h @@ -29,16 +29,29 @@ * TALLY face * * Tally face is designed to act as a tally counter. - * Based on the counter_face watch face by Shogo Okamoto. * - * To advance the counter, press the ALARM button. - * To reset, long press the ALARM button. + * Alarm + * Press: Decrement + * Hold : On initial value: Toggle Sound + * Else: Fast Decrement + * + * Light + * Press: Increment + * Hold : On initial value: Cycles through other initial values. + * Else: Fast Increment + * + * Mode + * Press: Next face + * Hold : On initial value: Go to first face. + * Else: Resets counter */ #include "movement.h" typedef struct { - uint32_t tally_idx; + int16_t tally_idx; + uint8_t tally_default_idx : 7; + bool soundOff; } tally_state_t; From c02ec307ff945e525bac73c19a1c5b8943a881fd Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 8 Sep 2024 11:44:01 -0400 Subject: [PATCH 137/161] Tally face sound now uses the preference's beep so decrementing fast can happen immedietly --- .../watch_faces/complication/tally_face.c | 57 ++++++++----------- .../watch_faces/complication/tally_face.h | 10 ++-- 2 files changed, 29 insertions(+), 38 deletions(-) diff --git a/movement/watch_faces/complication/tally_face.c b/movement/watch_faces/complication/tally_face.c index ca3ed32..32e3c52 100644 --- a/movement/watch_faces/complication/tally_face.c +++ b/movement/watch_faces/complication/tally_face.c @@ -43,7 +43,6 @@ void tally_face_setup(movement_settings_t *settings, uint8_t watch_face_index, v memset(*context_ptr, 0, sizeof(tally_state_t)); tally_state_t *state = (tally_state_t *)*context_ptr; state->tally_default_idx = 0; - state->soundOff = true; state->tally_idx = _tally_default[state->tally_default_idx]; _init_val = true; } @@ -65,34 +64,33 @@ static void stop_quick_cyc(void){ movement_request_tick_frequency(1); } -static void tally_face_increment(tally_state_t *state) { - bool soundOn = !_quick_ticks_running && !state->soundOff; +static void tally_face_increment(tally_state_t *state, bool sound_on) { + bool soundOn = !_quick_ticks_running && sound_on; _init_val = false; if (state->tally_idx >= TALLY_FACE_MAX){ if (soundOn) watch_buzzer_play_note(BUZZER_NOTE_E7, 30); } else { state->tally_idx++; - print_tally(state); + print_tally(state, sound_on); if (soundOn) watch_buzzer_play_note(BUZZER_NOTE_E6, 30); } } -static void tally_face_decrement(tally_state_t *state) { - bool soundOn = !_quick_ticks_running && !state->soundOff; +static void tally_face_decrement(tally_state_t *state, bool sound_on) { + bool soundOn = !_quick_ticks_running && sound_on; _init_val = false; if (state->tally_idx <= TALLY_FACE_MIN){ if (soundOn) watch_buzzer_play_note(BUZZER_NOTE_C5SHARP_D5FLAT, 30); } else { state->tally_idx--; - print_tally(state); + print_tally(state, sound_on); if (soundOn) watch_buzzer_play_note(BUZZER_NOTE_C6SHARP_D6FLAT, 30); } } bool tally_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { - (void) settings; tally_state_t *state = (tally_state_t *)context; static bool using_led = false; @@ -112,24 +110,17 @@ bool tally_face_loop(movement_event_t event, movement_settings_t *settings, void bool light_pressed = watch_get_pin_level(BTN_LIGHT); bool alarm_pressed = watch_get_pin_level(BTN_ALARM); if (light_pressed && alarm_pressed) stop_quick_cyc(); - else if (light_pressed) tally_face_increment(state); - else if (alarm_pressed) tally_face_decrement(state); + else if (light_pressed) tally_face_increment(state, settings->bit.button_should_sound); + else if (alarm_pressed) tally_face_decrement(state, settings->bit.button_should_sound); else stop_quick_cyc(); } break; case EVENT_ALARM_BUTTON_UP: - tally_face_decrement(state); + tally_face_decrement(state, settings->bit.button_should_sound); break; case EVENT_ALARM_LONG_PRESS: - if (_init_val) { - state->soundOff = !state->soundOff; - if (!state->soundOff) watch_buzzer_play_note(BUZZER_NOTE_E6, 30); - print_tally(state); - } - else{ - tally_face_decrement(state); - start_quick_cyc(); - } + tally_face_decrement(state, settings->bit.button_should_sound); + start_quick_cyc(); break; case EVENT_MODE_LONG_PRESS: if (state->tally_idx == _tally_default[state->tally_default_idx]) { @@ -140,14 +131,14 @@ bool tally_face_loop(movement_event_t event, movement_settings_t *settings, void state->tally_idx = _tally_default[state->tally_default_idx]; // reset tally index _init_val = true; //play a reset tune - if (!state->soundOff) watch_buzzer_play_note(BUZZER_NOTE_G6, 30); - if (!state->soundOff) watch_buzzer_play_note(BUZZER_NOTE_REST, 30); - if (!state->soundOff) watch_buzzer_play_note(BUZZER_NOTE_E6, 30); - print_tally(state); + if (settings->bit.button_should_sound) watch_buzzer_play_note(BUZZER_NOTE_G6, 30); + if (settings->bit.button_should_sound) watch_buzzer_play_note(BUZZER_NOTE_REST, 30); + if (settings->bit.button_should_sound) watch_buzzer_play_note(BUZZER_NOTE_E6, 30); + print_tally(state, settings->bit.button_should_sound); } break; case EVENT_LIGHT_BUTTON_UP: - tally_face_increment(state); + tally_face_increment(state, settings->bit.button_should_sound); break; case EVENT_LIGHT_BUTTON_DOWN: case EVENT_ALARM_BUTTON_DOWN: @@ -160,18 +151,18 @@ bool tally_face_loop(movement_event_t event, movement_settings_t *settings, void if (_init_val){ state->tally_default_idx = (state->tally_default_idx + 1) % _tally_default_size; state->tally_idx = _tally_default[state->tally_default_idx]; - if (!state->soundOff) watch_buzzer_play_note(BUZZER_NOTE_E6, 30); - if (!state->soundOff) watch_buzzer_play_note(BUZZER_NOTE_REST, 30); - if (!state->soundOff) watch_buzzer_play_note(BUZZER_NOTE_G6, 30); - print_tally(state); + if (settings->bit.button_should_sound) watch_buzzer_play_note(BUZZER_NOTE_E6, 30); + if (settings->bit.button_should_sound) watch_buzzer_play_note(BUZZER_NOTE_REST, 30); + if (settings->bit.button_should_sound) watch_buzzer_play_note(BUZZER_NOTE_G6, 30); + print_tally(state, settings->bit.button_should_sound); } else{ - tally_face_increment(state); + tally_face_increment(state, settings->bit.button_should_sound); start_quick_cyc(); } break; case EVENT_ACTIVATE: - print_tally(state); + print_tally(state, settings->bit.button_should_sound); break; case EVENT_TIMEOUT: // ignore timeout @@ -185,9 +176,9 @@ bool tally_face_loop(movement_event_t event, movement_settings_t *settings, void } // print tally index at the center of display. -void print_tally(tally_state_t *state) { +void print_tally(tally_state_t *state, bool sound_on) { char buf[14]; - if (!state->soundOff) + if (sound_on) watch_set_indicator(WATCH_INDICATOR_BELL); else watch_clear_indicator(WATCH_INDICATOR_BELL); diff --git a/movement/watch_faces/complication/tally_face.h b/movement/watch_faces/complication/tally_face.h index 727d218..286f1c0 100644 --- a/movement/watch_faces/complication/tally_face.h +++ b/movement/watch_faces/complication/tally_face.h @@ -32,8 +32,7 @@ * * Alarm * Press: Decrement - * Hold : On initial value: Toggle Sound - * Else: Fast Decrement + * Hold : Fast Decrement * * Light * Press: Increment @@ -44,14 +43,15 @@ * Press: Next face * Hold : On initial value: Go to first face. * Else: Resets counter + * + * Incrementing or Decrementing the tally will beep if Beeping is set in the global Preferences */ #include "movement.h" typedef struct { int16_t tally_idx; - uint8_t tally_default_idx : 7; - bool soundOff; + uint8_t tally_default_idx; } tally_state_t; @@ -60,7 +60,7 @@ void tally_face_activate(movement_settings_t *settings, void *context); bool tally_face_loop(movement_event_t event, movement_settings_t *settings, void *context); void tally_face_resign(movement_settings_t *settings, void *context); -void print_tally(tally_state_t *state); +void print_tally(tally_state_t *state, bool sound_on); #define tally_face ((const watch_face_t){ \ tally_face_setup, \ From 608199268e5367edd634aedbf7b8712c258689cd Mon Sep 17 00:00:00 2001 From: mcguirepr89 Date: Sun, 8 Sep 2024 11:49:01 -0400 Subject: [PATCH 138/161] removed break; from EVENT_ACTIVATE -- thanks @voloved --- movement/watch_faces/complication/simple_calculator_face.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/movement/watch_faces/complication/simple_calculator_face.c b/movement/watch_faces/complication/simple_calculator_face.c index 6bfd9e8..53fdd87 100644 --- a/movement/watch_faces/complication/simple_calculator_face.c +++ b/movement/watch_faces/complication/simple_calculator_face.c @@ -273,8 +273,6 @@ bool simple_calculator_face_loop(movement_event_t event, movement_settings_t *se switch (event.event_type) { case EVENT_ACTIVATE: - break; - case EVENT_TICK: switch (state->mode) { case MODE_ENTERING_FIRST_NUM: From c2723ebc8e5db6b42eee51543389bec95b60bc61 Mon Sep 17 00:00:00 2001 From: Matheus Afonso Martins Moreira Date: Sun, 8 Sep 2024 14:22:26 -0300 Subject: [PATCH 139/161] make: fix missing separator error --- movement/make/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/movement/make/Makefile b/movement/make/Makefile index a47b073..42c3106 100644 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -133,7 +133,7 @@ SRCS += \ ../watch_faces/complication/wordle_face.c \ ../watch_faces/complication/endless_runner_face.c \ ../watch_faces/complication/periodic_face.c \ - ../watch_faces/complication/deadline_face.c + ../watch_faces/complication/deadline_face.c \ ../watch_faces/complication/higher_lower_game_face.c \ ../watch_faces/clock/french_revolutionary_face.c \ ../watch_faces/clock/minimal_clock_face.c \ From 137906e14ac1f37c6d6399db5a30a77a814a6725 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 8 Sep 2024 17:23:53 -0400 Subject: [PATCH 140/161] Added more presets to the tally face --- movement/watch_faces/complication/tally_face.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/movement/watch_faces/complication/tally_face.c b/movement/watch_faces/complication/tally_face.c index 32e3c52..3893fc7 100644 --- a/movement/watch_faces/complication/tally_face.c +++ b/movement/watch_faces/complication/tally_face.c @@ -32,7 +32,7 @@ static bool _init_val; static bool _quick_ticks_running; -static const int16_t _tally_default[] = {0, 40, 20}; +static const int16_t _tally_default[] = {0, 100, 40, 20, 10}; static const uint8_t _tally_default_size = sizeof(_tally_default) / sizeof(int16_t); void tally_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { From 531bcfd28593f15476f129056986bccaeea2879b Mon Sep 17 00:00:00 2001 From: Matheus Afonso Martins Moreira Date: Sun, 8 Sep 2024 18:38:53 -0300 Subject: [PATCH 141/161] faces/tally: make mtg presets optin via macros Use preprocessor macros to conditionally compile in Magic the Gathering initial value presets for the tally mark, thereby making them opt in. --- movement/watch_faces/complication/tally_face.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/movement/watch_faces/complication/tally_face.c b/movement/watch_faces/complication/tally_face.c index 3893fc7..fb44df2 100644 --- a/movement/watch_faces/complication/tally_face.c +++ b/movement/watch_faces/complication/tally_face.c @@ -32,7 +32,17 @@ static bool _init_val; static bool _quick_ticks_running; -static const int16_t _tally_default[] = {0, 100, 40, 20, 10}; + +static const int16_t _tally_default[] = { + 0, + +#ifdef TALLY_FACE_PRESETS_MTG + 20, + 40, +#endif /* TALLY_FACE_PRESETS_MTG */ + +}; + static const uint8_t _tally_default_size = sizeof(_tally_default) / sizeof(int16_t); void tally_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { From 6b7ea8bbaa44c7b553cbcc06f65dfcefdd425330 Mon Sep 17 00:00:00 2001 From: Matheus Afonso Martins Moreira Date: Sun, 8 Sep 2024 18:39:55 -0300 Subject: [PATCH 142/161] faces/tally: add yugioh life point presets Add common Yu-Gi-Oh card game life point preset values for the tally face, opt in by defining a preprocessor macro. --- movement/watch_faces/complication/tally_face.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/movement/watch_faces/complication/tally_face.c b/movement/watch_faces/complication/tally_face.c index fb44df2..4da36cb 100644 --- a/movement/watch_faces/complication/tally_face.c +++ b/movement/watch_faces/complication/tally_face.c @@ -41,6 +41,11 @@ static const int16_t _tally_default[] = { 40, #endif /* TALLY_FACE_PRESETS_MTG */ +#ifdef TALLY_FACE_PRESETS_YUGIOH + 4000, + 8000, +#endif /* TALLY_FACE_PRESETS_YUGIOH */ + }; static const uint8_t _tally_default_size = sizeof(_tally_default) / sizeof(int16_t); From e4e0b611f33fc99d410fb5049302aafce9ff7a6c Mon Sep 17 00:00:00 2001 From: Matheus Afonso Martins Moreira Date: Sun, 8 Sep 2024 18:49:09 -0300 Subject: [PATCH 143/161] faces/tally: avoid resetting counters Don't reset the counters unless there are multiple presets. Move back to the first face directly if there are no presets to cycle through. --- movement/watch_faces/complication/tally_face.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/movement/watch_faces/complication/tally_face.c b/movement/watch_faces/complication/tally_face.c index 4da36cb..b2c553f 100644 --- a/movement/watch_faces/complication/tally_face.c +++ b/movement/watch_faces/complication/tally_face.c @@ -105,10 +105,15 @@ static void tally_face_decrement(tally_state_t *state, bool sound_on) { } } +static bool tally_face_should_move_back(tally_state_t *state) { + if (_tally_default_size <= 1) { return false; } + return state->tally_idx == _tally_default[state->tally_default_idx]; +} + bool tally_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { tally_state_t *state = (tally_state_t *)context; static bool using_led = false; - + if (using_led) { if(!watch_get_pin_level(BTN_MODE) && !watch_get_pin_level(BTN_LIGHT) && !watch_get_pin_level(BTN_ALARM)) using_led = false; @@ -118,7 +123,7 @@ bool tally_face_loop(movement_event_t event, movement_settings_t *settings, void return true; } } - + switch (event.event_type) { case EVENT_TICK: if (_quick_ticks_running) { @@ -138,7 +143,7 @@ bool tally_face_loop(movement_event_t event, movement_settings_t *settings, void start_quick_cyc(); break; case EVENT_MODE_LONG_PRESS: - if (state->tally_idx == _tally_default[state->tally_default_idx]) { + if (tally_face_should_move_back(state)) { _init_val = true; movement_move_to_face(0); } From 7a4424b2d4ae477b5bfb74ddc35a4708d03c9997 Mon Sep 17 00:00:00 2001 From: Matheus Afonso Martins Moreira Date: Sun, 8 Sep 2024 18:53:42 -0300 Subject: [PATCH 144/161] faces/tally: convert size into compile time const Creates a macro that returns the size of the presets array, converting it into a compile time constant value that occupies no memory and that compilers can constant fold into other expressions potentially leading to dead code elimination. --- movement/watch_faces/complication/tally_face.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/movement/watch_faces/complication/tally_face.c b/movement/watch_faces/complication/tally_face.c index b2c553f..7267b16 100644 --- a/movement/watch_faces/complication/tally_face.c +++ b/movement/watch_faces/complication/tally_face.c @@ -48,7 +48,7 @@ static const int16_t _tally_default[] = { }; -static const uint8_t _tally_default_size = sizeof(_tally_default) / sizeof(int16_t); +#define TALLY_FACE_PRESETS_SIZE() (sizeof(_tally_default) / sizeof(int16_t)) void tally_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { (void) settings; @@ -106,7 +106,7 @@ static void tally_face_decrement(tally_state_t *state, bool sound_on) { } static bool tally_face_should_move_back(tally_state_t *state) { - if (_tally_default_size <= 1) { return false; } + if (TALLY_FACE_PRESETS_SIZE() <= 1) { return false; } return state->tally_idx == _tally_default[state->tally_default_idx]; } @@ -169,7 +169,7 @@ bool tally_face_loop(movement_event_t event, movement_settings_t *settings, void break; case EVENT_LIGHT_LONG_PRESS: if (_init_val){ - state->tally_default_idx = (state->tally_default_idx + 1) % _tally_default_size; + state->tally_default_idx = (state->tally_default_idx + 1) % TALLY_FACE_PRESETS_SIZE(); state->tally_idx = _tally_default[state->tally_default_idx]; if (settings->bit.button_should_sound) watch_buzzer_play_note(BUZZER_NOTE_E6, 30); if (settings->bit.button_should_sound) watch_buzzer_play_note(BUZZER_NOTE_REST, 30); From a539bd7da77d036d4f31f0d009f86bd653187ab6 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 8 Sep 2024 18:03:16 -0400 Subject: [PATCH 145/161] Don't try to cycle through presets if the preset count is one --- movement/watch_faces/complication/tally_face.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/movement/watch_faces/complication/tally_face.c b/movement/watch_faces/complication/tally_face.c index 7267b16..3ea5503 100644 --- a/movement/watch_faces/complication/tally_face.c +++ b/movement/watch_faces/complication/tally_face.c @@ -168,7 +168,7 @@ bool tally_face_loop(movement_event_t event, movement_settings_t *settings, void } break; case EVENT_LIGHT_LONG_PRESS: - if (_init_val){ + if (TALLY_FACE_PRESETS_SIZE() > 1 && _init_val){ state->tally_default_idx = (state->tally_default_idx + 1) % TALLY_FACE_PRESETS_SIZE(); state->tally_idx = _tally_default[state->tally_default_idx]; if (settings->bit.button_should_sound) watch_buzzer_play_note(BUZZER_NOTE_E6, 30); From 22c25f5c239e982507a6ed1e3c527164b7b1d1c6 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 8 Sep 2024 18:09:38 -0400 Subject: [PATCH 146/161] Added commented-out defines for MTG and Yugioh --- movement/watch_faces/complication/tally_face.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/movement/watch_faces/complication/tally_face.h b/movement/watch_faces/complication/tally_face.h index 286f1c0..80623f4 100644 --- a/movement/watch_faces/complication/tally_face.h +++ b/movement/watch_faces/complication/tally_face.h @@ -54,6 +54,9 @@ typedef struct { uint8_t tally_default_idx; } tally_state_t; +//#define TALLY_FACE_PRESETS_MTG +//#define TALLY_FACE_PRESETS_YUGIOH + void tally_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); void tally_face_activate(movement_settings_t *settings, void *context); From f6f427ca7df21b455a9b5f8d4dbf5a05a7590328 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 8 Sep 2024 07:07:44 -0400 Subject: [PATCH 147/161] Daylight offsets now references the timezone of the destination; but it does cause an issue due to DST --- .../watch_faces/complication/sunrise_sunset_face.c | 12 +++++++++--- .../watch_faces/complication/sunrise_sunset_face.h | 7 ++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/movement/watch_faces/complication/sunrise_sunset_face.c b/movement/watch_faces/complication/sunrise_sunset_face.c index 4a3c884..29e6404 100644 --- a/movement/watch_faces/complication/sunrise_sunset_face.c +++ b/movement/watch_faces/complication/sunrise_sunset_face.c @@ -49,20 +49,26 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s double rise, set, minutes, seconds; bool show_next_match = false; movement_location_t movement_location; - if (state->longLatToUse == 0 || _location_count <= 1) + int16_t tz; + if (state->longLatToUse == 0 || _location_count <= 1) { movement_location = (movement_location_t) watch_get_backup_data(1); + tz = movement_timezone_offsets[settings->bit.time_zone]; + } else{ movement_location.bit.latitude = longLatPresets[state->longLatToUse].latitude; movement_location.bit.longitude = longLatPresets[state->longLatToUse].longitude; + tz = movement_timezone_offsets[longLatPresets[state->longLatToUse].timezone]; } if (movement_location.reg == 0) { + watch_clear_all_indicators(); + watch_clear_colon(); watch_display_string("RI no Loc", 0); return; } watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time - watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, state->tz * 60, 0); // the current date / time in UTC + watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, tz * 60, 0); // the current date / time in UTC watch_date_time scratch_time; // scratchpad, contains different values at different times scratch_time.reg = utc_now.reg; @@ -77,7 +83,7 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s // sunriset returns the rise/set times as signed decimal hours in UTC. // this can mean hours below 0 or above 31, which won't fit into a watch_date_time struct. // to deal with this, we set aside the offset in hours, and add it back before converting it to a watch_date_time. - double hours_from_utc = ((double)state->tz) / 60.0; + double hours_from_utc = ((double)tz) / 60.0; // we loop twice because if it's after sunset today, we need to recalculate to display values for tomorrow. for(int i = 0; i < 2; i++) { diff --git a/movement/watch_faces/complication/sunrise_sunset_face.h b/movement/watch_faces/complication/sunrise_sunset_face.h index f216a2f..429253e 100644 --- a/movement/watch_faces/complication/sunrise_sunset_face.h +++ b/movement/watch_faces/complication/sunrise_sunset_face.h @@ -76,14 +76,15 @@ typedef struct { char name[2]; int16_t latitude; int16_t longitude; + int16_t timezone; // References element in movement_timezone_offsets } long_lat_presets_t; static const long_lat_presets_t longLatPresets[] = { { .name = " "}, // Default, the long and lat get replaced by what's set in the watch -// { .name = "Ny", .latitude = 4072, .longitude = -7401 }, // New York City, NY -// { .name = "LA", .latitude = 3405, .longitude = -11824 }, // Los Angeles, CA -// { .name = "dE", .latitude = 4221, .longitude = -8305 }, // Detroit, MI + { .name = "Ny", .latitude = 4072, .longitude = -7401, .timezone = 35 }, // New York City, NY + { .name = "LA", .latitude = 3405, .longitude = -11824, .timezone = 30 }, // Los Angeles, CA + { .name = "dE", .latitude = 4221, .longitude = -8305, .timezone = 35 }, // Detroit, MI }; #endif // SUNRISE_SUNSET_FACE_H_ From 0d16329574a18f7ba8cbf9e8705d9c5b60b8fa3f Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 8 Sep 2024 09:03:43 -0400 Subject: [PATCH 148/161] Sunrise sunset face now warns about using DST --- .../complication/sunrise_sunset_face.c | 15 +++++++++------ .../complication/sunrise_sunset_face.h | 12 +++++++----- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/movement/watch_faces/complication/sunrise_sunset_face.c b/movement/watch_faces/complication/sunrise_sunset_face.c index 29e6404..a436776 100644 --- a/movement/watch_faces/complication/sunrise_sunset_face.c +++ b/movement/watch_faces/complication/sunrise_sunset_face.c @@ -57,7 +57,10 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s else{ movement_location.bit.latitude = longLatPresets[state->longLatToUse].latitude; movement_location.bit.longitude = longLatPresets[state->longLatToUse].longitude; - tz = movement_timezone_offsets[longLatPresets[state->longLatToUse].timezone]; + if (longLatPresets[state->longLatToUse].timezone == SUNRISE_USE_LOCAL_TZ) + tz = movement_timezone_offsets[settings->bit.time_zone]; + else + tz = movement_timezone_offsets[longLatPresets[state->longLatToUse].timezone]; } if (movement_location.reg == 0) { @@ -405,11 +408,11 @@ bool sunrise_sunset_face_loop(movement_event_t event, movement_settings_t *setti break; case EVENT_ALARM_LONG_PRESS: if (state->page == 0) { - if (state->longLatToUse != 0) { - state->longLatToUse = 0; - _sunrise_sunset_face_update(settings, state); - break; - } + if (state->longLatToUse != 0) { + state->longLatToUse = 0; + _sunrise_sunset_face_update(settings, state); + break; + } state->page++; state->active_digit = 0; watch_clear_display(); diff --git a/movement/watch_faces/complication/sunrise_sunset_face.h b/movement/watch_faces/complication/sunrise_sunset_face.h index 429253e..537fc51 100644 --- a/movement/watch_faces/complication/sunrise_sunset_face.h +++ b/movement/watch_faces/complication/sunrise_sunset_face.h @@ -59,6 +59,8 @@ typedef struct { uint8_t longLatToUse; } sunrise_sunset_state_t; +#define SUNRISE_USE_LOCAL_TZ 0xFF + void sunrise_sunset_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); void sunrise_sunset_face_activate(movement_settings_t *settings, void *context); bool sunrise_sunset_face_loop(movement_event_t event, movement_settings_t *settings, void *context); @@ -76,15 +78,15 @@ typedef struct { char name[2]; int16_t latitude; int16_t longitude; - int16_t timezone; // References element in movement_timezone_offsets + uint8_t timezone; // References element in movement_timezone_offsets; Set to 0xFF to use local timezone } long_lat_presets_t; +// Locations must either use the same timezone as local time, or not observe DST. static const long_lat_presets_t longLatPresets[] = { - { .name = " "}, // Default, the long and lat get replaced by what's set in the watch - { .name = "Ny", .latitude = 4072, .longitude = -7401, .timezone = 35 }, // New York City, NY - { .name = "LA", .latitude = 3405, .longitude = -11824, .timezone = 30 }, // Los Angeles, CA - { .name = "dE", .latitude = 4221, .longitude = -8305, .timezone = 35 }, // Detroit, MI + { .name = " "}, // Default, the long, lat, and timezone get replaced by what's set in the watch +// { .name = "dE", .latitude = 4221, .longitude = -8305, .timezone = SUNRISE_USE_LOCAL_TZ }, // Detroit, MI; Assumes you live in the Eastern Timezone +// { .name = "To", .latitude = 3567, .longitude = 13965, .timezone = 15 }, // Tokyo, JP }; #endif // SUNRISE_SUNSET_FACE_H_ From 9d1410780c34e990757164976a2e3e3f64643699 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 8 Sep 2024 10:37:36 -0400 Subject: [PATCH 149/161] sunrise sunset fix during DST --- .../watch_faces/complication/sunrise_sunset_face.c | 9 ++++----- .../watch_faces/complication/sunrise_sunset_face.h | 10 ++++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/movement/watch_faces/complication/sunrise_sunset_face.c b/movement/watch_faces/complication/sunrise_sunset_face.c index a436776..4c77fe5 100644 --- a/movement/watch_faces/complication/sunrise_sunset_face.c +++ b/movement/watch_faces/complication/sunrise_sunset_face.c @@ -50,15 +50,16 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s bool show_next_match = false; movement_location_t movement_location; int16_t tz; + watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time if (state->longLatToUse == 0 || _location_count <= 1) { + tz = get_timezone_offset(settings->bit.time_zone, date_time); movement_location = (movement_location_t) watch_get_backup_data(1); - tz = movement_timezone_offsets[settings->bit.time_zone]; } else{ movement_location.bit.latitude = longLatPresets[state->longLatToUse].latitude; movement_location.bit.longitude = longLatPresets[state->longLatToUse].longitude; - if (longLatPresets[state->longLatToUse].timezone == SUNRISE_USE_LOCAL_TZ) - tz = movement_timezone_offsets[settings->bit.time_zone]; + if (longLatPresets[state->longLatToUse].uses_dst && dst_occurring(date_time)) + tz = movement_timezone_dst_offsets[longLatPresets[state->longLatToUse].timezone]; else tz = movement_timezone_offsets[longLatPresets[state->longLatToUse].timezone]; } @@ -70,7 +71,6 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s return; } - watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, tz * 60, 0); // the current date / time in UTC watch_date_time scratch_time; // scratchpad, contains different values at different times scratch_time.reg = utc_now.reg; @@ -343,7 +343,6 @@ void sunrise_sunset_face_activate(movement_settings_t *settings, void *context) movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1); state->working_latitude = _sunrise_sunset_face_struct_from_latlon(movement_location.bit.latitude); state->working_longitude = _sunrise_sunset_face_struct_from_latlon(movement_location.bit.longitude); - state->tz = get_timezone_offset(settings->bit.time_zone, watch_rtc_get_date_time()); } bool sunrise_sunset_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { diff --git a/movement/watch_faces/complication/sunrise_sunset_face.h b/movement/watch_faces/complication/sunrise_sunset_face.h index 537fc51..d100dd4 100644 --- a/movement/watch_faces/complication/sunrise_sunset_face.h +++ b/movement/watch_faces/complication/sunrise_sunset_face.h @@ -52,7 +52,6 @@ typedef struct { uint8_t rise_index; uint8_t active_digit; bool location_changed; - int16_t tz; watch_date_time rise_set_expires; sunrise_sunset_lat_lon_settings_t working_latitude; sunrise_sunset_lat_lon_settings_t working_longitude; @@ -78,15 +77,18 @@ typedef struct { char name[2]; int16_t latitude; int16_t longitude; - uint8_t timezone; // References element in movement_timezone_offsets; Set to 0xFF to use local timezone + uint8_t timezone; // References element in movement_timezone_offsets + bool uses_dst; } long_lat_presets_t; // Locations must either use the same timezone as local time, or not observe DST. static const long_lat_presets_t longLatPresets[] = { { .name = " "}, // Default, the long, lat, and timezone get replaced by what's set in the watch -// { .name = "dE", .latitude = 4221, .longitude = -8305, .timezone = SUNRISE_USE_LOCAL_TZ }, // Detroit, MI; Assumes you live in the Eastern Timezone -// { .name = "To", .latitude = 3567, .longitude = 13965, .timezone = 15 }, // Tokyo, JP +// { .name = "Ny", .latitude = 4072, .longitude = -7401, .timezone = 33, .uses_dst = true }, // New York City, NY +// { .name = "LA", .latitude = 3405, .longitude = -11824, .timezone = 30, .uses_dst = true }, // Los Angeles, CA +// { .name = "dE", .latitude = 4221, .longitude = -8305, .timezone = 33, .uses_dst = true }, // Detroit, MI +// { .name = "To", .latitude = 3567, .longitude = 13965, .timezone = 15, .uses_dst = false }, // Tokyo, JP }; #endif // SUNRISE_SUNSET_FACE_H_ From 673f90b7242d1f0cdf5b09a3aed473b6ae4d4cf0 Mon Sep 17 00:00:00 2001 From: Matheus Afonso Martins Moreira Date: Mon, 9 Sep 2024 23:59:44 -0300 Subject: [PATCH 150/161] make: add beeps face to build PR #386 did not add the C file to the makefile, causing the build to fail at the link stage due to the missing object file and the symbols that were supposed to be provided by it. Adding the file to the build fixes #472. Reported-by: CarpeNoctem Fixed-by: CarpeNoctem Reviewed-by: Matheus Afonso Martins Moreira GitHub-Issue: https://github.com/joeycastillo/Sensor-Watch/issues/472 --- movement/make/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/movement/make/Makefile b/movement/make/Makefile index 42c3106..e347d59 100644 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -140,6 +140,7 @@ SRCS += \ ../watch_faces/complication/simon_face.c \ ../watch_faces/complication/simple_calculator_face.c \ ../watch_faces/sensor/alarm_thermometer_face.c \ + ../watch_faces/demo/beeps_face.c \ # New watch faces go above this line. # Leave this line at the bottom of the file; it has all the targets for making your project. From be969c4deb144280b59d0293cc45dc3443b096cf Mon Sep 17 00:00:00 2001 From: Matheus Afonso Martins Moreira Date: Tue, 10 Sep 2024 12:10:48 -0300 Subject: [PATCH 151/161] Revert PR #387 - fixes LE state restoration This commit caused state restored from backup registers to be overwritten. It is thereby reverted until a better solution is found or movement is refactored. This reverts commit 524098b9258232964d7756d4d44f16b006d340bf. Reviewed-by: Matheus Afonso Martins Moreira References: https://github.com/joeycastillo/Sensor-Watch/pull/474 --- movement/movement.c | 34 ---------------------------------- movement/movement.h | 2 -- movement/movement_config.h | 30 +++++++----------------------- 3 files changed, 7 insertions(+), 59 deletions(-) diff --git a/movement/movement.c b/movement/movement.c index ba21a4e..617f79a 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -106,31 +106,6 @@ of debounce time. #define MOVEMENT_DEFAULT_LED_DURATION 1 #endif -// Default to no set location latitude -#ifndef MOVEMENT_DEFAULT_LATITUDE -#define MOVEMENT_DEFAULT_LATITUDE 0 -#endif - -// Default to no set location longitude -#ifndef MOVEMENT_DEFAULT_LONGITUDE -#define MOVEMENT_DEFAULT_LONGITUDE 0 -#endif - -// Default to no set birthdate year -#ifndef MOVEMENT_DEFAULT_BIRTHDATE_YEAR -#define MOVEMENT_DEFAULT_BIRTHDATE_YEAR 0 -#endif - -// Default to no set birthdate month -#ifndef MOVEMENT_DEFAULT_BIRTHDATE_MONTH -#define MOVEMENT_DEFAULT_BIRTHDATE_MONTH 0 -#endif - -// Default to no set birthdate day -#ifndef MOVEMENT_DEFAULT_BIRTHDATE_DAY -#define MOVEMENT_DEFAULT_BIRTHDATE_DAY 0 -#endif - // Default to having DST get set #ifndef MOVEMENT_DEFAULT_DST_ACTIVE #define MOVEMENT_DEFAULT_DST_ACTIVE true @@ -541,11 +516,6 @@ void app_init(void) { movement_state.settings.bit.to_interval = MOVEMENT_DEFAULT_TIMEOUT_INTERVAL; movement_state.settings.bit.le_interval = MOVEMENT_DEFAULT_LOW_ENERGY_INTERVAL; movement_state.settings.bit.led_duration = MOVEMENT_DEFAULT_LED_DURATION; - movement_state.location.bit.latitude = MOVEMENT_DEFAULT_LATITUDE; - movement_state.location.bit.longitude = MOVEMENT_DEFAULT_LONGITUDE; - movement_state.birthdate.bit.year = MOVEMENT_DEFAULT_BIRTHDATE_YEAR; - movement_state.birthdate.bit.month = MOVEMENT_DEFAULT_BIRTHDATE_MONTH; - movement_state.birthdate.bit.day = MOVEMENT_DEFAULT_BIRTHDATE_DAY; movement_state.settings.bit.dst_active = MOVEMENT_DEFAULT_DST_ACTIVE; #ifdef MAKEFILE_TIMEZONE @@ -584,14 +554,10 @@ void app_init(void) { void app_wake_from_backup(void) { movement_state.settings.reg = watch_get_backup_data(0); - movement_state.location.reg = watch_get_backup_data(1); - movement_state.birthdate.reg = watch_get_backup_data(2); } void app_setup(void) { watch_store_backup_data(movement_state.settings.reg, 0); - watch_store_backup_data(movement_state.location.reg, 1); - watch_store_backup_data(movement_state.birthdate.reg, 2); static bool is_first_launch = true; diff --git a/movement/movement.h b/movement/movement.h index aeece58..6ca6537 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -245,8 +245,6 @@ typedef struct { typedef struct { // properties stored in BACKUP register movement_settings_t settings; - movement_location_t location; - movement_birthdate_t birthdate; // transient properties int16_t current_face_idx; diff --git a/movement/movement_config.h b/movement/movement_config.h index 566ea3e..46e1d2e 100644 --- a/movement/movement_config.h +++ b/movement/movement_config.h @@ -76,15 +76,15 @@ const watch_face_t watch_faces[] = { /* Set the timeout before switching to low energy mode * Valid values are: * 0: Never - * 1: 10 mins - * 2: 1 hour - * 3: 2 hours - * 4: 6 hours - * 5: 12 hours - * 6: 1 day + * 1: 1 hour + * 2: 2 hours + * 3: 6 hours + * 4: 12 hours + * 5: 1 day + * 6: 2 days * 7: 7 days */ -#define MOVEMENT_DEFAULT_LOW_ENERGY_INTERVAL 2 +#define MOVEMENT_DEFAULT_LOW_ENERGY_INTERVAL 1 /* Set the led duration * Valid values are: @@ -95,22 +95,6 @@ const watch_face_t watch_faces[] = { */ #define MOVEMENT_DEFAULT_LED_DURATION 1 -/* The latitude and longitude used for the wearers location - * Set signed values in 1/100ths of a degree - */ -#define MOVEMENT_DEFAULT_LATITUDE 0 -#define MOVEMENT_DEFAULT_LONGITUDE 0 - -/* The wearers birthdate - * Valid values: - * Year: 1 - 4095 - * Month: 1 - 12 - * Day: 1 - 31 - */ -#define MOVEMENT_DEFAULT_BIRTHDATE_YEAR 0 -#define MOVEMENT_DEFAULT_BIRTHDATE_MONTH 0 -#define MOVEMENT_DEFAULT_BIRTHDATE_DAY 0 - /* Set if using DST * Valid values are: * false: Don't allow the watch to use DST From 1462d183764ff577723a509d95861b197bd74e28 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 15 Sep 2024 19:13:38 -0400 Subject: [PATCH 152/161] Fixed the tz that the world clock references --- .../watch_faces/clock/world_clock2_face.c | 20 ++++++++++++------- .../watch_faces/clock/world_clock2_face.h | 1 + movement/watch_faces/clock/world_clock_face.c | 7 ++++--- movement/watch_faces/clock/world_clock_face.h | 1 + 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/movement/watch_faces/clock/world_clock2_face.c b/movement/watch_faces/clock/world_clock2_face.c index cf37a40..845ec37 100644 --- a/movement/watch_faces/clock/world_clock2_face.c +++ b/movement/watch_faces/clock/world_clock2_face.c @@ -184,8 +184,8 @@ static bool mode_display(movement_event_t event, movement_settings_t *settings, /* Determine current time at time zone and store date/time */ date_time = watch_rtc_get_date_time(); - timestamp = watch_utility_date_time_to_unix_time(date_time, state->tz * 60); - date_time = watch_utility_date_time_from_unix_time(timestamp, state->tz * 60); + timestamp = watch_utility_date_time_to_unix_time(date_time, state->tz * 60); + date_time = watch_utility_date_time_from_unix_time(timestamp, state->tz_curr * 60); previous_date_time = state->previous_date_time; state->previous_date_time = date_time.reg; @@ -234,14 +234,16 @@ static bool mode_display(movement_event_t event, movement_settings_t *settings, break; case EVENT_ALARM_BUTTON_UP: state->current_zone = find_selected_zone(state, FORWARD); - state->previous_date_time = REFRESH_TIME; + state->tz_curr = get_timezone_offset(state->current_zone, watch_rtc_get_date_time()); + state->previous_date_time = REFRESH_TIME; break; case EVENT_LIGHT_BUTTON_DOWN: /* Do nothing. */ break; case EVENT_LIGHT_BUTTON_UP: state->current_zone = find_selected_zone(state, BACKWARD); - state->previous_date_time = REFRESH_TIME; + state->tz_curr = get_timezone_offset(state->current_zone, watch_rtc_get_date_time()); + state->previous_date_time = REFRESH_TIME; break; case EVENT_LIGHT_LONG_PRESS: movement_illuminate_led(); @@ -285,7 +287,7 @@ static bool mode_settings(movement_event_t event, movement_settings_t *settings, watch_clear_indicator(WATCH_INDICATOR_PM); refresh_face = false; } - result = div(state->tz, 60); + result = div(state->tz_curr, 60); hours = result.quot; minutes = result.rem; @@ -314,17 +316,21 @@ static bool mode_settings(movement_event_t event, movement_settings_t *settings, break; case EVENT_ALARM_BUTTON_UP: state->current_zone = mod(state->current_zone + FORWARD, NUM_TIME_ZONES); + state->tz_curr = get_timezone_offset(state->current_zone, watch_rtc_get_date_time()); break; case EVENT_LIGHT_BUTTON_UP: state->current_zone = mod(state->current_zone + BACKWARD, NUM_TIME_ZONES); + state->tz_curr = get_timezone_offset(state->current_zone, watch_rtc_get_date_time()); break; case EVENT_LIGHT_BUTTON_DOWN: /* Do nothing */ break; case EVENT_ALARM_LONG_PRESS: /* Find next selected zone */ - if (!state->zones[state->current_zone].selected) - state->current_zone = find_selected_zone(state, FORWARD); + if (!state->zones[state->current_zone].selected) { + state->current_zone = find_selected_zone(state, FORWARD); + state->tz_curr = get_timezone_offset(state->current_zone, watch_rtc_get_date_time()); + } /* Switch to display mode */ state->current_mode = WORLD_CLOCK2_MODE_DISPLAY; diff --git a/movement/watch_faces/clock/world_clock2_face.h b/movement/watch_faces/clock/world_clock2_face.h index efc107e..5d8310f 100644 --- a/movement/watch_faces/clock/world_clock2_face.h +++ b/movement/watch_faces/clock/world_clock2_face.h @@ -105,6 +105,7 @@ typedef struct { uint8_t current_zone; uint32_t previous_date_time; int16_t tz; + int16_t tz_curr; } world_clock2_state_t; void world_clock2_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr); diff --git a/movement/watch_faces/clock/world_clock_face.c b/movement/watch_faces/clock/world_clock_face.c index e7f393e..b47d1f7 100644 --- a/movement/watch_faces/clock/world_clock_face.c +++ b/movement/watch_faces/clock/world_clock_face.c @@ -69,7 +69,7 @@ static bool world_clock_face_do_display_mode(movement_event_t event, movement_se case EVENT_LOW_ENERGY_UPDATE: date_time = watch_rtc_get_date_time(); timestamp = watch_utility_date_time_to_unix_time(date_time, state->tz * 60); - date_time = watch_utility_date_time_from_unix_time(timestamp, state->tz * 60); + date_time = watch_utility_date_time_from_unix_time(timestamp, state->tz_curr * 60); previous_date_time = state->previous_date_time; state->previous_date_time = date_time.reg; @@ -158,6 +158,7 @@ static bool _world_clock_face_do_settings_mode(movement_event_t event, movement_ case 3: state->settings.bit.timezone_index++; if (state->settings.bit.timezone_index > 40) state->settings.bit.timezone_index = 0; + state->tz_curr = get_timezone_offset(state->settings.bit.timezone_index, watch_rtc_get_date_time()); break; } break; @@ -172,8 +173,8 @@ static bool _world_clock_face_do_settings_mode(movement_event_t event, movement_ sprintf(buf, "%c%c %3d%02d ", movement_valid_position_0_chars[state->settings.bit.char_0], movement_valid_position_1_chars[state->settings.bit.char_1], - (int8_t) (state->tz / 60), - (int8_t) (state->tz % 60) * (state->tz < 0 ? -1 : 1)); + (int8_t) (state->tz_curr / 60), + (int8_t) (state->tz_curr % 60) * (state->tz_curr < 0 ? -1 : 1)); watch_set_colon(); watch_clear_indicator(WATCH_INDICATOR_PM); diff --git a/movement/watch_faces/clock/world_clock_face.h b/movement/watch_faces/clock/world_clock_face.h index e76d85e..2de3ced 100644 --- a/movement/watch_faces/clock/world_clock_face.h +++ b/movement/watch_faces/clock/world_clock_face.h @@ -63,6 +63,7 @@ typedef struct { uint8_t current_screen; uint32_t previous_date_time; int16_t tz; + int16_t tz_curr; } world_clock_state_t; void world_clock_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); From c9cbb8216365b07b9d8d8a9aa7fd521639fc155b Mon Sep 17 00:00:00 2001 From: Matheus Afonso Martins Moreira Date: Sun, 15 Sep 2024 21:00:22 -0300 Subject: [PATCH 153/161] Revert merge of PR #437 - debouncing logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit a715265af61ecab41eaefe0c5a7e7c806d0db41b, reversing changes made to 9c093f954084e61614cba1c259e4581a87420813. Insidious issues were found in the course of long term testing by the community, and further reviews of the code were not enough to pinpoint the issue and fix it. So for now the appropriate action is to revert these changes while development continues, and possibly merge them back in once they have been stabilized. Tested-on-hardware-by: David Volovskiy Tested-on-hardware-by: CarpeNoctem Tested-on-hardware-by: Krzysztof Gałka <@kshysztof@Discord> --- movement/movement.c | 76 +++++++-------------------------------------- movement/movement.h | 4 --- 2 files changed, 11 insertions(+), 69 deletions(-) diff --git a/movement/movement.c b/movement/movement.c index 617f79a..a8a22bd 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -23,16 +23,6 @@ */ #define MOVEMENT_LONG_PRESS_TICKS 64 -#define DEBOUNCE_TICKS_DOWN 0 -#define DEBOUNCE_TICKS_UP 0 -/* -DEBOUNCE_TICKS_DOWN and DEBOUNCE_TICKS_UP are in terms of fast_cb ticks after a button is pressed. -The logic is that pressed of a button are ignored until the cb_fast_tick function runs this variable amount of times. -Without modifying the code, the cb_fast_tick frequency is 128Hz, or 7.8125ms. -It is not suggested to set this value to one for debouncing, as the callback occurs asynchronously of the button's press, -meaning that if a button was pressed and 7ms passed since th elast time cb_fast_tick was called, then there will be only 812.5us -of debounce time. -*/ #include #include @@ -243,9 +233,6 @@ static inline void _movement_reset_inactivity_countdown(void) { static inline void _movement_enable_fast_tick_if_needed(void) { if (!movement_state.fast_tick_enabled) { movement_state.fast_ticks = 0; - movement_state.debounce_ticks_light = 0; - movement_state.debounce_ticks_alarm = 0; - movement_state.debounce_ticks_mode = 0; watch_rtc_register_periodic_callback(cb_fast_tick, 128); movement_state.fast_tick_enabled = true; } @@ -254,7 +241,6 @@ static inline void _movement_enable_fast_tick_if_needed(void) { static inline void _movement_disable_fast_tick_if_possible(void) { if ((movement_state.light_ticks == -1) && (movement_state.alarm_ticks == -1) && - ((movement_state.debounce_ticks_light + movement_state.debounce_ticks_mode + movement_state.debounce_ticks_alarm) == 0) && ((movement_state.light_down_timestamp + movement_state.mode_down_timestamp + movement_state.alarm_down_timestamp) == 0)) { movement_state.fast_tick_enabled = false; watch_rtc_disable_periodic_callback(128); @@ -610,7 +596,6 @@ void app_wake_from_standby(void) { static void _sleep_mode_app_loop(void) { movement_state.needs_wake = false; - movement_state.ignore_alarm_btn_after_sleep = true; // as long as le_mode_ticks is -1 (i.e. we are in low energy mode), we wake up here, update the screen, and go right back to sleep. while (movement_state.le_mode_ticks == -1) { // we also have to handle background tasks here in the mini-runloop @@ -785,66 +770,29 @@ static movement_event_type_t _figure_out_button_event(bool pin_level, movement_e // now that that's out of the way, handle falling edge uint16_t diff = movement_state.fast_ticks - *down_timestamp; *down_timestamp = 0; + _movement_disable_fast_tick_if_possible(); // any press over a half second is considered a long press. Fire the long-up event if (diff > MOVEMENT_LONG_PRESS_TICKS) return button_down_event_type + 3; else return button_down_event_type + 1; } } -static movement_event_type_t btn_action(bool pin_level, int code, uint16_t *timestamp) { - _movement_reset_inactivity_countdown(); - return _figure_out_button_event(pin_level, code, timestamp); -} - -static void light_btn_action(bool pin_level) { - event.event_type = btn_action(pin_level, EVENT_LIGHT_BUTTON_DOWN, &movement_state.light_down_timestamp); -} - -static void mode_btn_action(bool pin_level) { - event.event_type = btn_action(pin_level, EVENT_MODE_BUTTON_DOWN, &movement_state.mode_down_timestamp); -} - -static void alarm_btn_action(bool pin_level) { - uint8_t event_type = btn_action(pin_level, EVENT_ALARM_BUTTON_DOWN, &movement_state.alarm_down_timestamp); - if (movement_state.ignore_alarm_btn_after_sleep){ - if (event_type == EVENT_ALARM_BUTTON_UP || event_type == EVENT_ALARM_LONG_UP) movement_state.ignore_alarm_btn_after_sleep = false; - return; - } - event.event_type = event_type; -} - -static void debounce_btn_press(uint8_t pin, uint8_t *debounce_ticks, uint16_t *down_timestamp, void (*function)(bool)) { - if (*debounce_ticks == 0) { - bool pin_level = watch_get_pin_level(pin); - function(pin_level); - *debounce_ticks = pin_level ? DEBOUNCE_TICKS_DOWN : DEBOUNCE_TICKS_UP; - if (*debounce_ticks != 0) _movement_enable_fast_tick_if_needed(); - } - else - *down_timestamp = 0; -} - -static void disable_if_needed(uint8_t *ticks) { - if (*ticks > 0 && --*ticks == 0) - _movement_disable_fast_tick_if_possible(); -} - -static void movement_disable_if_debounce_complete(void) { - disable_if_needed(&movement_state.debounce_ticks_light); - disable_if_needed(&movement_state.debounce_ticks_alarm); - disable_if_needed(&movement_state.debounce_ticks_mode); -} - void cb_light_btn_interrupt(void) { - debounce_btn_press(BTN_LIGHT, &movement_state.debounce_ticks_light, &movement_state.light_down_timestamp, light_btn_action); + bool pin_level = watch_get_pin_level(BTN_LIGHT); + _movement_reset_inactivity_countdown(); + event.event_type = _figure_out_button_event(pin_level, EVENT_LIGHT_BUTTON_DOWN, &movement_state.light_down_timestamp); } void cb_mode_btn_interrupt(void) { - debounce_btn_press(BTN_MODE, &movement_state.debounce_ticks_mode, &movement_state.mode_down_timestamp, mode_btn_action); + bool pin_level = watch_get_pin_level(BTN_MODE); + _movement_reset_inactivity_countdown(); + event.event_type = _figure_out_button_event(pin_level, EVENT_MODE_BUTTON_DOWN, &movement_state.mode_down_timestamp); } void cb_alarm_btn_interrupt(void) { - debounce_btn_press(BTN_ALARM, &movement_state.debounce_ticks_alarm, &movement_state.alarm_down_timestamp, alarm_btn_action); + bool pin_level = watch_get_pin_level(BTN_ALARM); + _movement_reset_inactivity_countdown(); + event.event_type = _figure_out_button_event(pin_level, EVENT_ALARM_BUTTON_DOWN, &movement_state.alarm_down_timestamp); } void cb_alarm_btn_extwake(void) { @@ -857,9 +805,7 @@ void cb_alarm_fired(void) { } void cb_fast_tick(void) { - movement_disable_if_debounce_complete(); - if (movement_state.debounce_ticks_light + movement_state.debounce_ticks_mode + movement_state.debounce_ticks_alarm == 0) - movement_state.fast_ticks++; + movement_state.fast_ticks++; if (movement_state.light_ticks > 0) movement_state.light_ticks--; if (movement_state.alarm_ticks > 0) movement_state.alarm_ticks--; // check timestamps and auto-fire the long-press events diff --git a/movement/movement.h b/movement/movement.h index 6ca6537..46be32f 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -273,10 +273,6 @@ typedef struct { // low energy mode countdown int32_t le_mode_ticks; - uint8_t debounce_ticks_light; - uint8_t debounce_ticks_alarm; - uint8_t debounce_ticks_mode; - bool ignore_alarm_btn_after_sleep; // app resignation countdown (TODO: consolidate with LE countdown?) int16_t timeout_ticks; From 5926e81d25d0429ad9ea62d83618807565f56e14 Mon Sep 17 00:00:00 2001 From: Matheus Afonso Martins Moreira Date: Mon, 16 Sep 2024 14:42:43 -0300 Subject: [PATCH 154/161] faces/alarm: fix 24h indication in 024h mode The watch was not indicating to the user that it was in 24h mode when set to the leading zero 024h time format. This could lead to ambiguity and confusion, so make sure to indicate 24h mode. Closes #476. Reported-by: CarpeNoctem GitHub-Issue: https://github.com/joeycastillo/Sensor-Watch/issues/476 --- movement/watch_faces/complication/alarm_face.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/movement/watch_faces/complication/alarm_face.c b/movement/watch_faces/complication/alarm_face.c index c733c20..fcf9c11 100644 --- a/movement/watch_faces/complication/alarm_face.c +++ b/movement/watch_faces/complication/alarm_face.c @@ -82,11 +82,16 @@ static void _alarm_face_draw(movement_settings_t *settings, alarm_state_t *state watch_clear_indicator(WATCH_INDICATOR_PM); } if (h == 0) h = 12; - } else if (!settings->bit.clock_24h_leading_zero) { + } else { watch_set_indicator(WATCH_INDICATOR_24H); - } else if (h < 10) { - set_leading_zero = true; + + if (settings->bit.clock_24h_leading_zero) { + if (h < 10) { + set_leading_zero = true; + } + } } + sprintf(buf, "%c%c%2d%2d%02d ", _dow_strings[i][0], _dow_strings[i][1], (state->alarm_idx + 1), From 831aadddeabfec167c2ebfdec07e25c08757f61f Mon Sep 17 00:00:00 2001 From: Matheus Afonso Martins Moreira Date: Mon, 16 Sep 2024 14:46:06 -0300 Subject: [PATCH 155/161] faces/alarm: use snprintf formats for 24h and 024h This ensures that the display is always in a consistent state. Reported-by: CarpeNoctem GitHub-Issue: https://github.com/joeycastillo/Sensor-Watch/issues/476 --- movement/watch_faces/complication/alarm_face.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/movement/watch_faces/complication/alarm_face.c b/movement/watch_faces/complication/alarm_face.c index fcf9c11..d71ea20 100644 --- a/movement/watch_faces/complication/alarm_face.c +++ b/movement/watch_faces/complication/alarm_face.c @@ -92,7 +92,7 @@ static void _alarm_face_draw(movement_settings_t *settings, alarm_state_t *state } } - sprintf(buf, "%c%c%2d%2d%02d ", + sprintf(buf, set_leading_zero? "%c%c%2d%02d%02d " : "%c%c%2d%2d%02d ", _dow_strings[i][0], _dow_strings[i][1], (state->alarm_idx + 1), h, @@ -102,9 +102,7 @@ static void _alarm_face_draw(movement_settings_t *settings, alarm_state_t *state buf[_blink_idx[state->setting_state]] = buf[_blink_idx2[state->setting_state]] = ' '; } watch_display_string(buf, 0); - if (set_leading_zero) - watch_display_string("0", 4); - + if (state->is_setting) { // draw pitch level indicator if ((subsecond % 2) == 0 || (state->setting_state != alarm_setting_idx_pitch)) { From f2479bee4f0634fc7325f73858350b5f2778d1fe Mon Sep 17 00:00:00 2001 From: Matheus Afonso Martins Moreira Date: Mon, 16 Sep 2024 14:50:42 -0300 Subject: [PATCH 156/161] faces/simple_clock: clear segments if not in 024h There was an issue where the simple clock's display would remain in 024h mode even after switching back to 12h/24h mode because it only took into account the leading zero bit, whose value is meaningless unless the 24h mode bit is also set. The issue is fixed by taking both bits into account. Closes #476. Reported-by: CarpeNoctem GitHub-Issue: https://github.com/joeycastillo/Sensor-Watch/issues/476 --- movement/watch_faces/clock/simple_clock_face.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/movement/watch_faces/clock/simple_clock_face.c b/movement/watch_faces/clock/simple_clock_face.c index fe46077..68e1ccd 100644 --- a/movement/watch_faces/clock/simple_clock_face.c +++ b/movement/watch_faces/clock/simple_clock_face.c @@ -124,7 +124,7 @@ bool simple_clock_face_loop(movement_event_t event, movement_settings_t *setting } #endif - if (settings->bit.clock_24h_leading_zero && date_time.unit.hour < 10) { + if (settings->bit.clock_mode_24h && settings->bit.clock_24h_leading_zero && date_time.unit.hour < 10) { set_leading_zero = true; } @@ -137,8 +137,10 @@ bool simple_clock_face_loop(movement_event_t event, movement_settings_t *setting } } watch_display_string(buf, pos); + if (set_leading_zero) watch_display_string("0", 4); + // handle alarm indicator if (state->alarm_enabled != settings->bit.alarm_enabled) _update_alarm_indicator(settings->bit.alarm_enabled, state); break; From ea5efb4d828bd5ef588baa5969400ba59d9e548c Mon Sep 17 00:00:00 2001 From: Matheus Afonso Martins Moreira Date: Mon, 16 Sep 2024 15:34:19 -0300 Subject: [PATCH 157/161] faces/clock: clear segments if not in 024h mode There was an issue where the clock's display would remain in 024h mode even after switching back to 12h/24h mode because it only took into account the leading zero bit, whose value is meaningless unless the 24h mode bit is also set. The issue is fixed by taking both bits into account. Closes #476. Reported-by: CarpeNoctem GitHub-Issue: https://github.com/joeycastillo/Sensor-Watch/issues/476 --- movement/watch_faces/clock/clock_face.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/movement/watch_faces/clock/clock_face.c b/movement/watch_faces/clock/clock_face.c index 21d790f..d517897 100644 --- a/movement/watch_faces/clock/clock_face.c +++ b/movement/watch_faces/clock/clock_face.c @@ -60,6 +60,10 @@ static bool clock_is_in_24h_mode(movement_settings_t *settings) { #endif } +static bool clock_should_set_leading_zero(movement_settings_t *settings) { + return clock_is_in_24h_mode(settings) && settings->bit.clock_24h_leading_zero; +} + static void clock_indicate(WatchIndicatorSegment indicator, bool on) { if (on) { watch_set_indicator(indicator); @@ -180,7 +184,7 @@ static void clock_display_clock(movement_settings_t *settings, clock_state_t *cl clock_indicate_pm(settings, current); current = clock_24h_to_12h(current); } - clock_display_all(current, settings->bit.clock_24h_leading_zero); + clock_display_all(current, clock_should_set_leading_zero(settings)); } } From 683032219edc7ac725723136a22e07775205cd66 Mon Sep 17 00:00:00 2001 From: Matheus Afonso Martins Moreira Date: Tue, 17 Sep 2024 17:02:16 -0300 Subject: [PATCH 158/161] Revert PR #471 - add DST to sunrise/sunset The DST code has not yet been fully tested, the upcoming movement refactor is upon us and it will integrate with the micro timezone library anyway. Revert it so that next can be merged into main. This reverts commit 0cc28b9811e76de3dc2cc2919d5309cfa48edfeb, reversing changes made to 337864eb543c9b5cd83667ab0eafb476f63af54c. --- .../complication/sunrise_sunset_face.c | 28 +++++++------------ .../complication/sunrise_sunset_face.h | 15 ++++------ 2 files changed, 15 insertions(+), 28 deletions(-) diff --git a/movement/watch_faces/complication/sunrise_sunset_face.c b/movement/watch_faces/complication/sunrise_sunset_face.c index 4c77fe5..4a3c884 100644 --- a/movement/watch_faces/complication/sunrise_sunset_face.c +++ b/movement/watch_faces/complication/sunrise_sunset_face.c @@ -49,29 +49,20 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s double rise, set, minutes, seconds; bool show_next_match = false; movement_location_t movement_location; - int16_t tz; - watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time - if (state->longLatToUse == 0 || _location_count <= 1) { - tz = get_timezone_offset(settings->bit.time_zone, date_time); + if (state->longLatToUse == 0 || _location_count <= 1) movement_location = (movement_location_t) watch_get_backup_data(1); - } else{ movement_location.bit.latitude = longLatPresets[state->longLatToUse].latitude; movement_location.bit.longitude = longLatPresets[state->longLatToUse].longitude; - if (longLatPresets[state->longLatToUse].uses_dst && dst_occurring(date_time)) - tz = movement_timezone_dst_offsets[longLatPresets[state->longLatToUse].timezone]; - else - tz = movement_timezone_offsets[longLatPresets[state->longLatToUse].timezone]; } if (movement_location.reg == 0) { - watch_clear_all_indicators(); - watch_clear_colon(); watch_display_string("RI no Loc", 0); return; } - watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, tz * 60, 0); // the current date / time in UTC + watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time + watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, state->tz * 60, 0); // the current date / time in UTC watch_date_time scratch_time; // scratchpad, contains different values at different times scratch_time.reg = utc_now.reg; @@ -86,7 +77,7 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s // sunriset returns the rise/set times as signed decimal hours in UTC. // this can mean hours below 0 or above 31, which won't fit into a watch_date_time struct. // to deal with this, we set aside the offset in hours, and add it back before converting it to a watch_date_time. - double hours_from_utc = ((double)tz) / 60.0; + double hours_from_utc = ((double)state->tz) / 60.0; // we loop twice because if it's after sunset today, we need to recalculate to display values for tomorrow. for(int i = 0; i < 2; i++) { @@ -343,6 +334,7 @@ void sunrise_sunset_face_activate(movement_settings_t *settings, void *context) movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1); state->working_latitude = _sunrise_sunset_face_struct_from_latlon(movement_location.bit.latitude); state->working_longitude = _sunrise_sunset_face_struct_from_latlon(movement_location.bit.longitude); + state->tz = get_timezone_offset(settings->bit.time_zone, watch_rtc_get_date_time()); } bool sunrise_sunset_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { @@ -407,11 +399,11 @@ bool sunrise_sunset_face_loop(movement_event_t event, movement_settings_t *setti break; case EVENT_ALARM_LONG_PRESS: if (state->page == 0) { - if (state->longLatToUse != 0) { - state->longLatToUse = 0; - _sunrise_sunset_face_update(settings, state); - break; - } + if (state->longLatToUse != 0) { + state->longLatToUse = 0; + _sunrise_sunset_face_update(settings, state); + break; + } state->page++; state->active_digit = 0; watch_clear_display(); diff --git a/movement/watch_faces/complication/sunrise_sunset_face.h b/movement/watch_faces/complication/sunrise_sunset_face.h index d100dd4..f216a2f 100644 --- a/movement/watch_faces/complication/sunrise_sunset_face.h +++ b/movement/watch_faces/complication/sunrise_sunset_face.h @@ -52,14 +52,13 @@ typedef struct { uint8_t rise_index; uint8_t active_digit; bool location_changed; + int16_t tz; watch_date_time rise_set_expires; sunrise_sunset_lat_lon_settings_t working_latitude; sunrise_sunset_lat_lon_settings_t working_longitude; uint8_t longLatToUse; } sunrise_sunset_state_t; -#define SUNRISE_USE_LOCAL_TZ 0xFF - void sunrise_sunset_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); void sunrise_sunset_face_activate(movement_settings_t *settings, void *context); bool sunrise_sunset_face_loop(movement_event_t event, movement_settings_t *settings, void *context); @@ -77,18 +76,14 @@ typedef struct { char name[2]; int16_t latitude; int16_t longitude; - uint8_t timezone; // References element in movement_timezone_offsets - bool uses_dst; } long_lat_presets_t; -// Locations must either use the same timezone as local time, or not observe DST. static const long_lat_presets_t longLatPresets[] = { - { .name = " "}, // Default, the long, lat, and timezone get replaced by what's set in the watch -// { .name = "Ny", .latitude = 4072, .longitude = -7401, .timezone = 33, .uses_dst = true }, // New York City, NY -// { .name = "LA", .latitude = 3405, .longitude = -11824, .timezone = 30, .uses_dst = true }, // Los Angeles, CA -// { .name = "dE", .latitude = 4221, .longitude = -8305, .timezone = 33, .uses_dst = true }, // Detroit, MI -// { .name = "To", .latitude = 3567, .longitude = 13965, .timezone = 15, .uses_dst = false }, // Tokyo, JP + { .name = " "}, // Default, the long and lat get replaced by what's set in the watch +// { .name = "Ny", .latitude = 4072, .longitude = -7401 }, // New York City, NY +// { .name = "LA", .latitude = 3405, .longitude = -11824 }, // Los Angeles, CA +// { .name = "dE", .latitude = 4221, .longitude = -8305 }, // Detroit, MI }; #endif // SUNRISE_SUNSET_FACE_H_ From a9d503b807c59985cd87627d4cefa14cc2c81694 Mon Sep 17 00:00:00 2001 From: Matheus Afonso Martins Moreira Date: Tue, 17 Sep 2024 17:08:32 -0300 Subject: [PATCH 159/161] Revert PR #470 - implement automatic DST toggling The DST code has not yet been fully tested, the upcoming movement refactor is upon us and it will integrate with the micro timezone library anyway. Revert it so that next can be merged into main. This reverts commit ac5bf8cfce67cdb5662aeea618c2eb9511f0d190, reversing changes made to 5a8a49a8c77d6d5ba0f46f0e5b51dec2daba46db. --- movement/movement.c | 186 +++++++++--------- movement/movement.h | 4 +- movement/movement_config.h | 7 - movement/watch_faces/clock/beats_face.c | 4 +- .../clock/day_night_percentage_face.c | 5 +- movement/watch_faces/clock/mars_time_face.c | 2 +- .../watch_faces/clock/world_clock2_face.c | 7 +- .../watch_faces/clock/world_clock2_face.h | 2 - movement/watch_faces/clock/world_clock_face.c | 11 +- movement/watch_faces/clock/world_clock_face.h | 2 - .../watch_faces/complication/astronomy_face.c | 2 +- .../watch_faces/complication/countdown_face.c | 14 +- .../complication/moon_phase_face.c | 6 +- .../watch_faces/complication/orrery_face.c | 2 +- .../complication/planetary_hours_face.c | 7 +- .../complication/planetary_time_face.c | 20 +- .../watch_faces/complication/sailing_face.c | 15 +- .../watch_faces/complication/solstice_face.c | 3 +- .../complication/sunrise_sunset_face.c | 5 +- .../complication/sunrise_sunset_face.h | 1 - .../watch_faces/complication/timer_face.c | 11 +- .../watch_faces/complication/tomato_face.c | 11 +- movement/watch_faces/complication/totp_face.c | 5 +- .../watch_faces/complication/totp_face_lfs.c | 3 +- .../accelerometer_data_acquisition_face.c | 2 +- movement/watch_faces/settings/set_time_face.c | 19 +- .../settings/set_time_hackwatch_face.c | 15 +- watch-library/hardware/watch/watch_rtc.c | 14 -- watch-library/shared/watch/watch_rtc.h | 6 - watch-library/shared/watch/watch_utility.c | 38 ---- watch-library/shared/watch/watch_utility.h | 18 -- watch-library/simulator/watch/watch_rtc.c | 14 -- 32 files changed, 165 insertions(+), 296 deletions(-) diff --git a/movement/movement.c b/movement/movement.c index a8a22bd..2ea43fa 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -34,7 +34,6 @@ #include "filesystem.h" #include "movement.h" #include "shell.h" -#include "watch_utility.h" #ifndef MOVEMENT_FIRMWARE #include "movement_config.h" @@ -96,11 +95,6 @@ #define MOVEMENT_DEFAULT_LED_DURATION 1 #endif -// Default to having DST get set -#ifndef MOVEMENT_DEFAULT_DST_ACTIVE -#define MOVEMENT_DEFAULT_DST_ACTIVE true -#endif - #if __EMSCRIPTEN__ #include #endif @@ -112,8 +106,7 @@ const int32_t movement_le_inactivity_deadlines[8] = {INT_MAX, 600, 3600, 7200, 2 const int16_t movement_timeout_inactivity_deadlines[4] = {60, 120, 300, 1800}; movement_event_t event; -#define NUM_TIME_ZONES 41 -const int16_t movement_timezone_offsets[NUM_TIME_ZONES] = { +const int16_t movement_timezone_offsets[] = { 0, // 0 : 0:00:00 (UTC) 60, // 1 : 1:00:00 (Central European Time) 120, // 2 : 2:00:00 (South African Standard Time) @@ -170,50 +163,94 @@ const int16_t movement_timezone_offsets[NUM_TIME_ZONES] = { * having to separately change the hour and timezone info * in the time set face. */ -const int16_t movement_timezone_dst_offsets[NUM_TIME_ZONES] = { - 60, // 0 UTC + 1 = CET - 120, // 1 CET + 1 = SAST - 189, // 2 SAST + 1 = AST - 240, // 3 AST + 1 = GST - 270, // 4 IST + 1 = AT - 300, // 5 GST + 1 = PST - 330, // 6 AT + 1 = IST - 360, // 7 PST + 1 = KT - 390, // 8 IST + 1 = MT - 345, // 9 Nepal has no equivalent DST timezone, but they don't observe DST anyway - 420, // 10 KT + 1 = TST - 390, // 11 Myanmar has no equivalent DST timezone, but they don't observe DST anyway - 480, // 12 TST + 1 = CST - 540, // 13 CST + 1 = JST - 525, // 14 ACWST has no equivalent DST timezone, but they don't observe DST anyway - 600, // 15 JST + 1 = AEST - 630, // 16 ACST + 1 = LHST - 660, // 17 AEST + 1 = SIT - 630, // 18 LHST has no equivalent DST timezone, but they don't observe DST anyway - 720, // 19 SIT + 1 = NZST - 780, // 20 NZST + 1 = TT - 825, // 21 CST + 1 = CDT - 840, // 22 TT + 1 = LIT - 825, // 23 CDT is already a daylight timezone - 840, // 24 LIT has no equivalent DST timezone, but they don't observe DST anyway - -660, // 25 BIT + 1 = NT - -600, // 26 NT + 1 = HAST - -540, // 27 HAST + 1 = AST - -570, // 28 MIT has no equivalent DST timezone, but they don't observe DST anyway - -480, // 29 AST + 1 = PST - -420, // 30 PST + 1 = MST - -360, // 31 MST + 1 = CST - -300, // 32 CST + 1 = EST - -240, // 33 EST + 1 = AST - -210, // 34 VST + 1 = NST - -180, // 35 AST + 1 = BT - -150, // 36 NST + 1 = NDT - -120, // 37 BT + 1 = 39 - -150, // 38 NDT is already a daylight timezone - -60, // 39 FNT + 1 = AST +const uint8_t movement_dst_jump_table[] = { + 1, // 0 UTC + 1 = CET + 2, // 1 CET + 1 = SAST + 3, // 2 SAST + 1 = AST + 5, // 3 AST + 1 = GST + 6, // 4 IST + 1 = AT + 7, // 5 GST + 1 = PST + 8, // 6 AT + 1 = IST + 10, // 7 PST + 1 = KT + 11, // 8 IST + 1 = MT + 9, // 9 Nepal has no equivalent DST timezone, but they don't observe DST anyway + 12, // 10 KT + 1 = TST + 11, // 11 Myanmar has no equivalent DST timezone, but they don't observe DST anyway + 13, // 12 TST + 1 = CST + 15, // 13 CST + 1 = JST + 14, // 14 ACWST has no equivalent DST timezone, but they don't observe DST anyway + 17, // 15 JST + 1 = AEST + 18, // 16 ACST + 1 = LHST + 19, // 17 AEST + 1 = SIT + 18, // 18 LHST has no equivalent DST timezone, but they don't observe DST anyway + 20, // 19 SIT + 1 = NZST + 22, // 20 NZST + 1 = TT + 23, // 21 CST + 1 = CDT + 24, // 22 TT + 1 = LIT + 23, // 23 CDT is already a daylight timezone + 24, // 24 LIT has no equivalent DST timezone, but they don't observe DST anyway + 26, // 25 BIT + 1 = NT + 27, // 26 NT + 1 = HAST + 29, // 27 HAST + 1 = AST + 28, // 28 MIT has no equivalent DST timezone, but they don't observe DST anyway + 30, // 29 AST + 1 = PST + 31, // 30 PST + 1 = MST + 32, // 31 MST + 1 = CST + 33, // 32 CST + 1 = EST + 35, // 33 EST + 1 = AST + 36, // 34 VST + 1 = NST + 37, // 35 AST + 1 = BT + 38, // 36 NST + 1 = NDT + 39, // 37 BT + 1 = 39 + 38, // 38 NDT is already a daylight timezone + 40, // 39 FNT + 1 = AST 0 // 40 AST + 1 = UTC }; +const uint8_t movement_dst_inverse_jump_table[] = { + 40, // 0 + 0, // 1 + 1, // 2 + 2, // 3 + 4, // 4 + 3, // 5 + 4, // 6 + 5, // 7 + 6, // 8 + 9, // 9 + 7, // 10 + 8, // 11 + 10, // 12 + 12, // 13 + 14, // 14 + 13, // 15 + 16, // 16 + 15, // 17 + 16, // 18 + 17, // 19 + 19, // 20 + 21, // 21 + 20, // 22 + 21, // 23 + 24, // 24 + 25, // 25 + 25, // 26 + 26, // 27 + 28, // 28 + 27, // 29 + 29, // 30 + 30, // 31 + 31, // 32 + 32, // 33 + 34, // 34 + 33, // 35 + 34, // 36 + 35, // 37 + 36, // 38 + 37, // 39 + 39 // 40 +}; + const char movement_valid_position_0_chars[] = " AaBbCcDdEeFGgHhIiJKLMNnOoPQrSTtUuWXYZ-='+\\/0123456789"; const char movement_valid_position_1_chars[] = " ABCDEFHlJLNORTtUX-='01378"; @@ -247,31 +284,6 @@ static inline void _movement_disable_fast_tick_if_possible(void) { } } -static bool _check_and_act_on_daylight_savings(void) { - if (!movement_state.settings.bit.dst_active) return false; - watch_date_time date_time = watch_rtc_get_date_time(); - // No need for all of the unix time calculations for times not at the beginning or end of the hour - if (date_time.unit.minute > 1 && date_time.unit.minute < 59) return false; - uint8_t dst_result = get_dst_status(date_time); - bool dst_skip_rolling_back = get_dst_skip_rolling_back(); - - if (dst_skip_rolling_back && (dst_result == DST_ENDED)) { - clear_dst_skip_rolling_back(); - } - else if (dst_result == DST_ENDING && !dst_skip_rolling_back) { - date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24; - watch_rtc_set_date_time(date_time); - set_dst_skip_rolling_back(); - return true; - } - else if (dst_result == DST_STARTING) { - date_time.unit.hour = (date_time.unit.hour + 1) % 24; - watch_rtc_set_date_time(date_time); - return true; - } - return false; -} - static void _movement_handle_background_tasks(void) { for(uint8_t i = 0; i < MOVEMENT_NUM_FACES; i++) { // For each face, if the watch face wants a background task... @@ -281,7 +293,6 @@ static void _movement_handle_background_tasks(void) { watch_faces[i].loop(background_event, &movement_state.settings, watch_face_contexts[i]); } } - _check_and_act_on_daylight_savings(); movement_state.needs_background_tasks_handled = false; } @@ -479,12 +490,6 @@ uint8_t movement_claim_backup_register(void) { return movement_state.next_available_backup_register++; } -int16_t get_timezone_offset(uint8_t timezone_idx, watch_date_time date_time) { - if (movement_state.settings.bit.dst_active && dst_occurring(date_time)) - return movement_timezone_dst_offsets[timezone_idx]; - return movement_timezone_offsets[timezone_idx]; -} - void app_init(void) { #if defined(NO_FREQCORR) watch_rtc_freqcorr_write(0, 0); @@ -502,19 +507,6 @@ void app_init(void) { movement_state.settings.bit.to_interval = MOVEMENT_DEFAULT_TIMEOUT_INTERVAL; movement_state.settings.bit.le_interval = MOVEMENT_DEFAULT_LOW_ENERGY_INTERVAL; movement_state.settings.bit.led_duration = MOVEMENT_DEFAULT_LED_DURATION; - movement_state.settings.bit.dst_active = MOVEMENT_DEFAULT_DST_ACTIVE; - -#ifdef MAKEFILE_TIMEZONE - timezone_offsets = dst_occurring(watch_rtc_get_date_time()) ? movement_timezone_dst_offsets : movement_timezone_offsets; - for (int i = 0; i < NUM_TIME_ZONES; i++) { - if (timezone_offsets[i] == MAKEFILE_TIMEZONE) { - movement_state.settings.bit.time_zone = i; - break; - } - } -#else - movement_state.settings.bit.time_zone = 35; // Atlantic Time as default -#endif movement_state.light_ticks = -1; movement_state.alarm_ticks = -1; @@ -524,13 +516,11 @@ void app_init(void) { filesystem_init(); #if __EMSCRIPTEN__ - const int16_t* timezone_offsets; int32_t time_zone_offset = EM_ASM_INT({ return -new Date().getTimezoneOffset(); }); - timezone_offsets = dst_occurring(watch_rtc_get_date_time()) ? movement_timezone_dst_offsets : movement_timezone_offsets; - for (int i = 0; i < NUM_TIME_ZONES; i++) { - if (timezone_offsets[i] == time_zone_offset) { + for (int i = 0, count = sizeof(movement_timezone_offsets) / sizeof(movement_timezone_offsets[0]); i < count; i++) { + if (movement_timezone_offsets[i] == time_zone_offset) { movement_state.settings.bit.time_zone = i; break; } diff --git a/movement/movement.h b/movement/movement.h index 46be32f..ecf5d8b 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -130,7 +130,8 @@ typedef struct { } movement_event_t; extern const int16_t movement_timezone_offsets[]; -extern const int16_t movement_timezone_dst_offsets[]; +extern const uint8_t movement_dst_jump_table[]; +extern const uint8_t movement_dst_inverse_jump_table[]; extern const char movement_valid_position_0_chars[]; extern const char movement_valid_position_1_chars[]; @@ -314,6 +315,5 @@ void movement_play_alarm(void); void movement_play_alarm_beeps(uint8_t rounds, BuzzerNote alarm_note); uint8_t movement_claim_backup_register(void); -int16_t get_timezone_offset(uint8_t timezone_idx, watch_date_time date_time); #endif // MOVEMENT_H_ diff --git a/movement/movement_config.h b/movement/movement_config.h index 46e1d2e..10a30af 100644 --- a/movement/movement_config.h +++ b/movement/movement_config.h @@ -95,11 +95,4 @@ const watch_face_t watch_faces[] = { */ #define MOVEMENT_DEFAULT_LED_DURATION 1 -/* Set if using DST - * Valid values are: - * false: Don't allow the watch to use DST - * true: Allow the watch to use DST - */ -#define MOVEMENT_DEFAULT_DST_ACTIVE true - #endif // MOVEMENT_CONFIG_H_ diff --git a/movement/watch_faces/clock/beats_face.c b/movement/watch_faces/clock/beats_face.c index 6954396..85bcbe0 100644 --- a/movement/watch_faces/clock/beats_face.c +++ b/movement/watch_faces/clock/beats_face.c @@ -61,7 +61,7 @@ bool beats_face_loop(movement_event_t event, movement_settings_t *settings, void case EVENT_ACTIVATE: case EVENT_TICK: date_time = watch_rtc_get_date_time(); - centibeats = clock2beats(date_time.unit.hour, date_time.unit.minute, date_time.unit.second, event.subsecond, get_timezone_offset(settings->bit.time_zone, date_time)); + centibeats = clock2beats(date_time.unit.hour, date_time.unit.minute, date_time.unit.second, event.subsecond, movement_timezone_offsets[settings->bit.time_zone]); if (centibeats == state->last_centibeat_displayed) { // we missed this update, try again next subsecond state->next_subsecond_update = (event.subsecond + 1) % BEAT_REFRESH_FREQUENCY; @@ -76,7 +76,7 @@ bool beats_face_loop(movement_event_t event, movement_settings_t *settings, void case EVENT_LOW_ENERGY_UPDATE: if (!watch_tick_animation_is_running()) watch_start_tick_animation(432); date_time = watch_rtc_get_date_time(); - centibeats = clock2beats(date_time.unit.hour, date_time.unit.minute, date_time.unit.second, event.subsecond, get_timezone_offset(settings->bit.time_zone, date_time)); + centibeats = clock2beats(date_time.unit.hour, date_time.unit.minute, date_time.unit.second, event.subsecond, movement_timezone_offsets[settings->bit.time_zone]); sprintf(buf, "bt %4lu ", centibeats / 100); watch_display_string(buf, 0); diff --git a/movement/watch_faces/clock/day_night_percentage_face.c b/movement/watch_faces/clock/day_night_percentage_face.c index 4041c05..86f07f9 100644 --- a/movement/watch_faces/clock/day_night_percentage_face.c +++ b/movement/watch_faces/clock/day_night_percentage_face.c @@ -62,8 +62,7 @@ void day_night_percentage_face_setup(movement_settings_t *settings, uint8_t watc if (*context_ptr == NULL) { *context_ptr = malloc(sizeof(day_night_percentage_state_t)); day_night_percentage_state_t *state = (day_night_percentage_state_t *)*context_ptr; - watch_date_time date_time = watch_rtc_get_date_time(); - watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60, 0); + watch_date_time utc_now = watch_utility_date_time_convert_zone(watch_rtc_get_date_time(), movement_timezone_offsets[settings->bit.time_zone] * 60, 0); recalculate(utc_now, state); } } @@ -78,7 +77,7 @@ bool day_night_percentage_face_loop(movement_event_t event, movement_settings_t char buf[12]; watch_date_time date_time = watch_rtc_get_date_time(); - watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60, 0); + watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60, 0); switch (event.event_type) { case EVENT_ACTIVATE: diff --git a/movement/watch_faces/clock/mars_time_face.c b/movement/watch_faces/clock/mars_time_face.c index 304c615..30f6a34 100644 --- a/movement/watch_faces/clock/mars_time_face.c +++ b/movement/watch_faces/clock/mars_time_face.c @@ -70,7 +70,7 @@ static void _h_to_hms(mars_clock_hms_t *date_time, double h) { static void _update(movement_settings_t *settings, mars_time_state_t *state) { char buf[11]; watch_date_time date_time = watch_rtc_get_date_time(); - uint32_t now = watch_utility_date_time_to_unix_time(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60); + uint32_t now = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); // TODO: I'm skipping over some steps here. // https://www.giss.nasa.gov/tools/mars24/help/algorithm.html double jdut = 2440587.5 + ((double)now / 86400.0); diff --git a/movement/watch_faces/clock/world_clock2_face.c b/movement/watch_faces/clock/world_clock2_face.c index 09bda42..4d50552 100644 --- a/movement/watch_faces/clock/world_clock2_face.c +++ b/movement/watch_faces/clock/world_clock2_face.c @@ -154,7 +154,6 @@ void world_clock2_face_activate(movement_settings_t *settings, void *context) movement_request_tick_frequency(4); break; } - state->tz = get_timezone_offset(settings->bit.time_zone, watch_rtc_get_date_time()); refresh_face = true; } @@ -184,8 +183,8 @@ static bool mode_display(movement_event_t event, movement_settings_t *settings, /* Determine current time at time zone and store date/time */ date_time = watch_rtc_get_date_time(); - timestamp = watch_utility_date_time_to_unix_time(date_time, state->tz * 60); - date_time = watch_utility_date_time_from_unix_time(timestamp, state->tz_curr * 60); + timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); + date_time = watch_utility_date_time_from_unix_time(timestamp, movement_timezone_offsets[state->current_zone] * 60); previous_date_time = state->previous_date_time; state->previous_date_time = date_time.reg; @@ -292,7 +291,7 @@ static bool mode_settings(movement_event_t event, movement_settings_t *settings, watch_clear_indicator(WATCH_INDICATOR_PM); refresh_face = false; } - result = div(state->tz_curr, 60); + result = div(movement_timezone_offsets[state->current_zone], 60); hours = result.quot; minutes = result.rem; diff --git a/movement/watch_faces/clock/world_clock2_face.h b/movement/watch_faces/clock/world_clock2_face.h index 5d8310f..0baac21 100644 --- a/movement/watch_faces/clock/world_clock2_face.h +++ b/movement/watch_faces/clock/world_clock2_face.h @@ -104,8 +104,6 @@ typedef struct { world_clock2_mode_t current_mode; uint8_t current_zone; uint32_t previous_date_time; - int16_t tz; - int16_t tz_curr; } world_clock2_state_t; void world_clock2_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr); diff --git a/movement/watch_faces/clock/world_clock_face.c b/movement/watch_faces/clock/world_clock_face.c index 80f4ebf..c324b5f 100644 --- a/movement/watch_faces/clock/world_clock_face.c +++ b/movement/watch_faces/clock/world_clock_face.c @@ -60,16 +60,15 @@ static bool world_clock_face_do_display_mode(movement_event_t event, movement_se watch_date_time date_time; switch (event.event_type) { case EVENT_ACTIVATE: - state->tz = get_timezone_offset(settings->bit.time_zone, watch_rtc_get_date_time()); - if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H); + if (settings->bit.clock_mode_24h && !settings->bit.clock_24h_leading_zero) watch_set_indicator(WATCH_INDICATOR_24H); watch_set_colon(); state->previous_date_time = 0xFFFFFFFF; // fall through case EVENT_TICK: case EVENT_LOW_ENERGY_UPDATE: date_time = watch_rtc_get_date_time(); - timestamp = watch_utility_date_time_to_unix_time(date_time, state->tz * 60); - date_time = watch_utility_date_time_from_unix_time(timestamp, state->tz_curr * 60); + timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); + date_time = watch_utility_date_time_from_unix_time(timestamp, movement_timezone_offsets[state->settings.bit.timezone_index] * 60); previous_date_time = state->previous_date_time; state->previous_date_time = date_time.reg; @@ -178,8 +177,8 @@ static bool _world_clock_face_do_settings_mode(movement_event_t event, movement_ sprintf(buf, "%c%c %3d%02d ", movement_valid_position_0_chars[state->settings.bit.char_0], movement_valid_position_1_chars[state->settings.bit.char_1], - (int8_t) (state->tz_curr / 60), - (int8_t) (state->tz_curr % 60) * (state->tz_curr < 0 ? -1 : 1)); + (int8_t) (movement_timezone_offsets[state->settings.bit.timezone_index] / 60), + (int8_t) (movement_timezone_offsets[state->settings.bit.timezone_index] % 60) * (movement_timezone_offsets[state->settings.bit.timezone_index] < 0 ? -1 : 1)); watch_set_colon(); watch_clear_indicator(WATCH_INDICATOR_PM); diff --git a/movement/watch_faces/clock/world_clock_face.h b/movement/watch_faces/clock/world_clock_face.h index 2de3ced..92e91a6 100644 --- a/movement/watch_faces/clock/world_clock_face.h +++ b/movement/watch_faces/clock/world_clock_face.h @@ -62,8 +62,6 @@ typedef struct { uint8_t backup_register; uint8_t current_screen; uint32_t previous_date_time; - int16_t tz; - int16_t tz_curr; } world_clock_state_t; void world_clock_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); diff --git a/movement/watch_faces/complication/astronomy_face.c b/movement/watch_faces/complication/astronomy_face.c index 8db8760..204a0a3 100644 --- a/movement/watch_faces/complication/astronomy_face.c +++ b/movement/watch_faces/complication/astronomy_face.c @@ -80,7 +80,7 @@ static void _astronomy_face_recalculate(movement_settings_t *settings, astronomy #endif watch_date_time date_time = watch_rtc_get_date_time(); - uint32_t timestamp = watch_utility_date_time_to_unix_time(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60); + uint32_t timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); date_time = watch_utility_date_time_from_unix_time(timestamp, 0); double jd = astro_convert_date_to_julian_date(date_time.unit.year + WATCH_RTC_REFERENCE_YEAR, date_time.unit.month, date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second); diff --git a/movement/watch_faces/complication/countdown_face.c b/movement/watch_faces/complication/countdown_face.c index 825595e..f585ebc 100644 --- a/movement/watch_faces/complication/countdown_face.c +++ b/movement/watch_faces/complication/countdown_face.c @@ -45,8 +45,8 @@ static void abort_quick_ticks(countdown_state_t *state) { } } -static inline int32_t get_tz_offset(movement_settings_t *settings, watch_date_time date_time) { - return get_timezone_offset(settings->bit.time_zone, date_time) * 60; +static inline int32_t get_tz_offset(movement_settings_t *settings) { + return movement_timezone_offsets[settings->bit.time_zone] * 60; } static inline void store_countdown(countdown_state_t *state) { @@ -70,15 +70,13 @@ static inline void button_beep(movement_settings_t *settings) { } static void schedule_countdown(countdown_state_t *state, movement_settings_t *settings) { - watch_date_time now = watch_rtc_get_date_time(); - int16_t tz = get_tz_offset(settings, now); - // Calculate the new state->now_ts but don't update it until we've updated the target - + // Calculate the new state->now_ts but don't update it until we've updated the target - // avoid possible race where the old target is compared to the new time and immediately triggers - uint32_t new_now = watch_utility_date_time_to_unix_time(now, tz); + uint32_t new_now = watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), get_tz_offset(settings)); state->target_ts = watch_utility_offset_timestamp(new_now, state->hours, state->minutes, state->seconds); state->now_ts = new_now; - watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, tz); + watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, get_tz_offset(settings)); movement_schedule_background_task_for_face(state->watch_face_index, target_dt); } @@ -205,7 +203,7 @@ void countdown_face_activate(movement_settings_t *settings, void *context) { countdown_state_t *state = (countdown_state_t *)context; if(state->mode == cd_running) { watch_date_time now = watch_rtc_get_date_time(); - state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings, now)); + state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); watch_set_indicator(WATCH_INDICATOR_SIGNAL); } watch_set_colon(); diff --git a/movement/watch_faces/complication/moon_phase_face.c b/movement/watch_faces/complication/moon_phase_face.c index c571843..f74de64 100644 --- a/movement/watch_faces/complication/moon_phase_face.c +++ b/movement/watch_faces/complication/moon_phase_face.c @@ -59,9 +59,8 @@ static void _update(movement_settings_t *settings, moon_phase_state_t *state, ui (void)state; char buf[11]; watch_date_time date_time = watch_rtc_get_date_time(); - int16_t tz = get_timezone_offset(settings->bit.time_zone, date_time); - uint32_t now = watch_utility_date_time_to_unix_time(date_time, tz * 60) + offset; - date_time = watch_utility_date_time_from_unix_time(now, tz * 60); + uint32_t now = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60) + offset; + date_time = watch_utility_date_time_from_unix_time(now, movement_timezone_offsets[settings->bit.time_zone] * 60); double currentfrac = fmod(now - FIRST_MOON, LUNAR_SECONDS) / LUNAR_SECONDS; double currentday = currentfrac * LUNAR_DAYS; uint8_t phase_index = 0; @@ -139,7 +138,6 @@ bool moon_phase_face_loop(movement_event_t event, movement_settings_t *settings, switch (event.event_type) { case EVENT_ACTIVATE: - _update(settings, state, state->offset); break; case EVENT_TICK: diff --git a/movement/watch_faces/complication/orrery_face.c b/movement/watch_faces/complication/orrery_face.c index 0570dd4..42fdf81 100644 --- a/movement/watch_faces/complication/orrery_face.c +++ b/movement/watch_faces/complication/orrery_face.c @@ -48,7 +48,7 @@ static const char orrery_celestial_body_names[NUM_AVAILABLE_BODIES][3] = { static void _orrery_face_recalculate(movement_settings_t *settings, orrery_state_t *state) { watch_date_time date_time = watch_rtc_get_date_time(); - uint32_t timestamp = watch_utility_date_time_to_unix_time(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60); + uint32_t timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); date_time = watch_utility_date_time_from_unix_time(timestamp, 0); double jd = astro_convert_date_to_julian_date(date_time.unit.year + WATCH_RTC_REFERENCE_YEAR, date_time.unit.month, date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second); double et = astro_convert_jd_to_julian_millenia_since_j2000(jd); diff --git a/movement/watch_faces/complication/planetary_hours_face.c b/movement/watch_faces/complication/planetary_hours_face.c index e0c0a15..e13466b 100644 --- a/movement/watch_faces/complication/planetary_hours_face.c +++ b/movement/watch_faces/complication/planetary_hours_face.c @@ -134,8 +134,7 @@ static void _planetary_solar_phases(movement_settings_t *settings, planetary_hou state->no_location = false; watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time - int16_t tz = get_timezone_offset(settings->bit.time_zone, date_time); - watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, tz * 60, 0); // the current date / time in UTC + watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60, 0); // the current date / time in UTC watch_date_time scratch_time; // scratchpad, contains different values at different times watch_date_time midnight; scratch_time.reg = midnight.reg = utc_now.reg; @@ -148,7 +147,7 @@ static void _planetary_solar_phases(movement_settings_t *settings, planetary_hou double lon = (double)lon_centi / 100.0; // save UTC offset - state->utc_offset = ((double)tz) / 60.0; + state->utc_offset = ((double)movement_timezone_offsets[settings->bit.time_zone]) / 60.0; // calculate sunrise and sunset of current day in decimal hours after midnight sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset); @@ -239,7 +238,7 @@ static void _planetary_hours(movement_settings_t *settings, planetary_hours_stat // get current time watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time - watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60, 0); // the current date / time in UTC + watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60, 0); // the current date / time in UTC current_hour_epoch = watch_utility_date_time_to_unix_time(utc_now, 0); // set the current planetary hour as default screen diff --git a/movement/watch_faces/complication/planetary_time_face.c b/movement/watch_faces/complication/planetary_time_face.c index 1245cc6..7e7ac6c 100644 --- a/movement/watch_faces/complication/planetary_time_face.c +++ b/movement/watch_faces/complication/planetary_time_face.c @@ -37,7 +37,7 @@ // STATIC FUNCTIONS AND CONSTANTS ///////////////////////////////////////////// /** @brief Planetary rulers in the Chaldean order from slowest to fastest - * @details Planetary rulers in the Chaldean order from slowest to fastest: + * @details Planetary rulers in the Chaldean order from slowest to fastest: * Jupiter, Mars, Sun, Venus, Mercury, Moon */ static const char planets[7][3] = {"Sa", "Ju", "Ma", "So", "Ve", "Me", "Lu"}; // Latin @@ -129,8 +129,7 @@ static void _planetary_solar_phase(movement_settings_t *settings, planetary_time state->no_location = false; watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time - int16_t tz = get_timezone_offset(settings->bit.time_zone, date_time); - watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, tz * 60, 0); // the current date / time in UTC + watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60, 0); // the current date / time in UTC watch_date_time scratch_time; // scratchpad, contains different values at different times watch_date_time midnight; scratch_time.reg = midnight.reg = utc_now.reg; @@ -143,7 +142,7 @@ static void _planetary_solar_phase(movement_settings_t *settings, planetary_time double lon = (double)lon_centi / 100.0; // save UTC offset - state->utc_offset = ((double)tz) / 60.0; + state->utc_offset = ((double)movement_timezone_offsets[settings->bit.time_zone]) / 60.0; // get UNIX epoch time now_epoch = watch_utility_date_time_to_unix_time(utc_now, 0); @@ -151,7 +150,7 @@ static void _planetary_solar_phase(movement_settings_t *settings, planetary_time // calculate sunrise and sunset of current day in decimal hours after midnight sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset); - + // calculate sunrise and sunset UNIX timestamps sunrise_epoch = midnight_epoch + sunrise * 3600; sunset_epoch = midnight_epoch + sunset * 3600; @@ -192,7 +191,7 @@ static void _planetary_solar_phase(movement_settings_t *settings, planetary_time state->phase_end = sunrise_epoch; } - // calculate the duration of a planetary second during this solar phase + // calculate the duration of a planetary second during this solar phase // and convert to Hertz so we can call a faster tick rate state->freq = (1 / ((double)( state->phase_end - state->phase_start ) / 43200)); } @@ -208,12 +207,11 @@ static void _planetary_time(movement_event_t event, movement_settings_t *setting uint8_t weekday, planet, planetary_hour; double hour_duration, current_hour, current_minute, current_second; bool set_leading_zero = false; - watch_date_time date_time = watch_rtc_get_date_time(); watch_set_colon(); // get current time and convert to UTC - state->scratch = watch_utility_date_time_convert_zone(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60, 0); + state->scratch = watch_utility_date_time_convert_zone(watch_rtc_get_date_time(), movement_timezone_offsets[settings->bit.time_zone] * 60, 0); // when current phase ends calculate the next phase if ( watch_utility_date_time_to_unix_time(state->scratch, 0) >= state->phase_end ) { @@ -263,11 +261,11 @@ static void _planetary_time(movement_event_t event, movement_settings_t *setting if ( state->ruler == 0 ) strncpy(ruler, planets[planet], 3); if ( state->ruler == 1 ) strncpy(ruler, planetes[planet], 3); if ( state->ruler == 2 ) strncpy(ruler, " ", 3); - + // display planetary time with ruler of the hour or ruler of the day if ( state->day_ruler ) sprintf(buf, "%s d%2d%02d%02d", ruler, state->scratch.unit.hour, state->scratch.unit.minute, state->scratch.unit.second); else sprintf(buf, "%s h%2d%02d%02d", ruler, state->scratch.unit.hour, state->scratch.unit.minute, state->scratch.unit.second); - + watch_display_string(buf, 0); if (set_leading_zero) watch_display_string("0", 4); @@ -303,7 +301,7 @@ void planetary_time_face_activate(movement_settings_t *settings, void *context) #endif planetary_time_state_t *state = (planetary_time_state_t *)context; - + // calculate phase _planetary_solar_phase(settings, state); } diff --git a/movement/watch_faces/complication/sailing_face.c b/movement/watch_faces/complication/sailing_face.c index 16d60c9..a6c13fe 100644 --- a/movement/watch_faces/complication/sailing_face.c +++ b/movement/watch_faces/complication/sailing_face.c @@ -33,8 +33,8 @@ #define sl_SELECTIONS 6 #define DEFAULT_MINUTES { 5,4,1,0,0,0 } -static inline int32_t get_tz_offset(movement_settings_t *settings, watch_date_time date_time) { - return get_timezone_offset(settings->bit.time_zone, date_time) * 60; +static inline int32_t get_tz_offset(movement_settings_t *settings) { + return movement_timezone_offsets[settings->bit.time_zone] * 60; } static int lap = 0; @@ -165,8 +165,7 @@ static void ring(sailing_state_t *state, movement_settings_t *settings) { return; } state->nextbeep_ts = state->target_ts - beepseconds[beepflag+1]; - watch_date_time now = watch_rtc_get_date_time(); - watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->nextbeep_ts, get_tz_offset(settings, now)); + watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->nextbeep_ts, get_tz_offset(settings)); movement_schedule_background_task_for_face(state->watch_face_index, target_dt); //background task is set, now we have time to play the tune. If this is cancelled accidentally, the next alarm will still ring. Sound is implemented non-blocking, so that neither buttons nor display output are compromised. for (int i = 0; i < 5; i++) { @@ -195,7 +194,7 @@ static void start(sailing_state_t *state, movement_settings_t *settings) {//gets } if (state->index > 5 || state->minutes[state->index] == 0) { watch_date_time now = watch_rtc_get_date_time(); - state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings, now)); + state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); state->target_ts = state->now_ts; if (alarmflag != 0){ watch_buzzer_play_sequence(long_beep, NULL); @@ -206,7 +205,7 @@ static void start(sailing_state_t *state, movement_settings_t *settings) {//gets movement_request_tick_frequency(1); //synchronises tick with the moment the button was pressed. Solves 1s offset between sound and display, solves up to +-0.5s offset between button action and display. state->mode = sl_running; watch_date_time now = watch_rtc_get_date_time(); - state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings, now)); + state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); state->target_ts = watch_utility_offset_timestamp(state->now_ts, 0, state->minutes[state->index], 0); ring(state, settings); } @@ -254,11 +253,11 @@ void sailing_face_activate(movement_settings_t *settings, void *context) { sailing_state_t *state = (sailing_state_t *)context; if(state->mode == sl_running) { watch_date_time now = watch_rtc_get_date_time(); - state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings, now)); + state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); } if(state->mode == sl_counting) { watch_date_time now = watch_rtc_get_date_time(); - state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings, now)); + state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); watch_set_indicator(WATCH_INDICATOR_LAP); } switch (alarmflag) { diff --git a/movement/watch_faces/complication/solstice_face.c b/movement/watch_faces/complication/solstice_face.c index 76d0d26..e74f878 100644 --- a/movement/watch_faces/complication/solstice_face.c +++ b/movement/watch_faces/complication/solstice_face.c @@ -123,10 +123,9 @@ static watch_date_time jde_to_date_time(double JDE) { } static void calculate_datetimes(solstice_state_t *state, movement_settings_t *settings) { - watch_date_time date_time = watch_rtc_get_date_time(); for (int i = 0; i < 4; i++) { // TODO: handle DST changes - state->datetimes[i] = jde_to_date_time(calculate_solstice_equinox(2020 + state->year, i) + (get_timezone_offset(settings->bit.time_zone, date_time) / (60.0*24.0))); + state->datetimes[i] = jde_to_date_time(calculate_solstice_equinox(2020 + state->year, i) + (movement_timezone_offsets[settings->bit.time_zone] / (60.0*24.0))); } } diff --git a/movement/watch_faces/complication/sunrise_sunset_face.c b/movement/watch_faces/complication/sunrise_sunset_face.c index 4a3c884..8747bd8 100644 --- a/movement/watch_faces/complication/sunrise_sunset_face.c +++ b/movement/watch_faces/complication/sunrise_sunset_face.c @@ -62,7 +62,7 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s } watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time - watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, state->tz * 60, 0); // the current date / time in UTC + watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60, 0); // the current date / time in UTC watch_date_time scratch_time; // scratchpad, contains different values at different times scratch_time.reg = utc_now.reg; @@ -77,7 +77,7 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s // sunriset returns the rise/set times as signed decimal hours in UTC. // this can mean hours below 0 or above 31, which won't fit into a watch_date_time struct. // to deal with this, we set aside the offset in hours, and add it back before converting it to a watch_date_time. - double hours_from_utc = ((double)state->tz) / 60.0; + double hours_from_utc = ((double)movement_timezone_offsets[settings->bit.time_zone]) / 60.0; // we loop twice because if it's after sunset today, we need to recalculate to display values for tomorrow. for(int i = 0; i < 2; i++) { @@ -334,7 +334,6 @@ void sunrise_sunset_face_activate(movement_settings_t *settings, void *context) movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1); state->working_latitude = _sunrise_sunset_face_struct_from_latlon(movement_location.bit.latitude); state->working_longitude = _sunrise_sunset_face_struct_from_latlon(movement_location.bit.longitude); - state->tz = get_timezone_offset(settings->bit.time_zone, watch_rtc_get_date_time()); } bool sunrise_sunset_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { diff --git a/movement/watch_faces/complication/sunrise_sunset_face.h b/movement/watch_faces/complication/sunrise_sunset_face.h index f216a2f..df5fda5 100644 --- a/movement/watch_faces/complication/sunrise_sunset_face.h +++ b/movement/watch_faces/complication/sunrise_sunset_face.h @@ -52,7 +52,6 @@ typedef struct { uint8_t rise_index; uint8_t active_digit; bool location_changed; - int16_t tz; watch_date_time rise_set_expires; sunrise_sunset_lat_lon_settings_t working_latitude; sunrise_sunset_lat_lon_settings_t working_longitude; diff --git a/movement/watch_faces/complication/timer_face.c b/movement/watch_faces/complication/timer_face.c index 5ee2967..29392d6 100644 --- a/movement/watch_faces/complication/timer_face.c +++ b/movement/watch_faces/complication/timer_face.c @@ -36,8 +36,8 @@ static const int8_t _sound_seq_start[] = {BUZZER_NOTE_C8, 2, 0}; static uint8_t _beeps_to_play; // temporary counter for ring signals playing -static inline int32_t _get_tz_offset(movement_settings_t *settings, watch_date_time date_time) { - return get_timezone_offset(settings->bit.time_zone, date_time) * 60; +static inline int32_t _get_tz_offset(movement_settings_t *settings) { + return movement_timezone_offsets[settings->bit.time_zone] * 60; } static void _signal_callback() { @@ -50,8 +50,7 @@ static void _signal_callback() { static void _start(timer_state_t *state, movement_settings_t *settings, bool with_beep) { if (state->timers[state->current_timer].value == 0) return; watch_date_time now = watch_rtc_get_date_time(); - int32_t tz = _get_tz_offset(settings, now); - state->now_ts = watch_utility_date_time_to_unix_time(now, tz); + state->now_ts = watch_utility_date_time_to_unix_time(now, _get_tz_offset(settings)); if (state->mode == pausing) state->target_ts = state->now_ts + state->paused_left; else @@ -59,7 +58,7 @@ static void _start(timer_state_t *state, movement_settings_t *settings, bool wit state->timers[state->current_timer].unit.hours, state->timers[state->current_timer].unit.minutes, state->timers[state->current_timer].unit.seconds); - watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, tz); + watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, _get_tz_offset(settings)); state->mode = running; movement_schedule_background_task_for_face(state->watch_face_index, target_dt); watch_set_indicator(WATCH_INDICATOR_BELL); @@ -211,7 +210,7 @@ void timer_face_activate(movement_settings_t *settings, void *context) { watch_set_colon(); if(state->mode == running) { watch_date_time now = watch_rtc_get_date_time(); - state->now_ts = watch_utility_date_time_to_unix_time(now, _get_tz_offset(settings, now)); + state->now_ts = watch_utility_date_time_to_unix_time(now, _get_tz_offset(settings)); watch_set_indicator(WATCH_INDICATOR_BELL); } else { state->pausing_seconds = 1; diff --git a/movement/watch_faces/complication/tomato_face.c b/movement/watch_faces/complication/tomato_face.c index 15b67c6..3d46ba9 100644 --- a/movement/watch_faces/complication/tomato_face.c +++ b/movement/watch_faces/complication/tomato_face.c @@ -30,8 +30,8 @@ static uint8_t focus_min = 25; static uint8_t break_min = 5; -static inline int32_t get_tz_offset(movement_settings_t *settings, watch_date_time date_time) { - return get_timezone_offset(settings->bit.time_zone, date_time) * 60; +static inline int32_t get_tz_offset(movement_settings_t *settings) { + return movement_timezone_offsets[settings->bit.time_zone] * 60; } static uint8_t get_length(tomato_state_t *state) { @@ -47,13 +47,12 @@ static uint8_t get_length(tomato_state_t *state) { static void tomato_start(tomato_state_t *state, movement_settings_t *settings) { watch_date_time now = watch_rtc_get_date_time(); - int32_t tz = get_tz_offset(settings, now); int8_t length = (int8_t) get_length(state); state->mode = tomato_run; - state->now_ts = watch_utility_date_time_to_unix_time(now, tz); + state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); state->target_ts = watch_utility_offset_timestamp(state->now_ts, 0, length, 0); - watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, tz); + watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, get_tz_offset(settings)); movement_schedule_background_task(target_dt); watch_set_indicator(WATCH_INDICATOR_BELL); } @@ -127,7 +126,7 @@ void tomato_face_activate(movement_settings_t *settings, void *context) { tomato_state_t *state = (tomato_state_t *)context; if (state->mode == tomato_run) { watch_date_time now = watch_rtc_get_date_time(); - state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings, now)); + state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); watch_set_indicator(WATCH_INDICATOR_BELL); } watch_set_colon(); diff --git a/movement/watch_faces/complication/totp_face.c b/movement/watch_faces/complication/totp_face.c index c5b26f2..fdb4109 100644 --- a/movement/watch_faces/complication/totp_face.c +++ b/movement/watch_faces/complication/totp_face.c @@ -157,9 +157,8 @@ static void totp_generate_and_display(totp_state_t *totp_state) { totp_display(totp_state); } -static uint32_t totp_compute_base_timestamp(movement_settings_t *settings) { - watch_date_time date_time = watch_rtc_get_date_time(); - return watch_utility_date_time_to_unix_time(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60); +static inline uint32_t totp_compute_base_timestamp(movement_settings_t *settings) { + return watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), movement_timezone_offsets[settings->bit.time_zone] * 60); } void totp_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { diff --git a/movement/watch_faces/complication/totp_face_lfs.c b/movement/watch_faces/complication/totp_face_lfs.c index 1319d47..5d7defe 100644 --- a/movement/watch_faces/complication/totp_face_lfs.c +++ b/movement/watch_faces/complication/totp_face_lfs.c @@ -254,8 +254,7 @@ void totp_face_lfs_activate(movement_settings_t *settings, void *context) { } #endif - watch_date_time date_time = watch_rtc_get_date_time(); - totp_state->timestamp = watch_utility_date_time_to_unix_time(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60); + totp_state->timestamp = watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), movement_timezone_offsets[settings->bit.time_zone] * 60); totp_face_set_record(totp_state, 0); } diff --git a/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c b/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c index 9cadf67..6d174d5 100644 --- a/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c +++ b/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c @@ -442,7 +442,7 @@ static void start_reading(accelerometer_data_acquisition_state_t *state, movemen accelerometer_data_acquisition_record_t record; watch_date_time date_time = watch_rtc_get_date_time(); - state->starting_timestamp = watch_utility_date_time_to_unix_time(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60); + state->starting_timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); record.header.info.record_type = ACCELEROMETER_DATA_ACQUISITION_HEADER; record.header.info.range = ACCELEROMETER_RANGE; record.header.info.temperature = lis2dw_get_temperature(); diff --git a/movement/watch_faces/settings/set_time_face.c b/movement/watch_faces/settings/set_time_face.c index 0b70575..d50d24b 100644 --- a/movement/watch_faces/settings/set_time_face.c +++ b/movement/watch_faces/settings/set_time_face.c @@ -60,6 +60,13 @@ static void _handle_alarm_button(movement_settings_t *settings, watch_date_time if (settings->bit.time_zone > 40) settings->bit.time_zone = 0; break; case 7: // daylight savings time + if (settings->bit.dst_active) { // deactivate DST + date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24; + settings->bit.time_zone = movement_dst_inverse_jump_table[settings->bit.time_zone]; + } else { // activate DST + date_time.unit.hour = (date_time.unit.hour + 1) % 24; + settings->bit.time_zone = movement_dst_jump_table[settings->bit.time_zone]; + } settings->bit.dst_active = !settings->bit.dst_active; break; } @@ -128,9 +135,8 @@ bool set_time_face_loop(movement_event_t event, movement_settings_t *settings, v return movement_default_loop_handler(event, settings); } - char buf[13]; + char buf[11]; bool set_leading_zero = false; - if (current_page < 3) { watch_set_colon(); if (settings->bit.clock_mode_24h) { @@ -150,18 +156,17 @@ bool set_time_face_loop(movement_event_t event, movement_settings_t *settings, v watch_clear_indicator(WATCH_INDICATOR_PM); sprintf(buf, "%s %2d%02d%02d", set_time_face_titles[current_page], date_time.unit.year + 20, date_time.unit.month, date_time.unit.day); } else if (current_page < 7) { // zone - char dst_char = (settings->bit.dst_active && dst_occurring(watch_rtc_get_date_time())) ? 'd' : ' '; if (event.subsecond % 2) { watch_clear_colon(); - sprintf(buf, "%s %c", set_time_face_titles[current_page], dst_char); + sprintf(buf, "%s ", set_time_face_titles[current_page]); } else { - int16_t tz = get_timezone_offset(settings->bit.time_zone, date_time); watch_set_colon(); - sprintf(buf, "%s %3d%02d %c", set_time_face_titles[current_page], (int8_t) (tz / 60), (int8_t) (tz % 60) * (tz < 0 ? -1 : 1), dst_char); + sprintf(buf, "%s %3d%02d ", set_time_face_titles[current_page], (int8_t) (movement_timezone_offsets[settings->bit.time_zone] / 60), (int8_t) (movement_timezone_offsets[settings->bit.time_zone] % 60) * (movement_timezone_offsets[settings->bit.time_zone] < 0 ? -1 : 1)); } } else { // daylight savings watch_clear_colon(); - sprintf(buf, "%s dsT %c", set_time_face_titles[current_page], settings->bit.dst_active ? 'y' : 'n'); + if (settings->bit.dst_active) sprintf(buf, "%s dsT y", set_time_face_titles[current_page]); + else sprintf(buf, "%s dsT n", set_time_face_titles[current_page]); } // blink up the parameter we're setting diff --git a/movement/watch_faces/settings/set_time_hackwatch_face.c b/movement/watch_faces/settings/set_time_hackwatch_face.c index 6e67808..75bc7bb 100644 --- a/movement/watch_faces/settings/set_time_hackwatch_face.c +++ b/movement/watch_faces/settings/set_time_hackwatch_face.c @@ -132,9 +132,8 @@ bool set_time_hackwatch_face_loop(movement_event_t event, movement_settings_t *s } break; } - if (current_page != 2) { // Do not set time when we are at seconds, it was already set previously + if (current_page != 2) // Do not set time when we are at seconds, it was already set previously watch_rtc_set_date_time(date_time_settings); - } break; case EVENT_ALARM_LONG_UP://Setting seconds on long release @@ -172,16 +171,11 @@ bool set_time_hackwatch_face_loop(movement_event_t event, movement_settings_t *s if (settings->bit.time_zone > 40) settings->bit.time_zone = 0; break; } - if (date_time_settings.unit.day > days_in_month(date_time_settings.unit.month, date_time_settings.unit.year + WATCH_RTC_REFERENCE_YEAR)) date_time_settings.unit.day = 1; - - if (current_page != 2) { // Do not set time when we are at seconds, it was already set previously + if (current_page != 2) // Do not set time when we are at seconds, it was already set previously watch_rtc_set_date_time(date_time_settings); - } - //TODO: Do not update whole RTC, just what we are changing - break; case EVENT_TIMEOUT: movement_move_to_face(0); @@ -237,13 +231,12 @@ bool set_time_hackwatch_face_loop(movement_event_t event, movement_settings_t *s watch_clear_colon(); sprintf(buf, "%s ", set_time_hackwatch_face_titles[current_page]); } else { - int16_t tz = get_timezone_offset(settings->bit.time_zone, date_time_settings); watch_set_colon(); sprintf(buf, "%s %3d%02d ", set_time_hackwatch_face_titles[current_page], - (int8_t)(tz / 60), - (int8_t)(tz % 60) * (tz < 0 ? -1 : 1)); + (int8_t)(movement_timezone_offsets[settings->bit.time_zone] / 60), + (int8_t)(movement_timezone_offsets[settings->bit.time_zone] % 60) * (movement_timezone_offsets[settings->bit.time_zone] < 0 ? -1 : 1)); } } diff --git a/watch-library/hardware/watch/watch_rtc.c b/watch-library/hardware/watch/watch_rtc.c index 67ecb53..93cb9f1 100644 --- a/watch-library/hardware/watch/watch_rtc.c +++ b/watch-library/hardware/watch/watch_rtc.c @@ -30,19 +30,6 @@ ext_irq_cb_t btn_alarm_callback; ext_irq_cb_t a2_callback; ext_irq_cb_t a4_callback; -static bool dst_skip_rolling_back; -bool get_dst_skip_rolling_back(void) { - return dst_skip_rolling_back; -} - -void set_dst_skip_rolling_back(void) { - dst_skip_rolling_back = true; -} - -void clear_dst_skip_rolling_back(void) { - dst_skip_rolling_back = false; -} - bool _watch_rtc_is_enabled(void) { return RTC->MODE2.CTRLA.bit.ENABLE; } @@ -73,7 +60,6 @@ void watch_rtc_set_date_time(watch_date_time date_time) { _sync_rtc(); // Double sync as without it at high Hz faces setting time is unrealiable (specifically, set_time_hackwatch) RTC->MODE2.CLOCK.reg = date_time.reg; _sync_rtc(); - clear_dst_skip_rolling_back(); } watch_date_time watch_rtc_get_date_time(void) { diff --git a/watch-library/shared/watch/watch_rtc.h b/watch-library/shared/watch/watch_rtc.h index a9134eb..3e63bb5 100644 --- a/watch-library/shared/watch/watch_rtc.h +++ b/watch-library/shared/watch/watch_rtc.h @@ -157,11 +157,5 @@ void watch_rtc_enable(bool en); */ void watch_rtc_freqcorr_write(int16_t value, int16_t sign); -/** @brief Returns if we're currently at a point where the we rolled back for DST and need to ignore the next DST segment - */ -bool get_dst_skip_rolling_back(void); -void set_dst_skip_rolling_back(void); -void clear_dst_skip_rolling_back(void); - /// @} #endif diff --git a/watch-library/shared/watch/watch_utility.c b/watch-library/shared/watch/watch_utility.c index a7db658..c00791e 100644 --- a/watch-library/shared/watch/watch_utility.c +++ b/watch-library/shared/watch/watch_utility.c @@ -83,44 +83,6 @@ uint8_t is_leap(uint16_t y) return !(y%4) && ((y%100) || !(y%400)); } -uint8_t get_dst_status(watch_date_time date_time) { - watch_date_time dst_start_time; - watch_date_time dst_end_time; - uint32_t unix_dst_start_time; - uint32_t unix_dst_end_time; - uint32_t unix_curr_time; - - dst_start_time.unit.year = date_time.unit.year; - dst_start_time.unit.month = 3; - dst_start_time.unit.hour = 2; - dst_start_time.unit.minute = 0; - dst_start_time.unit.second = 0; - dst_start_time.unit.day = 15 - watch_utility_get_iso8601_weekday_number(dst_start_time.unit.year + WATCH_RTC_REFERENCE_YEAR, dst_start_time.unit.month, 1); - unix_dst_start_time = watch_utility_date_time_to_unix_time(dst_start_time, 0); - - dst_end_time.unit.year = date_time.unit.year; - dst_end_time.unit.month = 11; - dst_end_time.unit.hour = 2; - dst_end_time.unit.minute = 0; - dst_end_time.unit.second = 0; - dst_end_time.unit.day = 15 - watch_utility_get_iso8601_weekday_number(dst_end_time.unit.year + WATCH_RTC_REFERENCE_YEAR, dst_end_time.unit.month, 1); - unix_dst_end_time = watch_utility_date_time_to_unix_time(dst_end_time, 0); - - unix_curr_time = watch_utility_date_time_to_unix_time(date_time, 0); - unix_curr_time -= date_time.unit.second; - if (date_time.unit.second > 45) // In emu, it's been seen that we may trigger at 59sec rather than exactly 0 each time - unix_curr_time += 60; - - if (unix_curr_time == unix_dst_start_time) return DST_STARTING; - if (unix_curr_time == unix_dst_end_time) return DST_ENDING; - if (unix_curr_time > unix_dst_end_time || unix_curr_time < unix_dst_start_time) return DST_ENDED; - return DST_OCCURRING; -} - -bool dst_occurring(watch_date_time date_time) { - return get_dst_status(date_time) <= DST_OCCURRING; -} - uint16_t watch_utility_days_since_new_year(uint16_t year, uint8_t month, uint8_t day) { uint16_t DAYS_SO_FAR[] = { 0, // Jan diff --git a/watch-library/shared/watch/watch_utility.h b/watch-library/shared/watch/watch_utility.h index 12ee8d2..5533e19 100644 --- a/watch-library/shared/watch/watch_utility.h +++ b/watch-library/shared/watch/watch_utility.h @@ -45,13 +45,6 @@ typedef struct { uint32_t days; // 0-4294967295 } watch_duration_t; -typedef enum { - DST_STARTING, - DST_OCCURRING, - DST_ENDING, - DST_ENDED -} dst_t; - /** @brief Returns a two-letter weekday for the given timestamp, suitable for display * in positions 0-1 of the watch face * @param date_time The watch_date_time whose weekday you want. @@ -85,17 +78,6 @@ uint16_t watch_utility_days_since_new_year(uint16_t year, uint8_t month, uint8_t */ uint8_t is_leap(uint16_t year); -/** @brief Returns off of dst_t based off if DST is occurring, srted, ended, or none of those. - * @param date_time The watch_date_time that you wish to convert. - * @return DST_OCCURRING, DST_HAPPENING, DST_ENDING, DST_ENDED - */ -uint8_t get_dst_status(watch_date_time date_time); - -/** @brief Returns true if it's DST and false otherwise. - * @param date_time The watch_date_time that you wish to convert. - */ -bool dst_occurring(watch_date_time date_time); - /** @brief Returns the UNIX time (seconds since 1970) for a given date/time in UTC. * @param date_time The watch_date_time that you wish to convert. * @param year The year of the date you wish to convert. diff --git a/watch-library/simulator/watch/watch_rtc.c b/watch-library/simulator/watch/watch_rtc.c index 9fe9e29..2bb6074 100644 --- a/watch-library/simulator/watch/watch_rtc.c +++ b/watch-library/simulator/watch/watch_rtc.c @@ -39,19 +39,6 @@ ext_irq_cb_t btn_alarm_callback; ext_irq_cb_t a2_callback; ext_irq_cb_t a4_callback; -static bool dst_skip_rolling_back; -bool get_dst_skip_rolling_back(void) { - return dst_skip_rolling_back; -} - -void set_dst_skip_rolling_back(void) { - dst_skip_rolling_back = true; -} - -void clear_dst_skip_rolling_back(void) { - dst_skip_rolling_back = false; -} - bool _watch_rtc_is_enabled(void) { return true; } @@ -70,7 +57,6 @@ void watch_rtc_set_date_time(watch_date_time date_time) { const date = new Date(year, month - 1, day, hour, minute, second); return date - Date.now(); }, date_time.reg); - clear_dst_skip_rolling_back(); } watch_date_time watch_rtc_get_date_time(void) { From 30267dfc0cec2f06058c9c76e44ac9bf8b549ba4 Mon Sep 17 00:00:00 2001 From: Matheus Afonso Martins Moreira Date: Tue, 17 Sep 2024 17:19:05 -0300 Subject: [PATCH 160/161] Revert PR #470 - fixes world_clock2 face The DST code has not yet been fully tested, the upcoming movement refactor is upon us and it will integrate with the micro timezone library anyway. Revert it so that next can be merged into main. This reverts commit 3c86a42aa83356735be2bd84b7027b156e8d3a2c, reversing changes made to be969c4deb144280b59d0293cc45dc3443b096cf. --- movement/watch_faces/clock/world_clock2_face.c | 14 ++++---------- movement/watch_faces/clock/world_clock_face.c | 1 - 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/movement/watch_faces/clock/world_clock2_face.c b/movement/watch_faces/clock/world_clock2_face.c index 4d50552..5b3f8a9 100644 --- a/movement/watch_faces/clock/world_clock2_face.c +++ b/movement/watch_faces/clock/world_clock2_face.c @@ -238,16 +238,14 @@ static bool mode_display(movement_event_t event, movement_settings_t *settings, break; case EVENT_ALARM_BUTTON_UP: state->current_zone = find_selected_zone(state, FORWARD); - state->tz_curr = get_timezone_offset(state->current_zone, watch_rtc_get_date_time()); - state->previous_date_time = REFRESH_TIME; + state->previous_date_time = REFRESH_TIME; break; case EVENT_LIGHT_BUTTON_DOWN: /* Do nothing. */ break; case EVENT_LIGHT_BUTTON_UP: state->current_zone = find_selected_zone(state, BACKWARD); - state->tz_curr = get_timezone_offset(state->current_zone, watch_rtc_get_date_time()); - state->previous_date_time = REFRESH_TIME; + state->previous_date_time = REFRESH_TIME; break; case EVENT_LIGHT_LONG_PRESS: movement_illuminate_led(); @@ -320,21 +318,17 @@ static bool mode_settings(movement_event_t event, movement_settings_t *settings, break; case EVENT_ALARM_BUTTON_UP: state->current_zone = mod(state->current_zone + FORWARD, NUM_TIME_ZONES); - state->tz_curr = get_timezone_offset(state->current_zone, watch_rtc_get_date_time()); break; case EVENT_LIGHT_BUTTON_UP: state->current_zone = mod(state->current_zone + BACKWARD, NUM_TIME_ZONES); - state->tz_curr = get_timezone_offset(state->current_zone, watch_rtc_get_date_time()); break; case EVENT_LIGHT_BUTTON_DOWN: /* Do nothing */ break; case EVENT_ALARM_LONG_PRESS: /* Find next selected zone */ - if (!state->zones[state->current_zone].selected) { - state->current_zone = find_selected_zone(state, FORWARD); - state->tz_curr = get_timezone_offset(state->current_zone, watch_rtc_get_date_time()); - } + if (!state->zones[state->current_zone].selected) + state->current_zone = find_selected_zone(state, FORWARD); /* Switch to display mode */ state->current_mode = WORLD_CLOCK2_MODE_DISPLAY; diff --git a/movement/watch_faces/clock/world_clock_face.c b/movement/watch_faces/clock/world_clock_face.c index c324b5f..6b731e8 100644 --- a/movement/watch_faces/clock/world_clock_face.c +++ b/movement/watch_faces/clock/world_clock_face.c @@ -162,7 +162,6 @@ static bool _world_clock_face_do_settings_mode(movement_event_t event, movement_ case 3: state->settings.bit.timezone_index++; if (state->settings.bit.timezone_index > 40) state->settings.bit.timezone_index = 0; - state->tz_curr = get_timezone_offset(state->settings.bit.timezone_index, watch_rtc_get_date_time()); break; } break; From fe9a0a693d4a9f721c589dad5d7499c408aea198 Mon Sep 17 00:00:00 2001 From: Matheus Afonso Martins Moreira Date: Tue, 17 Sep 2024 17:09:02 -0300 Subject: [PATCH 161/161] Revert PR #268 - add daylight savings time toggle The DST code has not yet been fully tested, the upcoming movement refactor is upon us and it will integrate with the micro timezone library anyway. Revert it so that next can be merged into main. This reverts commit 5a8a49a8c77d6d5ba0f46f0e5b51dec2daba46db, reversing changes made to bfadb81e82dfe47c4445ce7978d9dbc3b4706808. --- movement/movement.c | 101 ------------------ movement/movement.h | 5 +- movement/watch_faces/settings/set_time_face.c | 23 +--- 3 files changed, 4 insertions(+), 125 deletions(-) diff --git a/movement/movement.c b/movement/movement.c index 2ea43fa..fe6f9d1 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -150,107 +150,6 @@ const int16_t movement_timezone_offsets[] = { -60, // 40 : -1:00:00 (Azores Standard Time) }; -/* These are approximate equivalent DST timezones for each - * timezone in the offset table. Unlike the full tzinfo file, - * the time-offsets used above are incomplete, so there are - * cases below where an approximate DST timezone is proposed - * for a timezone where no one observes DST, and cases - * where we can't propose an equivaent DST timezone since - * there isn't an appropriate one in the offset table. - * - * However, this should be good enough for anyone living in - * a DST-observing region to manually toggle DST without - * having to separately change the hour and timezone info - * in the time set face. - */ -const uint8_t movement_dst_jump_table[] = { - 1, // 0 UTC + 1 = CET - 2, // 1 CET + 1 = SAST - 3, // 2 SAST + 1 = AST - 5, // 3 AST + 1 = GST - 6, // 4 IST + 1 = AT - 7, // 5 GST + 1 = PST - 8, // 6 AT + 1 = IST - 10, // 7 PST + 1 = KT - 11, // 8 IST + 1 = MT - 9, // 9 Nepal has no equivalent DST timezone, but they don't observe DST anyway - 12, // 10 KT + 1 = TST - 11, // 11 Myanmar has no equivalent DST timezone, but they don't observe DST anyway - 13, // 12 TST + 1 = CST - 15, // 13 CST + 1 = JST - 14, // 14 ACWST has no equivalent DST timezone, but they don't observe DST anyway - 17, // 15 JST + 1 = AEST - 18, // 16 ACST + 1 = LHST - 19, // 17 AEST + 1 = SIT - 18, // 18 LHST has no equivalent DST timezone, but they don't observe DST anyway - 20, // 19 SIT + 1 = NZST - 22, // 20 NZST + 1 = TT - 23, // 21 CST + 1 = CDT - 24, // 22 TT + 1 = LIT - 23, // 23 CDT is already a daylight timezone - 24, // 24 LIT has no equivalent DST timezone, but they don't observe DST anyway - 26, // 25 BIT + 1 = NT - 27, // 26 NT + 1 = HAST - 29, // 27 HAST + 1 = AST - 28, // 28 MIT has no equivalent DST timezone, but they don't observe DST anyway - 30, // 29 AST + 1 = PST - 31, // 30 PST + 1 = MST - 32, // 31 MST + 1 = CST - 33, // 32 CST + 1 = EST - 35, // 33 EST + 1 = AST - 36, // 34 VST + 1 = NST - 37, // 35 AST + 1 = BT - 38, // 36 NST + 1 = NDT - 39, // 37 BT + 1 = 39 - 38, // 38 NDT is already a daylight timezone - 40, // 39 FNT + 1 = AST - 0 // 40 AST + 1 = UTC -}; - -const uint8_t movement_dst_inverse_jump_table[] = { - 40, // 0 - 0, // 1 - 1, // 2 - 2, // 3 - 4, // 4 - 3, // 5 - 4, // 6 - 5, // 7 - 6, // 8 - 9, // 9 - 7, // 10 - 8, // 11 - 10, // 12 - 12, // 13 - 14, // 14 - 13, // 15 - 16, // 16 - 15, // 17 - 16, // 18 - 17, // 19 - 19, // 20 - 21, // 21 - 20, // 22 - 21, // 23 - 24, // 24 - 25, // 25 - 25, // 26 - 26, // 27 - 28, // 28 - 27, // 29 - 29, // 30 - 30, // 31 - 31, // 32 - 32, // 33 - 34, // 34 - 33, // 35 - 34, // 36 - 35, // 37 - 36, // 38 - 37, // 39 - 39 // 40 -}; - const char movement_valid_position_0_chars[] = " AaBbCcDdEeFGgHhIiJKLMNnOoPQrSTtUuWXYZ-='+\\/0123456789"; const char movement_valid_position_1_chars[] = " ABCDEFHlJLNORTtUX-='01378"; diff --git a/movement/movement.h b/movement/movement.h index ecf5d8b..6747ff6 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -63,8 +63,7 @@ typedef union { bool clock_24h_leading_zero : 1; // indicates whether clock should leading zero to indicate 24 hour mode. bool use_imperial_units : 1; // indicates whether to use metric units (the default) or imperial. bool alarm_enabled : 1; // indicates whether there is at least one alarm enabled. - bool dst_active : 1; // indicates whether daylight savings time is active - uint8_t reserved : 4; // room for more preferences if needed. + uint8_t reserved : 5; // room for more preferences if needed. } bit; uint32_t reg; } movement_settings_t; @@ -130,8 +129,6 @@ typedef struct { } movement_event_t; extern const int16_t movement_timezone_offsets[]; -extern const uint8_t movement_dst_jump_table[]; -extern const uint8_t movement_dst_inverse_jump_table[]; extern const char movement_valid_position_0_chars[]; extern const char movement_valid_position_1_chars[]; diff --git a/movement/watch_faces/settings/set_time_face.c b/movement/watch_faces/settings/set_time_face.c index d50d24b..605849f 100644 --- a/movement/watch_faces/settings/set_time_face.c +++ b/movement/watch_faces/settings/set_time_face.c @@ -27,8 +27,8 @@ #include "watch.h" #include "watch_utility.h" -#define SET_TIME_FACE_NUM_SETTINGS (8) -const char set_time_face_titles[SET_TIME_FACE_NUM_SETTINGS][3] = {"HR", "M1", "SE", "YR", "MO", "DA", "ZO", "DS"}; +#define SET_TIME_FACE_NUM_SETTINGS (7) +const char set_time_face_titles[SET_TIME_FACE_NUM_SETTINGS][3] = {"HR", "M1", "SE", "YR", "MO", "DA", "ZO"}; static bool _quick_ticks_running; @@ -59,16 +59,6 @@ static void _handle_alarm_button(movement_settings_t *settings, watch_date_time settings->bit.time_zone++; if (settings->bit.time_zone > 40) settings->bit.time_zone = 0; break; - case 7: // daylight savings time - if (settings->bit.dst_active) { // deactivate DST - date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24; - settings->bit.time_zone = movement_dst_inverse_jump_table[settings->bit.time_zone]; - } else { // activate DST - date_time.unit.hour = (date_time.unit.hour + 1) % 24; - settings->bit.time_zone = movement_dst_jump_table[settings->bit.time_zone]; - } - settings->bit.dst_active = !settings->bit.dst_active; - break; } if (date_time.unit.day > days_in_month(date_time.unit.month, date_time.unit.year + WATCH_RTC_REFERENCE_YEAR)) date_time.unit.day = 1; @@ -155,7 +145,7 @@ bool set_time_face_loop(movement_event_t event, movement_settings_t *settings, v watch_clear_indicator(WATCH_INDICATOR_24H); watch_clear_indicator(WATCH_INDICATOR_PM); sprintf(buf, "%s %2d%02d%02d", set_time_face_titles[current_page], date_time.unit.year + 20, date_time.unit.month, date_time.unit.day); - } else if (current_page < 7) { // zone + } else { if (event.subsecond % 2) { watch_clear_colon(); sprintf(buf, "%s ", set_time_face_titles[current_page]); @@ -163,10 +153,6 @@ bool set_time_face_loop(movement_event_t event, movement_settings_t *settings, v watch_set_colon(); sprintf(buf, "%s %3d%02d ", set_time_face_titles[current_page], (int8_t) (movement_timezone_offsets[settings->bit.time_zone] / 60), (int8_t) (movement_timezone_offsets[settings->bit.time_zone] % 60) * (movement_timezone_offsets[settings->bit.time_zone] < 0 ? -1 : 1)); } - } else { // daylight savings - watch_clear_colon(); - if (settings->bit.dst_active) sprintf(buf, "%s dsT y", set_time_face_titles[current_page]); - else sprintf(buf, "%s dsT n", set_time_face_titles[current_page]); } // blink up the parameter we're setting @@ -184,9 +170,6 @@ bool set_time_face_loop(movement_event_t event, movement_settings_t *settings, v case 5: buf[8] = buf[9] = ' '; break; - case 7: - buf[9] = ' '; - break; } }