#include #include #include enum parser_state_e { ST_NUMBER_BEFORE_DICE = 0, ST_NUMBER_AFTER_DICE = 1, }; struct state_s { enum parser_state_e parser_state; unsigned number_before_dice; unsigned number_after_dice; unsigned result; int eof; int syntax_error; }; void print_usage(char* argv0) { printf("usage: %s \n", argv0); puts(" examples: d20, 2d8+4, 1d6+d8+5"); } unsigned diceroll(unsigned dice, unsigned rolls) { unsigned result = 0; for (; rolls > 0; rolls--) { result += 1 + rand() % dice; } return result; } struct state_s eval_step(struct state_s state, char c) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* anything numeric */ if (state.parser_state == ST_NUMBER_BEFORE_DICE) { state.number_before_dice *= 10; state.number_before_dice += c - '0'; } else if (state.parser_state == ST_NUMBER_AFTER_DICE) { state.number_after_dice *= 10; state.number_after_dice += c - '0'; } break; case 'd': case 'D': /* dice indicator */ if (state.parser_state == ST_NUMBER_BEFORE_DICE) { if (state.number_before_dice == 0) { /* allow shorthand, treat e.g. "d20" as "1d20" */ state.number_before_dice = 1; } state.parser_state = ST_NUMBER_AFTER_DICE; } else { /* not in the format ABdXY */ state.syntax_error = 1; } break; case '+': case '\0': /* end of input or just end of an element */ if (state.number_after_dice > 0) { state.result += diceroll(state.number_after_dice, state.number_before_dice); } else if (state.parser_state == ST_NUMBER_BEFORE_DICE) { /* we don't have a number after the dice */ /* in this case we just have a "naked" value that we take as-is */ state.result += state.number_before_dice; } else { /* missing number after dice indicator */ state.syntax_error = 1; break; } state.number_before_dice = 0; state.number_after_dice = 0; state.parser_state = ST_NUMBER_BEFORE_DICE; if (c == '\0') { state.eof = 1; } break; default: state.syntax_error = 1; break; } return state; } int eval(char* c) { struct state_s state = {0}; while (1) { state = eval_step(state, *c); if (state.syntax_error) { puts("syntax error"); return 1; } if (state.eof) { break; } c++; } printf("%d\n", state.result); return 0; } /** * platform dependent way to generate a good seed for the random number * generator */ unsigned get_seed() { unsigned seed; #ifdef __linux__ FILE* fp = fopen("/dev/urandom", "rb"); fread(&seed, sizeof(unsigned), 1, fp); fclose(fp); #else seed = time(NULL); #endif return seed; } int main(int argc, char* argv[]) { if (argc < 2) { print_usage(argv[0]); return 1; } srand(get_seed()); return eval(argv[1]); }