#include "yatzee_icons.h" #include #include #include #include #include #include #include #define BASE_X 18 #define BASE_Y 44 #define DICE_OFFSET 12 #define HOLD "*" #define MAX_DICE 5 #define NUM_SCORES 13 bool new_game = true; bool game_over = false; bool bonus_added = false; int8_t num_bonus_yatzees = 0; // struct to hold image posistion for dice typedef struct { // +-----x // | // | // y uint8_t x; uint8_t y; } ImagePosition; typedef struct { char* name; uint32_t value; bool used; int8_t row; int8_t col; uint8_t (*fn)(); // pointer to function that calculates score } Score; typedef struct { uint8_t index; uint8_t value; bool isHeld; } Die; typedef struct { int index; char* symbol; } Cursor; // locations for the dice images ImagePosition position[5] = { {.x = BASE_X-DICE_OFFSET, .y = BASE_Y}, {.x = BASE_X*2-DICE_OFFSET, .y = BASE_Y}, {.x = BASE_X*3-DICE_OFFSET, .y = BASE_Y}, {.x = BASE_X*4-DICE_OFFSET, .y = BASE_Y}, {.x = BASE_X*5-DICE_OFFSET, .y = BASE_Y}, }; // these are the positions that the score cursor will cycle through ImagePosition score_positions[13] = { {.x=15, .y=0}, {.x=15, .y=9}, {.x=15, .y=18}, {.x=15, .y=27}, {.x=44, .y=0}, {.x=44, .y=9}, {.x=44, .y=18}, {.x=44, .y=27}, {.x=77, .y=0}, {.x=77, .y=9}, {.x=77, .y=18}, {.x=77, .y=27}, {.x=91, .y=21}, }; // cursor to select dice Cursor cursor = { .index = 0, .symbol = "^" }; // cursor to select score Cursor scoreCursor = { .index = -1, .symbol = "_" }; // setup array to store dice info Die die[5] = { {.index = 0, .value = 1, .isHeld = false}, {.index = 1, .value = 1, .isHeld = false}, {.index = 2, .value = 1, .isHeld = false}, {.index = 3, .value = 1, .isHeld = false}, {.index = 4, .value = 1, .isHeld = false}, }; uint8_t upperScore = 0; int32_t lowerScore = 0; int32_t totalScore = 0; uint8_t roll = 0; uint8_t totalrolls = 0; // ############################################# // # The following methods add the score for # // # whichever number is mentioned. # // ############################################# static uint8_t ones() { uint8_t sum = 0; for (uint8_t i = 0; i < 5; i++) { if (die[i].value == 1) { sum++; } } return sum; } static uint8_t twos() { uint8_t sum = 0; for (uint8_t i = 0; i < 5; i++) { if (die[i].value == 2) { sum = sum+2; } } return sum; } static uint8_t threes() { uint8_t sum = 0; for (uint8_t i = 0; i < 5; i++) { if (die[i].value == 3) { sum = sum+3; } } return sum; } static uint8_t fours() { uint8_t sum = 0; for (uint8_t i = 0; i < 5; i++) { if (die[i].value == 4) { sum = sum+4; } } return sum; } static uint8_t fives() { uint8_t sum = 0; for (uint8_t i = 0; i < 5; i++) { if (die[i].value == 5) { sum = sum+5; } } return sum; } static uint8_t sixes() { uint8_t sum = 0; for (uint8_t i = 0; i < 5; i++) { if (die[i].value == 6) { sum = sum+6; } } return sum; } // #################################################### // # Helper methods for the special score types # // # defined before them so they can be used # // # since this whole thing is a linear mess # // # lol. # // # add_dice: # // # inputs: none # // # output: int8_t value of roll # // # check_if_score_used: // # inputs: Score // # output: true if score.used = true // # # # # # # # # # # # # # # # # # # # # # # # # # # int8_t add_dice() { int8_t sum = 0; for (int8_t i=0; i 2) { score = add_dice(); } } } return score; } static uint8_t fourkind() { int8_t score = 0; for (int8_t num=1; num<7; num++) { int8_t sum = 0; for (int8_t i=0; i 3) { score = add_dice(); } } } return score; } static uint8_t fullhouse() { bool check1 = false; bool check2 = false; int8_t val1 = 0; int8_t val2 = 0; UNUSED(val2); UNUSED(val1); //check 1 for three of a kind for (int8_t num=1; num<7; num++) { int8_t sum = 0; for (int8_t i=0; i 2) { val1 = die[i].value; check1 = true; } } } // return if check 1 failed if (check1 == false) { return 0; } // check 2 for two of a kind. for (int8_t num=1; num<7; num++) { if (num==val1) {continue;} int8_t sum = 0; for (int8_t i=0; i 1) { val2 = die[i].value; check2 = true; } } if (check1 && check2) { return 25; } } return 0; } // # # # # # # # # # # # # # # # # # # # # # # # # # # # // # I'm dumb so I asked ChatGPT to write the # // # smallstraight function for me. Then I adapted it # // # fo the largestraight function. # // # # # # # # # # # # # # # # # # # # # # # # # # # # # static uint8_t smallstraight() { // Create a new array with the frequencies of the different die faces int8_t frequencies[6] = {0}; for (int8_t i = 0; i < 5; i++) { int8_t face = die[i].value; frequencies[face - 1]++; } // Check if there is a sequence of 4 consecutive die faces with at least one die bool found_small_straight = false; for (int i = 0; i < 3 && !found_small_straight; i++) { if (frequencies[i] > 0 && frequencies[i + 1] > 0 && frequencies[i + 2] > 0 && frequencies[i + 3] > 0) { found_small_straight = true; } } if (found_small_straight) { return 30; } else { return 0; } } static uint8_t largestraight() { // Create a new array with the frequencies of the different die faces int8_t frequencies[6] = {0}; for (int8_t i = 0; i < 5; i++) { int8_t face = die[i].value; frequencies[face - 1]++; } // Check if there is a sequence of 4 consecutive die faces with at least one die bool found_large_straight = false; for (int i = 0; i < 3 && !found_large_straight; i++) { if (frequencies[i] > 0 && frequencies[i + 1] > 0 && frequencies[i + 2] > 0 && frequencies[i + 3] > 0 && frequencies[i + 4] > 0) { found_large_straight = true; } } if (found_large_straight) { return 40; } else { return 0; } } static uint8_t chance() { // chance allows your roll to count for the raw number of pips showing int8_t sum = 0; for (int8_t i = 0; i63 at the end of the game, // a 35 point bonus is added to the total score snprintf(buffer, sizeof(buffer), "Sub\n%u", upperScore); elements_multiline_text_aligned(canvas, 117, 0, AlignCenter, AlignTop, buffer); snprintf(buffer, sizeof(buffer), "Total\n%ld", totalScore); elements_multiline_text_aligned(canvas, 117, 22, AlignCenter, AlignTop, buffer); if (totalrolls == 0) { snprintf(buffer, sizeof(buffer), "Roll\n%s", " "); elements_multiline_text_aligned(canvas, 117, 64, AlignCenter, AlignBottom, buffer); } else { snprintf(buffer, sizeof(buffer), "Roll\n%u", totalrolls); elements_multiline_text_aligned(canvas, 117, 64, AlignCenter, AlignBottom, buffer); } // Check for then handle end of game // add num_bonus_yatzees to total rounds so that multiple // yatzees can be scored without impacting the number of rounds before // the game is over int8_t total_rounds = num_bonus_yatzees; // add up number of scores counted so far for (int8_t i = 0; i= 63 && bonus_added == false) { totalScore+=35; bonus_added = true; } // set game over to true and tell the user the game is over game_over = true; elements_button_center(canvas, "Game Over"); } } } // define the callback for helping ViewPort get InputEvent and place it in the event_queue defined in the main method static void app_input_callback(InputEvent* input_event, void* ctx) { furi_assert(ctx); FuriMessageQueue* event_queue = ctx; furi_message_queue_put(event_queue, input_event, FuriWaitForever); } // roll them diiiiceeee static void roll_dice() { // increment roll count totalrolls++; for (uint8_t i = 0; i < MAX_DICE; i++) { // dont reroll if the dice is being held if (die[i].isHeld == false) { die[i].value = 1 + rand() % 6; } } // if 3 rolls have been used, force user to select a score. if(totalrolls == 3) { scoreCursor.index = 0; } } static void clear_board() { // reset board after adding score totalrolls = 0; for (int8_t i=0; i < MAX_DICE; i++) { die[i].isHeld = false; } scoreCursor.index = -1; cursor.index = 0; } static void add_score() { // return when scoring is not possible if (cursor.index != -1 || totalrolls == 0 || (scorecard[scoreCursor.index].used && strcmp(scorecard[scoreCursor.index].name,"Yz")!=0)){ return; } // extra yatzee scores if (scoreCursor.index == 12 && scorecard[scoreCursor.index].used) { uint8_t yatzee_score = (*scorecard[12].fn)(); scorecard[12].value += 2*yatzee_score; lowerScore+=100; num_bonus_yatzees++; } // upper score for (int8_t i = 0; i < 6; i++) { if (scoreCursor.index == i && scorecard[scoreCursor.index].used == false) { scorecard[i].value =(*scorecard[i].fn)(); upperScore+=scorecard[i].value; scorecard[i].used = true; } } // lower score for (int8_t i = 6; i < 13; i++) { if (scoreCursor.index == i && scorecard[scoreCursor.index].used == false) { scorecard[i].value = (*scorecard[i].fn)(); lowerScore+=scorecard[i].value; scorecard[i].used = true; } } // recalculate total score totalScore = lowerScore + upperScore; clear_board(); } // Entry Point int32_t yatzee_main(void* p) { UNUSED(p); // Initialize event queue to handle incoming events like button presses // Use FuriMessageQueue as type as defined in furi api // InputEvents are supported by app_input_callback FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); // Initialize viewport ViewPort* view_port = view_port_alloc(); // Set system callbacks view_port_draw_callback_set(view_port, app_draw_callback, view_port); view_port_input_callback_set(view_port, app_input_callback, event_queue); // Open GUI & register viewport Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); // hold input event InputEvent event; // Create a loop for the app to run in and handle InputEvents bool isRunning = true; while(isRunning) { if (totalrolls == 3) { cursor.index = -1; } if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) { if((event.type == InputTypePress) || event.type == InputTypeRepeat) { switch(event.key) { case InputKeyLeft: if(cursor.index == -1) { if(scoreCursor.index == 0 && totalrolls == 3){ scoreCursor.index = NUM_SCORES-1; } else if (scoreCursor.index==0) { scoreCursor.index = -1; cursor.index = 4; } else { scoreCursor.index--; } } else { if(cursor.index == 0) { cursor.index = -1; scoreCursor.index = NUM_SCORES-1; } else { cursor.index--; } } break; case InputKeyRight: // cursor.index == -1 means that scoreCursor is active if(cursor.index == -1) { if(scoreCursor.index == NUM_SCORES-1 && totalrolls == 3){ scoreCursor.index = 0; } else if (scoreCursor.index == NUM_SCORES-1) { scoreCursor.index = -1; cursor.index = 0; } else { scoreCursor.index++; } // if cursor.index is not -1, then dice cursor is active } else { if(cursor.index == 4) { cursor.index = -1; scoreCursor.index = 0; } else { cursor.index++; } } break; case InputKeyUp: if (totalrolls < 3) { roll_dice(); } // if (check_for_bonus_yatzee() && scorecard[13].used) { // num_bonus_yatzees++; // totalScore+=100; // // clear_board(); // } break; case InputKeyDown: add_score(); break; case InputKeyOk: if (new_game) { new_game = false; break; } if (game_over) { isRunning = false; } if (cursor.index == -1 || totalrolls == 0) { break; } if (die[cursor.index].isHeld == false) { die[cursor.index].isHeld = true; } else { die[cursor.index].isHeld = false; } break; default: isRunning = false; break; } } } // after every event, update view_port // uses app_draw_callback which is set before the game loop begins. view_port_update(view_port); } // cleanup view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); view_port_free(view_port); furi_message_queue_free(event_queue); furi_record_close(RECORD_GUI); return 0; }