Coin & Dice Toss & Geomantic Divination Watch Faces (#235)
* init * advanced latlon setting * simple functionality done * lat lon high precision fwd bwd * edit toggle * added readme for branch * DD DMS conversion & cleanup * DD to OLC conversion * olc encoding & decoding * OLC implementation * swapped bools for modes, code cleanup * place name editor * updated button logic, fixed display * load and save places in state array * todo list * simplified OLC functions * geohash conversion functions * geohash display & digit functions * todo * finished geohash implementation * code display function, defaults, bugfixes * read/write file/reg logic * long light in DATA to cancel * write to registry * todo * read & write backup register * file read/write * todo * new more concise button logic, optimizations * todo * renamed & cleaned up, fixed button logic * documentation * documentation * LAP mode for all coordinate screens * faster and more precise geohash algorithm * updated description * updated docu * simple place face * bugfixes, updated documentation * init * meh * added public functions for OLC and Geohash * randonauting face * fix * display fix * cleanup * bugfixes * bugfix * added place * fixed TRNG call * fixed declaration conflict * modulo bias filter * simplified things, chance RNG selection * fixed button logic, better menus * cleanup * documentation * docu fixes * init * basic functions * all needed static functions done * progress * coins and dice done * progress * place update * divination faces functionality done * better divine_bit * figure numbers and names * captions optional * coin animation * dice animation & optimizations * animation * changed names, documented * bugfix * cleanup * reset config --------- Co-authored-by: joeycastillo <joeycastillo@utexas.edu>
This commit is contained in:
		
							parent
							
								
									ccf44281e7
								
							
						
					
					
						commit
						34030bf3e4
					
				| @ -112,6 +112,8 @@ SRCS += \ | ||||
|   ../watch_faces/complication/invaders_face.c \
 | ||||
|   ../watch_faces/clock/world_clock2_face.c \
 | ||||
|   ../watch_faces/complication/time_left_face.c \
 | ||||
|   ../watch_faces/complication/toss_up_face.c \
 | ||||
|   ../watch_faces/complication/geomancy_face.c \
 | ||||
| # New watch faces go above this line.
 | ||||
| 
 | ||||
| # Leave this line at the bottom of the file; it has all the targets for making your project.
 | ||||
|  | ||||
| @ -87,6 +87,8 @@ | ||||
| #include "invaders_face.h" | ||||
| #include "world_clock2_face.h" | ||||
| #include "time_left_face.h" | ||||
| #include "toss_up_face.h" | ||||
| #include "geomancy_face.h" | ||||
| #include "dual_timer_face.h" | ||||
| // New includes go above this line.
 | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										356
									
								
								movement/watch_faces/complication/geomancy_face.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										356
									
								
								movement/watch_faces/complication/geomancy_face.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,356 @@ | ||||
| /*
 | ||||
|  * MIT License | ||||
|  * | ||||
|  * Copyright (c) 2023 Tobias Raayoni Last / @randogoth | ||||
|  * | ||||
|  * 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 <stdlib.h> | ||||
| #include <string.h> | ||||
| #include "toss_up_face.h" | ||||
| #include "geomancy_face.h" | ||||
| 
 | ||||
| // CONSTANTS //////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| // The Bagua 八卦 Trigrams encoded as 3bit tribbles, represented as binary integer
 | ||||
| static const uint32_t bagua = 0b00000101001110010111011100000000; | ||||
| 
 | ||||
| // The King Wen Sequence 文王卦序 of the I Ching 易經 Hexagrams 卦 encoded as an array
 | ||||
| // of decimal integers in the order of two combined Trigram tribbles from 0b000000 to 
 | ||||
| // 0b111111 
 | ||||
| static const uint8_t wen_order[] = { | ||||
|      1, 22,  7, 19, 15, 34, 44, 11,  | ||||
|     14, 51, 38, 52, 61, 55, 30, 32,  | ||||
|      6,  3, 28, 58, 39, 63, 46,  5,  | ||||
|     45, 17, 47, 56, 31, 49, 27, 43,  | ||||
|     23, 26,  2, 41, 50, 20, 16, 24,  | ||||
|     35, 21, 62, 36, 54, 29, 48, 12,  | ||||
|     18, 40, 59, 60, 53, 37, 57,  9,  | ||||
|     10, 25,  4,  8, 33, 13, 42,  0 | ||||
| }; | ||||
| 
 | ||||
| // The geomantic figures encoded as 4 bit nibbles, represented as hexadecimal integer
 | ||||
| static const uint64_t geomantic = 0x4ABF39D25E76C180; | ||||
| 
 | ||||
| // Abbreviations of the Names of the Geomantic Figures in the order of the 4 bit nibbles
 | ||||
| // from 0b0000 to 0b1111
 | ||||
| static const char figures[16][2] = { | ||||
|     "VI" /* Via */, "Hd" /* Head of the Dragon */, "PA" /* Puella */, "GF" /* Greater Fortune*/,  | ||||
|     "PR" /* Puer */, "AQ" /* Acquisitio */, "CA" /* Carcer */, "TR" /* Tristitia */, | ||||
|     "Td" /* Tail of the Dragon */, "CO" /* Conjunctio */, "AM" /* Amissio */, "AL" /* Albus */, | ||||
|     "LF" /* Lesser Fortune */, "RU" /* Rubeus */, "LA" /* Laetitia */, "PO" /* Populus */ | ||||
| }; | ||||
| 
 | ||||
| // DECLARATIONS ///////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| static void geomancy_face_display(); | ||||
| static nibble_t _geomancy_pick_figure(); | ||||
| static tribble_t _iching_pick_trigram(); | ||||
| static uint8_t _iching_form_hexagram(); | ||||
| static void _geomancy_display(nibble_t code); | ||||
| static void _display_hexagram(uint8_t hexagram, char* str); | ||||
| static void _fix_broken_line(uint8_t hexagram); | ||||
| static void _throw_animation(geomancy_state_t *state); | ||||
| 
 | ||||
