From 4bef172a1810a61b4402cf0acb84dd9728ddaa3c Mon Sep 17 00:00:00 2001 From: olinox Date: Sun, 24 Jun 2018 18:34:53 +0200 Subject: [PATCH] #1 Add fudge dices (XdF) --- README.rst | 2 +- test.py | 6 ++++++ xdice.py | 31 ++++++++++++++++++++----------- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/README.rst b/README.rst index 77b2422..a78d672 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,7 @@ *xdice* is a lightweight python library for managing dice, scores, and dice-notation patterns. -- Parse almost any Dice Notation pattern: '1d6+1', 'd20', '3d%', '1d20//2 - 2*(6d6+2)', 'max(1d4+1,1d6)', '3D6L2', 'R3(1d6+1)'...etc. +- Parse almost any Dice Notation pattern: '1d6+1', 'd20', '3d%', '1d20//2 - 2*(6d6+2)', 'max(1d4+1,1d6)', '3D6L2', 'R3(1d6+1)', '3dF'...etc. - API help you to easily manipulate dices, patterns, and scores as objects - A command line tool for convenience diff --git a/test.py b/test.py index 48c7c2d..5e442f6 100644 --- a/test.py +++ b/test.py @@ -58,6 +58,8 @@ def test_patterns_validation(self): xdice.roll("3d6!") xdice.roll("3d6x") xdice.roll("3d6h1x") + xdice.roll("3df") + xdice.roll("3d6+1df") # test invalid expressions self.assertRaises(ValueError, xdice.roll, "") @@ -67,6 +69,8 @@ def test_patterns_validation(self): self.assertRaises(ValueError, xdice.roll, "1d6l2") self.assertRaises(ValueError, xdice.roll, "1d6h2") self.assertRaises(ValueError, xdice.roll, "1d6lh") + self.assertRaises(SyntaxError, xdice.roll, "1d6f") + self.assertRaises(SyntaxError, xdice.roll, "3f") self.assertRaises(SyntaxError, xdice.roll, "1+R3(1d6+1") def test_dice_object(self): @@ -109,6 +113,8 @@ def test_dice_object(self): self.assertEqual(xdice.Dice.parse("3d1x").roll(), 6) self.assertEqual(xdice.Dice.parse("3d1lhx").roll(), 2) + self.assertIn(xdice.Dice("f", 1).roll(), [-1, 0, 1]) + def test_score_object(self): s = xdice.Score([1, 2, 3]) diff --git a/xdice.py b/xdice.py index a341c16..5be54fc 100644 --- a/xdice.py +++ b/xdice.py @@ -8,7 +8,7 @@ import random import re -__VERSION__ = 1.1 +__VERSION__ = 1.2 def compile(pattern_string): # @ReservedAssignment """ @@ -69,7 +69,7 @@ class Dice(): Use roll() to get a Score() object. """ DEFAULT_SIDES = 20 - DICE_RE_STR = r"(?P\d*)d(?P\d*)(?:l(?P\d*))?(?:h(?P\d*))?([x!])?" + DICE_RE_STR = r"(?P\d*)d(?Pf|\d*)(?:l(?P\d*))?(?:h(?P\d*))?([x!])?" DICE_RE = re.compile(DICE_RE_STR) def __init__(self, sides, amount=1, drop_lowest=0, drop_highest=0, explode=False): @@ -93,7 +93,9 @@ def sides(self): @sides.setter def sides(self, sides): """ Set the number of faces of the dice """ - _assert_int_ge_to(sides, 1, "Invalid value for sides ('{}')".format(sides)) + if sides != "f": + _assert_int_ge_to(sides, 1, "Invalid value for sides ('{}')".format(sides)) + sides = int(sides) self._sides = sides @property @@ -160,7 +162,8 @@ def __repr__(self): lowstr = "; drop_lowest={}".format(self.drop_lowest) if self.drop_lowest else "" highstr = "; drop_highest={}".format(self.drop_highest) if self.drop_highest else "" explodestr = "; explode"if self.explode else "" - return "".format(self.sides, self.amount, lowstr, highstr, explodestr) + fudgestr = "; fudge"if self.sides == "f" else "" + return "".format(self.sides, self.amount, lowstr, highstr, explodestr, fudgestr) def __eq__(self, d): """ @@ -169,14 +172,17 @@ def __eq__(self, d): """ return self.sides == d.sides and self.amount == d.amount + def _rollone(self): + return random.randint(1, self._sides) if self._sides != "f" else random.randint(-1, 1) + def roll(self): """ Role the dice and return a Score object """ # Sort results - results = [random.randint(1, self._sides) for _ in range(self._amount)] + results = [self._rollone() for _ in range(self._amount)] dropped = [_pop_lowest(results) for _ in range(self._drop_lowest)] + \ [_pop_highest(results) for _ in range(self._drop_highest)] if self._explode: - exploded = [random.randint(1, self._sides) for _ in range(len([score for score in results if score == self._sides]))] + exploded = [self._rollone() for _ in range(len([score for score in results if score == self._sides]))] results += exploded return Score(results, dropped, self.name) @@ -193,11 +199,14 @@ def parse(cls, pattern): amount = amount or 1 sides = sides or cls.DEFAULT_SIDES - lowest = (lowest or 1) if lowest is not None else 0 - highest = (highest or 1) if highest is not None else 0 - explode = bool(explode) - - return Dice(*map(int, [sides, amount, lowest, highest, explode])) + if lowest == "": + lowest = 1 + lowest = lowest or 0 + if highest == "": + highest = 1 + highest = highest or 0 + + return Dice(*[sides, int(amount), int(lowest), int(highest), bool(explode)]) class Score(int): """ Score is a subclass of integer.