diff options
| -rw-r--r-- | Makefile | 1 | ||||
| -rw-r--r-- | dice.c | 195 |
2 files changed, 196 insertions, 0 deletions
@@ -11,6 +11,7 @@ BIN_ =\ backlight\ clock\ counter\ + dice\ stopwatch OBJ =\ @@ -0,0 +1,195 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#include <sys/random.h> + +/* TODO add support for up and down arrows to browse previous entries in the same session */ + +USAGE(""); + + +struct die { + intmax_t count; + intmax_t min; + intmax_t max; +}; + + +static intmax_t +random_int(intmax_t min, intmax_t max) +{ + uintmax_t res, max0 = (uintmax_t)max - (uintmax_t)min, max1; + int steps; + ssize_t r; + size_t n; + + for (max1 = max0, steps = 0; max1 < UINTMAX_MAX && (max1 & (max1 + 1)); steps++) + max1 |= max1 >> steps; + + do { + for (n = 0; n < sizeof(res); n += (size_t)r) { + r = getrandom(&((char *)&res)[n], sizeof(res) - n, 0); + if (r < 0) + eprintf("getrandom <buffer> %zu 0:", sizeof(res) - n); + } + res &= max1; + } while (res > max0); + + return (intmax_t)res + min; +} + + +static void +roll_dice(const struct die *dice, size_t n) +{ + intmax_t sum = 0, res, j; + size_t i; + for (i = 0; i < n; i++) { + for (j = 0; j < dice[i].count; j++) { + sum += res = random_int(dice[i].min, dice[i].max); + printf("Die roll: %ji\n", res); + } + } + if (n > 1 || (n == 1 && dice[0].count > 1)) { + printf("Sum: %ji\n", sum); + } +} + + +static int +parse_dice(const char *s, struct die **dicep, size_t *ndicep) +{ + struct die *dice = NULL, *die; + size_t ndice = 0; + intmax_t min_sum = 0, max_sum = 0, num, i; + + ndice = 0; + + for (;;) { + while (*s == ' ' || *s == '\t') + s++; + + if (!*s) + break; + + if (!isdigit(*s) && *s != '-' && *s != 'd' && *s != 'D') + goto invalid; + + dice = erealloc2(dice, ++ndice, sizeof(*dice)); + die = &dice[ndice - 1]; + die->count = 1; + die->min = 1; + num = 1; + if (*s == 'd' || *s == 'D') + goto at_d; + errno = 0; + num = strtoimax(s, (void *)&s, 0); + if (errno) + goto invalid; + if (*s == 'd' || *s == 'D') { + at_d: + s++; + if (num < 1) + goto invalid; + die->count = (size_t)num; + if (!isdigit(*s) && *s != '-') + goto invalid; + errno = 0; + num = strtoimax(s, (void *)&s, 0); + if (errno) + goto invalid; + } + die->max = num; + if (*s == '-') { + s++; + die->min = num; + errno = 0; + die->max = strtoimax(s, (void *)&s, 0); + if (errno) + goto invalid; + } + if (die->min > die->max) + goto invalid; + + for (i = 0; i < die->count; i++) { + if (die->min < 0 && min_sum < INTMAX_MIN - die->min) { + printf("\033[31m%s\033[m\n", "Negative overflow in sum of minimums"); + return -1; + } + if (die->min > 0 && min_sum > INTMAX_MAX - die->min) { + printf("\033[31m%s\033[m\n", "Positive overflow in sum of minimums"); + return -1; + } + if (die->max < 0 && max_sum < INTMAX_MIN - die->max) { + printf("\033[31m%s\033[m\n", "Negative overflow in sum of maximums"); + return -1; + } + if (die->max > 0 && max_sum > INTMAX_MAX - die->max) { + printf("\033[31m%s\033[m\n", "Positive overflow in sum of maximums"); + return -1; + } + min_sum += die->min; + max_sum += die->max; + } + + if (*s && *s != ',' && *s != ' ' && *s != '\t') + goto invalid; + + if (*s == ',') + s++; + } + + if (*s || !ndice) + goto invalid; + + free(*dicep); + *dicep = dice; + *ndicep = ndice; + + return 0; + +invalid: + printf("\033[31m%s\033[m\n", "Invalid input"); + return -1; +} + + +int +main(int argc, char *argv[]) +{ + struct die *dice = NULL; + size_t ndice = 0; + char *line = NULL; + size_t size = 0; + ssize_t len; + int first; + + NOFLAGS(argc); + + for (first = 1;; first = 0) { + again: + if (first) + printf("\033[1m%s: \033[m", "Enter dice to roll"); + else + printf("\033[1m%s\033[m (%s): ", "Enter dice to roll", "Leave empty to reroll"); + fflush(stdout); + len = getdelim(&line, &size, '\n', stdin); + if (len < 0) { + printf("\n"); + break; + } + if (len && line[len - 1] == '\n') + line[--len] = '\0'; + if (!strcasecmp(line, "q") || !strcasecmp(line, "exit") || !strcasecmp(line, "quit")) + break; + if (!*line ? first : parse_dice(line, &dice, &ndice)) + goto again; + roll_dice(dice, ndice); + } + + if (ferror(stdin)) + eprintf("getdelim <stdin>:"); + + free(dice); + free(line); + return 0; +} |
