From 1285190ee7a73feb817cbce7cd9a0e369c5fdead Mon Sep 17 00:00:00 2001 From: dnutiu Date: Sun, 21 Jan 2024 22:25:23 +0200 Subject: [PATCH] update dice parser --- src/__init__.py | 0 src/dice/dice.py | 15 ++++++++------- src/dice/parser.py | 4 ++-- src/dice/semantics.py | 16 +++++++++++----- tests/__init__.py | 0 5 files changed, 21 insertions(+), 14 deletions(-) create mode 100644 src/__init__.py create mode 100644 tests/__init__.py diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/dice/dice.py b/src/dice/dice.py index fff4c8c..731e95f 100644 --- a/src/dice/dice.py +++ b/src/dice/dice.py @@ -5,18 +5,19 @@ from src.dice.parser import DieParser class DiceRoller: """ - DiceRoller is a simple class that allows you to roll dices. + DiceRoller is a simple class that allows you to roll dices. - A die can be rolled using the following expression: - - 1d20 will roll a 20-faceted die and output the result a random number between 1 and 20. - - 1d100 will roll a 100 faceted die. - - 2d20 will roll a two d20 dies and multiply the result by two. - - 2d20+5 will roll a two d20 dies and multiply the result by two and ads 5. + A die can be rolled using the following expression: + - 1d20 will roll a 20-faceted die and output the result a random number between 1 and 20. + - 1d100 will roll a 100 faceted die. + - 2d20 will roll a two d20 dies and multiply the result by two. + - 2d20+5 will roll a two d20 dies and multiply the result by two and ads 5. """ + _parser = DieParser() @staticmethod - def roll(expression: str, advantage: typing.Optional[bool]) -> int: + def roll(expression: str, advantage: typing.Optional[bool] = None) -> int: """ Roll die and return the result. :param expression: The die expression. diff --git a/src/dice/parser.py b/src/dice/parser.py index 964f934..8c901a4 100644 --- a/src/dice/parser.py +++ b/src/dice/parser.py @@ -8,9 +8,9 @@ DIE_GRAMMAR = """ @@grammar::Die @@whitespace :: None - start = die:die [modifier:die_modifier] $; + start = die:die $; - die = [number_of_dies:number] die_type:die_type die_number:number; + die = [number_of_dies:number] die_type:die_type die_number:number [modifier:die_modifier]; die_modifier = op:operator modifier:number; operator = '+' | '-'; diff --git a/src/dice/semantics.py b/src/dice/semantics.py index 7de8e20..3cb4c47 100644 --- a/src/dice/semantics.py +++ b/src/dice/semantics.py @@ -8,9 +8,7 @@ class DieSemantics: return int(ast) def start(self, ast): - modifier = ast.get("modifier") or 0 - die = ast.get("die") - return die + modifier + return ast.get("die") def die(self, ast): if not isinstance(ast, AST): @@ -18,14 +16,22 @@ class DieSemantics: number_of_dies = ast.get("number_of_dies") or 1 die_type = ast.get("die_type") die_number = ast.get("die_number") or 1 + die_modifier = ast.get("modifier") or 0 if die_number <= 0: return 0 # normal die if die_type == "d": - return sum([random.randint(1, die_number) for _ in range(number_of_dies)]) + die_sum = sum( + [random.randint(1, die_number) for _ in range(number_of_dies)] + ) + # do not let die to underflow + return max(die_sum + die_modifier, 1) # zero-based die can output 0. if die_type == "zd": - return sum([random.randint(0, die_number) for _ in range(number_of_dies)]) + die_sum = sum( + [random.randint(0, die_number) for _ in range(number_of_dies)] + ) + return max(die_sum + die_modifier, 0) raise ValueError(f"Invalid die type: {die_type}") def die_modifier(self, ast): diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29