Loading gamelogic.c +126 −99 Original line number Diff line number Diff line Loading @@ -108,29 +108,36 @@ void clear_held_values(game_context *ctx) { } // todo: this should only map the selection to the method, and then yaht.c will handle the scoreboard assignment (I think -- revisit to confirm) unsigned int calculate_selection_score(game_context *ctx, char *selection) { unsigned int calculate_selection_score(game_context *ctx, enum scorecard_command selection) { // discern score action based on the scoring selection // we pass in a whole game_context because we need to write to the scorecard and read the dice values int points_awarded = 0; if (strcasecmp(selection, "aces") == 0) { switch (selection) { case ACES: ctx->game_state.scorecard.aces = sum_dice_matching_value(ctx->game_state.dice, 1); points_awarded = ctx->game_state.scorecard.aces; } else if (strcasecmp(selection, "twos") == 0) { break; case TWOS: ctx->game_state.scorecard.twos = sum_dice_matching_value(ctx->game_state.dice, 2); points_awarded = ctx->game_state.scorecard.twos; } else if (strcasecmp(selection, "threes") == 0) { break; case THREES: ctx->game_state.scorecard.threes = sum_dice_matching_value(ctx->game_state.dice, 3); points_awarded = ctx->game_state.scorecard.threes; } else if (strcasecmp(selection, "fours") == 0) { break; case FOURS: ctx->game_state.scorecard.fours = sum_dice_matching_value(ctx->game_state.dice, 4); points_awarded = ctx->game_state.scorecard.fours; } else if (strcasecmp(selection, "fives") == 0) { break; case FIVES: ctx->game_state.scorecard.fives = sum_dice_matching_value(ctx->game_state.dice, 5); points_awarded = ctx->game_state.scorecard.fives; } else if (strcasecmp(selection, "sixes") == 0) { break; case SIXES: ctx->game_state.scorecard.sixes = sum_dice_matching_value(ctx->game_state.dice, 6); points_awarded = ctx->game_state.scorecard.sixes; } else if (strcasecmp(selection, "three of a kind") == 0) { break; case THREE_OF_A_KIND: // sum all dice if 3 of a kind match; 0 otherwise if (n_of_a_kind(ctx->game_state.dice, NUM_DICE, 3, 0)) { ctx->game_state.scorecard.three_of_a_kind = sum_dice(ctx->game_state.dice); Loading @@ -138,7 +145,8 @@ unsigned int calculate_selection_score(game_context *ctx, char *selection) { ctx->game_state.scorecard.three_of_a_kind = 0; } points_awarded = ctx->game_state.scorecard.three_of_a_kind; } else if (strcasecmp(selection, "four of a kind") == 0) { break; case FOUR_OF_A_KIND: // sum all dice if 4 of a kind match; 0 otherwise if (n_of_a_kind(ctx->game_state.dice, NUM_DICE, 4, 0)) { ctx->game_state.scorecard.four_of_a_kind = sum_dice(ctx->game_state.dice); Loading @@ -146,20 +154,8 @@ unsigned int calculate_selection_score(game_context *ctx, char *selection) { ctx->game_state.scorecard.four_of_a_kind = 0; } points_awarded = ctx->game_state.scorecard.four_of_a_kind; } else if (strcasecmp(selection, "full house") == 0) { // 25 points for a combination of three of one number + two of another; 0 otherwise unsigned int three_of_a_kind_value = n_of_a_kind(ctx->game_state.dice, NUM_DICE, 3, 0); unsigned int two_of_a_kind_value = 0; if (three_of_a_kind_value) { two_of_a_kind_value = n_of_a_kind(ctx->game_state.dice, NUM_DICE, 2, three_of_a_kind_value); } if (three_of_a_kind_value && two_of_a_kind_value) { ctx->game_state.scorecard.full_house = 25; } else { ctx->game_state.scorecard.full_house = 0; } points_awarded = ctx->game_state.scorecard.full_house; } else if (strcasecmp(selection, "small straight") == 0) { break; case SMALL_STRAIGHT: // 30 points for a sequence of four consecutive numbers; 0 otherwise if (n_in_a_row(ctx->game_state.dice, NUM_DICE, 4)) { ctx->game_state.scorecard.small_straight = 30; Loading @@ -167,7 +163,8 @@ unsigned int calculate_selection_score(game_context *ctx, char *selection) { ctx->game_state.scorecard.small_straight = 0; } points_awarded = ctx->game_state.scorecard.small_straight; } else if (strcasecmp(selection, "large straight") == 0) { break; case LARGE_STRAIGHT: // 40 points for a sequence of five consecutive numbers; 0 otherwise if (n_in_a_row(ctx->game_state.dice, NUM_DICE, 5)) { ctx->game_state.scorecard.large_straight = 40; Loading @@ -175,7 +172,22 @@ unsigned int calculate_selection_score(game_context *ctx, char *selection) { ctx->game_state.scorecard.large_straight = 0; } points_awarded = ctx->game_state.scorecard.large_straight; } else if (strcasecmp(selection, "yahtc") == 0) { break; case FULL_HOUSE: // 25 points for a combination of three of one number + two of another; 0 otherwise unsigned int three_of_a_kind_value = n_of_a_kind(ctx->game_state.dice, NUM_DICE, 3, 0); unsigned int two_of_a_kind_value = 0; if (three_of_a_kind_value) { two_of_a_kind_value = n_of_a_kind(ctx->game_state.dice, NUM_DICE, 2, three_of_a_kind_value); } if (three_of_a_kind_value && two_of_a_kind_value) { ctx->game_state.scorecard.full_house = 25; } else { ctx->game_state.scorecard.full_house = 0; } points_awarded = ctx->game_state.scorecard.full_house; break; case YAHTC: // 50 points for five of a kind; 0 otherwise if (n_of_a_kind(ctx->game_state.dice, NUM_DICE, NUM_DICE, 0)) { ctx->game_state.scorecard.yahtc = 50; Loading @@ -183,10 +195,14 @@ unsigned int calculate_selection_score(game_context *ctx, char *selection) { ctx->game_state.scorecard.yahtc = 0; } points_awarded = ctx->game_state.scorecard.yahtc; } else if (strcasecmp(selection, "chance") == 0) { break; case CHANCE: // sum of all dice ctx->game_state.scorecard.chance = sum_dice(ctx->game_state.dice); points_awarded = ctx->game_state.scorecard.chance; break; case SCORING_MENU_BACK: break; } return points_awarded; Loading Loading @@ -235,11 +251,6 @@ void hold_dice_with_menu(struct die_st *dice, unsigned int num_dice) { dice_hold_selections[i].enabled = 1; // todo cm delete: you don't even need a command for this because we go by the "multi_selected" value for dice hold assignments // dice_hold_selections[i].command = malloc(SCORECARD_NUM_ITEMS * sizeof(char)); // sprintf(dice_hold_selections[i].command, "toggle_hold_%1d", i); dice_hold_selections[i].command = ""; } char *action = menu_prompt_multi_select(dice_hold_selections, num_dice, "Hold dice", "held"); Loading @@ -252,28 +263,25 @@ void hold_dice_with_menu(struct die_st *dice, unsigned int num_dice) { if (dice_hold_selections[i].title) { free(dice_hold_selections[i].title); } // if (dice_hold_selections[i].command) { // free(dice_hold_selections[i].command); // } } } char *get_player_score_selection_with_menu(struct scorecard_st *scorecard) { int get_player_score_selection_with_menu(struct scorecard_st *scorecard) { const menu_item scoring_menu[] = { {.title = "aces", .command = "aces", .multi_selected = 0, .enabled = scorecard->aces == SCORE_UNSCORED}, {.title = "twos", .command = "twos", .multi_selected = 0, .enabled = scorecard->twos == SCORE_UNSCORED}, {.title = "threes", .command = "threes", .multi_selected = 0, .enabled = scorecard->threes == SCORE_UNSCORED}, {.title = "fours", .command = "fours", .multi_selected = 0, .enabled = scorecard->fours == SCORE_UNSCORED}, {.title = "fives", .command = "fives", .multi_selected = 0, .enabled = scorecard->fives == SCORE_UNSCORED}, {.title = "sixes", .command = "sixes", .multi_selected = 0, .enabled = scorecard->sixes == SCORE_UNSCORED}, {.title = "three of a kind", .command = "three of a kind", .multi_selected = 0, .enabled = scorecard->three_of_a_kind == SCORE_UNSCORED}, {.title = "four of a kind", .command = "four of a kind", .multi_selected = 0, .enabled = scorecard->four_of_a_kind == SCORE_UNSCORED}, {.title = "small straight", .command = "small straight", .multi_selected = 0, .enabled = scorecard->small_straight == SCORE_UNSCORED}, {.title = "large straight", .command = "large straight", .multi_selected = 0, .enabled = scorecard->large_straight == SCORE_UNSCORED}, {.title = "full house", .command = "full house", .multi_selected = 0, .enabled = scorecard->full_house == SCORE_UNSCORED}, {.title = "yahtc", .command = "yahtc", .multi_selected = 0, .enabled = scorecard->yahtc == SCORE_UNSCORED}, {.title = "chance", .command = "chance", .multi_selected = 0, .enabled = scorecard->chance == SCORE_UNSCORED}, {.title = "{BACK}", .command = "back", .multi_selected = 0, .enabled = 1} {.title = "aces", .command = ACES, .multi_selected = 0, .enabled = scorecard->aces == SCORE_UNSCORED}, {.title = "twos", .command = TWOS, .multi_selected = 0, .enabled = scorecard->twos == SCORE_UNSCORED}, {.title = "threes", .command = THREES, .multi_selected = 0, .enabled = scorecard->threes == SCORE_UNSCORED}, {.title = "fours", .command = FOURS, .multi_selected = 0, .enabled = scorecard->fours == SCORE_UNSCORED}, {.title = "fives", .command = FIVES, .multi_selected = 0, .enabled = scorecard->fives == SCORE_UNSCORED}, {.title = "sixes", .command = SIXES, .multi_selected = 0, .enabled = scorecard->sixes == SCORE_UNSCORED}, {.title = "three of a kind", .command = THREE_OF_A_KIND, .multi_selected = 0, .enabled = scorecard->three_of_a_kind == SCORE_UNSCORED}, {.title = "four of a kind", .command = FOUR_OF_A_KIND, .multi_selected = 0, .enabled = scorecard->four_of_a_kind == SCORE_UNSCORED}, {.title = "small straight", .command = SMALL_STRAIGHT, .multi_selected = 0, .enabled = scorecard->small_straight == SCORE_UNSCORED}, {.title = "large straight", .command = LARGE_STRAIGHT, .multi_selected = 0, .enabled = scorecard->large_straight == SCORE_UNSCORED}, {.title = "full house", .command = FULL_HOUSE, .multi_selected = 0, .enabled = scorecard->full_house == SCORE_UNSCORED}, {.title = "yahtc", .command = YAHTC, .multi_selected = 0, .enabled = scorecard->yahtc == SCORE_UNSCORED}, {.title = "chance", .command = CHANCE, .multi_selected = 0, .enabled = scorecard->chance == SCORE_UNSCORED}, {.title = "{BACK}", .command = SCORING_MENU_BACK, .multi_selected = 0, .enabled = 1} }; unsigned int num_menu_items = sizeof(scoring_menu) / sizeof(menu_item); return menu_prompt(scoring_menu, num_menu_items, "Scoring", "already played"); Loading Loading @@ -367,3 +375,22 @@ unsigned int n_in_a_row(struct die_st *dice, unsigned int num_dice, unsigned int } return 0; } const char *command_name[] = { "aces", "twos", "threes", "fours", "fives", "sixes", "three of a kind", "four of a kind", "small straight", "large straight", "yahtc", "chance", "" }; const char *name_of_scorecard_command(enum scorecard_command command) { return command_name[command]; } gamelogic.h +20 −2 Original line number Diff line number Diff line Loading @@ -33,6 +33,23 @@ struct scorecard_st { }; extern const unsigned int SCORECARD_NUM_ITEMS; enum scorecard_command { ACES, TWOS, THREES, FOURS, FIVES, SIXES, THREE_OF_A_KIND, FOUR_OF_A_KIND, SMALL_STRAIGHT, LARGE_STRAIGHT, FULL_HOUSE, YAHTC, CHANCE, SCORING_MENU_BACK }; struct game_state_st { struct die_st dice[5]; struct scorecard_st scorecard; Loading @@ -57,11 +74,12 @@ int sum_dice_matching_value(struct die_st *dice, unsigned int value); int sum_dice(struct die_st *dice); // scorecard functions unsigned int calculate_selection_score(game_context *ctx, char *selection); unsigned int calculate_selection_score(game_context *ctx, enum scorecard_command selection); unsigned int calculate_total_score(struct scorecard_st *scorecard); void initialize_scorecard(struct scorecard_st *scorecard); char *get_player_score_selection_with_menu(struct scorecard_st *scorecard); int get_player_score_selection_with_menu(struct scorecard_st *scorecard); unsigned int n_of_a_kind(struct die_st *dice, unsigned int num_dice, unsigned int n, unsigned int value_to_avoid); unsigned int n_in_a_row(struct die_st *dice, unsigned int num_dice, unsigned int n); const char *name_of_scorecard_command(enum scorecard_command command); #endif //DICELOGIC_H menu.c +7 −6 Original line number Diff line number Diff line Loading @@ -5,14 +5,15 @@ #include "menu.h" MENU_DIRECTION get_direction(void); const int UNSELECTED = -1; void *menu_prompt(const menu_item *items, unsigned int num_menu_items, const char *title, const char *item_disabled_label) { int menu_prompt(const menu_item *items, unsigned int num_menu_items, const char *title, const char *item_disabled_label) { printf("%s\n", title); for (unsigned int i = 0; i < num_menu_items; i++) { printf("\n"); // make downward room for the menu } char *selected_action = ""; int selected_action = UNSELECTED; int hovered_item_changed = 1; unsigned int hovered_selection_index = 0; // menu logic! TODO make this cross platform (play nice with Windows...) Loading Loading @@ -64,7 +65,7 @@ void *menu_prompt(const menu_item *items, unsigned int num_menu_items, const cha hovered_item_changed = 0; break; } } while (strcasecmp(selected_action, "") == 0); } while (selected_action == UNSELECTED); return selected_action; } Loading Loading @@ -177,12 +178,12 @@ MENU_DIRECTION get_direction(void) { } } menu_item *get_menu_item_by_command(menu_item *items, const unsigned int num_menu_items, char *command_to_find) { menu_item *get_menu_item_by_command(menu_item *items, const unsigned int num_menu_items, int command_to_find) { for (unsigned int i = 0; i < num_menu_items; i++) { if (strcasecmp(items[i].command, command_to_find) == 0) { if (items[i].command == command_to_find) { return &items[i]; } } printf("get_menu_item_by_command(): item %s not found", command_to_find); printf("get_menu_item_by_command(): item %d not found", command_to_find); return NULL; } menu.h +2 −2 Original line number Diff line number Diff line Loading @@ -14,13 +14,13 @@ typedef enum { } MENU_DIRECTION; typedef struct { void *command; unsigned int command; unsigned int enabled; unsigned int multi_selected; char *title; } menu_item; void *menu_prompt(const menu_item *items, unsigned int num_menu_items, const char *title, const char *item_disabled_label); int menu_prompt(const menu_item *items, unsigned int num_menu_items, const char *title, const char *item_disabled_label); char *menu_prompt_multi_select(menu_item *items, unsigned int num_menu_items, const char *title, const char *item_disabled_label); #endif //MENU_H yaht.c +66 −43 Original line number Diff line number Diff line Loading @@ -28,32 +28,42 @@ int main(void) { printf("yaht.c\n"); enum item_commands { START, RULES, EXIT }; menu_item items[] = { {.title = "New Game", .command = "start", .enabled = 1}, {.title = "New Game", .command = START, .enabled = 1}, // {.title = "Demo", .command = "demo", .enabled = 1}, // todo deprecate demo {.title = "Rules", .command = "rules", .enabled = 1}, {.title = "Exit", .command = "exit", .enabled = 1}, {.title = "Rules", .command = RULES, .enabled = 1}, {.title = "Exit", .command = EXIT, .enabled = 1}, }; const unsigned int num_menu_items = sizeof(items) / sizeof(menu_item); int exiting = 0; // to be modified by the internal menu selection while (exiting == 0) { const char *selected_action = menu_prompt(items, num_menu_items, "Main menu", ""); const int selected_action = menu_prompt(items, num_menu_items, "Main menu", ""); if (strcasecmp(selected_action, "start") == 0) { unsigned int game_over = 0; switch (selected_action) { unsigned int game_over; case START: game_over = 0; while (can_continue_playing(&ctx.game_state.scorecard) && !game_over) { game_over = take_turn(&ctx, NUM_DICE); } printf("Your final score was %d.\nThank you for playing!\n", calculate_total_score(&ctx.game_state.scorecard)); // } else if (strcasecmp(selected_action, "demo") == 0) { // do_demo_dice_rolls(&ctx, NUM_DICE); // } } else if (strcasecmp(selected_action, "rules") == 0) { break; case RULES: show_rules(); } else if (strcasecmp(selected_action, "exit") == 0) { break; case EXIT: exiting = 1; break; default: printf("invalid selection"); break; } } Loading Loading @@ -83,34 +93,47 @@ unsigned int take_turn(game_context *ctx, unsigned int num_dice) { // (also don't allow user to roll again if on the third roll) unsigned int ready_for_next_roll = 0; while (!ready_for_next_roll) { enum in_turn_menu_commands { ENTER_HOLD_SELECTION, ROLL, SCORE, QUIT }; const menu_item in_turn_menu[] = { {.title = "Hold dice", .command = "enter_hold_selection", .enabled = which_roll_number < 3}, {.title = "Roll again", .command = "roll", .enabled = which_roll_number < 3}, {.title = "Score", .command = "score", .enabled = 1}, {.title = "End game", .command = "quit", .enabled = 1}, {.title = "Hold dice", .command = ENTER_HOLD_SELECTION, .enabled = which_roll_number < 3}, {.title = "Roll again", .command = ROLL, .enabled = which_roll_number < 3}, {.title = "Score", .command = SCORE, .enabled = 1}, {.title = "End game", .command = QUIT, .enabled = 1}, }; const unsigned int num_menu_items = sizeof(in_turn_menu) / sizeof(menu_item); const char *player_action = menu_prompt(in_turn_menu, num_menu_items, "Choose from the following:", const enum in_turn_menu_commands player_action = menu_prompt(in_turn_menu, num_menu_items, "Choose from the following:", "disabled"); if (strcasecmp(player_action, "enter_hold_selection") == 0) { enum scorecard_command selection; switch (player_action) { case ENTER_HOLD_SELECTION: // enter the menu for which dice to hold hold_dice_with_menu(ctx->game_state.dice, NUM_DICE); } else if (strcasecmp(player_action, "roll") == 0) { break; case ROLL: // printf("Rolling again...\n"); ready_for_next_roll = 1; } else if (strcasecmp(player_action, "score") == 0) { break; case SCORE: // enter the score menu char *selection = get_player_score_selection_with_menu(&ctx->game_state.scorecard); if (strcasecmp(selection, "back") != 0) { selection = get_player_score_selection_with_menu(&ctx->game_state.scorecard); if (selection != SCORING_MENU_BACK) { unsigned int points_awarded = calculate_selection_score(ctx, selection); // todo we should use the return value for this to assign the point value, not do it within get_player_score_selection() printf("for selection %s, points awarded: %d\n", selection, points_awarded); printf("for selection %s, points awarded: %d\n", name_of_scorecard_command(selection), points_awarded); return 0; // 0 signifies user_quit == false, which will move on to the next turn } } else if (strcasecmp(player_action, "quit") == 0) { case QUIT: // todo this could be more robust or interactive. maybe add the final score? // also you could maybe ask to start a new game or to quit the program return 1; // 1 signifies user_quit == true, which will end the game default: break; } } } Loading Loading
gamelogic.c +126 −99 Original line number Diff line number Diff line Loading @@ -108,29 +108,36 @@ void clear_held_values(game_context *ctx) { } // todo: this should only map the selection to the method, and then yaht.c will handle the scoreboard assignment (I think -- revisit to confirm) unsigned int calculate_selection_score(game_context *ctx, char *selection) { unsigned int calculate_selection_score(game_context *ctx, enum scorecard_command selection) { // discern score action based on the scoring selection // we pass in a whole game_context because we need to write to the scorecard and read the dice values int points_awarded = 0; if (strcasecmp(selection, "aces") == 0) { switch (selection) { case ACES: ctx->game_state.scorecard.aces = sum_dice_matching_value(ctx->game_state.dice, 1); points_awarded = ctx->game_state.scorecard.aces; } else if (strcasecmp(selection, "twos") == 0) { break; case TWOS: ctx->game_state.scorecard.twos = sum_dice_matching_value(ctx->game_state.dice, 2); points_awarded = ctx->game_state.scorecard.twos; } else if (strcasecmp(selection, "threes") == 0) { break; case THREES: ctx->game_state.scorecard.threes = sum_dice_matching_value(ctx->game_state.dice, 3); points_awarded = ctx->game_state.scorecard.threes; } else if (strcasecmp(selection, "fours") == 0) { break; case FOURS: ctx->game_state.scorecard.fours = sum_dice_matching_value(ctx->game_state.dice, 4); points_awarded = ctx->game_state.scorecard.fours; } else if (strcasecmp(selection, "fives") == 0) { break; case FIVES: ctx->game_state.scorecard.fives = sum_dice_matching_value(ctx->game_state.dice, 5); points_awarded = ctx->game_state.scorecard.fives; } else if (strcasecmp(selection, "sixes") == 0) { break; case SIXES: ctx->game_state.scorecard.sixes = sum_dice_matching_value(ctx->game_state.dice, 6); points_awarded = ctx->game_state.scorecard.sixes; } else if (strcasecmp(selection, "three of a kind") == 0) { break; case THREE_OF_A_KIND: // sum all dice if 3 of a kind match; 0 otherwise if (n_of_a_kind(ctx->game_state.dice, NUM_DICE, 3, 0)) { ctx->game_state.scorecard.three_of_a_kind = sum_dice(ctx->game_state.dice); Loading @@ -138,7 +145,8 @@ unsigned int calculate_selection_score(game_context *ctx, char *selection) { ctx->game_state.scorecard.three_of_a_kind = 0; } points_awarded = ctx->game_state.scorecard.three_of_a_kind; } else if (strcasecmp(selection, "four of a kind") == 0) { break; case FOUR_OF_A_KIND: // sum all dice if 4 of a kind match; 0 otherwise if (n_of_a_kind(ctx->game_state.dice, NUM_DICE, 4, 0)) { ctx->game_state.scorecard.four_of_a_kind = sum_dice(ctx->game_state.dice); Loading @@ -146,20 +154,8 @@ unsigned int calculate_selection_score(game_context *ctx, char *selection) { ctx->game_state.scorecard.four_of_a_kind = 0; } points_awarded = ctx->game_state.scorecard.four_of_a_kind; } else if (strcasecmp(selection, "full house") == 0) { // 25 points for a combination of three of one number + two of another; 0 otherwise unsigned int three_of_a_kind_value = n_of_a_kind(ctx->game_state.dice, NUM_DICE, 3, 0); unsigned int two_of_a_kind_value = 0; if (three_of_a_kind_value) { two_of_a_kind_value = n_of_a_kind(ctx->game_state.dice, NUM_DICE, 2, three_of_a_kind_value); } if (three_of_a_kind_value && two_of_a_kind_value) { ctx->game_state.scorecard.full_house = 25; } else { ctx->game_state.scorecard.full_house = 0; } points_awarded = ctx->game_state.scorecard.full_house; } else if (strcasecmp(selection, "small straight") == 0) { break; case SMALL_STRAIGHT: // 30 points for a sequence of four consecutive numbers; 0 otherwise if (n_in_a_row(ctx->game_state.dice, NUM_DICE, 4)) { ctx->game_state.scorecard.small_straight = 30; Loading @@ -167,7 +163,8 @@ unsigned int calculate_selection_score(game_context *ctx, char *selection) { ctx->game_state.scorecard.small_straight = 0; } points_awarded = ctx->game_state.scorecard.small_straight; } else if (strcasecmp(selection, "large straight") == 0) { break; case LARGE_STRAIGHT: // 40 points for a sequence of five consecutive numbers; 0 otherwise if (n_in_a_row(ctx->game_state.dice, NUM_DICE, 5)) { ctx->game_state.scorecard.large_straight = 40; Loading @@ -175,7 +172,22 @@ unsigned int calculate_selection_score(game_context *ctx, char *selection) { ctx->game_state.scorecard.large_straight = 0; } points_awarded = ctx->game_state.scorecard.large_straight; } else if (strcasecmp(selection, "yahtc") == 0) { break; case FULL_HOUSE: // 25 points for a combination of three of one number + two of another; 0 otherwise unsigned int three_of_a_kind_value = n_of_a_kind(ctx->game_state.dice, NUM_DICE, 3, 0); unsigned int two_of_a_kind_value = 0; if (three_of_a_kind_value) { two_of_a_kind_value = n_of_a_kind(ctx->game_state.dice, NUM_DICE, 2, three_of_a_kind_value); } if (three_of_a_kind_value && two_of_a_kind_value) { ctx->game_state.scorecard.full_house = 25; } else { ctx->game_state.scorecard.full_house = 0; } points_awarded = ctx->game_state.scorecard.full_house; break; case YAHTC: // 50 points for five of a kind; 0 otherwise if (n_of_a_kind(ctx->game_state.dice, NUM_DICE, NUM_DICE, 0)) { ctx->game_state.scorecard.yahtc = 50; Loading @@ -183,10 +195,14 @@ unsigned int calculate_selection_score(game_context *ctx, char *selection) { ctx->game_state.scorecard.yahtc = 0; } points_awarded = ctx->game_state.scorecard.yahtc; } else if (strcasecmp(selection, "chance") == 0) { break; case CHANCE: // sum of all dice ctx->game_state.scorecard.chance = sum_dice(ctx->game_state.dice); points_awarded = ctx->game_state.scorecard.chance; break; case SCORING_MENU_BACK: break; } return points_awarded; Loading Loading @@ -235,11 +251,6 @@ void hold_dice_with_menu(struct die_st *dice, unsigned int num_dice) { dice_hold_selections[i].enabled = 1; // todo cm delete: you don't even need a command for this because we go by the "multi_selected" value for dice hold assignments // dice_hold_selections[i].command = malloc(SCORECARD_NUM_ITEMS * sizeof(char)); // sprintf(dice_hold_selections[i].command, "toggle_hold_%1d", i); dice_hold_selections[i].command = ""; } char *action = menu_prompt_multi_select(dice_hold_selections, num_dice, "Hold dice", "held"); Loading @@ -252,28 +263,25 @@ void hold_dice_with_menu(struct die_st *dice, unsigned int num_dice) { if (dice_hold_selections[i].title) { free(dice_hold_selections[i].title); } // if (dice_hold_selections[i].command) { // free(dice_hold_selections[i].command); // } } } char *get_player_score_selection_with_menu(struct scorecard_st *scorecard) { int get_player_score_selection_with_menu(struct scorecard_st *scorecard) { const menu_item scoring_menu[] = { {.title = "aces", .command = "aces", .multi_selected = 0, .enabled = scorecard->aces == SCORE_UNSCORED}, {.title = "twos", .command = "twos", .multi_selected = 0, .enabled = scorecard->twos == SCORE_UNSCORED}, {.title = "threes", .command = "threes", .multi_selected = 0, .enabled = scorecard->threes == SCORE_UNSCORED}, {.title = "fours", .command = "fours", .multi_selected = 0, .enabled = scorecard->fours == SCORE_UNSCORED}, {.title = "fives", .command = "fives", .multi_selected = 0, .enabled = scorecard->fives == SCORE_UNSCORED}, {.title = "sixes", .command = "sixes", .multi_selected = 0, .enabled = scorecard->sixes == SCORE_UNSCORED}, {.title = "three of a kind", .command = "three of a kind", .multi_selected = 0, .enabled = scorecard->three_of_a_kind == SCORE_UNSCORED}, {.title = "four of a kind", .command = "four of a kind", .multi_selected = 0, .enabled = scorecard->four_of_a_kind == SCORE_UNSCORED}, {.title = "small straight", .command = "small straight", .multi_selected = 0, .enabled = scorecard->small_straight == SCORE_UNSCORED}, {.title = "large straight", .command = "large straight", .multi_selected = 0, .enabled = scorecard->large_straight == SCORE_UNSCORED}, {.title = "full house", .command = "full house", .multi_selected = 0, .enabled = scorecard->full_house == SCORE_UNSCORED}, {.title = "yahtc", .command = "yahtc", .multi_selected = 0, .enabled = scorecard->yahtc == SCORE_UNSCORED}, {.title = "chance", .command = "chance", .multi_selected = 0, .enabled = scorecard->chance == SCORE_UNSCORED}, {.title = "{BACK}", .command = "back", .multi_selected = 0, .enabled = 1} {.title = "aces", .command = ACES, .multi_selected = 0, .enabled = scorecard->aces == SCORE_UNSCORED}, {.title = "twos", .command = TWOS, .multi_selected = 0, .enabled = scorecard->twos == SCORE_UNSCORED}, {.title = "threes", .command = THREES, .multi_selected = 0, .enabled = scorecard->threes == SCORE_UNSCORED}, {.title = "fours", .command = FOURS, .multi_selected = 0, .enabled = scorecard->fours == SCORE_UNSCORED}, {.title = "fives", .command = FIVES, .multi_selected = 0, .enabled = scorecard->fives == SCORE_UNSCORED}, {.title = "sixes", .command = SIXES, .multi_selected = 0, .enabled = scorecard->sixes == SCORE_UNSCORED}, {.title = "three of a kind", .command = THREE_OF_A_KIND, .multi_selected = 0, .enabled = scorecard->three_of_a_kind == SCORE_UNSCORED}, {.title = "four of a kind", .command = FOUR_OF_A_KIND, .multi_selected = 0, .enabled = scorecard->four_of_a_kind == SCORE_UNSCORED}, {.title = "small straight", .command = SMALL_STRAIGHT, .multi_selected = 0, .enabled = scorecard->small_straight == SCORE_UNSCORED}, {.title = "large straight", .command = LARGE_STRAIGHT, .multi_selected = 0, .enabled = scorecard->large_straight == SCORE_UNSCORED}, {.title = "full house", .command = FULL_HOUSE, .multi_selected = 0, .enabled = scorecard->full_house == SCORE_UNSCORED}, {.title = "yahtc", .command = YAHTC, .multi_selected = 0, .enabled = scorecard->yahtc == SCORE_UNSCORED}, {.title = "chance", .command = CHANCE, .multi_selected = 0, .enabled = scorecard->chance == SCORE_UNSCORED}, {.title = "{BACK}", .command = SCORING_MENU_BACK, .multi_selected = 0, .enabled = 1} }; unsigned int num_menu_items = sizeof(scoring_menu) / sizeof(menu_item); return menu_prompt(scoring_menu, num_menu_items, "Scoring", "already played"); Loading Loading @@ -367,3 +375,22 @@ unsigned int n_in_a_row(struct die_st *dice, unsigned int num_dice, unsigned int } return 0; } const char *command_name[] = { "aces", "twos", "threes", "fours", "fives", "sixes", "three of a kind", "four of a kind", "small straight", "large straight", "yahtc", "chance", "" }; const char *name_of_scorecard_command(enum scorecard_command command) { return command_name[command]; }
gamelogic.h +20 −2 Original line number Diff line number Diff line Loading @@ -33,6 +33,23 @@ struct scorecard_st { }; extern const unsigned int SCORECARD_NUM_ITEMS; enum scorecard_command { ACES, TWOS, THREES, FOURS, FIVES, SIXES, THREE_OF_A_KIND, FOUR_OF_A_KIND, SMALL_STRAIGHT, LARGE_STRAIGHT, FULL_HOUSE, YAHTC, CHANCE, SCORING_MENU_BACK }; struct game_state_st { struct die_st dice[5]; struct scorecard_st scorecard; Loading @@ -57,11 +74,12 @@ int sum_dice_matching_value(struct die_st *dice, unsigned int value); int sum_dice(struct die_st *dice); // scorecard functions unsigned int calculate_selection_score(game_context *ctx, char *selection); unsigned int calculate_selection_score(game_context *ctx, enum scorecard_command selection); unsigned int calculate_total_score(struct scorecard_st *scorecard); void initialize_scorecard(struct scorecard_st *scorecard); char *get_player_score_selection_with_menu(struct scorecard_st *scorecard); int get_player_score_selection_with_menu(struct scorecard_st *scorecard); unsigned int n_of_a_kind(struct die_st *dice, unsigned int num_dice, unsigned int n, unsigned int value_to_avoid); unsigned int n_in_a_row(struct die_st *dice, unsigned int num_dice, unsigned int n); const char *name_of_scorecard_command(enum scorecard_command command); #endif //DICELOGIC_H
menu.c +7 −6 Original line number Diff line number Diff line Loading @@ -5,14 +5,15 @@ #include "menu.h" MENU_DIRECTION get_direction(void); const int UNSELECTED = -1; void *menu_prompt(const menu_item *items, unsigned int num_menu_items, const char *title, const char *item_disabled_label) { int menu_prompt(const menu_item *items, unsigned int num_menu_items, const char *title, const char *item_disabled_label) { printf("%s\n", title); for (unsigned int i = 0; i < num_menu_items; i++) { printf("\n"); // make downward room for the menu } char *selected_action = ""; int selected_action = UNSELECTED; int hovered_item_changed = 1; unsigned int hovered_selection_index = 0; // menu logic! TODO make this cross platform (play nice with Windows...) Loading Loading @@ -64,7 +65,7 @@ void *menu_prompt(const menu_item *items, unsigned int num_menu_items, const cha hovered_item_changed = 0; break; } } while (strcasecmp(selected_action, "") == 0); } while (selected_action == UNSELECTED); return selected_action; } Loading Loading @@ -177,12 +178,12 @@ MENU_DIRECTION get_direction(void) { } } menu_item *get_menu_item_by_command(menu_item *items, const unsigned int num_menu_items, char *command_to_find) { menu_item *get_menu_item_by_command(menu_item *items, const unsigned int num_menu_items, int command_to_find) { for (unsigned int i = 0; i < num_menu_items; i++) { if (strcasecmp(items[i].command, command_to_find) == 0) { if (items[i].command == command_to_find) { return &items[i]; } } printf("get_menu_item_by_command(): item %s not found", command_to_find); printf("get_menu_item_by_command(): item %d not found", command_to_find); return NULL; }
menu.h +2 −2 Original line number Diff line number Diff line Loading @@ -14,13 +14,13 @@ typedef enum { } MENU_DIRECTION; typedef struct { void *command; unsigned int command; unsigned int enabled; unsigned int multi_selected; char *title; } menu_item; void *menu_prompt(const menu_item *items, unsigned int num_menu_items, const char *title, const char *item_disabled_label); int menu_prompt(const menu_item *items, unsigned int num_menu_items, const char *title, const char *item_disabled_label); char *menu_prompt_multi_select(menu_item *items, unsigned int num_menu_items, const char *title, const char *item_disabled_label); #endif //MENU_H
yaht.c +66 −43 Original line number Diff line number Diff line Loading @@ -28,32 +28,42 @@ int main(void) { printf("yaht.c\n"); enum item_commands { START, RULES, EXIT }; menu_item items[] = { {.title = "New Game", .command = "start", .enabled = 1}, {.title = "New Game", .command = START, .enabled = 1}, // {.title = "Demo", .command = "demo", .enabled = 1}, // todo deprecate demo {.title = "Rules", .command = "rules", .enabled = 1}, {.title = "Exit", .command = "exit", .enabled = 1}, {.title = "Rules", .command = RULES, .enabled = 1}, {.title = "Exit", .command = EXIT, .enabled = 1}, }; const unsigned int num_menu_items = sizeof(items) / sizeof(menu_item); int exiting = 0; // to be modified by the internal menu selection while (exiting == 0) { const char *selected_action = menu_prompt(items, num_menu_items, "Main menu", ""); const int selected_action = menu_prompt(items, num_menu_items, "Main menu", ""); if (strcasecmp(selected_action, "start") == 0) { unsigned int game_over = 0; switch (selected_action) { unsigned int game_over; case START: game_over = 0; while (can_continue_playing(&ctx.game_state.scorecard) && !game_over) { game_over = take_turn(&ctx, NUM_DICE); } printf("Your final score was %d.\nThank you for playing!\n", calculate_total_score(&ctx.game_state.scorecard)); // } else if (strcasecmp(selected_action, "demo") == 0) { // do_demo_dice_rolls(&ctx, NUM_DICE); // } } else if (strcasecmp(selected_action, "rules") == 0) { break; case RULES: show_rules(); } else if (strcasecmp(selected_action, "exit") == 0) { break; case EXIT: exiting = 1; break; default: printf("invalid selection"); break; } } Loading Loading @@ -83,34 +93,47 @@ unsigned int take_turn(game_context *ctx, unsigned int num_dice) { // (also don't allow user to roll again if on the third roll) unsigned int ready_for_next_roll = 0; while (!ready_for_next_roll) { enum in_turn_menu_commands { ENTER_HOLD_SELECTION, ROLL, SCORE, QUIT }; const menu_item in_turn_menu[] = { {.title = "Hold dice", .command = "enter_hold_selection", .enabled = which_roll_number < 3}, {.title = "Roll again", .command = "roll", .enabled = which_roll_number < 3}, {.title = "Score", .command = "score", .enabled = 1}, {.title = "End game", .command = "quit", .enabled = 1}, {.title = "Hold dice", .command = ENTER_HOLD_SELECTION, .enabled = which_roll_number < 3}, {.title = "Roll again", .command = ROLL, .enabled = which_roll_number < 3}, {.title = "Score", .command = SCORE, .enabled = 1}, {.title = "End game", .command = QUIT, .enabled = 1}, }; const unsigned int num_menu_items = sizeof(in_turn_menu) / sizeof(menu_item); const char *player_action = menu_prompt(in_turn_menu, num_menu_items, "Choose from the following:", const enum in_turn_menu_commands player_action = menu_prompt(in_turn_menu, num_menu_items, "Choose from the following:", "disabled"); if (strcasecmp(player_action, "enter_hold_selection") == 0) { enum scorecard_command selection; switch (player_action) { case ENTER_HOLD_SELECTION: // enter the menu for which dice to hold hold_dice_with_menu(ctx->game_state.dice, NUM_DICE); } else if (strcasecmp(player_action, "roll") == 0) { break; case ROLL: // printf("Rolling again...\n"); ready_for_next_roll = 1; } else if (strcasecmp(player_action, "score") == 0) { break; case SCORE: // enter the score menu char *selection = get_player_score_selection_with_menu(&ctx->game_state.scorecard); if (strcasecmp(selection, "back") != 0) { selection = get_player_score_selection_with_menu(&ctx->game_state.scorecard); if (selection != SCORING_MENU_BACK) { unsigned int points_awarded = calculate_selection_score(ctx, selection); // todo we should use the return value for this to assign the point value, not do it within get_player_score_selection() printf("for selection %s, points awarded: %d\n", selection, points_awarded); printf("for selection %s, points awarded: %d\n", name_of_scorecard_command(selection), points_awarded); return 0; // 0 signifies user_quit == false, which will move on to the next turn } } else if (strcasecmp(player_action, "quit") == 0) { case QUIT: // todo this could be more robust or interactive. maybe add the final score? // also you could maybe ask to start a new game or to quit the program return 1; // 1 signifies user_quit == true, which will end the game default: break; } } } Loading