From e3fd8815b0f59d4e2a1944409ab256622876283f Mon Sep 17 00:00:00 2001 From: Florian Stinglmayr Date: Fri, 23 Mar 2018 17:44:52 +0100 Subject: [PATCH] implement fudge dices, complete with unit tests --- lib/dice.c | 28 ++++++++- lib/dice.h | 6 ++ lib/dice_lexer.l | 7 +++ lib/dice_parse.y | 16 ++++- tests/Makefile.am | 1 + tests/test_dice_fudge.c | 127 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 182 insertions(+), 3 deletions(-) create mode 100644 tests/test_dice_fudge.c diff --git a/lib/dice.c b/lib/dice.c index fc3d847..c2317c9 100644 --- a/lib/dice.c +++ b/lib/dice.c @@ -22,6 +22,7 @@ #include #include +#include #include "config.h" @@ -39,6 +40,7 @@ extern void yy_delete_buffer(void *b, void *scanner); struct dice_ { int consumed; + bool fudge; uint32_t amount; uint32_t sides; @@ -137,6 +139,7 @@ bool dice_set(dice_t d, dice_option_t opt, ...) switch (opt) { case DICEOPTION_AMOUNT: d->amount = va_arg(lst, uint32_t); break; case DICEOPTION_SIDES: d->sides = va_arg(lst, uint32_t); break; + case DICEOPTION_FUDGE: d->fudge = va_arg(lst, uint32_t); break; case DICEOPTION_ERROR: { char const *err = va_arg(lst, char const *); @@ -175,6 +178,12 @@ bool dice_get(dice_t d, dice_option_t opt, ...) *ptr = d->error; } break; + case DICEOPTION_FUDGE: + { + bool *ptr = va_arg(lst, uint32_t*); + *ptr = d->fudge; + } break; + default: return false; } va_end(lst); @@ -182,13 +191,24 @@ bool dice_get(dice_t d, dice_option_t opt, ...) return true; } +static int dice_roll_fudge(void) +{ + static int results[6] = {-1, -1, 0, 0, +1, +1}; + int idx = arc4random_uniform(sizeof(results)/sizeof(int)) + 1; + return results[idx]; +} + int64_t dice_roll(dice_t d) { int64_t result = 0; uint32_t i = 0; for (i = 0; i < d->amount; i++) { - result += arc4random_uniform(d->sides) + 1; + if (!d->fudge) { + result += arc4random_uniform(d->sides) + 1; + } else { + result += dice_roll_fudge(); + } } return result; @@ -211,7 +231,11 @@ bool dice_evaluate(dice_t d, dice_result_t **res, size_t *reslen) } for (i = 0; i < d->amount; i++) { - r[i].result = arc4random_uniform(d->sides) + 1; + if (!d->fudge) { + r[i].result = arc4random_uniform(d->sides) + 1; + } else { + r[i].result = dice_roll_fudge(); + } } *reslen = len; diff --git a/lib/dice.h b/lib/dice.h index 6a00604..487b82c 100644 --- a/lib/dice.h +++ b/lib/dice.h @@ -53,6 +53,12 @@ typedef enum { * set: char const * */ DICEOPTION_ERROR, + + /* Whether the dice has no amount but is a fudge dice instead. + * get: uint32_t * + * set: uint32_t + */ + DICEOPTION_FUDGE, } dice_option_t; /* Creates a new dice object that is not initialised yet. diff --git a/lib/dice_lexer.l b/lib/dice_lexer.l index 83b4bb5..a90a239 100644 --- a/lib/dice_lexer.l +++ b/lib/dice_lexer.l @@ -22,4 +22,11 @@ extern void dice_update_consumed(dice_t d, int off); dice_update_consumed(d, yyleng); return TOK_DICESEP; } + +[fF] { + dice_t d = yyget_extra(yyscanner); + dice_update_consumed(d, yyleng); + return TOK_FUDGE; +} + %% diff --git a/lib/dice_parse.y b/lib/dice_parse.y index 4ccfb75..1c09530 100644 --- a/lib/dice_parse.y +++ b/lib/dice_parse.y @@ -25,7 +25,7 @@ int yywrap(void) double number; } -%token TOK_DICESEP +%token TOK_DICESEP TOK_FUDGE %token TOK_INTEGER %% @@ -42,6 +42,20 @@ dice: TOK_INTEGER TOK_DICESEP TOK_INTEGER dice_set(dice, DICEOPTION_AMOUNT, 1); dice_set(dice, DICEOPTION_SIDES, $2); + YYACCEPT; + } + | TOK_DICESEP TOK_FUDGE + { + dice_set(dice, DICEOPTION_AMOUNT, 1L); + dice_set(dice, DICEOPTION_FUDGE, 1L); + + YYACCEPT; + } + | TOK_INTEGER TOK_DICESEP TOK_FUDGE + { + dice_set(dice, DICEOPTION_AMOUNT, $1); + dice_set(dice, DICEOPTION_FUDGE, 1L); + YYACCEPT; } ; diff --git a/tests/Makefile.am b/tests/Makefile.am index 4ae54df..8d9184e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,6 +1,7 @@ check_PROGRAMS = test_dice_simple_roll \ test_dice_parse \ test_dice_evaluate \ + test_dice_fudge \ test_expr_parse AM_CFLAGS = -I../lib ${CMOCKA_CFLAGS} diff --git a/tests/test_dice_fudge.c b/tests/test_dice_fudge.c new file mode 100644 index 0000000..87efaee --- /dev/null +++ b/tests/test_dice_fudge.c @@ -0,0 +1,127 @@ +/* + * This file is part of libdice. + * + * Copyright (C) 2018 Florian Stinglmayr + * + * libdice is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * libdice is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libdice. If not, see . + */ + +#include + +#include +#include +#include +#include +#include + +static void test_dice_fudge_construct(void **data) +{ + dice_t d = dice_new(); + bool fudge = true; + + dice_get(d, DICEOPTION_FUDGE, &fudge); + assert_false(fudge); + + dice_free(d); +} + +static void test_dice_fudge_set(void **data) +{ + dice_t d = dice_new(); + bool fudge = false; + + dice_set(d, DICEOPTION_FUDGE, true); + dice_get(d, DICEOPTION_FUDGE, &fudge); + + assert_true(fudge); + dice_free(d); +} + +static void test_dice_fudge_roll_one(void **data) +{ + dice_t d = dice_new(); + int result = 0, i = 0; + + dice_set(d, DICEOPTION_FUDGE, true); + dice_set(d, DICEOPTION_AMOUNT, 1L); + + for (i = 0; i < 100000; i++) { + result = dice_roll(d); + assert_true(result >= -1 && result <= 1); + } + + dice_free(d); +} + +static void test_dice_fudge_roll_more(void **data) +{ + dice_t d = dice_new(); + int result = 0, i = 0; + + dice_set(d, DICEOPTION_FUDGE, true); + dice_set(d, DICEOPTION_AMOUNT, 6L); + + for (i = 0; i < 100000; i++) { + result = dice_roll(d); + assert_true(result >= -6 && result <= 6); + } + + dice_free(d); +} + +static void test_dice_fudge_parse1(void **data) +{ + dice_t d = dice_new(); + int amount = 0, fudge = false; + + assert_true(dice_parse(d, "4dF")); + + dice_get(d, DICEOPTION_AMOUNT, &amount); + dice_get(d, DICEOPTION_FUDGE, &fudge); + + assert_int_equal(amount, 4); + assert_true(fudge); + + dice_free(d); +} + +static void test_dice_fudge_parse2(void **data) +{ + dice_t d = dice_new(); + int amount = 0, fudge = false; + + assert_true(dice_parse(d, "df")); + + dice_get(d, DICEOPTION_AMOUNT, &amount); + dice_get(d, DICEOPTION_FUDGE, &fudge); + + assert_int_equal(amount, 1); + assert_true(fudge); + + dice_free(d); +} + +int main(int ac, char **av) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_dice_fudge_construct), + cmocka_unit_test(test_dice_fudge_set), + cmocka_unit_test(test_dice_fudge_roll_one), + cmocka_unit_test(test_dice_fudge_roll_more), + cmocka_unit_test(test_dice_fudge_parse1), + cmocka_unit_test(test_dice_fudge_parse2), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +}