+++ /dev/null
-/*
- * 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();
-}
+++ /dev/null
-/*
- * 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_
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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_