From d0ce60111eb74e0965b1ae3dbf6bee7308778205 Mon Sep 17 00:00:00 2001 From: Eli Dowling Date: Fri, 8 Aug 2025 22:23:33 +1000 Subject: [PATCH 01/25] use 0 contrast for custom lcd this greatly improves off axis viewing, for an extremely slightly reduction in actual contrast when viewed on axis. --- watch-library/hardware/watch/watch_slcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/watch-library/hardware/watch/watch_slcd.c b/watch-library/hardware/watch/watch_slcd.c index db6b1942..4ba2421b 100644 --- a/watch-library/hardware/watch/watch_slcd.c +++ b/watch-library/hardware/watch/watch_slcd.c @@ -252,7 +252,7 @@ void watch_enable_display(void) { slcd_clear(); if (_installed_display == WATCH_LCD_TYPE_CUSTOM) { - slcd_set_contrast(4); + slcd_set_contrast(0); } else { slcd_set_contrast(9); } From 5ecc3d6384b5143be7a19b920be0eabe719a076f Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sun, 24 Aug 2025 08:28:50 -0400 Subject: [PATCH 02/25] increase delay before checking VBUS_DET --- movement.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/movement.c b/movement.c index 0046a489..ca0b0f36 100644 --- a/movement.c +++ b/movement.c @@ -610,7 +610,7 @@ void app_init(void) { // check if we are plugged into USB power. HAL_GPIO_VBUS_DET_in(); HAL_GPIO_VBUS_DET_pulldown(); - delay_ms(10); + delay_ms(100); if (HAL_GPIO_VBUS_DET_read()){ /// if so, enable USB functionality. _watch_enable_usb(); From 838aa1e6f701e2d8b832f64c083261aae749a368 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Tue, 2 Sep 2025 21:34:29 -0400 Subject: [PATCH 03/25] initial commit of working code --- movement_faces.h | 1 + watch-faces.mk | 1 + watch-faces/complication/blackjack_face.c | 326 ++++++++++++++++++++++ watch-faces/complication/blackjack_face.h | 55 ++++ 4 files changed, 383 insertions(+) create mode 100755 watch-faces/complication/blackjack_face.c create mode 100755 watch-faces/complication/blackjack_face.h diff --git a/movement_faces.h b/movement_faces.h index fc43716d..fab9ee31 100644 --- a/movement_faces.h +++ b/movement_faces.h @@ -73,4 +73,5 @@ #include "wareki_face.h" #include "deadline_face.h" #include "wordle_face.h" +#include "blackjack_face.h" // New includes go above this line. diff --git a/watch-faces.mk b/watch-faces.mk index 22e262cf..52919f53 100644 --- a/watch-faces.mk +++ b/watch-faces.mk @@ -48,4 +48,5 @@ SRCS += \ ./watch-faces/sensor/lis2dw_monitor_face.c \ ./watch-faces/complication/wareki_face.c \ ./watch-faces/complication/deadline_face.c \ + ./watch-faces/complication/blackjack_face.c \ # New watch faces go above this line. diff --git a/watch-faces/complication/blackjack_face.c b/watch-faces/complication/blackjack_face.c new file mode 100755 index 00000000..ee3d274a --- /dev/null +++ b/watch-faces/complication/blackjack_face.c @@ -0,0 +1,326 @@ +/* + * MIT License + * + * Copyright (c) 2025 David Volovskiy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * 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. + */ + +// Emulator only: need time() to seed the random number generator. +#if __EMSCRIPTEN__ +#include +#endif + +#include +#include +#include "blackjack_face.h" +#include "watch_common_display.h" + +#define KING 12 +#define QUEEN 11 +#define JACK 10 + +#define MIN_CARD_VALUE 1 +#define MAX_CARD_VALUE KING +#define CARD_RANK_COUNT (MAX_CARD_VALUE - MIN_CARD_VALUE + 1) +#define CARD_SUIT_COUNT 4 +#define DECK_SIZE (CARD_SUIT_COUNT * CARD_RANK_COUNT) + +#define BLACKJACK_MAX_HAND_SIZE 11 // 4*1 + 4*2 + 3*3 = 21; 11 cards total +#define MAX_PLAYER_CARDS_DISPLAY 4 +#define BOARD_DISPLAY_START 4 + +uint8_t score_player = 0; +uint8_t score_dealer = 0; +uint8_t hand_player[BLACKJACK_MAX_HAND_SIZE] = {0xFF}; +uint8_t hand_dealer[BLACKJACK_MAX_HAND_SIZE] = {0xFF}; +uint8_t idx_player = 0; +uint8_t idx_dealer = 0; + +typedef enum { + BJ_HIT, + BJ_STAND, + BJ_BUST, +} guess_t; + +typedef enum { + BJ_TITLE_SCREEN, + BJ_PLAYING, + BJ_DEALER_PLAYING, + BJ_RESULT, +} game_state_t; + +static game_state_t game_state = BJ_TITLE_SCREEN; +static uint8_t deck[DECK_SIZE] = {0}; +static uint8_t current_card = 0; + +static uint8_t generate_random_number(uint8_t num_values) { + // Emulator: use rand. Hardware: use arc4random. +#if __EMSCRIPTEN__ + return rand() % num_values; +#else + return arc4random_uniform(num_values); +#endif +} + +static void stack_deck(void) { + for (size_t i = 0; i < CARD_RANK_COUNT; i++) { + for (size_t j = 0; j < CARD_SUIT_COUNT; j++) + deck[(i * CARD_SUIT_COUNT) + j] = MIN_CARD_VALUE + i; + } +} + +static void shuffle_deck(void) { + // Randomize shuffle with Fisher Yates + size_t i; + size_t j; + uint8_t tmp; + + for (i = DECK_SIZE - 1; i > 0; i--) { + j = generate_random_number(0xFF) % (i + 1); + tmp = deck[j]; + deck[j] = deck[i]; + deck[i] = tmp; + } +} + +static void reset_deck(void) { + current_card = 0; + stack_deck(); + shuffle_deck(); +} + +static uint8_t get_next_card(void) { + if (current_card >= DECK_SIZE) + reset_deck(); + uint8_t card = deck[current_card++]; + if (card > 10) return 10; + return card; +} + +static void reset_hands(void) { + score_player = 0; + score_dealer = 0; + idx_player = 0; + idx_dealer = 0; + memset(hand_player, 0xFF, sizeof(hand_player)); + memset(hand_dealer, 0xFF, sizeof(hand_dealer)); + reset_deck(); +} + +static void give_player_card(void) { + uint8_t card = get_next_card(); + hand_player[idx_player++] = card; + score_player += card; +} + +static void give_dealer_card(void) { + uint8_t card = get_next_card(); + hand_dealer[idx_dealer++] = card; + score_dealer += card; +} + +static void display_player_hand(void) { + uint8_t cards_to_display = idx_player > MAX_PLAYER_CARDS_DISPLAY ? MAX_PLAYER_CARDS_DISPLAY : idx_player; + for (uint8_t i=0; i 21) { + display_bust(); + } else { + display_player_hand(); + display_player_score(); + } +} + +static void perform_stand(void) { + game_state = BJ_DEALER_PLAYING; + watch_display_text(WATCH_POSITION_BOTTOM, "Stnd"); + display_player_score(); +} + +static void dealer_performs_hits(void) { + give_dealer_card(); + display_dealer_hand(); + if (score_dealer > 21) { + display_win(); + } else if (score_dealer > score_player) { + display_lose(); + } else { + display_dealer_hand(); + display_dealer_score(); + } +} + +static void see_if_dealer_hits(void) { + if (score_dealer > 16) { + if (score_dealer > score_player) { + display_lose(); + } else if (score_dealer < score_player) { + display_win(); + } else { + display_tie(); + } + } else { + dealer_performs_hits(); + } +} + +void handle_button_presses(bool hit) { + switch (game_state) + { + case BJ_TITLE_SCREEN: + begin_playing(); + break; + case BJ_PLAYING: + if (hit) { + perform_hit(); + } else { + perform_stand(); + } + break; + case BJ_DEALER_PLAYING: + see_if_dealer_hits(); + break; + case BJ_RESULT: + display_title(); + break; + } +} + +void blackjack_face_setup(uint8_t watch_face_index, void **context_ptr) { + (void) watch_face_index; + + if (*context_ptr == NULL) { + *context_ptr = malloc(sizeof(blackjack_face_state_t)); + memset(*context_ptr, 0, sizeof(blackjack_face_state_t)); + } +} + +void blackjack_face_activate(void *context) { + blackjack_face_state_t *state = (blackjack_face_state_t *) context; + (void) state; + display_title(); +} + +bool blackjack_face_loop(movement_event_t event, void *context) { + blackjack_face_state_t *state = (blackjack_face_state_t *) context; + (void) state; + + switch (event.event_type) { + case EVENT_ACTIVATE: + break; + case EVENT_TICK: + if (game_state == BJ_DEALER_PLAYING) { + see_if_dealer_hits(); + } + break; + case EVENT_LIGHT_BUTTON_UP: + handle_button_presses(false); + case EVENT_LIGHT_BUTTON_DOWN: + break; + case EVENT_ALARM_BUTTON_UP: + handle_button_presses(true); + break; + case EVENT_TIMEOUT: + break; + default: + return movement_default_loop_handler(event); + } + return true; +} + +void blackjack_face_resign(void *context) { + (void) context; +} diff --git a/watch-faces/complication/blackjack_face.h b/watch-faces/complication/blackjack_face.h new file mode 100755 index 00000000..36eb4425 --- /dev/null +++ b/watch-faces/complication/blackjack_face.h @@ -0,0 +1,55 @@ +/* + * MIT License + * + * Copyright (c) 2023 Chris Ellis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BLACKJACK_FACE_H_ +#define BLACKJACK_FACE_H_ + +#include "movement.h" + +/* + * Blackjack face + * ====================== + * + */ + + + +typedef struct { + // Anything you need to keep track of, put it here! +} blackjack_face_state_t; + +void blackjack_face_setup(uint8_t watch_face_index, void ** context_ptr); +void blackjack_face_activate(void *context); +bool blackjack_face_loop(movement_event_t event, void *context); +void blackjack_face_resign(void *context); + +#define blackjack_face ((const watch_face_t){ \ + blackjack_face_setup, \ + blackjack_face_activate, \ + blackjack_face_loop, \ + blackjack_face_resign, \ + NULL, \ +}) + +#endif // blackjack_FACE_H_ From 3760aeb947306de9cf70d8b74b25db3d53b43c4c Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Tue, 2 Sep 2025 21:57:42 -0400 Subject: [PATCH 04/25] Changed order of how cards are displayed for player --- watch-faces/complication/blackjack_face.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/watch-faces/complication/blackjack_face.c b/watch-faces/complication/blackjack_face.c index ee3d274a..b0f78b09 100755 --- a/watch-faces/complication/blackjack_face.c +++ b/watch-faces/complication/blackjack_face.c @@ -53,12 +53,6 @@ uint8_t hand_dealer[BLACKJACK_MAX_HAND_SIZE] = {0xFF}; uint8_t idx_player = 0; uint8_t idx_dealer = 0; -typedef enum { - BJ_HIT, - BJ_STAND, - BJ_BUST, -} guess_t; - typedef enum { BJ_TITLE_SCREEN, BJ_PLAYING, @@ -66,7 +60,7 @@ typedef enum { BJ_RESULT, } game_state_t; -static game_state_t game_state = BJ_TITLE_SCREEN; +static game_state_t game_state; static uint8_t deck[DECK_SIZE] = {0}; static uint8_t current_card = 0; @@ -138,11 +132,11 @@ static void give_dealer_card(void) { static void display_player_hand(void) { uint8_t cards_to_display = idx_player > MAX_PLAYER_CARDS_DISPLAY ? MAX_PLAYER_CARDS_DISPLAY : idx_player; - for (uint8_t i=0; i0; i--) { + uint8_t card = hand_player[idx_player-i]; if (card == 10) card = 0; const char display_char = card + '0'; - watch_display_character(display_char, BOARD_DISPLAY_START + i); + watch_display_character(display_char, BOARD_DISPLAY_START + cards_to_display - i); } } @@ -167,7 +161,7 @@ static void display_dealer_score(void) { static void display_win(void) { game_state = BJ_RESULT; - watch_display_text(WATCH_POSITION_BOTTOM, " WIN"); + watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "WlN ", " WIN"); display_player_score(); display_dealer_score(); } @@ -181,13 +175,13 @@ static void display_lose(void) { static void display_tie(void) { game_state = BJ_RESULT; - watch_display_text(WATCH_POSITION_BOTTOM, " TIE"); + watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "TlE ", " TIE"); display_player_score(); } static void display_bust(void) { game_state = BJ_RESULT; - watch_display_text(WATCH_POSITION_BOTTOM, "BUST"); + watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "8UST ", " BUST"); display_player_score(); } From 8a831f5cfded756822c51a25870eac12746fd72a Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Tue, 2 Sep 2025 22:15:06 -0400 Subject: [PATCH 05/25] Added tap controls --- watch-faces/complication/blackjack_face.c | 28 +++++++++++++++++++++-- watch-faces/complication/blackjack_face.h | 2 +- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/watch-faces/complication/blackjack_face.c b/watch-faces/complication/blackjack_face.c index b0f78b09..9b0de5a5 100755 --- a/watch-faces/complication/blackjack_face.c +++ b/watch-faces/complication/blackjack_face.c @@ -273,12 +273,28 @@ void handle_button_presses(bool hit) { } } +static void toggle_tap_control(blackjack_face_state_t *state) { + if (state->tap_control_on) { + movement_disable_tap_detection_if_available(); + state->tap_control_on = false; + watch_clear_indicator(WATCH_INDICATOR_SIGNAL); + } else { + bool tap_could_enable = movement_enable_tap_detection_if_available(); + if (tap_could_enable) { + state->tap_control_on = true; + watch_set_indicator(WATCH_INDICATOR_SIGNAL); + } + } +} + void blackjack_face_setup(uint8_t watch_face_index, void **context_ptr) { (void) watch_face_index; if (*context_ptr == NULL) { *context_ptr = malloc(sizeof(blackjack_face_state_t)); memset(*context_ptr, 0, sizeof(blackjack_face_state_t)); + blackjack_face_state_t *state = (blackjack_face_state_t *)*context_ptr; + state->tap_control_on = false; } } @@ -290,10 +306,11 @@ void blackjack_face_activate(void *context) { bool blackjack_face_loop(movement_event_t event, void *context) { blackjack_face_state_t *state = (blackjack_face_state_t *) context; - (void) state; - switch (event.event_type) { case EVENT_ACTIVATE: + if (state->tap_control_on) { + movement_enable_tap_detection_if_available(); + } break; case EVENT_TICK: if (game_state == BJ_DEALER_PLAYING) { @@ -301,12 +318,19 @@ bool blackjack_face_loop(movement_event_t event, void *context) { } break; case EVENT_LIGHT_BUTTON_UP: + case EVENT_DOUBLE_TAP: handle_button_presses(false); case EVENT_LIGHT_BUTTON_DOWN: break; case EVENT_ALARM_BUTTON_UP: + case EVENT_SINGLE_TAP: handle_button_presses(true); break; + case EVENT_ALARM_LONG_PRESS: + if (game_state == BJ_TITLE_SCREEN) { + toggle_tap_control(state); + } + break; case EVENT_TIMEOUT: break; default: diff --git a/watch-faces/complication/blackjack_face.h b/watch-faces/complication/blackjack_face.h index 36eb4425..ff52d79f 100755 --- a/watch-faces/complication/blackjack_face.h +++ b/watch-faces/complication/blackjack_face.h @@ -36,7 +36,7 @@ typedef struct { - // Anything you need to keep track of, put it here! + bool tap_control_on; } blackjack_face_state_t; void blackjack_face_setup(uint8_t watch_face_index, void ** context_ptr); From 3b3ecffd3e55064a81fde9a71675d3e187756e2d Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Tue, 2 Sep 2025 22:23:36 -0400 Subject: [PATCH 06/25] Fixed a few warnings. --- watch-faces/complication/blackjack_face.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/watch-faces/complication/blackjack_face.c b/watch-faces/complication/blackjack_face.c index 9b0de5a5..5234d02a 100755 --- a/watch-faces/complication/blackjack_face.c +++ b/watch-faces/complication/blackjack_face.c @@ -148,13 +148,13 @@ static void display_dealer_hand(void) { } static void display_player_score(void) { - char buf[3]; + char buf[4]; sprintf(buf, "%2d", score_player); watch_display_text(WATCH_POSITION_SECONDS, buf); } static void display_dealer_score(void) { - char buf[3]; + char buf[4]; sprintf(buf, "%2d", score_dealer); watch_display_text(WATCH_POSITION_TOP_RIGHT, buf); } @@ -251,7 +251,7 @@ static void see_if_dealer_hits(void) { } } -void handle_button_presses(bool hit) { +static void handle_button_presses(bool hit) { switch (game_state) { case BJ_TITLE_SCREEN: From b66e10406e44152c38307cf1d94da4389db0a80b Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Wed, 3 Sep 2025 06:45:14 -0400 Subject: [PATCH 07/25] Made hand info into a struct --- watch-faces/complication/blackjack_face.c | 53 +++++++++++------------ 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/watch-faces/complication/blackjack_face.c b/watch-faces/complication/blackjack_face.c index 5234d02a..c403579a 100755 --- a/watch-faces/complication/blackjack_face.c +++ b/watch-faces/complication/blackjack_face.c @@ -46,12 +46,11 @@ #define MAX_PLAYER_CARDS_DISPLAY 4 #define BOARD_DISPLAY_START 4 -uint8_t score_player = 0; -uint8_t score_dealer = 0; -uint8_t hand_player[BLACKJACK_MAX_HAND_SIZE] = {0xFF}; -uint8_t hand_dealer[BLACKJACK_MAX_HAND_SIZE] = {0xFF}; -uint8_t idx_player = 0; -uint8_t idx_dealer = 0; +typedef struct { + uint8_t score; + uint8_t idx_hand; + uint8_t hand[BLACKJACK_MAX_HAND_SIZE]; +} hand_info_t; typedef enum { BJ_TITLE_SCREEN, @@ -63,6 +62,8 @@ typedef enum { static game_state_t game_state; static uint8_t deck[DECK_SIZE] = {0}; static uint8_t current_card = 0; +hand_info_t player; +hand_info_t dealer; static uint8_t generate_random_number(uint8_t num_values) { // Emulator: use rand. Hardware: use arc4random. @@ -109,31 +110,27 @@ static uint8_t get_next_card(void) { } static void reset_hands(void) { - score_player = 0; - score_dealer = 0; - idx_player = 0; - idx_dealer = 0; - memset(hand_player, 0xFF, sizeof(hand_player)); - memset(hand_dealer, 0xFF, sizeof(hand_dealer)); + memset(&player, 0, sizeof(player)); + memset(&dealer, 0, sizeof(dealer)); reset_deck(); } static void give_player_card(void) { uint8_t card = get_next_card(); - hand_player[idx_player++] = card; - score_player += card; + player.hand[player.idx_hand++] = card; + player.score += card; } static void give_dealer_card(void) { uint8_t card = get_next_card(); - hand_dealer[idx_dealer++] = card; - score_dealer += card; + dealer.hand[dealer.idx_hand++] = card; + dealer.score += card; } static void display_player_hand(void) { - uint8_t cards_to_display = idx_player > MAX_PLAYER_CARDS_DISPLAY ? MAX_PLAYER_CARDS_DISPLAY : idx_player; + uint8_t cards_to_display = player.idx_hand > MAX_PLAYER_CARDS_DISPLAY ? MAX_PLAYER_CARDS_DISPLAY : player.idx_hand; for (uint8_t i=cards_to_display; i>0; i--) { - uint8_t card = hand_player[idx_player-i]; + uint8_t card = player.hand[player.idx_hand-i]; if (card == 10) card = 0; const char display_char = card + '0'; watch_display_character(display_char, BOARD_DISPLAY_START + cards_to_display - i); @@ -141,7 +138,7 @@ static void display_player_hand(void) { } static void display_dealer_hand(void) { - uint8_t card = hand_dealer[idx_dealer - 1]; + uint8_t card = dealer.hand[dealer.idx_hand - 1]; if (card == 10) card = 0; const char display_char = card + '0'; watch_display_character(display_char, 0); @@ -149,13 +146,13 @@ static void display_dealer_hand(void) { static void display_player_score(void) { char buf[4]; - sprintf(buf, "%2d", score_player); + sprintf(buf, "%2d", player.score); watch_display_text(WATCH_POSITION_SECONDS, buf); } static void display_dealer_score(void) { char buf[4]; - sprintf(buf, "%2d", score_dealer); + sprintf(buf, "%2d", dealer.score); watch_display_text(WATCH_POSITION_TOP_RIGHT, buf); } @@ -206,11 +203,11 @@ static void begin_playing(void) { } static void perform_hit(void) { - if (score_player == 21) { + if (player.score == 21) { return; // Assume hitting on 21 is a mistake and ignore } give_player_card(); - if (score_player > 21) { + if (player.score > 21) { display_bust(); } else { display_player_hand(); @@ -227,9 +224,9 @@ static void perform_stand(void) { static void dealer_performs_hits(void) { give_dealer_card(); display_dealer_hand(); - if (score_dealer > 21) { + if (dealer.score > 21) { display_win(); - } else if (score_dealer > score_player) { + } else if (dealer.score > player.score) { display_lose(); } else { display_dealer_hand(); @@ -238,10 +235,10 @@ static void dealer_performs_hits(void) { } static void see_if_dealer_hits(void) { - if (score_dealer > 16) { - if (score_dealer > score_player) { + if (dealer.score > 16) { + if (dealer.score > player.score) { display_lose(); - } else if (score_dealer < score_player) { + } else if (dealer.score < player.score) { display_win(); } else { display_tie(); From 1d40c384cb63bd81e7f950a05e8f2f6514c8e42a Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Wed, 3 Sep 2025 07:07:51 -0400 Subject: [PATCH 08/25] Added logic for supporting Ace --- watch-faces/complication/blackjack_face.c | 55 +++++++++++++++-------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/watch-faces/complication/blackjack_face.c b/watch-faces/complication/blackjack_face.c index c403579a..beb14446 100755 --- a/watch-faces/complication/blackjack_face.c +++ b/watch-faces/complication/blackjack_face.c @@ -32,12 +32,13 @@ #include "blackjack_face.h" #include "watch_common_display.h" +#define ACE 13 #define KING 12 #define QUEEN 11 #define JACK 10 -#define MIN_CARD_VALUE 1 -#define MAX_CARD_VALUE KING +#define MIN_CARD_VALUE 2 +#define MAX_CARD_VALUE ACE #define CARD_RANK_COUNT (MAX_CARD_VALUE - MIN_CARD_VALUE + 1) #define CARD_SUIT_COUNT 4 #define DECK_SIZE (CARD_SUIT_COUNT * CARD_RANK_COUNT) @@ -49,6 +50,7 @@ typedef struct { uint8_t score; uint8_t idx_hand; + int8_t high_aces_in_hand; uint8_t hand[BLACKJACK_MAX_HAND_SIZE]; } hand_info_t; @@ -101,30 +103,45 @@ static void reset_deck(void) { shuffle_deck(); } -static uint8_t get_next_card(void) { +static uint8_t get_next_card(hand_info_t *hand_info) { if (current_card >= DECK_SIZE) reset_deck(); uint8_t card = deck[current_card++]; - if (card > 10) return 10; + switch (card) + { + case ACE: + card = 11; + hand_info->high_aces_in_hand++; + break; + case KING: + case QUEEN: + case JACK: + card = 10; + + default: + break; + } return card; } +static void modify_score_from_aces(hand_info_t *hand_info) { + while (hand_info->score > 21 && hand_info->high_aces_in_hand > 0) { + hand_info->score -= 10; + hand_info->high_aces_in_hand--; + } +} + static void reset_hands(void) { memset(&player, 0, sizeof(player)); memset(&dealer, 0, sizeof(dealer)); reset_deck(); } -static void give_player_card(void) { - uint8_t card = get_next_card(); - player.hand[player.idx_hand++] = card; - player.score += card; -} - -static void give_dealer_card(void) { - uint8_t card = get_next_card(); - dealer.hand[dealer.idx_hand++] = card; - dealer.score += card; +static void give_card(hand_info_t *hand_info) { + uint8_t card = get_next_card(hand_info); + hand_info->hand[hand_info->idx_hand++] = card; + hand_info->score += card; + modify_score_from_aces(hand_info); } static void display_player_hand(void) { @@ -193,11 +210,11 @@ static void begin_playing(void) { game_state = BJ_PLAYING; reset_hands(); // Give player their first 2 cards - give_player_card(); - give_player_card(); + give_card(&player); + give_card(&player); display_player_hand(); display_player_score(); - give_dealer_card(); + give_card(&dealer); display_dealer_hand(); display_dealer_score(); } @@ -206,7 +223,7 @@ static void perform_hit(void) { if (player.score == 21) { return; // Assume hitting on 21 is a mistake and ignore } - give_player_card(); + give_card(&player); if (player.score > 21) { display_bust(); } else { @@ -222,7 +239,7 @@ static void perform_stand(void) { } static void dealer_performs_hits(void) { - give_dealer_card(); + give_card(&dealer); display_dealer_hand(); if (dealer.score > 21) { display_win(); From 9ac62e8e53a39aab692212396fd2df9663158c44 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Wed, 3 Sep 2025 07:28:38 -0400 Subject: [PATCH 09/25] Display of face cards and Ace --- watch-faces/complication/blackjack_face.c | 102 +++++++++++++++------- 1 file changed, 69 insertions(+), 33 deletions(-) diff --git a/watch-faces/complication/blackjack_face.c b/watch-faces/complication/blackjack_face.c index beb14446..ee490426 100755 --- a/watch-faces/complication/blackjack_face.c +++ b/watch-faces/complication/blackjack_face.c @@ -61,6 +61,10 @@ typedef enum { BJ_RESULT, } game_state_t; +typedef enum { + A, B, C, D, E, F, G +} segment_t; + static game_state_t game_state; static uint8_t deck[DECK_SIZE] = {0}; static uint8_t current_card = 0; @@ -106,22 +110,23 @@ static void reset_deck(void) { static uint8_t get_next_card(hand_info_t *hand_info) { if (current_card >= DECK_SIZE) reset_deck(); - uint8_t card = deck[current_card++]; + return deck[current_card++]; +} + +static uint8_t get_card_value(uint8_t card) { + uint8_t card_value; switch (card) { case ACE: - card = 11; - hand_info->high_aces_in_hand++; + return 11; break; case KING: case QUEEN: case JACK: card = 10; - default: - break; + return card; } - return card; } static void modify_score_from_aces(hand_info_t *hand_info) { @@ -139,64 +144,95 @@ static void reset_hands(void) { static void give_card(hand_info_t *hand_info) { uint8_t card = get_next_card(hand_info); + if (card == ACE) hand_info->high_aces_in_hand++; hand_info->hand[hand_info->idx_hand++] = card; - hand_info->score += card; + uint8_t card_value = get_card_value(card); + hand_info->score += card_value; modify_score_from_aces(hand_info); } +static void set_segment_at_position(segment_t segment, uint8_t position) { + digit_mapping_t segmap; + if (watch_get_lcd_type() == WATCH_LCD_TYPE_CUSTOM) { + segmap = Custom_LCD_Display_Mapping[position]; + } else { + segmap = Classic_LCD_Display_Mapping[position]; + } + const uint8_t com_pin = segmap.segment[segment].address.com; + const uint8_t seg = segmap.segment[segment].address.seg; + watch_set_pixel(com_pin, seg); +} + +static void display_card_at_position(uint8_t card, uint8_t display_position) { + switch (card) { + case KING: + watch_display_character(' ', display_position); + set_segment_at_position(A, display_position); + set_segment_at_position(D, display_position); + set_segment_at_position(G, display_position); + break; + case QUEEN: + watch_display_character(' ', display_position); + set_segment_at_position(A, display_position); + set_segment_at_position(D, display_position); + break; + case JACK: + watch_display_character('-', display_position); + break; + case ACE: + watch_display_character('A', display_position); + break; + default: { + const char display_char = card + '0'; + watch_display_character(display_char, display_position); + break; + } + } +} + static void display_player_hand(void) { uint8_t cards_to_display = player.idx_hand > MAX_PLAYER_CARDS_DISPLAY ? MAX_PLAYER_CARDS_DISPLAY : player.idx_hand; for (uint8_t i=cards_to_display; i>0; i--) { uint8_t card = player.hand[player.idx_hand-i]; - if (card == 10) card = 0; - const char display_char = card + '0'; - watch_display_character(display_char, BOARD_DISPLAY_START + cards_to_display - i); + display_card_at_position(card, BOARD_DISPLAY_START + cards_to_display - i); } } static void display_dealer_hand(void) { uint8_t card = dealer.hand[dealer.idx_hand - 1]; - if (card == 10) card = 0; - const char display_char = card + '0'; - watch_display_character(display_char, 0); + display_card_at_position(card, 0); } -static void display_player_score(void) { +display_score(uint8_t score, watch_position_t pos) { char buf[4]; - sprintf(buf, "%2d", player.score); - watch_display_text(WATCH_POSITION_SECONDS, buf); -} - -static void display_dealer_score(void) { - char buf[4]; - sprintf(buf, "%2d", dealer.score); - watch_display_text(WATCH_POSITION_TOP_RIGHT, buf); + sprintf(buf, "%2d", score); + watch_display_text(pos, buf); } static void display_win(void) { game_state = BJ_RESULT; watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "WlN ", " WIN"); - display_player_score(); - display_dealer_score(); + display_score(player.score, WATCH_POSITION_SECONDS); + display_score(dealer.score, WATCH_POSITION_TOP_RIGHT); } static void display_lose(void) { game_state = BJ_RESULT; watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "LOSE", "lOSE"); - display_player_score(); - display_dealer_score(); + display_score(player.score, WATCH_POSITION_SECONDS); + display_score(dealer.score, WATCH_POSITION_TOP_RIGHT); } static void display_tie(void) { game_state = BJ_RESULT; watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "TlE ", " TIE"); - display_player_score(); + display_score(player.score, WATCH_POSITION_SECONDS); } static void display_bust(void) { game_state = BJ_RESULT; watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "8UST ", " BUST"); - display_player_score(); + display_score(player.score, WATCH_POSITION_SECONDS); } static void display_title(void) { @@ -213,10 +249,10 @@ static void begin_playing(void) { give_card(&player); give_card(&player); display_player_hand(); - display_player_score(); + display_score(player.score, WATCH_POSITION_SECONDS); give_card(&dealer); display_dealer_hand(); - display_dealer_score(); + display_score(dealer.score, WATCH_POSITION_TOP_RIGHT); } static void perform_hit(void) { @@ -228,14 +264,14 @@ static void perform_hit(void) { display_bust(); } else { display_player_hand(); - display_player_score(); + display_score(player.score, WATCH_POSITION_SECONDS); } } static void perform_stand(void) { game_state = BJ_DEALER_PLAYING; watch_display_text(WATCH_POSITION_BOTTOM, "Stnd"); - display_player_score(); + display_score(player.score, WATCH_POSITION_SECONDS); } static void dealer_performs_hits(void) { @@ -247,7 +283,7 @@ static void dealer_performs_hits(void) { display_lose(); } else { display_dealer_hand(); - display_dealer_score(); + display_score(dealer.score, WATCH_POSITION_TOP_RIGHT); } } From 93a2a5a5eef056d40c142ff7495181ac83397cc5 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Wed, 3 Sep 2025 07:46:38 -0400 Subject: [PATCH 10/25] Documented face --- watch-faces/complication/blackjack_face.c | 13 ++++++---- watch-faces/complication/blackjack_face.h | 29 +++++++++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/watch-faces/complication/blackjack_face.c b/watch-faces/complication/blackjack_face.c index ee490426..c9c7a3cd 100755 --- a/watch-faces/complication/blackjack_face.c +++ b/watch-faces/complication/blackjack_face.c @@ -32,10 +32,10 @@ #include "blackjack_face.h" #include "watch_common_display.h" -#define ACE 13 -#define KING 12 -#define QUEEN 11 -#define JACK 10 +#define ACE 14 +#define KING 13 +#define QUEEN 12 +#define JACK 11 #define MIN_CARD_VALUE 2 #define MAX_CARD_VALUE ACE @@ -180,7 +180,10 @@ static void display_card_at_position(uint8_t card, uint8_t display_position) { watch_display_character('-', display_position); break; case ACE: - watch_display_character('A', display_position); + watch_display_character(watch_get_lcd_type() == WATCH_LCD_TYPE_CUSTOM ? 'A' : 'a', display_position); + break; + case 10: + watch_display_character('0', display_position); break; default: { const char display_char = card + '0'; diff --git a/watch-faces/complication/blackjack_face.h b/watch-faces/complication/blackjack_face.h index ff52d79f..8771561c 100755 --- a/watch-faces/complication/blackjack_face.h +++ b/watch-faces/complication/blackjack_face.h @@ -31,6 +31,35 @@ * Blackjack face * ====================== * + * Simple blackjack game. + * + * Aces are 11 unless you'd but, and if so, they become 1. + * King, Queen, and jack are all 10 points. + * Dealer deals to themselves until they get at least 17. + * The game plays with one shuffled deck that gets reshuffled with every game. + * + * Press either ALARM or LIGHT to begin playing. + * Your score is in the Seconds position. + * The dealer's score is in the Top-Right position. + * The dealer's last-shown card is in the Top-Left position. + * Your cards are in the Bottom row. From left to right, they are oldest to newest. Up to four cards will be dislayed. + * + * To hit, press the ALARM button. + * To stand, press the LIGHT button. + * If you're at 21, you cannoy hit, since we just assume it's a mispress on the button. + * + * Once you stand, the dealer will deal out to themselves once per second (or immidietly when you press the LIGHT or ALARM buttons). + * The game results are: + * WIN: You have a higher score than the dealer, but no more than 21. Or the dealer's score is over 21. + * LOSE: Your score is lower than the dealer's. + * BUST: Your score is above 21. + * TIE: Your score matches the dealer's final score + * + * | Cards | | + * |---------|--------------------------| + * | Value |2|3|4|5|6|7|8|9|10|J|Q|K|A| + * | Display |2|3|4|5|6|7|8|9| 0|-|=|≡|a| + * If you're using a custom display, Ace will display as 'A', not 'a' */ From d6e4a1ad441f86ffb586c62db2dea4ff88b91515 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Wed, 3 Sep 2025 07:52:18 -0400 Subject: [PATCH 11/25] Added delay before showing Bust --- watch-faces/complication/blackjack_face.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/watch-faces/complication/blackjack_face.c b/watch-faces/complication/blackjack_face.c index c9c7a3cd..78270bf0 100755 --- a/watch-faces/complication/blackjack_face.c +++ b/watch-faces/complication/blackjack_face.c @@ -58,6 +58,7 @@ typedef enum { BJ_TITLE_SCREEN, BJ_PLAYING, BJ_DEALER_PLAYING, + BJ_BUST, BJ_RESULT, } game_state_t; @@ -206,7 +207,7 @@ static void display_dealer_hand(void) { display_card_at_position(card, 0); } -display_score(uint8_t score, watch_position_t pos) { +static void display_score(uint8_t score, watch_position_t pos) { char buf[4]; sprintf(buf, "%2d", score); watch_display_text(pos, buf); @@ -235,7 +236,6 @@ static void display_tie(void) { static void display_bust(void) { game_state = BJ_RESULT; watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "8UST ", " BUST"); - display_score(player.score, WATCH_POSITION_SECONDS); } static void display_title(void) { @@ -264,11 +264,10 @@ static void perform_hit(void) { } give_card(&player); if (player.score > 21) { - display_bust(); - } else { - display_player_hand(); - display_score(player.score, WATCH_POSITION_SECONDS); + game_state = BJ_BUST; } + display_player_hand(); + display_score(player.score, WATCH_POSITION_SECONDS); } static void perform_stand(void) { @@ -320,6 +319,8 @@ static void handle_button_presses(bool hit) { case BJ_DEALER_PLAYING: see_if_dealer_hits(); break; + case BJ_BUST: + display_bust(); case BJ_RESULT: display_title(); break; @@ -369,6 +370,9 @@ bool blackjack_face_loop(movement_event_t event, void *context) { if (game_state == BJ_DEALER_PLAYING) { see_if_dealer_hits(); } + else if (game_state == BJ_BUST) { + display_bust(); + } break; case EVENT_LIGHT_BUTTON_UP: case EVENT_DOUBLE_TAP: From 20bb59f02f44ecdc337b576a48993ef8b7300633 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Wed, 3 Sep 2025 07:56:33 -0400 Subject: [PATCH 12/25] Compiler warning fixes --- watch-faces/complication/blackjack_face.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/watch-faces/complication/blackjack_face.c b/watch-faces/complication/blackjack_face.c index 78270bf0..2545fd7a 100755 --- a/watch-faces/complication/blackjack_face.c +++ b/watch-faces/complication/blackjack_face.c @@ -108,23 +108,21 @@ static void reset_deck(void) { shuffle_deck(); } -static uint8_t get_next_card(hand_info_t *hand_info) { +static uint8_t get_next_card(void) { if (current_card >= DECK_SIZE) reset_deck(); return deck[current_card++]; } static uint8_t get_card_value(uint8_t card) { - uint8_t card_value; switch (card) { case ACE: return 11; - break; case KING: case QUEEN: case JACK: - card = 10; + return 10; default: return card; } @@ -144,7 +142,7 @@ static void reset_hands(void) { } static void give_card(hand_info_t *hand_info) { - uint8_t card = get_next_card(hand_info); + uint8_t card = get_next_card(); if (card == ACE) hand_info->high_aces_in_hand++; hand_info->hand[hand_info->idx_hand++] = card; uint8_t card_value = get_card_value(card); @@ -321,6 +319,7 @@ static void handle_button_presses(bool hit) { break; case BJ_BUST: display_bust(); + break; case BJ_RESULT: display_title(); break; From eb1b551e7bd86e5e4e2aebf3fd0162f25c332127 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Wed, 3 Sep 2025 08:06:03 -0400 Subject: [PATCH 13/25] Added documentation on tap controls and retained the tap indicator --- watch-faces/complication/blackjack_face.c | 20 ++++++++++++++------ watch-faces/complication/blackjack_face.h | 5 +++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/watch-faces/complication/blackjack_face.c b/watch-faces/complication/blackjack_face.c index 2545fd7a..1497b7d6 100755 --- a/watch-faces/complication/blackjack_face.c +++ b/watch-faces/complication/blackjack_face.c @@ -242,8 +242,11 @@ static void display_title(void) { watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, " JACK ", "BLaKJK"); } -static void begin_playing(void) { +static void begin_playing(bool tap_control_on) { watch_clear_display(); + if (tap_control_on) { + watch_set_indicator(WATCH_INDICATOR_SIGNAL); + } game_state = BJ_PLAYING; reset_hands(); // Give player their first 2 cards @@ -301,11 +304,11 @@ static void see_if_dealer_hits(void) { } } -static void handle_button_presses(bool hit) { +static void handle_button_presses(bool tap_control_on, bool hit) { switch (game_state) { case BJ_TITLE_SCREEN: - begin_playing(); + begin_playing(tap_control_on); break; case BJ_PLAYING: if (hit) { @@ -362,7 +365,12 @@ bool blackjack_face_loop(movement_event_t event, void *context) { switch (event.event_type) { case EVENT_ACTIVATE: if (state->tap_control_on) { - movement_enable_tap_detection_if_available(); + bool tap_could_enable = movement_enable_tap_detection_if_available(); + if (tap_could_enable) { + watch_set_indicator(WATCH_INDICATOR_SIGNAL); + } else { + state->tap_control_on = false; + } } break; case EVENT_TICK: @@ -375,12 +383,12 @@ bool blackjack_face_loop(movement_event_t event, void *context) { break; case EVENT_LIGHT_BUTTON_UP: case EVENT_DOUBLE_TAP: - handle_button_presses(false); + handle_button_presses(state->tap_control_on, false); case EVENT_LIGHT_BUTTON_DOWN: break; case EVENT_ALARM_BUTTON_UP: case EVENT_SINGLE_TAP: - handle_button_presses(true); + handle_button_presses(state->tap_control_on, true); break; case EVENT_ALARM_LONG_PRESS: if (game_state == BJ_TITLE_SCREEN) { diff --git a/watch-faces/complication/blackjack_face.h b/watch-faces/complication/blackjack_face.h index 8771561c..84041517 100755 --- a/watch-faces/complication/blackjack_face.h +++ b/watch-faces/complication/blackjack_face.h @@ -55,6 +55,11 @@ * BUST: Your score is above 21. * TIE: Your score matches the dealer's final score * + * On a watch that has the accelerometer, long-pressing the ALARM button will turn on the ability to play by tapping. + * The SIGNAL indicator will display when tapping is enabled. + * Tapping once will behave like the ALARM button and hit. + * Tapping twice behave like the LIGHT button and stand. + * * | Cards | | * |---------|--------------------------| * | Value |2|3|4|5|6|7|8|9|10|J|Q|K|A| From a2c400ff8711f5bc90eb9e2c6c620079f7e55eec Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Wed, 3 Sep 2025 18:18:03 -0400 Subject: [PATCH 14/25] Added Win percentage face --- watch-faces/complication/blackjack_face.c | 62 +++++++++++++++++++++-- watch-faces/complication/blackjack_face.h | 2 + 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/watch-faces/complication/blackjack_face.c b/watch-faces/complication/blackjack_face.c index 1497b7d6..3d549dca 100755 --- a/watch-faces/complication/blackjack_face.c +++ b/watch-faces/complication/blackjack_face.c @@ -60,6 +60,7 @@ typedef enum { BJ_DEALER_PLAYING, BJ_BUST, BJ_RESULT, + BJ_WIN_RATIO, } game_state_t; typedef enum { @@ -69,6 +70,8 @@ typedef enum { static game_state_t game_state; static uint8_t deck[DECK_SIZE] = {0}; static uint8_t current_card = 0; +static bool add_to_games_played = false; +static bool add_to_games_won = false; hand_info_t player; hand_info_t dealer; @@ -193,10 +196,15 @@ static void display_card_at_position(uint8_t card, uint8_t display_position) { } static void display_player_hand(void) { - uint8_t cards_to_display = player.idx_hand > MAX_PLAYER_CARDS_DISPLAY ? MAX_PLAYER_CARDS_DISPLAY : player.idx_hand; - for (uint8_t i=cards_to_display; i>0; i--) { - uint8_t card = player.hand[player.idx_hand-i]; - display_card_at_position(card, BOARD_DISPLAY_START + cards_to_display - i); + uint8_t card; + if (player.idx_hand <= MAX_PLAYER_CARDS_DISPLAY) { + card = player.hand[player.idx_hand - 1]; + display_card_at_position(card, BOARD_DISPLAY_START + player.idx_hand - 1); + } else { + for (uint8_t i=0; igames_played > 0) { // Avoid dividing by zero + win_ratio = (uint8_t)(100 * state->games_won) / state->games_played; + } + watch_display_text_with_fallback(WATCH_POSITION_TOP, "WINS ", "WR "); + sprintf(buf, "%3dPct", win_ratio); + watch_display_text(WATCH_POSITION_BOTTOM, buf); +} + static void begin_playing(bool tap_control_on) { watch_clear_display(); if (tap_control_on) { @@ -251,6 +276,7 @@ static void begin_playing(bool tap_control_on) { reset_hands(); // Give player their first 2 cards give_card(&player); + display_player_hand(); give_card(&player); display_player_hand(); display_score(player.score, WATCH_POSITION_SECONDS); @@ -304,6 +330,25 @@ static void see_if_dealer_hits(void) { } } +static void add_to_game_scores(blackjack_face_state_t *state) { + if (add_to_games_played) { + add_to_games_played = false; + state->games_played++; + if (state->games_played == 0) { + // Overflow + state->games_won = 0; + } + } + if (add_to_games_won) { + add_to_games_won = false; + state->games_won++; + if (state->games_won == 0) { + // Overflow + state->games_played = 0; + } + } +} + static void handle_button_presses(bool tap_control_on, bool hit) { switch (game_state) { @@ -324,6 +369,7 @@ static void handle_button_presses(bool tap_control_on, bool hit) { display_bust(); break; case BJ_RESULT: + case BJ_WIN_RATIO: display_title(); break; } @@ -362,6 +408,9 @@ void blackjack_face_activate(void *context) { bool blackjack_face_loop(movement_event_t event, void *context) { blackjack_face_state_t *state = (blackjack_face_state_t *) context; + if (game_state == BJ_RESULT) { + add_to_game_scores(state); + } switch (event.event_type) { case EVENT_ACTIVATE: if (state->tap_control_on) { @@ -390,6 +439,11 @@ bool blackjack_face_loop(movement_event_t event, void *context) { case EVENT_SINGLE_TAP: handle_button_presses(state->tap_control_on, true); break; + case EVENT_LIGHT_LONG_PRESS: + if (game_state == BJ_TITLE_SCREEN) { + display_win_ratio(state); + } + break; case EVENT_ALARM_LONG_PRESS: if (game_state == BJ_TITLE_SCREEN) { toggle_tap_control(state); diff --git a/watch-faces/complication/blackjack_face.h b/watch-faces/complication/blackjack_face.h index 84041517..c93143b9 100755 --- a/watch-faces/complication/blackjack_face.h +++ b/watch-faces/complication/blackjack_face.h @@ -71,6 +71,8 @@ typedef struct { bool tap_control_on; + uint16_t games_played; + uint16_t games_won; } blackjack_face_state_t; void blackjack_face_setup(uint8_t watch_face_index, void ** context_ptr); From a7c2cede060577ce716a3650aebc2bb0f593ffb8 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Thu, 4 Sep 2025 18:29:07 -0400 Subject: [PATCH 15/25] Typo fix --- watch-faces/complication/blackjack_face.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/watch-faces/complication/blackjack_face.c b/watch-faces/complication/blackjack_face.c index 3d549dca..9db6db88 100755 --- a/watch-faces/complication/blackjack_face.c +++ b/watch-faces/complication/blackjack_face.c @@ -246,7 +246,7 @@ static void display_tie(void) { static void display_bust(void) { game_state = BJ_RESULT; add_to_games_played = true; - watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "8UST ", " BUST"); + watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "8UST", " BUST"); } static void display_title(void) { From 30c378a3ce2950f39532838a15427bfac220d213 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Fri, 5 Sep 2025 17:07:24 -0400 Subject: [PATCH 16/25] Text fixes for classic display --- watch-faces/complication/blackjack_face.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/watch-faces/complication/blackjack_face.c b/watch-faces/complication/blackjack_face.c index 9db6db88..1ff1c2ed 100755 --- a/watch-faces/complication/blackjack_face.c +++ b/watch-faces/complication/blackjack_face.c @@ -246,12 +246,13 @@ static void display_tie(void) { static void display_bust(void) { game_state = BJ_RESULT; add_to_games_played = true; - watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "8UST", " BUST"); + watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "8UST", "BUST"); } static void display_title(void) { game_state = BJ_TITLE_SCREEN; - watch_display_text_with_fallback(WATCH_POSITION_TOP, "BLACK ", "21 "); + watch_display_text(WATCH_POSITION_TOP_RIGHT, " "); + watch_display_text_with_fallback(WATCH_POSITION_TOP, "BLACK ", "21"); watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, " JACK ", "BLaKJK"); } @@ -262,7 +263,8 @@ static void display_win_ratio(blackjack_face_state_t *state) { if (state->games_played > 0) { // Avoid dividing by zero win_ratio = (uint8_t)(100 * state->games_won) / state->games_played; } - watch_display_text_with_fallback(WATCH_POSITION_TOP, "WINS ", "WR "); + watch_display_text(WATCH_POSITION_TOP_RIGHT, " "); + watch_display_text_with_fallback(WATCH_POSITION_TOP, "WINS ", "WR"); sprintf(buf, "%3dPct", win_ratio); watch_display_text(WATCH_POSITION_BOTTOM, buf); } From 2c4d3d7f503a26b32effb680bcbddde97f875519 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sat, 6 Sep 2025 12:07:17 -0400 Subject: [PATCH 17/25] Can now turn on LED with long press --- watch-faces/complication/blackjack_face.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/watch-faces/complication/blackjack_face.c b/watch-faces/complication/blackjack_face.c index 1ff1c2ed..4f7c89db 100755 --- a/watch-faces/complication/blackjack_face.c +++ b/watch-faces/complication/blackjack_face.c @@ -444,6 +444,8 @@ bool blackjack_face_loop(movement_event_t event, void *context) { case EVENT_LIGHT_LONG_PRESS: if (game_state == BJ_TITLE_SCREEN) { display_win_ratio(state); + } else { + movement_illuminate_led(); } break; case EVENT_ALARM_LONG_PRESS: From 9764c0f84dc8d23ebc7f09b070295f6a9815b0e4 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 7 Sep 2025 08:51:47 -0400 Subject: [PATCH 18/25] We only stack the deck on activate and not before every new shuffle --- watch-faces/complication/blackjack_face.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/watch-faces/complication/blackjack_face.c b/watch-faces/complication/blackjack_face.c index 4f7c89db..7b538e6b 100755 --- a/watch-faces/complication/blackjack_face.c +++ b/watch-faces/complication/blackjack_face.c @@ -107,7 +107,6 @@ static void shuffle_deck(void) { static void reset_deck(void) { current_card = 0; - stack_deck(); shuffle_deck(); } @@ -406,6 +405,7 @@ void blackjack_face_activate(void *context) { blackjack_face_state_t *state = (blackjack_face_state_t *) context; (void) state; display_title(); + stack_deck(); } bool blackjack_face_loop(movement_event_t event, void *context) { From 639f42c5d8d843fd91279d7828ab55916fb56cf4 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Mon, 8 Sep 2025 19:44:14 -0400 Subject: [PATCH 19/25] Tapping turned off when not being used since tap control uses 90uA --- watch-faces/complication/blackjack_face.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/watch-faces/complication/blackjack_face.c b/watch-faces/complication/blackjack_face.c index 7b538e6b..ff4d73a4 100755 --- a/watch-faces/complication/blackjack_face.c +++ b/watch-faces/complication/blackjack_face.c @@ -67,6 +67,7 @@ typedef enum { A, B, C, D, E, F, G } segment_t; +static bool tap_turned_on = false; static game_state_t game_state; static uint8_t deck[DECK_SIZE] = {0}; static uint8_t current_card = 0; @@ -354,6 +355,9 @@ static void handle_button_presses(bool tap_control_on, bool hit) { switch (game_state) { case BJ_TITLE_SCREEN: + if (!tap_turned_on && tap_control_on) { + if (movement_enable_tap_detection_if_available()) tap_turned_on = true; + } begin_playing(tap_control_on); break; case BJ_PLAYING: @@ -415,14 +419,7 @@ bool blackjack_face_loop(movement_event_t event, void *context) { } switch (event.event_type) { case EVENT_ACTIVATE: - if (state->tap_control_on) { - bool tap_could_enable = movement_enable_tap_detection_if_available(); - if (tap_could_enable) { - watch_set_indicator(WATCH_INDICATOR_SIGNAL); - } else { - state->tap_control_on = false; - } - } + if (state->tap_control_on) watch_set_indicator(WATCH_INDICATOR_SIGNAL); break; case EVENT_TICK: if (game_state == BJ_DEALER_PLAYING) { @@ -454,6 +451,10 @@ bool blackjack_face_loop(movement_event_t event, void *context) { } break; case EVENT_TIMEOUT: + case EVENT_LOW_ENERGY_UPDATE: + if (tap_turned_on) { + movement_disable_tap_detection_if_available(); + } break; default: return movement_default_loop_handler(event); @@ -463,4 +464,8 @@ bool blackjack_face_loop(movement_event_t event, void *context) { void blackjack_face_resign(void *context) { (void) context; + if (tap_turned_on) { + tap_turned_on = false; + movement_disable_tap_detection_if_available(); + } } From af0051a160d9c41535b915314e18fee6820944a0 Mon Sep 17 00:00:00 2001 From: Alessandro Genova Date: Mon, 22 Sep 2025 23:42:47 -0400 Subject: [PATCH 20/25] fix int32 overflow when setting a year past 2067 --- watch-faces/settings/set_time_face.c | 2 +- watch-library/shared/watch/watch_utility.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/watch-faces/settings/set_time_face.c b/watch-faces/settings/set_time_face.c index 63fc34fd..f394347d 100644 --- a/watch-faces/settings/set_time_face.c +++ b/watch-faces/settings/set_time_face.c @@ -46,7 +46,7 @@ static void _handle_alarm_button(watch_date_time_t date_time, uint8_t current_pa current_offset = movement_get_current_timezone_offset_for_zone(movement_get_timezone_index()); return; case 0: // year - date_time.unit.year = ((date_time.unit.year % 60) + 1); + date_time.unit.year = (date_time.unit.year + 1) % 60; break; case 1: // month date_time.unit.month = (date_time.unit.month % 12) + 1; diff --git a/watch-library/shared/watch/watch_utility.c b/watch-library/shared/watch/watch_utility.c index 6d024032..7179aa72 100644 --- a/watch-library/shared/watch/watch_utility.c +++ b/watch-library/shared/watch/watch_utility.c @@ -205,7 +205,8 @@ uint32_t watch_utility_date_time_to_unix_time(watch_date_time_t date_time, int32 watch_date_time_t watch_utility_date_time_from_unix_time(uint32_t timestamp, int32_t utc_offset) { watch_date_time_t retval; retval.reg = 0; - int32_t days, secs; + uint32_t secs; + int32_t days; int32_t remdays, remsecs, remyears; int32_t qc_cycles, c_cycles, q_cycles; int32_t years, months; From 796f83a19f95dddcac565e23d1f9129d90a08cae Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Fri, 26 Sep 2025 10:02:20 -0400 Subject: [PATCH 21/25] Attempt at fixing blackjack win rate counter --- watch-faces/complication/blackjack_face.c | 51 ++++++++++------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/watch-faces/complication/blackjack_face.c b/watch-faces/complication/blackjack_face.c index ff4d73a4..654b883e 100755 --- a/watch-faces/complication/blackjack_face.c +++ b/watch-faces/complication/blackjack_face.c @@ -71,8 +71,7 @@ static bool tap_turned_on = false; static game_state_t game_state; static uint8_t deck[DECK_SIZE] = {0}; static uint8_t current_card = 0; -static bool add_to_games_played = false; -static bool add_to_games_won = false; +static blackjack_face_state_t *g_state = NULL; hand_info_t player; hand_info_t dealer; @@ -219,10 +218,27 @@ static void display_score(uint8_t score, watch_position_t pos) { watch_display_text(pos, buf); } +static void add_to_game_scores(bool add_to_wins) { + g_state->games_played++; + if (g_state->games_played == 0) { + // Overflow + g_state->games_played = 1; + g_state->games_won = add_to_wins ? 1 : 0; + return; + } + if (add_to_wins) { + g_state->games_won++; + if (g_state->games_won == 0) { + // Overflow + g_state->games_played = 1; + g_state->games_won = 1; + } + } +} + static void display_win(void) { game_state = BJ_RESULT; - add_to_games_played = true; - add_to_games_won = true; + add_to_game_scores(true); watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "WlN ", " WIN"); display_score(player.score, WATCH_POSITION_SECONDS); display_score(dealer.score, WATCH_POSITION_TOP_RIGHT); @@ -230,7 +246,7 @@ static void display_win(void) { static void display_lose(void) { game_state = BJ_RESULT; - add_to_games_played = true; + add_to_game_scores(false); watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "LOSE", "lOSE"); display_score(player.score, WATCH_POSITION_SECONDS); display_score(dealer.score, WATCH_POSITION_TOP_RIGHT); @@ -245,7 +261,7 @@ static void display_tie(void) { static void display_bust(void) { game_state = BJ_RESULT; - add_to_games_played = true; + add_to_game_scores(false); watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "8UST", "BUST"); } @@ -332,25 +348,6 @@ static void see_if_dealer_hits(void) { } } -static void add_to_game_scores(blackjack_face_state_t *state) { - if (add_to_games_played) { - add_to_games_played = false; - state->games_played++; - if (state->games_played == 0) { - // Overflow - state->games_won = 0; - } - } - if (add_to_games_won) { - add_to_games_won = false; - state->games_won++; - if (state->games_won == 0) { - // Overflow - state->games_played = 0; - } - } -} - static void handle_button_presses(bool tap_control_on, bool hit) { switch (game_state) { @@ -403,6 +400,7 @@ void blackjack_face_setup(uint8_t watch_face_index, void **context_ptr) { blackjack_face_state_t *state = (blackjack_face_state_t *)*context_ptr; state->tap_control_on = false; } + g_state = (blackjack_face_state_t *)*context_ptr; } void blackjack_face_activate(void *context) { @@ -414,9 +412,6 @@ void blackjack_face_activate(void *context) { bool blackjack_face_loop(movement_event_t event, void *context) { blackjack_face_state_t *state = (blackjack_face_state_t *) context; - if (game_state == BJ_RESULT) { - add_to_game_scores(state); - } switch (event.event_type) { case EVENT_ACTIVATE: if (state->tap_control_on) watch_set_indicator(WATCH_INDICATOR_SIGNAL); From 6609b3f79a6a45ace613f9d803bf6b3d686a5f47 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sat, 15 Nov 2025 12:48:16 -0500 Subject: [PATCH 22/25] LIS2DW tap mode uses low-power mode --- movement.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/movement.c b/movement.c index ca0b0f36..0a41b306 100644 --- a/movement.c +++ b/movement.c @@ -521,7 +521,7 @@ bool movement_enable_tap_detection_if_available(void) { // ramp data rate up to 400 Hz and high performance mode lis2dw_set_low_noise_mode(true); lis2dw_set_data_rate(LIS2DW_DATA_RATE_HP_400_HZ); - lis2dw_set_mode(LIS2DW_MODE_HIGH_PERFORMANCE); + lis2dw_set_mode(LIS2DW_MODE_LOW_POWER); // Settling time (1 sample duration, i.e. 1/400Hz) delay_ms(3); From 437e513d1f416b2df2901e636263bd7d5488fec4 Mon Sep 17 00:00:00 2001 From: David Volovskiy Date: Sun, 16 Nov 2025 08:28:46 -0500 Subject: [PATCH 23/25] Stand on 21 if we ask for a hit --- watch-faces/complication/blackjack_face.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/watch-faces/complication/blackjack_face.c b/watch-faces/complication/blackjack_face.c index 654b883e..375e0435 100755 --- a/watch-faces/complication/blackjack_face.c +++ b/watch-faces/complication/blackjack_face.c @@ -303,9 +303,16 @@ static void begin_playing(bool tap_control_on) { display_score(dealer.score, WATCH_POSITION_TOP_RIGHT); } +static void perform_stand(void) { + game_state = BJ_DEALER_PLAYING; + watch_display_text(WATCH_POSITION_BOTTOM, "Stnd"); + display_score(player.score, WATCH_POSITION_SECONDS); +} + static void perform_hit(void) { if (player.score == 21) { - return; // Assume hitting on 21 is a mistake and ignore + perform_stand(); + return; // Assume hitting on 21 is a mistake and stand } give_card(&player); if (player.score > 21) { @@ -315,12 +322,6 @@ static void perform_hit(void) { display_score(player.score, WATCH_POSITION_SECONDS); } -static void perform_stand(void) { - game_state = BJ_DEALER_PLAYING; - watch_display_text(WATCH_POSITION_BOTTOM, "Stnd"); - display_score(player.score, WATCH_POSITION_SECONDS); -} - static void dealer_performs_hits(void) { give_card(&dealer); display_dealer_hand(); From 6f08545e127d353ecdfec2d7415b40cf8f7001ee Mon Sep 17 00:00:00 2001 From: Daniel Bergman Date: Sat, 9 Aug 2025 20:20:10 +0200 Subject: [PATCH 24/25] Fix compiler warnings: missing prototype, unused variables, implicit declaration --- watch-faces/complication/simple_coin_flip_face.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/watch-faces/complication/simple_coin_flip_face.c b/watch-faces/complication/simple_coin_flip_face.c index 446832be..635c70ff 100644 --- a/watch-faces/complication/simple_coin_flip_face.c +++ b/watch-faces/complication/simple_coin_flip_face.c @@ -26,6 +26,7 @@ #include #include #include "simple_coin_flip_face.h" +#include "delay.h" void simple_coin_flip_face_setup(uint8_t watch_face_index, void ** context_ptr) { (void) watch_face_index; @@ -36,7 +37,7 @@ void simple_coin_flip_face_setup(uint8_t watch_face_index, void ** context_ptr) } void simple_coin_flip_face_activate(void *context) { - simple_coin_flip_face_state_t *state = (simple_coin_flip_face_state_t *)context; + (void) context; } static uint32_t get_random(uint32_t max) { @@ -48,7 +49,7 @@ static uint32_t get_random(uint32_t max) { } -void draw_start_face() { +static void draw_start_face(void) { watch_clear_display(); if (watch_get_lcd_type() == WATCH_LCD_TYPE_CLASSIC) { watch_display_text(WATCH_POSITION_BOTTOM, " Flip"); @@ -57,7 +58,7 @@ void draw_start_face() { } } -void set_pixels(int pixels[3][4][2], int j_len) { +static void set_pixels(int pixels[3][4][2], int j_len) { for(int loopruns = 0; loopruns<2; loopruns++) { for(int i = 0; i<3; i++) { watch_clear_display(); @@ -69,7 +70,7 @@ void set_pixels(int pixels[3][4][2], int j_len) { } } -void load_animation() { +static void load_animation(void) { if (watch_get_lcd_type() == WATCH_LCD_TYPE_CLASSIC) { int j_len = 2; int pixels[3][4][2] = { @@ -114,6 +115,7 @@ void load_animation() { } static void _blink_face_update_lcd(simple_coin_flip_face_state_t *state) { + (void) state; watch_clear_display(); load_animation(); watch_clear_display(); From 3e4c6f23637d113df4fd9e2c1a0ebd95bb03f646 Mon Sep 17 00:00:00 2001 From: Ante Vukorepa Date: Fri, 14 Nov 2025 19:32:51 +0100 Subject: [PATCH 25/25] Make deadline face respect button volumes Just blindly replaced watch_buzzer_play_note with watch_buzzer_play_note_with_volume --- watch-faces/complication/deadline_face.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/watch-faces/complication/deadline_face.c b/watch-faces/complication/deadline_face.c index 41240e76..d3204f20 100644 --- a/watch-faces/complication/deadline_face.c +++ b/watch-faces/complication/deadline_face.c @@ -161,19 +161,19 @@ static inline void _beep(beep_type_t beep_type) switch (beep_type) { case BEEP_BUTTON: - watch_buzzer_play_note(BUZZER_NOTE_C7, 50); + watch_buzzer_play_note_with_volume(BUZZER_NOTE_C7, 50, movement_button_volume()); break; case BEEP_ENABLE: - watch_buzzer_play_note(BUZZER_NOTE_G7, 50); + watch_buzzer_play_note_with_volume(BUZZER_NOTE_G7, 50, movement_button_volume()); watch_buzzer_play_note(BUZZER_NOTE_REST, 75); - watch_buzzer_play_note(BUZZER_NOTE_C8, 75); + watch_buzzer_play_note_with_volume(BUZZER_NOTE_C8, 50, movement_button_volume()); break; case BEEP_DISABLE: - watch_buzzer_play_note(BUZZER_NOTE_C8, 50); + watch_buzzer_play_note_with_volume(BUZZER_NOTE_C8, 50, movement_button_volume()); watch_buzzer_play_note(BUZZER_NOTE_REST, 75); - watch_buzzer_play_note(BUZZER_NOTE_G7, 75); + watch_buzzer_play_note_with_volume(BUZZER_NOTE_G7, 50, movement_button_volume()); break; } }