]> git.earman.xyz Git - sensor-watch.git/commitdiff
Moved Simon face
authorDavid Volovskiy <devolov@gmail.com>
Tue, 19 Aug 2025 02:10:49 +0000 (22:10 -0400)
committerDavid Volovskiy <devolov@gmail.com>
Tue, 19 Aug 2025 02:10:49 +0000 (22:10 -0400)
legacy/watch_faces/complication/simon_face.c [deleted file]
legacy/watch_faces/complication/simon_face.h [deleted file]
watch-faces/complication/simon_face.c [new file with mode: 0644]
watch-faces/complication/simon_face.h [new file with mode: 0644]

diff --git a/legacy/watch_faces/complication/simon_face.c b/legacy/watch_faces/complication/simon_face.c
deleted file mode 100644 (file)
index 41bc5c3..0000000
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-// Emulator only: need time() to seed the random number generator
-#if __EMSCRIPTEN__
-#include <time.h>
-#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__
-    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);
-    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) {
-    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:
-            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:
-            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:
-            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:
-            if (state->soundOff)
-                delay_ms(800);
-            else
-               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, (_delay_beep * 2)/3);
-        }
-    }
-}
-
-
-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++;
-        _timer = 0;
-
-        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;
-}
-
-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(uint8_t watch_face_index,
-        void **context_ptr) {
-    (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(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,
-        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_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
-                _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);
-                }
-            }
-            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);
-            }
-            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);
-            }
-            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);
-            break;
-        case EVENT_LOW_ENERGY_UPDATE:
-            break;
-        default:
-            return movement_default_loop_handler(event);
-    }
-
-    return true;
-}
-
-void simon_face_resign(void *context) {
-    (void)context;
-    watch_set_led_off();
-    watch_set_buzzer_off();
-}
diff --git a/legacy/watch_faces/complication/simon_face.h b/legacy/watch_faces/complication/simon_face.h
deleted file mode 100644 (file)
index 63e8389..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * 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 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;
-
-void simon_face_setup(uint8_t watch_face_index, void **context_ptr);
-void simon_face_activate(void *context);
-bool simon_face_loop(movement_event_t event, void *context);
-void simon_face_resign(void *context);
-
-#define simon_face                                                             \
-    ((const watch_face_t){                                                       \
-     simon_face_setup,                                                        \
-     simon_face_activate,                                                     \
-     simon_face_loop,                                                         \
-     simon_face_resign,                                                       \
-     NULL,                                                                    \
-     })
-
-#define TIMER_MAX 5
-#define SIMON_FACE_FREQUENCY 8
-#define DELAY_FOR_TONE_MS 300
-
-#endif // SIMON_FACE_H_
diff --git a/watch-faces/complication/simon_face.c b/watch-faces/complication/simon_face.c
new file mode 100644 (file)
index 0000000..41bc5c3
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// Emulator only: need time() to seed the random number generator
+#if __EMSCRIPTEN__
+#include <time.h>
+#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__
+    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);
+    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) {
+    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:
+            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:
+            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:
+            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:
+            if (state->soundOff)
+                delay_ms(800);
+            else
+               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, (_delay_beep * 2)/3);
+        }
+    }
+}
+
+
+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++;
+        _timer = 0;
+
+        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;
+}
+
+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(uint8_t watch_face_index,
+        void **context_ptr) {
+    (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(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,
+        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_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
+                _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);
+                }
+            }
+            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);
+            }
+            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);
+            }
+            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);
+            break;
+        case EVENT_LOW_ENERGY_UPDATE:
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    return true;
+}
+
+void simon_face_resign(void *context) {
+    (void)context;
+    watch_set_led_off();
+    watch_set_buzzer_off();
+}
diff --git a/watch-faces/complication/simon_face.h b/watch-faces/complication/simon_face.h
new file mode 100644 (file)
index 0000000..63e8389
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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 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;
+
+void simon_face_setup(uint8_t watch_face_index, void **context_ptr);
+void simon_face_activate(void *context);
+bool simon_face_loop(movement_event_t event, void *context);
+void simon_face_resign(void *context);
+
+#define simon_face                                                             \
+    ((const watch_face_t){                                                       \
+     simon_face_setup,                                                        \
+     simon_face_activate,                                                     \
+     simon_face_loop,                                                         \
+     simon_face_resign,                                                       \
+     NULL,                                                                    \
+     })
+
+#define TIMER_MAX 5
+#define SIMON_FACE_FREQUENCY 8
+#define DELAY_FOR_TONE_MS 300
+
+#endif // SIMON_FACE_H_