| // WATCH FACE FUNCTIONS ///////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| void geomancy_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { | ||||
|     (void) settings; | ||||
|     if (*context_ptr == NULL) { | ||||
|         *context_ptr = malloc(sizeof(geomancy_state_t)); | ||||
|         memset(*context_ptr, 0, sizeof(geomancy_state_t)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void geomancy_face_activate(movement_settings_t *settings, void *context) { | ||||
|     (void) settings; | ||||
|     (void) context; | ||||
| } | ||||
| 
 | ||||
| bool geomancy_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { | ||||
|     geomancy_state_t *state = (geomancy_state_t *)context; | ||||
| 
 | ||||
|     switch (event.event_type) { | ||||
|         case EVENT_ACTIVATE: | ||||
|             state->animate = false; | ||||
|             state->animation = 0; | ||||
|             watch_display_string("    IChing", 0); | ||||
|             break; | ||||
|         case EVENT_TICK: | ||||
|             if ( state->animate ) { | ||||
|                 state->animation = (state->animation + 1) % 39; | ||||
|                 geomancy_face_display(state); | ||||
|             } | ||||
|             break; | ||||
|         case EVENT_LIGHT_BUTTON_DOWN: | ||||
|             break; | ||||
|         case EVENT_LIGHT_BUTTON_UP: | ||||
|             if ( state->animate ) break; | ||||
|             if ( state->mode <= 1 ) state->mode = 2; | ||||
|             else if ( state->mode >= 2 ) state->mode = 0; | ||||
|             geomancy_face_display(state); | ||||
|             break; | ||||
|         case EVENT_ALARM_BUTTON_UP: | ||||
|             if ( state->animate ) break; | ||||
|             switch ( state->mode ) { | ||||
|                 case 0: | ||||
|                     state->mode++; | ||||
|                 case 1: | ||||
|                     state->animate = true; | ||||
|                     state->i_ching_hexagram = _iching_form_hexagram(); | ||||
|                     break; | ||||
|                 case 2: | ||||
|                     state->mode++; | ||||
|                 case 3: | ||||
|                     state->animate = true; | ||||
|                     state->geomantic_figure = _geomancy_pick_figure().bits; | ||||
|                     break; | ||||
|                 default: | ||||
|                     break; | ||||
|             } | ||||
|             geomancy_face_display(state); | ||||
|             break; | ||||
|         case EVENT_ALARM_LONG_PRESS: | ||||
|             if ( state->animate ) break; | ||||
|             state->caption = !state->caption; | ||||
|             watch_display_string("    ", 0); | ||||
|             geomancy_face_display(state); | ||||
|             break; | ||||
|         default: | ||||
|             return movement_default_loop_handler(event, settings); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void geomancy_face_resign(movement_settings_t *settings, void *context) { | ||||
|     (void) settings; | ||||
|     (void) context; | ||||
| } | ||||
| 
 | ||||
| // STATIC FUNCTIONS ///////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| /** @brief display handler */ | ||||
| static void geomancy_face_display(geomancy_state_t *state) { | ||||
|     char token[7] = {0}; | ||||
|     nibble_t figure = *((nibble_t*) &state->geomantic_figure); | ||||
|     switch ( state->mode ) { | ||||
|         case 0: | ||||
|             watch_display_string("    IChing", 0); | ||||
|             break; | ||||
|         case 1: | ||||
|             _throw_animation(state); | ||||
|             if ( !state->animate ) { | ||||
|                 _display_hexagram(state->i_ching_hexagram, token); | ||||
|                 watch_display_string(token, 4); | ||||
|                 _fix_broken_line(state->i_ching_hexagram); | ||||
|                 if (state->caption) { | ||||
|                     sprintf(token, "%2d", wen_order[state->i_ching_hexagram] + 1); | ||||
|                     watch_display_string(token, 2); | ||||
|                 } | ||||
|             } | ||||
|             break; | ||||
|         case 2: | ||||
|             watch_display_string("    GeomCy", 0); | ||||
|             break; | ||||
|         case 3: | ||||
|             _throw_animation(state); | ||||
|             if ( !state->animate ) { | ||||
|                 if ( state->caption ) { | ||||
|                     sprintf(token, "%c%c", figures[state->geomantic_figure][0], figures[state->geomantic_figure][1]); | ||||
|                     watch_display_string(token, 0); | ||||
|                 } | ||||
|                 _geomancy_display(figure); | ||||
|             } | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** @brief screen clearing animation between castings */ | ||||
| static void _throw_animation(geomancy_state_t *state) { | ||||
|     movement_request_tick_frequency(16); | ||||
|     switch ( state->animation ) { | ||||
|         case 0: | ||||
|             watch_set_pixel(0, 22); | ||||
|             break; | ||||
|         case 1: | ||||
|             watch_set_pixel(2, 22); | ||||
|             watch_set_pixel(2, 23); | ||||
|             watch_clear_pixel(0, 22); | ||||
|             break; | ||||
|         case 2: | ||||
|             watch_set_pixel(1, 22); | ||||
|             watch_set_pixel(0, 23); | ||||
|             break; | ||||
|         case 3: | ||||
|             watch_set_pixel(2, 0); | ||||
|             watch_set_pixel(1, 0); | ||||
|             watch_set_pixel(2, 21); | ||||
|             watch_set_pixel(1, 21); | ||||
|             watch_clear_pixel(2, 22); | ||||
|             watch_clear_pixel(1, 22); | ||||
|             watch_clear_pixel(2, 23); | ||||
|             watch_clear_pixel(0, 23); | ||||
|             watch_clear_pixel(1, 23); | ||||
|             break; | ||||
|         case 4: | ||||
|             watch_set_pixel(1, 17); | ||||
|             watch_set_pixel(0, 20); | ||||
|             watch_set_pixel(2, 10); | ||||
|             watch_set_pixel(0, 1); | ||||
|             break; | ||||
|         case 5: | ||||
|             watch_clear_pixel(2, 21); | ||||
|             watch_clear_pixel(1, 21); | ||||
|             watch_clear_pixel(2, 0); | ||||
|             watch_clear_pixel(1, 0); | ||||
|             watch_clear_pixel(1, 20); | ||||
|             watch_clear_pixel(2, 20); | ||||
|             watch_clear_pixel(0, 21); | ||||
|             watch_clear_pixel(1, 1); | ||||
|             watch_clear_pixel(0, 0); | ||||
|             watch_clear_pixel(2, 1); | ||||
|             watch_set_pixel(2, 19); | ||||
|             watch_set_pixel(0, 19); | ||||
|             watch_set_pixel(1, 2); | ||||
|             watch_set_pixel(0, 2); | ||||
|             break; | ||||
|         case 6: | ||||
|             watch_clear_pixel(1, 17); | ||||
|             watch_clear_pixel(0, 20); | ||||
|             watch_clear_pixel(2, 10); | ||||
|             watch_clear_pixel(0, 1); | ||||
|             watch_set_pixel(2, 18); | ||||
|             watch_set_pixel(0, 18); | ||||
|             watch_set_pixel(2, 3); | ||||
|             watch_set_pixel(0, 4); | ||||
|             break; | ||||
|         case 7: | ||||
|             watch_clear_pixel(2, 19); | ||||
|             watch_clear_pixel(0, 19); | ||||
|             watch_clear_pixel(1, 18); | ||||
|             watch_clear_pixel(1, 19); | ||||
|             watch_clear_pixel(1, 2); | ||||
|             watch_clear_pixel(0, 2); | ||||
|             watch_clear_pixel(1, 3); | ||||
|             watch_clear_pixel(0, 3); | ||||
|             watch_clear_pixel(2, 2); | ||||
|             watch_set_pixel(1, 4); | ||||
|             watch_set_pixel(0, 5); | ||||
|             break; | ||||
|         case 8: | ||||
|             watch_clear_pixel(2, 18); | ||||
|             watch_clear_pixel(0, 18); | ||||
|             watch_clear_pixel(2, 3); | ||||
|             watch_clear_pixel(0, 4); | ||||
|             watch_set_pixel(2, 5); | ||||
|             watch_set_pixel(1, 6); | ||||
|             break; | ||||
|         case 9: | ||||
|             watch_clear_pixel(1, 4); | ||||
|             watch_clear_pixel(0, 5); | ||||
|             watch_clear_pixel(1, 5); | ||||
|             watch_clear_pixel(2, 4); | ||||
|             watch_clear_pixel(0, 6); | ||||
|             break; | ||||
|         case 10: | ||||
|             watch_clear_pixel(2, 5); | ||||
|             watch_clear_pixel(1, 6); | ||||
|             break; | ||||
|         case 11: | ||||
|             state->animate = false; | ||||
|             state->animation = 0; | ||||
|             movement_request_tick_frequency(1); | ||||
|             break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // I CHING FUNCTIONS //////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| /** @brief form a trigram from three random bit picks
 | ||||
|  */ | ||||
| static tribble_t _iching_pick_trigram() { | ||||
|     uint8_t index = (divine_bit() << 2) | (divine_bit() << 1) | divine_bit(); | ||||
|     tribble_t trigram = {(bagua >> (3 * index)) & 0b111}; | ||||
|     return trigram; | ||||
| } | ||||
| 
 | ||||
| /** @brief form a hexagram from two trigrams
 | ||||
|  */ | ||||
| static uint8_t _iching_form_hexagram() { | ||||
|     tribble_t inner = _iching_pick_trigram(); | ||||
|     tribble_t outer = _iching_pick_trigram(); | ||||
|     uint8_t hexagram = (inner.bits << 3) | outer.bits; | ||||
|     return hexagram; | ||||
| } | ||||
| 
 | ||||
| /** @brief display hexagram
 | ||||
|  *  @details | for unbroken lines and Ξ for broken lines, left of display is bottom | ||||
|  */ | ||||
| static void _display_hexagram(uint8_t hexagram, char* str) { | ||||
|     str[6] = '\0';  // Null-terminate the string
 | ||||
|     for (uint8_t i = 0; i < 6; i++) { | ||||
|         if (hexagram & (1 << (5 - i))) { | ||||
|             str[i] = '1'; | ||||
|         } else { | ||||
|             str[i] = '='; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** @brief when Ξ digits show as = then manually add a line on top
 | ||||
|  */ | ||||
| static void _fix_broken_line(uint8_t hexagram) { | ||||
|     for (uint8_t i = 0; i < 6; i++) { | ||||
|         if (!(hexagram & (1 << (5 - i)))) { | ||||
|             if ( i == 1 ) watch_set_pixel(2, 20); | ||||
|             if ( i == 3 ) watch_set_pixel(2, 1); | ||||
|             if ( i == 4 ) watch_set_pixel(2, 2); | ||||
|             if ( i == 5 ) watch_set_pixel(2, 4); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // GEOMANCY FUNCTIONS /////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| /** @brief choose a geomantic figure from four random bits
 | ||||
|  *  @details 0 represents · and 1 represents : counting from the bottom | ||||
|  */ | ||||
| static nibble_t _geomancy_pick_figure() { | ||||
|     uint8_t index = (divine_bit() << 3) | (divine_bit() << 2) | (divine_bit() << 1) | divine_bit(); | ||||
|     nibble_t figure = {(geomantic >> (4 * (15 - index))) & 0xF}; | ||||
|     return figure; | ||||
| } | ||||
| 
 | ||||
| /** @brief display the geomantic figure, left of display is bottom
 | ||||
|  */ | ||||
| static void _geomancy_display(nibble_t code) { | ||||
|     // draw geomantic figures
 | ||||
|     bool row1 = (code.bits >> 3) & 1; | ||||
|     bool row2 = (code.bits >> 2) & 1; | ||||
|     bool row3 = (code.bits >> 1) & 1; | ||||
|     bool row4 = code.bits & 1; | ||||
| 
 | ||||
|     if ( row1 ) watch_set_pixel(1, 18); else watch_set_pixel(1, 19); | ||||
|     if ( row2 ) { watch_set_pixel(2, 20); watch_set_pixel(0, 21);} else watch_set_pixel(1, 20); | ||||
|     if ( row3 ) watch_set_pixel(0, 22); else watch_set_pixel(1, 23); | ||||
|     if ( row4 ) { watch_set_pixel(2, 1); watch_set_pixel(0, 0);} else watch_set_pixel(1, 1); | ||||
| } | ||||
							
								
								
									
										99
									
								
								movement/watch_faces/complication/geomancy_face.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								movement/watch_faces/complication/geomancy_face.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,99 @@ | ||||
| /*
 | ||||
|  * MIT License | ||||
|  * | ||||
|  * Copyright (c) 2023 Tobias Raayoni Last / @randogoth | ||||
|  * | ||||
|  * 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 GEOMANCY_FACE_H_ | ||||
| #define GEOMANCY_FACE_H_ | ||||
| 
 | ||||
| #include "movement.h" | ||||
| 
 | ||||
| /*
 | ||||
|  * GEOMANCY WATCH FACE | ||||
|  * | ||||
|  * A simple and straightforward watch face for the ancient Eastern geomantic divination system | ||||
|  * of I Ching and the western system of "Geomancy". It is an optional addition to the Toss Up | ||||
|  * Face. | ||||
|  *  | ||||
|  * The LIGHT button toggles between the two systems of geomancy. | ||||
|  *  | ||||
|  * The ALARM button casts an I Ching hexagram or Geomantic figure based on drawing virtual | ||||
|  * stalks from the True Random Number Generator in the Sensor Watch. | ||||
|  *  | ||||
|  * The figures are flipped 90 degrees clockwise, so the left side is the bottom and the | ||||
|  * right side the top. | ||||
|  *  | ||||
|  * LONG PRESSING ALARM toggles the display of the King Wen sequence index for the cast I Ching | ||||
|  * Hexagram (https://en.wikipedia.org/wiki/King_Wen_sequence )or the abbreviated name for the
 | ||||
|  * cast Geomantic Figure: | ||||
|  *  | ||||
|  * GF - Greater Fortune (Fortuna Major) | ||||
|  * LF - Lesser Fortune (Fortuna Minor) | ||||
|  * PO - Populus | ||||
|  * VI - Via | ||||
|  * AL - Albus | ||||
|  * CO - Conjunctio | ||||
|  * PA - Puella | ||||
|  * AM - Amissio | ||||
|  * PR - Puer | ||||
|  * RU - Rubeus | ||||
|  * AQ - Acquisitio | ||||
|  * LA - Laetitia | ||||
|  * TR - Tristitia | ||||
|  * CA - Carcer | ||||
|  * HD - Head of the Dragon (Caput Draconis) | ||||
|  * TD - Tail of the Dragon (Cauda Draconis) | ||||
|  *  | ||||
|  */ | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint8_t bits : 4; | ||||
| } nibble_t; | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint8_t bits : 3; | ||||
| } tribble_t; | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint8_t mode : 3; | ||||
|     uint8_t geomantic_figure; | ||||
|     uint8_t i_ching_hexagram : 6; | ||||
|     bool caption; | ||||
|     uint8_t animation; | ||||
|     bool animate; | ||||
| } geomancy_state_t; | ||||
| 
 | ||||
| void geomancy_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); | ||||
| void geomancy_face_activate(movement_settings_t *settings, void *context); | ||||
| bool geomancy_face_loop(movement_event_t event, movement_settings_t *settings, void *context); | ||||
| void geomancy_face_resign(movement_settings_t *settings, void *context); | ||||
| 
 | ||||
| #define geomancy_face ((const watch_face_t){ \ | ||||
|     geomancy_face_setup, \ | ||||
|     geomancy_face_activate, \ | ||||
|     geomancy_face_loop, \ | ||||
|     geomancy_face_resign, \ | ||||
|     NULL, \ | ||||
| }) | ||||
| 
 | ||||
| #endif // GEOMANCY_FACE_H_
 | ||||
| 
 | ||||
							
								
								
									
										790
									
								
								movement/watch_faces/complication/toss_up_face.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										790
									
								
								movement/watch_faces/complication/toss_up_face.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,790 @@ | ||||
| /*
 | ||||
|  * MIT License | ||||
|  * | ||||
|  * Copyright (c) 2023 Tobias Raayoni Last / @randogoth | ||||
|  * | ||||
|  * 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 <stdlib.h> | ||||
| #include <string.h> | ||||
| #include "toss_up_face.h" | ||||
| #if __EMSCRIPTEN__ | ||||
| #include <time.h> | ||||
| #else | ||||
| #include "saml22j18a.h" | ||||
| #endif | ||||
| 
 | ||||
| static const char heads[] = { '8', 'h', '4', 'E', '(' }; | ||||
| static const char tails[] = { '0', '+', 'N', '3', ')' }; | ||||
| static const uint8_t dd[] = {2, 4, 6, 8, 10,12,20,24,30,32,36,48,99};  | ||||
| 
 | ||||
| static void _roll_dice_multiple(char* result, uint8_t* dice, uint8_t num_dice); | ||||
| static void _sort_coins(char* token, uint8_t num_bits, uint8_t bits, char* heads, char* tails); | ||||
| void _display_coins(char* token, bool* bit_array, uint8_t length, toss_up_state_t *state); | ||||
| static void _toss_up_face_display(toss_up_state_t *state); | ||||
| static void _dice_animation(toss_up_state_t *state); | ||||
| static void _coin_animation(toss_up_state_t *state); | ||||
| 
 | ||||
| // PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| void toss_up_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { | ||||
|     (void) settings; | ||||
|     if (*context_ptr == NULL) { | ||||
|         *context_ptr = malloc(sizeof(toss_up_state_t)); | ||||
|         memset(*context_ptr, 0, sizeof(toss_up_state_t)); | ||||
|         toss_up_state_t *state = (toss_up_state_t *)*context_ptr; | ||||
| 
 | ||||
|         // defaults
 | ||||
|         state->coin_num = 1; | ||||
|         state->dice_num = 1; | ||||
|         state->dice_sides[0] = 6; | ||||
|         state->dice_sides[1] = 6; | ||||
|         state->dice_sides[2] = 6; | ||||
|         state->coin_style[0] = '8'; | ||||
|         state->coin_style[1] = '0'; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void toss_up_face_activate(movement_settings_t *settings, void *context) { | ||||
|     (void) settings; | ||||
|     (void) context; | ||||
| } | ||||
| 
 | ||||
| bool toss_up_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { | ||||
|     toss_up_state_t *state = (toss_up_state_t *)context; | ||||
|     uint8_t i = 0; | ||||
|     switch (event.event_type) { | ||||
|         case EVENT_ACTIVATE: | ||||
|             watch_display_string("    Coins ", 0); | ||||
|             break; | ||||
|         case EVENT_TICK: | ||||
|             if ( state->animate ) { | ||||
|                 state->animation = (state->animation + 1); | ||||
|                 _toss_up_face_display(state); | ||||
|             }  | ||||
|             break; | ||||
|         case EVENT_LIGHT_BUTTON_DOWN: | ||||
|             break; | ||||
|         case EVENT_LIGHT_BUTTON_UP: | ||||
|             if ( state->animate ) break; | ||||
|             // change between coins and dice
 | ||||
|             if ( state->mode <= 1 ) state->mode = 2; | ||||
|             else if ( state->mode >= 2 ) state->mode = 0; | ||||
|             _toss_up_face_display(state); | ||||
|             break; | ||||
|         case EVENT_ALARM_BUTTON_UP: | ||||
|             // toss
 | ||||
|             if ( state->animate ) break; | ||||
|             switch (state->mode) { | ||||
|                 case 0: | ||||
|                     state->mode++; | ||||
|                 case 1: | ||||
|                     state->animate = true; | ||||
|                     for (i = 0; i < state->coin_num; i++) { | ||||
|                         state->coins[i] = divine_bit(); | ||||
|                     }              | ||||
|                     break; | ||||
|                 case 2: | ||||
|                     state->mode++; | ||||
|                 case 3: | ||||
|                     state->animate = true; | ||||
|                     for (i = 0; i < state->dice_num; i++) { | ||||
|                         state->dice[i] = roll_dice(state->dice_sides[i]); | ||||
|                     }   | ||||
|                     break; | ||||
|                 default: | ||||
|                     break; | ||||
|             } | ||||
|             _toss_up_face_display(state); | ||||
|             break; | ||||
|         case EVENT_LIGHT_LONG_PRESS: | ||||
|             if ( state->animate ) break; | ||||
|             state->animate = false; | ||||
|             switch (state->mode) { | ||||
|                 case 0: // change to default coin style
 | ||||
|                     state->coin_style[0] = heads[0]; | ||||
|                     state->coin_style[1] = tails[0]; | ||||
|                     state->coinface = 0; | ||||
|                     break; | ||||
|                 case 1: // change the coin style
 | ||||
|                     state->coinface = (state->coinface + 1) % 5; | ||||
|                     state->coin_style[0] = heads[state->coinface]; | ||||
|                     state->coin_style[1] = tails[state->coinface];          | ||||
|                     break; | ||||
|                 case 2: // change to default dice sides
 | ||||
|                     state->dice_sides[0] = 6; | ||||
|                     state->dice_sides[1] = 6; | ||||
|                     state->dice_sides[2] = 6; | ||||
|                     state->dd = 0; | ||||
|                     break; | ||||
|                 case 3: // change the sides of the dice
 | ||||
|                     state->dd = (state->dd + 1) % 13; | ||||
|                     state->dice_sides[state->dice_num-1] = dd[state->dd]; | ||||
|                     state->dice[state->dice_num-1] = dd[state->dd]; | ||||
|                     break; | ||||
|                 default: | ||||
|                     break; | ||||
|             } | ||||
|             _toss_up_face_display(state); | ||||
|             break; | ||||
|         case EVENT_ALARM_LONG_PRESS: | ||||
|             if ( state->animate ) break; | ||||
|             state->animate = false; | ||||
|             switch (state->mode) { | ||||
|                 case 0: // back to one coin
 | ||||
|                     state->coin_num = 1; | ||||
|                     break; | ||||
|                 case 1: // up to 6 coins total
 | ||||
|                     state->coin_num = (state->coin_num % 6) + 1;              | ||||
|                     break; | ||||
|                 case 2: // back to one dice
 | ||||
|                     state->dice_num = 1; | ||||
|                     break; | ||||
|                 case 3: // add up to 3 dice total
 | ||||
|                     state->dice_num = (state->dice_num % 3) + 1; | ||||
|                     state->dd = 0; | ||||
|                     break; | ||||
|                 default: | ||||
|                     break; | ||||
|             } | ||||
|             _toss_up_face_display(state); | ||||
|             break; | ||||
|         default: | ||||
|             return movement_default_loop_handler(event, settings); | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void toss_up_face_resign(movement_settings_t *settings, void *context) { | ||||
|     (void) settings; | ||||
|     (void) context; | ||||
| } | ||||
| 
 | ||||
| // STATIC FUNCTIONS ///////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| /** @brief handles the display 
 | ||||
|  */ | ||||
| static void _toss_up_face_display(toss_up_state_t *state) { | ||||
|     char buf[11] = {0}; | ||||
|     char token[7] = {0}; | ||||
|     switch ( state->mode ) { | ||||
|         case 0: // coins title
 | ||||
|             sprintf(buf, "    Coins "); | ||||
|             break; | ||||
|         case 1: // coins divination
 | ||||
|             _coin_animation(state); | ||||
|             if ( !state->animate ) { | ||||
|                 watch_clear_display(); | ||||
|                 _display_coins(token, state->coins, state->coin_num, state); | ||||
|                 sprintf(buf, "    %s", token); | ||||
|             } | ||||
|             break; | ||||
|         case 2: // dice title
 | ||||
|             sprintf(buf, "    Dice "); | ||||
|             break; | ||||
|         case 3: // dice divination
 | ||||
|             _dice_animation(state); | ||||
|             if ( !state->animate ) { | ||||
|                 _roll_dice_multiple(token, state->dice, state->dice_num + 1); | ||||
|                 sprintf(buf, "    %s", token); | ||||
|             } | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|     } | ||||
|     watch_display_string(buf, 0); | ||||
| } | ||||
| 
 | ||||
| /** @brief divination method to derive a bit from 32 TRNG bits
 | ||||
|  */ | ||||
| uint8_t divine_bit(void) { | ||||
|     uint32_t stalks; | ||||
|     do { // modulo bias filter
 | ||||
|         stalks = get_true_entropy(); // get 32 TRNG bits as stalks
 | ||||
|     } while (stalks >= INT32_MAX || stalks <= 0); | ||||
| 
 | ||||
|     uint8_t pile1_xor = 0; | ||||
|     uint8_t pile2_xor = 0; | ||||
|     // Divide the stalks into two piles, alternating ends
 | ||||
|     for (uint8_t i = 0; i < 16; i++) { | ||||
|         uint8_t left_bit = (stalks >> (31 - 2*i)) & 1; | ||||
|         uint8_t right_bit = (stalks >> (30 - 2*i)) & 1; | ||||
|         if (i % 2 == 0) { | ||||
|             pile1_xor ^= left_bit; | ||||
|             pile2_xor ^= right_bit; | ||||
|         } else { | ||||
|             pile1_xor ^= right_bit; | ||||
|             pile2_xor ^= left_bit; | ||||
|         } | ||||
|     } | ||||
|     // Take the XOR of the pile results
 | ||||
|     uint8_t result_xor = pile1_xor ^ pile2_xor; | ||||
|     // Output 1 if result_xor is 1, 0 otherwise
 | ||||
|     return result_xor; | ||||
| } | ||||
| 
 | ||||
| /** @brief get 32 True Random Number bits
 | ||||
|  */ | ||||
| uint32_t get_true_entropy(void) { | ||||
|     #if __EMSCRIPTEN__ | ||||
|     return rand() % INT32_MAX; | ||||
|     #else | ||||
|     hri_mclk_set_APBCMASK_TRNG_bit(MCLK); | ||||
|     hri_trng_set_CTRLA_ENABLE_bit(TRNG); | ||||
| 
 | ||||
|     while (!hri_trng_get_INTFLAG_reg(TRNG, TRNG_INTFLAG_DATARDY)); // Wait for TRNG data to be ready
 | ||||
| 
 | ||||
|     hri_trng_clear_CTRLA_ENABLE_bit(TRNG); | ||||
|     hri_mclk_clear_APBCMASK_TRNG_bit(MCLK); | ||||
|     return hri_trng_read_DATA_reg(TRNG); // Read a single 32-bit word from TRNG and return it
 | ||||
|     #endif | ||||
| } | ||||
| 
 | ||||
| // COIN FUNCTIONS /////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| /** @brief sort tossed coins into a pile of heads and a pile of tails
 | ||||
|  */ | ||||
| static void _sort_coins(char* token, uint8_t num_bits, uint8_t bits, char* heads, char* tails) { | ||||
|     uint8_t num_ones = 0; | ||||
|     for (uint8_t i = 0; i < num_bits; i++) { | ||||
|         if ((bits >> i) & 1) { | ||||
|             *token++ = *heads; | ||||
|             num_ones++; | ||||
|         } | ||||
|     } | ||||
|     if ( num_bits < 6 ) { | ||||
|         for (uint8_t i = 0; i < (6 - num_bits); i++) { | ||||
|             *token++ = ' '; | ||||
|         } | ||||
|     } | ||||
|     for (uint8_t i = 0; i < (num_bits - num_ones); i++) { | ||||
| 
 | ||||
|         *token++ = *tails; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** @brief convert bool array of coinflips to integer for sorting
 | ||||
|  */ | ||||
| void _display_coins(char* token, bool* bit_array, uint8_t length, toss_up_state_t *state) { | ||||
|     uint8_t bits = 0; | ||||
|     for (uint8_t i = 0; i < length; i++) { | ||||
|         if (bit_array[i]) { | ||||
|             bits |= (1 << (length - 1 - i)); | ||||
|         } | ||||
|     } | ||||
|     _sort_coins(token, length, bits, &state->coin_style[0], &state->coin_style[1]); | ||||
| } | ||||
| 
 | ||||
| /** @brief coin animation
 | ||||
|  */ | ||||
| static void _coin_animation(toss_up_state_t *state) { | ||||
|     bool heads = false; | ||||
|     bool tails = false; | ||||
|     for (uint8_t i = 0; i < state->coin_num; i++) { | ||||
|         if (state->coins[i] == true) { | ||||
|             heads++; | ||||
|         } else { | ||||
|             tails++; | ||||
|         } | ||||
|     } | ||||
|     movement_request_tick_frequency(32); | ||||
|     switch ( state->animation ) { | ||||
|         case 0: | ||||
|             watch_display_string("      ", 4); | ||||
|             if ( heads ) { | ||||
|                 watch_set_pixel(0, 18); | ||||
|                 watch_set_pixel(2, 18); | ||||
|             } else { | ||||
|                 state->animation = 12; | ||||
|             } | ||||
|             break; | ||||
|         case 1: | ||||
|             if ( heads ) { | ||||
|                 watch_set_pixel(1, 18); | ||||
|             } | ||||
|             break; | ||||
|         case 2: | ||||
|             if ( heads ) { | ||||
|                 watch_set_pixel(0, 19); | ||||
|                 watch_set_pixel(2, 19); | ||||
|             } | ||||
|             break; | ||||
|         case 3: | ||||
|             if ( heads ) { | ||||
|                 watch_clear_pixel(0, 18); | ||||
|                 watch_clear_pixel(2, 18); | ||||
|             } | ||||
|             break; | ||||
|         case 4: | ||||
|             if ( heads ) { | ||||
|                 watch_clear_pixel(1, 18); | ||||
|             } | ||||
|             break; | ||||
|         case 5: | ||||
|             if ( heads ) { | ||||
|                 watch_clear_pixel(0, 19); | ||||
|                 watch_clear_pixel(2, 19); | ||||
|                 watch_set_pixel(1, 17); | ||||
|                 watch_set_pixel(0, 20); | ||||
|             } | ||||
|             break; | ||||
|         case 6: | ||||
|             if ( heads ) { | ||||
|                 watch_set_pixel(2, 20); | ||||
|                 watch_set_pixel(0, 21); | ||||
|             } | ||||
|             break; | ||||
|         case 7: | ||||
|             if ( heads ) { | ||||
|                 watch_set_pixel(1, 21); | ||||
|                 watch_set_pixel(2, 21); | ||||
|             } | ||||
|             break; | ||||
|         case 8: | ||||
|             if ( heads ) { | ||||
|                 watch_clear_pixel(1, 17); | ||||
|                 watch_clear_pixel(0, 20); | ||||
|             } | ||||
|             break; | ||||
|         case 9: | ||||
|             if ( heads ) { | ||||
|                 watch_clear_pixel(2, 20); | ||||
|                 watch_clear_pixel(0, 21); | ||||
|             } | ||||
|             break; | ||||
|         case 10: | ||||
|             if ( heads ) { | ||||
|                 watch_clear_pixel(1, 21); | ||||
|                 watch_clear_pixel(2, 21); | ||||
|                 watch_set_pixel(1, 22); | ||||
|                 watch_set_pixel(2, 22); | ||||
|             } | ||||
|             break; | ||||
|         case 11: | ||||
|             if ( heads ) { | ||||
|                 watch_set_pixel(0, 22); | ||||
|             } | ||||
|             break; | ||||
|         case 12: | ||||
|             if ( heads ) { | ||||
|                 watch_set_pixel(2, 23); | ||||
|                 watch_set_pixel(0, 23); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_set_pixel(0, 18); | ||||
|                 watch_set_pixel(2, 18); | ||||
|             } | ||||
|             break; | ||||
|         case 13: | ||||
|             if ( heads ) { | ||||
|                 watch_clear_pixel(1, 22); | ||||
|                 watch_clear_pixel(2, 22); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_set_pixel(1, 18); | ||||
|             } | ||||
|             break; | ||||
|         case 14: | ||||
|             if ( heads ) { | ||||
|                 watch_clear_pixel(0, 22); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_set_pixel(0, 19); | ||||
|                 watch_set_pixel(2, 19); | ||||
|             } | ||||
|             break; | ||||
|         case 15: | ||||
|             if ( heads ) { | ||||
|                 watch_clear_pixel(2, 23); | ||||
|                 watch_clear_pixel(0, 23); | ||||
|                 watch_set_pixel(2, 0); | ||||
|                 watch_set_pixel(1, 0); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_clear_pixel(0, 18); | ||||
|                 watch_clear_pixel(2, 18); | ||||
|             } | ||||
|             break; | ||||
|         case 16: | ||||
|             if ( heads ) { | ||||
|                 watch_set_pixel(2, 1); | ||||
|                 watch_set_pixel(0, 0); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_clear_pixel(1, 18); | ||||
|             } | ||||
|             break; | ||||
|         case 17: | ||||
|             if ( heads ) { | ||||
|                 watch_set_pixel(2, 10); | ||||
|                 watch_set_pixel(0, 1); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_clear_pixel(0, 19); | ||||
|                 watch_clear_pixel(2, 19); | ||||
|                 watch_set_pixel(1, 17); | ||||
|                 watch_set_pixel(0, 20); | ||||
|             } | ||||
|             break; | ||||
|         case 18: | ||||
|             if ( heads ) { | ||||
|                 watch_clear_pixel(2, 0); | ||||
|                 watch_clear_pixel(1, 0); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_set_pixel(2, 20); | ||||
|                 watch_set_pixel(0, 21); | ||||
|             } | ||||
|             break; | ||||
|         case 19: | ||||
|             if ( heads ) { | ||||
|                 watch_clear_pixel(2, 1); | ||||
|                 watch_clear_pixel(0, 0); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_set_pixel(1, 21); | ||||
|                 watch_set_pixel(2, 21); | ||||
|             } | ||||
|             break; | ||||
|         case 20: | ||||
|             if ( heads ) { | ||||
|                 watch_set_pixel(2, 1); | ||||
|                 watch_set_pixel(0, 0); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_clear_pixel(1, 17); | ||||
|                 watch_clear_pixel(0, 20); | ||||
|             } | ||||
|             break; | ||||
|         case 21: | ||||
|             if ( heads ) { | ||||
|                 watch_set_pixel(2, 0); | ||||
|                 watch_set_pixel(1, 0); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_clear_pixel(2, 20); | ||||
|                 watch_clear_pixel(0, 21); | ||||
|             } | ||||
|             break; | ||||
|         case 22: | ||||
|             if ( heads ) { | ||||
|                 watch_clear_pixel(2, 10); | ||||
|                 watch_clear_pixel(0, 1); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_clear_pixel(1, 21); | ||||
|                 watch_clear_pixel(2, 21); | ||||
|                 watch_set_pixel(1, 22); | ||||
|                 watch_set_pixel(2, 22); | ||||
|             } | ||||
|             break; | ||||
|         case 23: | ||||
|             if ( heads ) { | ||||
|                 watch_clear_pixel(2, 1); | ||||
|                 watch_clear_pixel(0, 0); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_set_pixel(0, 22); | ||||
|             } | ||||
|             break; | ||||
|         case 24: | ||||
|             if ( heads ) { | ||||
|                 watch_set_pixel(2, 23); | ||||
|                 watch_set_pixel(0, 23); | ||||
|                 watch_clear_pixel(2, 0); | ||||
|                 watch_clear_pixel(1, 0); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_set_pixel(2, 23); | ||||
|                 watch_set_pixel(0, 23); | ||||
|             } | ||||
|             break; | ||||
|         case 25: | ||||
|             if ( heads ) { | ||||
|                 watch_set_pixel(0, 22); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_clear_pixel(1, 22); | ||||
|                 watch_clear_pixel(2, 22); | ||||
|             } | ||||
|             break; | ||||
|         case 26: | ||||
|             if ( heads ) { | ||||
|                 watch_set_pixel(1, 22); | ||||
|                 watch_set_pixel(2, 22); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_clear_pixel(0, 22); | ||||
|             } | ||||
|             break; | ||||
|         case 27: | ||||
|             if ( heads ) { | ||||
|                 watch_clear_pixel(2, 23); | ||||
|                 watch_clear_pixel(0, 23); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_clear_pixel(2, 23); | ||||
|                 watch_clear_pixel(0, 23); | ||||
|                 watch_set_pixel(2, 0); | ||||
|                 watch_set_pixel(1, 0); | ||||
|             } | ||||
|             break; | ||||
|         case 28: | ||||
|             if ( heads ) { | ||||
|                 watch_clear_pixel(0, 22); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_set_pixel(2, 1); | ||||
|                 watch_set_pixel(0, 0); | ||||
|             } | ||||
|             break; | ||||
|         case 29: | ||||
|             if ( heads ) { | ||||
|                 watch_set_pixel(1, 21); | ||||
|                 watch_set_pixel(2, 21); | ||||
|                 watch_clear_pixel(1, 22); | ||||
|                 watch_clear_pixel(2, 22); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_set_pixel(2, 10); | ||||
|                 watch_set_pixel(0, 1); | ||||
|             } | ||||
|             break; | ||||
|         case 30: | ||||
|             if ( heads ) { | ||||
|                 watch_set_pixel(2, 20); | ||||
|                 watch_set_pixel(0, 21); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_clear_pixel(1, 0); | ||||
|                 watch_clear_pixel(2, 0); | ||||
|             } | ||||
|             break; | ||||
|         case 31: | ||||
|             if ( heads ) { | ||||
|                 watch_set_pixel(1, 17); | ||||
|                 watch_set_pixel(0, 20); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_clear_pixel(2, 1); | ||||
|                 watch_clear_pixel(0, 0); | ||||
|             } | ||||
|             break; | ||||
|         case 32: | ||||
|             if ( heads ) { | ||||
|                 watch_clear_pixel(1, 21); | ||||
|                 watch_clear_pixel(2, 21); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_clear_pixel(2, 10); | ||||
|                 watch_clear_pixel(0, 1); | ||||
|                 watch_set_pixel(0, 2); | ||||
|                 watch_set_pixel(1, 2); | ||||
|             } | ||||
|             break; | ||||
|         case 33: | ||||
|             if ( heads ) { | ||||
|                 watch_clear_pixel(2, 20); | ||||
|                 watch_clear_pixel(0, 21); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_set_pixel(2, 2); | ||||
|                 watch_set_pixel(0, 3); | ||||
|             } | ||||
|             break; | ||||
|         case 34: | ||||
|             if ( heads ) { | ||||
|                 watch_set_pixel(0, 19); | ||||
|                 watch_set_pixel(2, 19); | ||||
|                 watch_clear_pixel(1, 17); | ||||
|                 watch_clear_pixel(0, 20); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_set_pixel(2, 3); | ||||
|                 watch_set_pixel(0, 4); | ||||
|             } | ||||
|             break; | ||||
|         case 35: | ||||
|             if ( heads ) { | ||||
|                 watch_set_pixel(1, 18); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_clear_pixel(1, 2); | ||||
|                 watch_clear_pixel(0, 2); | ||||
|             } | ||||
|             break; | ||||
|         case 36: | ||||
|             if ( heads ) { | ||||
|                 watch_set_pixel(0, 18); | ||||
|                 watch_set_pixel(2, 18); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_clear_pixel(2, 2); | ||||
|                 watch_clear_pixel(0, 3); | ||||
|             } | ||||
|             break; | ||||
|         case 37: | ||||
|             if ( heads ) { | ||||
|                 watch_clear_pixel(0, 19); | ||||
|                 watch_clear_pixel(2, 19); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_clear_pixel(2, 3); | ||||
|                 watch_clear_pixel(0, 4); | ||||
|                 watch_set_pixel(1, 4); | ||||
|                 watch_set_pixel(0, 5); | ||||
|             } | ||||
|             break; | ||||
|         case 38: | ||||
|             if ( heads ) { | ||||
|                 watch_clear_pixel(1, 18); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_set_pixel(2, 4); | ||||
|                 watch_set_pixel(0, 6); | ||||
|             } | ||||
|             break; | ||||
|         case 39: | ||||
|             if ( heads ) { | ||||
|                 watch_clear_pixel(0, 18); | ||||
|                 watch_clear_pixel(2, 18); | ||||
|             } | ||||
|             if ( tails ) { | ||||
|                 watch_set_pixel(1, 6); | ||||
|                 watch_set_pixel(2, 5); | ||||
|             } | ||||
|             state->animate = false; | ||||
|             state->animation = 0; | ||||
|             movement_request_tick_frequency(1); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // DICE FUNCTIONS /////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| /** @brief rolls a dice
 | ||||
|  */ | ||||
| uint8_t roll_dice(uint8_t sides) { | ||||
|     uint8_t bits_needed = 0; | ||||
|     uint8_t temp_sides = sides - 1; | ||||
|     uint8_t result = 0; | ||||
|     while (temp_sides > 0) { | ||||
|         bits_needed++; // how many bits do we need to represent this number?
 | ||||
|         temp_sides >>= 1; // Shift right to check the next bit
 | ||||
|     } | ||||
|     do { | ||||
|         result = 0; | ||||
|         for (int i = 0; i < bits_needed; i++) { | ||||
|             result <<= 1; // Shift left to make room for the next bit
 | ||||
|             result |= divine_bit(); // Add the next bit to the result
 | ||||
|         } | ||||
|     } while ( result > sides -1 ); | ||||
|     return result + 1; // Add 1 to convert the range from 0 to sides-1 to 1 to sides
 | ||||
| } | ||||
| 
 | ||||
| /** @brief roll multiple dice and print a char array for displaying them
 | ||||
|  */ | ||||
| static void _roll_dice_multiple(char* result, uint8_t* dice, uint8_t num_dice) { | ||||
|     // initialize the result array to all spaces
 | ||||
|     memset(result, ' ', 6); | ||||
| 
 | ||||
|     // roll the dice and write the result to the result array
 | ||||
|     for (uint8_t i = 0; i < num_dice-1; i++) { | ||||
|         uint8_t dice_result = dice[i]; | ||||
|         uint8_t tens_digit = dice_result / 10; | ||||
|         uint8_t ones_digit = dice_result % 10; | ||||
|         result[(i * 2)] = tens_digit == 0 ? ' ' : (char)('0' + tens_digit); | ||||
|         result[(i * 2) + 1] = (char)('0' + ones_digit); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** @brief dice animation
 | ||||
|  */ | ||||
| static void _dice_animation(toss_up_state_t *state) { | ||||
|     watch_display_string("      ", 4); | ||||
|     for (uint8_t i = 0; i < state->dice_num; i++) { | ||||
|         watch_display_string("0",i*2 + 5); | ||||
|     } | ||||
|     movement_request_tick_frequency(16); | ||||
|     switch ( state->animation ) { | ||||
|         case 0: | ||||
|             watch_clear_pixel(1, 17); | ||||
|             watch_clear_pixel(0, 0); | ||||
|             watch_clear_pixel(1, 6); | ||||
|             break; | ||||
|         case 1: | ||||
|             watch_clear_pixel(2, 20); | ||||
|             watch_clear_pixel(1, 0); | ||||
|             watch_clear_pixel(0, 6); | ||||
|             break; | ||||
|         case 2: | ||||
|             watch_clear_pixel(2, 21); | ||||
|             watch_clear_pixel(2, 0); | ||||
|             watch_clear_pixel(0, 5); | ||||
|             break; | ||||
|         case 3: | ||||
|             watch_clear_pixel(1, 21); | ||||
|             watch_clear_pixel(2, 1); | ||||
|             watch_clear_pixel(1, 4); | ||||
|             break; | ||||
|         case 4: | ||||
|             watch_clear_pixel(0, 21); | ||||
|             watch_clear_pixel(2, 10); | ||||
|             watch_clear_pixel(2, 4); | ||||
|             break; | ||||
|         case 5: | ||||
|             watch_clear_pixel(0, 20); | ||||
|             watch_clear_pixel(0, 1); | ||||
|             watch_clear_pixel(2, 5); | ||||
|             break; | ||||
|         case 6: | ||||
|             watch_clear_pixel(1, 17); | ||||
|             watch_clear_pixel(0, 0); | ||||
|             watch_clear_pixel(1, 6); | ||||
|             break; | ||||
|         case 7: | ||||
|             watch_clear_pixel(2, 20); | ||||
|             watch_clear_pixel(1, 0); | ||||
|             watch_clear_pixel(0, 6); | ||||
|             break; | ||||
|         case 8: | ||||
|             watch_clear_pixel(2, 21); | ||||
|             watch_clear_pixel(2, 0); | ||||
|             watch_clear_pixel(0, 5); | ||||
|             break; | ||||
|         case 9: | ||||
|             watch_clear_pixel(1, 21); | ||||
|             watch_clear_pixel(2, 1); | ||||
|             watch_clear_pixel(1, 4); | ||||
|             break; | ||||
|         case 10: | ||||
|             watch_clear_pixel(0, 21); | ||||
|             watch_clear_pixel(2, 10); | ||||
|             watch_clear_pixel(2, 4); | ||||
|             break; | ||||
|         case 11: | ||||
|             watch_clear_pixel(0, 20); | ||||
|             watch_clear_pixel(0, 1); | ||||
|             watch_clear_pixel(2, 5); | ||||
|             state->animate = false; | ||||
|             state->animation = 0; | ||||
|             movement_request_tick_frequency(1); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										112
									
								
								movement/watch_faces/complication/toss_up_face.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								movement/watch_faces/complication/toss_up_face.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,112 @@ | ||||
| /*
 | ||||
|  * MIT License | ||||
|  * | ||||
|  * Copyright (c) 2023 Tobias Raayoni Last / @randogoth | ||||
|  * | ||||
|  * 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 TOSS_UP_FACE_H_ | ||||
| #define TOSS_UP_FACE_H_ | ||||
| 
 | ||||
| #include "movement.h" | ||||
| 
 | ||||
| /*
 | ||||
|  * TOSS UP FACE | ||||
|  * ============ | ||||
|  * | ||||
|  * Playful watch face for games of chance or divination using coins or dice. | ||||
|  *  | ||||
|  * LIGHT switches between Coins and Dice mode | ||||
|  *  | ||||
|  * COINS | ||||
|  * ===== | ||||
|  *  | ||||
|  * ALARM tosses a coin. If it lands on heads it gets sorted to the left side of the | ||||
|  * display, if it lands on tails then sorted to the right side. | ||||
|  *  | ||||
|  * LONG PRESSING ALARM adds up to 5 more coins to the toss for more nuance in the decision | ||||
|  * making (e.g. three heads vs two tails could be read as "yes, but with serious doubts"). | ||||
|  *  | ||||
|  * LONG PRESSING LIGHT flips through additional style for the coins from the default Ө/O  | ||||
|  * to H/T (heads/tails), Y/N (yes/no), E/Ǝ, C/Ↄ | ||||
|  *  | ||||
|  * LONG PRESSING ALARM on the "Coins" title page resets to one coin. | ||||
|  * LONG PRESSING LIGHT on the "Coins" title page resets the style to Ө/O | ||||
|  *  | ||||
|  * DICE | ||||
|  * ==== | ||||
|  *  | ||||
|  * ALARM rolls a six sided dice. | ||||
|  *  | ||||
|  * LONG PRESSING ALARM adds up to 2 more dice to the roll. | ||||
|  *  | ||||
|  * LONG PRESSING LIGHT flips through other available polyhedral dice types with less or more | ||||
|  * than the default 6 sides. The options are D2, D4, D6, D8, D10, D12, D20, D24, D30, D32, D36,  | ||||
|  * D48, and a hypothetical D99.  | ||||
|  *  | ||||
|  * When more than one dice is used for a roll this changes only the last added dice. (see Note | ||||
|  * below) | ||||
|  * | ||||
|  * LONG PRESSING ALARM on the "Dice" title page resets to one dice. | ||||
|  * LONG PRESSING LIGHT on the "Dice" title page resets the dice to D6. | ||||
|  *  | ||||
|  * Please Note: If you need let's say a D8, D12, and D20 for your rolls then the procedure to | ||||
|  * set this up would be as follows: from the default screen where you can roll the one D6 dice  | ||||
|  * you would LONG PRESS LIGHT a few times to change the D6 to a D8, then LONG PRESS ALARM to add  | ||||
|  * a second dice, LONG PRESS LIGHT again until the second dice changes to D12, then LONG PRESS | ||||
|  * ALARM to add the third dice and LONG PRESS LIGHT again a few times until it becomes a D20. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| typedef struct { | ||||
|     // Anything you need to keep track of, put it here!
 | ||||
|     uint32_t entropy; | ||||
|     uint8_t mode : 4; // 1 coin, 2 coins, 3 coins, 4 coins, dice, iching, geomnc
 | ||||
|     bool setup; | ||||
|     bool coins[6]; | ||||
|     uint8_t coin_num : 3; | ||||
|     char coin_style[2]; | ||||
|     uint8_t coinface : 3; | ||||
|     uint8_t dice[3]; | ||||
|     uint8_t dice_num : 2; | ||||
|     uint8_t dd : 6; | ||||
|     uint8_t dice_sides[3]; | ||||
|     uint8_t animation; | ||||
|     bool animate; | ||||
| } toss_up_state_t; | ||||
| 
 | ||||
| uint32_t get_true_entropy(void); | ||||
| uint8_t divine_bit(void); | ||||
| uint8_t roll_dice(uint8_t sides); | ||||
| void toss_up_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); | ||||
| void toss_up_face_activate(movement_settings_t *settings, void *context); | ||||
| bool toss_up_face_loop(movement_event_t event, movement_settings_t *settings, void *context); | ||||
| void toss_up_face_resign(movement_settings_t *settings, void *context); | ||||
| 
 | ||||
| #define toss_up_face ((const watch_face_t){ \ | ||||
|     toss_up_face_setup, \ | ||||
|     toss_up_face_activate, \ | ||||
|     toss_up_face_loop, \ | ||||
|     toss_up_face_resign, \ | ||||
|     NULL, \ | ||||
| }) | ||||
| 
 | ||||
| #endif // TOSS_UP_FACE_H_
 | ||||
| 
 | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user