olinox 8 anni fa
parent
commit
fc4f601482
2 ha cambiato i file con 74 aggiunte e 15 eliminazioni
  1. 1 1
      docs/API.md
  2. 73 14
      xdice.py

+ 1 - 1
docs/API.md

@@ -81,7 +81,7 @@
 
 > The list of parsed dice.
 
-> *Eg: '1d6+4+1d4' => [<Dice; sides=6;amount=1>, <Dice; sides=4;amount=1>]*
+> *Eg: '1d6+4+1d4' => [(Dice; sides=6;amount=1), (Dice; sides=4;amount=1)]*
 
 #### pattern.roll()
 

+ 73 - 14
xdice.py

@@ -13,23 +13,36 @@ __VERSION__ = 1.0
 # TODO: 'L', 'LX', 'H' and 'HX' notations: drop the x lowest or highest results => eg: 'AdXl3'
 # TODO: (?) 'Rx(...)' notation: roll x times the pattern in the parenthesis => eg: R3(1d4+3)
 # TODO: 'd%' notation: d% <=> d100
-# TODO: (?) Dice pools, 6-sided variations, 10-sided variations, Open-ended variations (https://en.wikipedia.org/wiki/Dice_notation)
+# TODO: (?) Dice pools, 6-sided variations, 10-sided variations,
+# Open-ended variations (https://en.wikipedia.org/wiki/Dice_notation)
 
 def compile(pattern_string):  # @ReservedAssignment
-    p = Pattern(pattern_string)
-    p.compile()
-    return p
+    """
+    > Similar to xdice.Pattern(pattern_string).compile()
+    Returns a compiled Pattern object.
+    Pattern object can then be rolled to obtain a PatternScore object.
+    """
+    pattern = Pattern(pattern_string)
+    pattern.compile()
+    return pattern
 
 def roll(pattern_string):
+    """
+    > Similar to xdice.Pattern(pattern_string).roll()
+    """
     return Pattern(pattern_string).roll()
 
 def rolldice(faces, amount=1):
+    """
+    > Similar to xdice.Dice(faces, amount).roll()
+    """
     return Dice(faces, amount).roll()
 
 _ALLOWED = {'abs': abs, 'max': max, 'min': min}
 
 def _secured_eval(raw):
-    """ securely evaluate the incoming raw string by avoiding the use of any non-allowed function """
+    """ securely evaluate the incoming raw string
+    by avoiding the use of any non-allowed function """
     return eval(raw, {"__builtins__":None}, _ALLOWED)
 
 class Dice():
@@ -42,6 +55,7 @@ class Dice():
     DEFAULT_SIDES = 20
 
     def __init__(self, sides, amount=1):
+        """ Instanciate a Die object """
         self._sides = 1
         self._amount = 0
 
@@ -50,12 +64,14 @@ class Dice():
 
     @property
     def sides(self):
+        """ Number of faces of the dice """
         return self._sides
 
     @sides.setter
     def sides(self, sides):
+        """ Set the number of faces of the dice """
         try:
-            if not int(sides) >= 1:
+            if int(sides) < 1:
                 raise ValueError()
         except (TypeError, ValueError):
             raise ValueError("Invalid value for sides (given: '{}')".format(sides))
@@ -63,21 +79,28 @@ class Dice():
 
     @property
     def amount(self):
+        """ Amount of dice """
         return self._amount
 
     @amount.setter
     def amount(self, amount):
+        """ Set the amount of dice """
         try:
-            if not int(amount) >= 0:
+            if int(amount) < 0:
                 raise ValueError()
         except (TypeError, ValueError):
             raise ValueError("Invalid value for amount (given: '{}')".format(amount))
         self._amount = amount
 
     def __repr__(self):
+        """ Return a string representation of the Dice """
         return "<Dice; sides={}; amount={}>".format(self.sides, self.amount)
 
     def __eq__(self, d):
