summaryrefslogtreecommitdiff
path: root/roll.c
diff options
context:
space:
mode:
Diffstat (limited to 'roll.c')
-rw-r--r--roll.c156
1 files changed, 156 insertions, 0 deletions
diff --git a/roll.c b/roll.c
new file mode 100644
index 0000000..660eeab
--- /dev/null
+++ b/roll.c
@@ -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]);
+}