summaryrefslogtreecommitdiffstats
path: root/dice.c
diff options
context:
space:
mode:
authorMattias Andrée <maandree@kth.se>2021-03-28 17:23:21 +0200
committerMattias Andrée <maandree@kth.se>2021-03-28 17:23:21 +0200
commit0c7a7fc4509185513428ac08a32ad8d210ee428a (patch)
tree2ca5cd6d7d90ec7d5c802746d5a243a389df0ad9 /dice.c
parentm (diff)
downloadpdatools-0c7a7fc4509185513428ac08a32ad8d210ee428a.tar.gz
pdatools-0c7a7fc4509185513428ac08a32ad8d210ee428a.tar.bz2
pdatools-0c7a7fc4509185513428ac08a32ad8d210ee428a.tar.xz
Add dice
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat (limited to 'dice.c')
-rw-r--r--dice.c195
1 files changed, 195 insertions, 0 deletions
diff --git a/dice.c b/dice.c
new file mode 100644
index 0000000..69fc911
--- /dev/null
+++ b/dice.c
@@ -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;
+}