/* See LICENSE file for copyright and license details. */ #include "common.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 %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 = ereallocn(dice, ++ndice, sizeof(*dice), 0); 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 = 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 :"); free(dice); free(line); return 0; }