]> git.earman.xyz Git - sensor-watch.git/commitdiff
Gave the wordle face the ability to give random guesses on the first word
authorDavid Volovskiy <devolov@gmail.com>
Sat, 17 Aug 2024 15:36:35 +0000 (11:36 -0400)
committerDavid Volovskiy <devolov@gmail.com>
Tue, 3 Sep 2024 20:08:12 +0000 (16:08 -0400)
movement/movement_config.h
movement/watch_faces/complication/wordle_face.c
movement/watch_faces/complication/wordle_face.h
utils/wordle_face/wordle_list.py

index abceacf1c07c3419dde2ae2863e71c6cdd9a4e5f..f8a1958f73ec7fda14a8063565a8ffc68217ba1d 100644 (file)
@@ -29,6 +29,7 @@
 
 const watch_face_t watch_faces[] = {
     simple_clock_face,
+    wordle_face,
     world_clock_face,
     sunrise_sunset_face,
     moon_phase_face,
index c6f28bb4895b342331250c3e882843ce2531bd61..32c396bec9ba4615d5c014c8d0adf033cf2c705a 100644 (file)
@@ -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;
index aee40f4a708c2057a9a69c40b0bd8cf14d37062c..64a1665ce5130e9f2f1a82889cbfedfab1a25e73 100644 (file)
  *        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
index 87f2f821696c940ad625f71a7810d48d373f742d..38e951043f3d6801b2027574e79adf4c5ca5b307 100644 (file)
@@ -982,6 +982,11 @@ def list_of_valid_words(letters, words=legal_list):
     return legal_words\r
 \r
 \r
+def rearrange_words_by_uniqueness(words):\r
+    unique = [word for word in words if len(word) == len(set(word))]\r
+    duplicates = [word for word in words if len(word) != len(set(word))]\r
+    return unique + duplicates, len(unique)\r
+\r
 def capitalize_all_and_remove_duplicates(arr):\r
     for i,word in enumerate(arr):\r
         arr[i] = word.upper()\r
@@ -1001,6 +1006,7 @@ def print_valid_words(letters=alphabet):
     legal_words = capitalize_all_and_remove_duplicates(legal_words)\r
     random.shuffle(legal_words) \r
     # 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\r
+    legal_words, num_uniq = rearrange_words_by_uniqueness(legal_words)\r
             \r
     print("static const char _valid_letters[] = {", end='')\r
     for letter in letters[:-1]:\r
@@ -1035,6 +1041,8 @@ def print_valid_words(letters=alphabet):
             i+=1\r
         print('')\r
     print("};")\r
+    \r
+    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.")\r
 \r
 \r
 def get_sec_val_and_units(seconds):\r