Skip to content

Commit fe87b1c

Browse files
committed
Added Sort, Keep and Drop, with some other minor changes
1 parent 0893f9a commit fe87b1c

File tree

7 files changed

+75
-25
lines changed

7 files changed

+75
-25
lines changed

dice/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
def roll(string, single=True, verbose=False):
1717
"""Parses and evaluates a dice expression"""
1818
ast = dice.grammar.expression.parseString(string, parseAll=True)
19+
if verbose:
20+
dice.elements.Element.verbose = True
1921
result = [element.evaluate_cached(verbose=verbose) for element in ast]
2022
if single:
2123
return dice.utilities.single(result)

dice/elements.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010

1111
class Element(object):
12-
verbose = True
12+
verbose = False
1313

1414
def evaluate(self):
1515
"""Evaluate the current object - a no-op by default"""
@@ -103,15 +103,14 @@ def evaluate(self):
103103
class Operator(Element):
104104
@classmethod
105105
def parse(cls, string, location, tokens):
106-
return cls(operands=tokens)
106+
return cls(*tokens)
107107

108-
def __init__(self, operands):
109-
self.operands = operands
108+
def __init__(self, *operands):
109+
self.operands = self.orginal_operands = operands
110110

111111
def __repr__(self):
112112
return "{0}({1})".format(
113-
classname(self),
114-
', '.join(map(str, self.operands)))
113+
classname(self), ', '.join(map(str, self.orginal_operands)))
115114

116115
def evaluate(self):
117116
self.operands = map(self.evaluate_object, self.operands)
@@ -145,3 +144,23 @@ class Add(IntegerOperator):
145144

146145
class Total(Operator):
147146
function = sum
147+
148+
149+
class Sort(Operator):
150+
def function(self, iterable):
151+
iterable.sort()
152+
return iterable
153+
154+
155+
class Drop(Operator):
156+
def function(self, iterable, n):
157+
for die in sorted(iterable)[:n]:
158+
iterable.remove(die)
159+
return iterable
160+
161+
162+
class Keep(Operator):
163+
def function(self, iterable, n):
164+
for die in sorted(iterable)[:-n]:
165+
iterable.remove(die)
166+
return iterable

dice/grammar.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
CaselessLiteral, Forward, Literal, OneOrMore, StringStart, StringEnd,
1313
Suppress, Word, nums, opAssoc)
1414

15-
from dice.elements import Integer, Dice, Total, Mul, Div, Sub, Add
15+
from dice.elements import (
16+
Integer, Dice, Mul, Div, Sub, Add, Total, Sort, Drop, Keep)
1617
from dice.utilities import patch_pyparsing
1718

1819
patch_pyparsing()
@@ -82,14 +83,18 @@ def parse_operator(expr, arity, association, action=None):
8283

8384
# An expression in dice notation
8485
expression = StringStart() + operatorPrecedence(integer, [
85-
(Literal('d').suppress(), 2, opAssoc.LEFT, Dice.parse_binary),
86-
(Literal('d').suppress(), 1, opAssoc.RIGHT, Dice.parse_unary),
86+
(CaselessLiteral('d').suppress(), 2, opAssoc.LEFT, Dice.parse_binary),
87+
(CaselessLiteral('d').suppress(), 1, opAssoc.RIGHT, Dice.parse_unary),
8788

8889
(Literal('/').suppress(), 2, opAssoc.LEFT, Div.parse),
8990
(Literal('*').suppress(), 2, opAssoc.LEFT, Mul.parse),
9091
(Literal('-').suppress(), 2, opAssoc.LEFT, Sub.parse),
9192
(Literal('+').suppress(), 2, opAssoc.LEFT, Add.parse),
9293

9394
(CaselessLiteral('t').suppress(), 1, opAssoc.LEFT, Total.parse),
95+
(CaselessLiteral('s').suppress(), 1, opAssoc.LEFT, Sort.parse),
96+
97+
(Literal('^').suppress(), 2, opAssoc.LEFT, Keep.parse),
98+
(Literal('v').suppress(), 2, opAssoc.LEFT, Drop.parse),
9499
]) + StringEnd()
95100
expression.setName("expression")

dice/tests/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
"""Tests for the dice package"""
2+
3+
import dice.elements
4+
5+
dice.elements.Element.verbose = True

dice/tests/test_elements.py

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,30 @@
11
from __future__ import absolute_import
22

3-
from dice.elements import Integer, Roll, Dice
3+
from dice.elements import Integer, Roll, Dice, Total
4+
from dice import roll
45

56

6-
def test_integer():
7-
assert isinstance(Integer(1), int)
7+
class TestElements(object):
8+
def test_integer(self):
9+
assert isinstance(Integer(1), int)
810

11+
def test_dice_from_iterable(self):
12+
d = Dice.from_iterable((2, 6))
13+
assert d.amount == 2 and d.sides == 6
914

10-
def test_dice_from_iterable():
11-
d = Dice.from_iterable((2, 6))
12-
assert d.amount == 2 and d.sides == 6
15+
def test_dice_from_string(self):
16+
d = Dice.from_string('2d6')
17+
assert d.amount == 2 and d.sides == 6
1318

19+
def test_roll(self):
20+
amount, sides = 6, 6
21+
assert len(Roll.roll(amount, sides)) == amount
22+
assert (1 * sides) <= sum(Roll.roll(amount, sides)) <= (amount * sides)
1423

15-
def test_dice_from_string():
16-
d = Dice.from_string('2d6')
17-
assert d.amount == 2 and d.sides == 6
1824

19-
20-
def test_roll():
21-
amount, sides = 6, 6
22-
assert len(Roll.roll(amount, sides)) == amount
23-
assert (1 * sides) <= sum(Roll.roll(amount, sides)) <= (amount * sides)
25+
class TestEvaluate(object):
26+
def test_cache(self):
27+
"""Test that evaluation returns the same result on successive runs"""
28+
roll('6d(6d6)t')
29+
ast = Total(Dice(6, Dice(6, 6)))
30+
assert ast.evaluate_cached() is ast.evaluate_cached()

dice/tests/test_grammar.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def test_dice_values(self):
2727
assert 0 < die <= 6
2828

2929

30-
class TestFunctionOperators(object):
30+
class TestOperators(object):
3131
def test_add(self):
3232
assert roll('2 + 2') == 4
3333

@@ -44,6 +44,19 @@ def test_div(self):
4444
def test_total(self):
4545
assert (6 * 1) <= roll('6d6t') <= (6 * 6)
4646

47+
def test_sort(self):
48+
value = roll('6d6s')
49+
assert value == sorted(value)
50+
assert isinstance(value, Roll)
51+
52+
def test_drop(self):
53+
value = roll('6d6 v 3')
54+
assert len(value) == 3
55+
56+
def test_keep(self):
57+
value = roll('6d6 ^ 3')
58+
assert len(value) == 3
59+
4760

4861
class TestOperatorPrecedence(object):
4962
def test_operator_precedence_1(self):

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[tox]
22
minversion=1.5.0
33
toxworkdir=.cache
4-
envlist=py27-pep8,py27-pyflakes,py27-coverage,py26,py27,py32,py33,pypy
4+
envlist=py27-pyflakes,py27-pep8,py26,py27,py32,py33,pypy
55

66
[testenv]
77
commands=py.test dice

0 commit comments

Comments
 (0)