--- /dev/null
+/*\r
+ * MIT License\r
+ *\r
+ * Copyright (c) 2025 David Volovskiy\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy\r
+ * of this software and associated documentation files (the "Software"), to deal\r
+ * in the Software without restriction, including without limitation the rights\r
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
+ * copies of the Software, and to permit persons to whom the Software is\r
+ * furnished to do so, subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
+ * SOFTWARE.\r
+ */\r
+\r
+// Emulator only: need time() to seed the random number generator.\r
+#if __EMSCRIPTEN__\r
+#include <time.h>\r
+#endif\r
+\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include "blackjack_face.h"\r
+#include "watch_common_display.h"\r
+\r
+#define KING 12\r
+#define QUEEN 11\r
+#define JACK 10\r
+\r
+#define MIN_CARD_VALUE 1\r
+#define MAX_CARD_VALUE KING\r
+#define CARD_RANK_COUNT (MAX_CARD_VALUE - MIN_CARD_VALUE + 1)\r
+#define CARD_SUIT_COUNT 4\r
+#define DECK_SIZE (CARD_SUIT_COUNT * CARD_RANK_COUNT)\r
+\r
+#define BLACKJACK_MAX_HAND_SIZE 11 // 4*1 + 4*2 + 3*3 = 21; 11 cards total\r
+#define MAX_PLAYER_CARDS_DISPLAY 4\r
+#define BOARD_DISPLAY_START 4\r
+\r
+uint8_t score_player = 0;\r
+uint8_t score_dealer = 0;\r
+uint8_t hand_player[BLACKJACK_MAX_HAND_SIZE] = {0xFF};\r
+uint8_t hand_dealer[BLACKJACK_MAX_HAND_SIZE] = {0xFF};\r
+uint8_t idx_player = 0;\r
+uint8_t idx_dealer = 0;\r
+\r
+typedef enum {\r
+ BJ_HIT,\r
+ BJ_STAND,\r
+ BJ_BUST,\r
+} guess_t;\r
+\r
+typedef enum {\r
+ BJ_TITLE_SCREEN,\r
+ BJ_PLAYING,\r
+ BJ_DEALER_PLAYING,\r
+ BJ_RESULT,\r
+} game_state_t;\r
+\r
+static game_state_t game_state = BJ_TITLE_SCREEN;\r
+static uint8_t deck[DECK_SIZE] = {0};\r
+static uint8_t current_card = 0;\r
+\r
+static uint8_t generate_random_number(uint8_t num_values) {\r
+ // Emulator: use rand. Hardware: use arc4random.\r
+#if __EMSCRIPTEN__\r
+ return rand() % num_values;\r
+#else\r
+ return arc4random_uniform(num_values);\r
+#endif\r
+}\r
+\r
+static void stack_deck(void) {\r
+ for (size_t i = 0; i < CARD_RANK_COUNT; i++) {\r
+ for (size_t j = 0; j < CARD_SUIT_COUNT; j++)\r
+ deck[(i * CARD_SUIT_COUNT) + j] = MIN_CARD_VALUE + i;\r
+ }\r
+}\r
+\r
+static void shuffle_deck(void) {\r
+ // Randomize shuffle with Fisher Yates\r
+ size_t i;\r
+ size_t j;\r
+ uint8_t tmp;\r
+\r
+ for (i = DECK_SIZE - 1; i > 0; i--) {\r
+ j = generate_random_number(0xFF) % (i + 1);\r
+ tmp = deck[j];\r
+ deck[j] = deck[i];\r
+ deck[i] = tmp;\r
+ }\r
+}\r
+\r
+static void reset_deck(void) {\r
+ current_card = 0;\r
+ stack_deck();\r
+ shuffle_deck();\r
+}\r
+\r
+static uint8_t get_next_card(void) {\r
+ if (current_card >= DECK_SIZE)\r
+ reset_deck();\r
+ uint8_t card = deck[current_card++];\r
+ if (card > 10) return 10;\r
+ return card;\r
+}\r
+\r
+static void reset_hands(void) {\r
+ score_player = 0;\r
+ score_dealer = 0;\r
+ idx_player = 0;\r
+ idx_dealer = 0;\r
+ memset(hand_player, 0xFF, sizeof(hand_player));\r
+ memset(hand_dealer, 0xFF, sizeof(hand_dealer));\r
+ reset_deck();\r
+}\r
+\r
+static void give_player_card(void) {\r
+ uint8_t card = get_next_card();\r
+ hand_player[idx_player++] = card;\r
+ score_player += card;\r
+}\r
+\r
+static void give_dealer_card(void) {\r
+ uint8_t card = get_next_card();\r
+ hand_dealer[idx_dealer++] = card;\r
+ score_dealer += card;\r
+}\r
+\r
+static void display_player_hand(void) { \r
+ uint8_t cards_to_display = idx_player > MAX_PLAYER_CARDS_DISPLAY ? MAX_PLAYER_CARDS_DISPLAY : idx_player;\r
+ for (uint8_t i=0; i<cards_to_display; i++) {\r
+ uint8_t card = hand_player[i];\r
+ if (card == 10) card = 0;\r
+ const char display_char = card + '0';\r
+ watch_display_character(display_char, BOARD_DISPLAY_START + i);\r
+ }\r
+}\r
+\r
+static void display_dealer_hand(void) {\r
+ uint8_t card = hand_dealer[idx_dealer - 1];\r
+ if (card == 10) card = 0;\r
+ const char display_char = card + '0';\r
+ watch_display_character(display_char, 0);\r
+}\r
+\r
+static void display_player_score(void) {\r
+ char buf[3];\r
+ sprintf(buf, "%2d", score_player);\r
+ watch_display_text(WATCH_POSITION_SECONDS, buf);\r
+}\r
+\r
+static void display_dealer_score(void) {\r
+ char buf[3];\r
+ sprintf(buf, "%2d", score_dealer);\r
+ watch_display_text(WATCH_POSITION_TOP_RIGHT, buf);\r
+}\r
+\r
+static void display_win(void) {\r
+ game_state = BJ_RESULT;\r
+ watch_display_text(WATCH_POSITION_BOTTOM, " WIN");\r
+ display_player_score();\r
+ display_dealer_score();\r
+}\r
+\r
+static void display_lose(void) {\r
+ game_state = BJ_RESULT;\r
+ watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "LOSE", "lOSE");\r
+ display_player_score();\r
+ display_dealer_score();\r
+}\r
+\r
+static void display_tie(void) {\r
+ game_state = BJ_RESULT;\r
+ watch_display_text(WATCH_POSITION_BOTTOM, " TIE");\r
+ display_player_score();\r
+}\r
+\r
+static void display_bust(void) {\r
+ game_state = BJ_RESULT;\r
+ watch_display_text(WATCH_POSITION_BOTTOM, "BUST");\r
+ display_player_score();\r
+}\r
+\r
+static void display_title(void) {\r
+ game_state = BJ_TITLE_SCREEN;\r
+ watch_display_text_with_fallback(WATCH_POSITION_TOP, "BLACK ", "21 ");\r
+ watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, " JACK ", "BLaKJK");\r
+}\r
+\r
+static void begin_playing(void) {\r
+ watch_clear_display();\r
+ game_state = BJ_PLAYING;\r
+ reset_hands();\r
+ // Give player their first 2 cards\r
+ give_player_card();\r
+ give_player_card();\r
+ display_player_hand();\r
+ display_player_score();\r
+ give_dealer_card();\r
+ display_dealer_hand();\r
+ display_dealer_score();\r
+}\r
+\r
+static void perform_hit(void) {\r
+ if (score_player == 21) {\r
+ return; // Assume hitting on 21 is a mistake and ignore\r
+ }\r
+ give_player_card();\r
+ if (score_player > 21) {\r
+ display_bust();\r
+ } else {\r
+ display_player_hand();\r
+ display_player_score();\r
+ }\r
+}\r
+\r
+static void perform_stand(void) {\r
+ game_state = BJ_DEALER_PLAYING;\r
+ watch_display_text(WATCH_POSITION_BOTTOM, "Stnd");\r
+ display_player_score();\r
+}\r
+\r
+static void dealer_performs_hits(void) {\r
+ give_dealer_card();\r
+ display_dealer_hand();\r
+ if (score_dealer > 21) {\r
+ display_win();\r
+ } else if (score_dealer > score_player) {\r
+ display_lose();\r
+ } else {\r
+ display_dealer_hand();\r
+ display_dealer_score();\r
+ } \r
+}\r
+\r
+static void see_if_dealer_hits(void) {\r
+ if (score_dealer > 16) {\r
+ if (score_dealer > score_player) {\r
+ display_lose();\r
+ } else if (score_dealer < score_player) {\r
+ display_win();\r
+ } else {\r
+ display_tie();\r
+ }\r
+ } else {\r
+ dealer_performs_hits();\r
+ }\r
+}\r
+\r
+void handle_button_presses(bool hit) {\r
+ switch (game_state)\r
+ {\r
+ case BJ_TITLE_SCREEN:\r
+ begin_playing();\r
+ break;\r
+ case BJ_PLAYING:\r
+ if (hit) {\r
+ perform_hit();\r
+ } else {\r
+ perform_stand();\r
+ }\r
+ break;\r
+ case BJ_DEALER_PLAYING:\r
+ see_if_dealer_hits();\r
+ break;\r
+ case BJ_RESULT:\r
+ display_title();\r
+ break;\r
+ }\r
+}\r
+\r
+void blackjack_face_setup(uint8_t watch_face_index, void **context_ptr) {\r
+ (void) watch_face_index;\r
+\r
+ if (*context_ptr == NULL) {\r
+ *context_ptr = malloc(sizeof(blackjack_face_state_t));\r
+ memset(*context_ptr, 0, sizeof(blackjack_face_state_t));\r
+ }\r
+}\r
+\r
+void blackjack_face_activate(void *context) {\r
+ blackjack_face_state_t *state = (blackjack_face_state_t *) context;\r
+ (void) state;\r
+ display_title();\r
+}\r
+\r
+bool blackjack_face_loop(movement_event_t event, void *context) {\r
+ blackjack_face_state_t *state = (blackjack_face_state_t *) context;\r
+ (void) state;\r
+\r
+ switch (event.event_type) {\r
+ case EVENT_ACTIVATE:\r
+ break;\r
+ case EVENT_TICK:\r
+ if (game_state == BJ_DEALER_PLAYING) {\r
+ see_if_dealer_hits();\r
+ }\r
+ break;\r
+ case EVENT_LIGHT_BUTTON_UP:\r
+ handle_button_presses(false);\r
+ case EVENT_LIGHT_BUTTON_DOWN:\r
+ break;\r
+ case EVENT_ALARM_BUTTON_UP:\r
+ handle_button_presses(true);\r
+ break;\r
+ case EVENT_TIMEOUT:\r
+ break;\r
+ default:\r
+ return movement_default_loop_handler(event);\r
+ }\r
+ return true;\r
+}\r
+\r
+void blackjack_face_resign(void *context) {\r
+ (void) context;\r
+}\r
--- /dev/null
+/*\r
+ * MIT License\r
+ *\r
+ * Copyright (c) 2023 Chris Ellis\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy\r
+ * of this software and associated documentation files (the "Software"), to deal\r
+ * in the Software without restriction, including without limitation the rights\r
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
+ * copies of the Software, and to permit persons to whom the Software is\r
+ * furnished to do so, subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
+ * SOFTWARE.\r
+ */\r
+\r
+#ifndef BLACKJACK_FACE_H_\r
+#define BLACKJACK_FACE_H_\r
+\r
+#include "movement.h"\r
+\r
+/*\r
+ * Blackjack face\r
+ * ======================\r
+ *\r
+ */\r
+\r
+\r
+\r
+typedef struct {\r
+ // Anything you need to keep track of, put it here!\r
+} blackjack_face_state_t;\r
+\r
+void blackjack_face_setup(uint8_t watch_face_index, void ** context_ptr);\r
+void blackjack_face_activate(void *context);\r
+bool blackjack_face_loop(movement_event_t event, void *context);\r
+void blackjack_face_resign(void *context);\r
+\r
+#define blackjack_face ((const watch_face_t){ \\r
+ blackjack_face_setup, \\r
+ blackjack_face_activate, \\r
+ blackjack_face_loop, \\r
+ blackjack_face_resign, \\r
+ NULL, \\r
+})\r
+\r
+#endif // blackjack_FACE_H_\r