diff options
| -rw-r--r-- | .clang-format | 10 | ||||
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Makefile | 24 | ||||
| -rw-r--r-- | roll.c | 156 |
4 files changed, 192 insertions, 0 deletions
diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..b88d285 --- /dev/null +++ b/.clang-format @@ -0,0 +1,10 @@ +# Linux-like style +BasedOnStyle: LLVM +IndentWidth: 4 +UseTab: Never +BreakBeforeBraces: Linux +AllowShortIfStatementsOnASingleLine: true +IndentCaseLabels: false +# Force pointers to the type +DerivePointerAlignment: false +PointerAlignment: Left diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ec886bb --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +roll diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..150c5c4 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +CC ?= gcc +CFLAGS ?= -ansi -Wall -Werror +OBJ = roll.o +BIN = roll +PREFIX ?= /usr + +prog: $(OBJ) + $(CC) $(CFLAGS) $(OBJ) -o $(BIN) + +%.o: %.c + $(CC) $(CFLAGS) -c $< + +install: prog + mkdir -p ${PREFIX}/bin + install -m 755 -o root -g root -s ${BIN} ${PREFIX}/bin + +format: + clang-format -i *.c + +clean: + rm *.o + rm $(BIN) + +.PHONY: install format clean @@ -0,0 +1,156 @@ +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + +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 <command>\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]); +} |
