From: David Volovskiy Date: Tue, 19 Aug 2025 02:10:49 +0000 (-0400) Subject: Moved Simon face X-Git-Url: https://git.earman.xyz/?a=commitdiff_plain;h=4fe6a7bbb183271763c757d4cabf372a38de1bb1;p=sensor-watch.git Moved Simon face --- diff --git a/legacy/watch_faces/complication/simon_face.c b/legacy/watch_faces/complication/simon_face.c deleted file mode 100644 index 41bc5c3..0000000 --- a/legacy/watch_faces/complication/simon_face.c +++ /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 -#include -#include - -// Emulator only: need time() to seed the random number generator -#if __EMSCRIPTEN__ -#include -#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 index 63e8389..0000000 --- a/legacy/watch_faces/complication/simon_face.h +++ /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 index 0000000..41bc5c3 --- /dev/null +++ b/watch-faces/complication/simon_face.c @@ -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 +#include +#include + +// Emulator only: need time() to seed the random number generator +#if __EMSCRIPTEN__ +#include +#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 index 0000000..63e8389 --- /dev/null +++ b/watch-faces/complication/simon_face.h @@ -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_