+        """
+        Eval equality of two Dice objects
+        used for testing matters
+        """
         return self.sides == d.sides and self.amount == d.amount
 
     def roll(self):
@@ -89,13 +112,13 @@ class Dice():
         """ parse a pattern of the form 'xdx', where x are positive integers """
         pattern = str(pattern).replace(" ", "").lower()
 
-        a, x = pattern.split("d")
-        if not a:
-            a = 1
-        if not x:
-            x = cls.DEFAULT_SIDES
+        amount, sides = pattern.split("d")
+        if not amount:
+            amount = 1
+        if not sides:
+            sides = cls.DEFAULT_SIDES
 
-        return Dice(*map(int, [x, a]))
+        return Dice(*map(int, [sides, amount]))
 
 class Score(int):
     """ Score is a subclass of integer.
@@ -117,25 +140,37 @@ class Score(int):
 
     """
     def __new__(cls, detail):
+        """
+        detail should only contain integers
+        Score value will be the sum of the list's values.
+        """
         score = super(Score, cls).__new__(cls, sum(detail))
         score._detail = detail
         return score
 
     @property
     def detail(self):
+        """ Return the detailed score
+        as a list of integers,
+        which are the results of each die rolled """
         return self._detail
 
     def __repr__(self):
+        """ Return a string representation of the Score """
         return "<Score; score={}; detail={}>".format(int(self), self.detail)
 
     def __contains__(self, value):
+        """ Does score contains the given result """
         return self.detail.__contains__(value)
 
     def __iter__(self):
+        """ Iterate over results """
         return self.detail.__iter__()
 
 class Pattern():
+    """ A dice-notation pattern """
     def __init__(self, instr):
+        """ Instantiate a Pattern object. """
         if not instr:
             raise ValueError("Invalid value for 'instr' ('{}')".format(instr))
         self.instr = Pattern._normalize(instr)
@@ -148,22 +183,40 @@ class Pattern():
         return str(instr).replace(" ", "").lower()
 
     def compile(self):
+        """
+        Parse the pattern. Two properties are updated at this time:
+
+        * pattern.format_string:
+        The ready-to-be-formatted string built from the instr argument.
+        > Eg: '1d6+4+1d4' => '{0}+4-{1}'
 
+        * pattern.dices
+        The list of parsed dice.
+        > Eg: '1d6+4+1d4' => [(Dice; sides=6;amount=1), (Dice; sides=4;amount=1)]
+        """
         def _submatch(match):
             dice = Dice.parse(match.group(0))
             index = len(self.dices)
             self.dices.append(dice)
             return "{{{}}}".format(index)
 
-        self.format_string = re.sub('\d*d\d*', _submatch, self.instr)
+        self.format_string = re.sub(r'\d*d\d*', _submatch, self.instr)
 
     def roll(self):
+        """
+        Compile the pattern if it has not been yet, then roll the dice.
+        Return a PatternScore object.
+        """
         if not self.format_string:
             self.compile()
         scores = [dice.roll() for dice in self.dices]
         return PatternScore(self.format_string, scores)
 
 class PatternScore(int):
+    """
+    PatternScore is a subclass of integer, you can then manipulate it as you would do with an integer.
+    Moreover, you can get the list of the scores with the score(i) or scores() methods, and retrieve a formatted result with the format() method.
+    """
     def __new__(cls, eval_string, scores):
         ps = super(PatternScore, cls).__new__(cls, _secured_eval(eval_string.format(*scores)))
 
@@ -173,10 +226,16 @@ class PatternScore(int):
         return ps
 
     def format(self):
+        """
+        Return a formatted string detailing the result of the roll.
+        > Eg: '3d6+4' => '[1,5,6]+4'
+        """
         return self._eval_string.format(*[str(list(score)) for score in self._scores])
 
     def score(self, i):
+        """ Returns the Score object at index i. """
         return self._scores[i]
 
     def scores(self):
+        """ Returns the list of Score objects extracted from the pattern and rolled. """
         return self._scores