#include #include #include /* 0000 0000 0000 0000 0000 0000 0000 0000 rc topscore botflag. botscore. topflag x x 0000 0000 0000 0000 0000 0000 0000 0000 xd4. d3.d 2.d1 .d0. prng state.... .... */ typedef struct { uint8_t topflag_w_roll; uint8_t topscore; uint8_t botflag; uint8_t botscore; uint16_t dice; uint16_t seed; } yhtz_state; enum Category { ACE = 0, TWO = 1, THREE = 2, FOUR = 3, FIVE = 4, SIX = 5, THREE_OF_A_KIND = 10, FOUR_OF_A_KIND = 11, FULL_HOUSE = 12, SMALL_STRAIGHT = 13, LARGE_STRAIGHT = 14, YAHTZEE = 15, CHANCE = 16 }; static enum Category CATEGORIES[] = { ACE, TWO, THREE, FOUR, FIVE, SIX, THREE_OF_A_KIND, FOUR_OF_A_KIND, FULL_HOUSE, SMALL_STRAIGHT, LARGE_STRAIGHT, YAHTZEE, CHANCE }; static int NUMBER_OF_CATEGORIES = 13; /* Note: all macros take in a yhtz_state* as input 's' */ /* ROLLS */ #define GET_ROLL(s) ((uint8_t)((s->topflag_w_roll & 0xC0) >> 6)) /* top 2 bits */ #define SET_ROLL(s, x) (s->topflag_w_roll = (s->topflag_w_roll & 0x3F | ((uint8_t)x << 6))) /* FLAGS -- x is an offset based on Category */ #define GET_TOPFLAG(s, x) ((uint8_t)((s->topflag_w_roll & (1 << x)) >> x)) #define SET_TOPFLAG(s, x) (s->topflag_w_roll = (s->topflag_w_roll | (1 << x))) #define GET_BOTFLAG(s, x) ((uint8_t)((s->botflag & (1 << x)) >> x)) #define SET_BOTFLAG(s, x) (s->botflag = (s->botflag | (1 << x))) #define GET_FLAG(s, x) (x < 10 ? GET_TOPFLAG(s, x) : GET_BOTFLAG(s, x - 10)) /* DICE -- x is die number (0-4), y is value (1-6) */ #define GET_DIE(s, x) ((uint8_t)((s->dice & (0x07 << (3 * x))) >> (3 * x))) #define SET_DIE(s, x, y) (s->dice = (s->dice & (0xFFFF ^ (0x0007 << (3 * x))) | (y << (3 * x)))) /* 16-bit prng, with thanks/apologies to Dr. Lemire (https://lemire.me/blog/2019/07/03/a-fast-16-bit-random-number-generator/) */ uint32_t hash16(uint32_t input, uint32_t key) { uint32_t hash = input * key; return ((hash >> 16) ^ hash) & 0xFFFF; } uint16_t wyhash16(uint16_t* seed) { *seed += 0xfc15; return hash16(*seed, 0x2ab); } uint16_t roll_die(uint16_t* seed) { const uint16_t s = 6; /* generating in the range of [0,6) */ uint16_t x = wyhash16(seed); uint32_t m = (uint32_t)x * (uint32_t)s; uint32_t l = (uint16_t)m; if (l < s) { uint16_t t = -s % s; while (l < t) { x = wyhash16(seed); m = (uint32_t)x * (uint32_t)s; l = (uint16_t)m; } } return (m >> 16) + 1; /* we ultimately want [1,7) */ } void print_dice(yhtz_state* state) { char* label = " %d "; char* top = ",---,"; char* mid = "| %d | "; char* bot = "'---'"; printf("\n%s %s %s %s %s\n", top, top, top, top, top); for (int i = 0; i < 5; i++) printf(mid, GET_DIE(state, i)); printf("\n%s %s %s %s %s\n", bot, bot, bot, bot, bot); for (int i = 0; i < 5; i++) printf(label, i); printf("\n\n"); } void ask_for_rerolls(yhtz_state* state) { char buf[10]; printf("which dice should be re-rolled? "); fgets(buf, sizeof(buf), stdin); int dice[6] = { -1, -1, -1, -1, -1, -1 }; /* we only ever look for 5 dice, so should always hit -1 at some point */ sscanf(buf, "%d %d %d %d %d", &dice[0], &dice[1], &dice[2], &dice[3], &dice[4]); for (int i = 0; i < 5; i++) { if (dice[i] == -1) break; SET_DIE(state, dice[i], 0); } } void do_roll(yhtz_state* state) { int roll = GET_ROLL(state); bool reset = false; // if we're out of rolls, reset if (roll == 3) { reset = true; roll = 0; } for (int i = 0; i < 5; i++) { if (reset || (GET_DIE(state, i) == 0)) { SET_DIE(state, i, roll_die(&(state->seed))); } } SET_ROLL(state, ++roll); } void ask_for_category(yhtz_state* state) { char buf[2]; printf("what category should this be scored in? "); fgets(buf, sizeof(buf), stdin); } void do_turn(yhtz_state* state) { for (int i = 0; i < 3; i++) { do_roll(state); print_dice(state); if (i < 2) ask_for_rerolls(state); } ask_for_category(state); } int turn_count(yhtz_state* state) { int ret = 1; for (int i = 0; i < NUMBER_OF_CATEGORIES; i++) ret += GET_FLAG(state, CATEGORIES[i]); return ret; } void init_yhtz_state(yhtz_state* state) { state->topflag_w_roll = 0; state->topscore = 0; state->botflag = 0; state->botscore = 0; state->dice = 0; state->seed = 13; /* TODO this isn't random */ } void main() { yhtz_state y; init_yhtz_state(&y); /* * until the win condition is met * - roll all dice * - up to 2 times, ask user which dice to reroll * - ask user which category to mark it in * - validate and update state * * */ int tc; while (true) { tc = turn_count(&y); printf("~*~ turn %d ~*~\n", tc); do_turn(&y); } }