Skip to content

Commit 5a8ecf4

Browse files
committed
[dice] Add back arithmetic expression functionality.
-Also allows for using many dice expressions in one command, just like before.
1 parent acce528 commit 5a8ecf4

File tree

1 file changed

+68
-35
lines changed

1 file changed

+68
-35
lines changed

willie/modules/dice.py

Lines changed: 68 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
77
http://willie.dftba.net/
88
"""
9-
109
import random
11-
import willie.module
1210
import re
1311

12+
import willie.module
13+
from willie.tools import eval_equation
14+
1415

1516
class DicePouch:
1617
def __init__(self, num_of_die, type_of_die, addition):
@@ -108,60 +109,92 @@ def get_number_of_faces(self):
108109
return len(self.dice) + len(self.dropped)
109110

110111

111-
@willie.module.commands("roll")
112-
@willie.module.commands("dice")
113-
@willie.module.commands("d")
114-
@willie.module.priority("medium")
115-
@willie.module.example(".roll 3d1+1", 'You roll 3d1+1: (1+1+1)+1 = 4')
116-
@willie.module.example(".roll 3d1v2+1", 'You roll 3d1v2+1: (1[+1+1])+1 = 2')
117-
@willie.module.example(".roll 2d4", re='You roll 2d4: \(\d\+\d\) = \d')
118-
@willie.module.example(".roll 100d1", re='[^:]*: \(100x1\) = 100')
119-
def roll(bot, trigger):
120-
""".dice XdY[vZ][+N], rolls dice and reports the result.
121-
122-
X is the number of dice. Y is the number of faces in the dice. Z is the
123-
number of lowest dice to be dropped from the result. N is the constant to
124-
be applied to the end result.
125-
"""
112+
def _roll_dice(dice_expression):
126113
result = re.search(r"""
127-
(?P<dice_num>\d+)
114+
(?P<dice_num>\d*)
128115
d
129116
(?P<dice_type>\d+)
130117
(v(?P<drop_lowest>\d+))?
131-
(?P<plus>(-|\+)\d+)?
132118
$""",
133-
trigger.group(2),
119+
dice_expression,
134120
re.IGNORECASE | re.VERBOSE)
135-
if not result:
136-
bot.reply("Syntax for rolling dice is XdY[vZ][+N].")
137-
return
138121

139-
dice_num = int(result.group('dice_num'))
122+
dice_num = int(result.group('dice_num') or 1)
140123
dice_type = int(result.group('dice_type'))
141-
addition = int(result.group('plus') or 0)
142124

143125
# Upper limit for dice should be at most a million. Creating a dict with
144126
# more than a million elements already takes a noticeable amount of time
145127
# on a fast computer and ~55kB of memory.
146128
if dice_num > 1000:
147-
bot.reply("I only have 1000 dice. =(")
148-
return
129+
return None
149130

150-
dice = DicePouch(dice_num, dice_type, addition)
131+
dice = DicePouch(dice_num, dice_type, 0)
151132

152133
if result.group('drop_lowest'):
153134
drop = int(result.group('drop_lowest'))
154135
dice.drop_lowest(drop)
155136

156-
if dice_num <= 10:
157-
dice_str = dice.get_simple_string()
158-
elif dice.get_number_of_faces() <= 10:
159-
dice_str = dice.get_compressed_string()
160-
else:
161-
dice_str = "(...)"
137+
return dice
138+
139+
140+
@willie.module.commands("roll")
141+
@willie.module.commands("dice")
142+
@willie.module.commands("d")
143+
@willie.module.priority("medium")
144+
@willie.module.example(".roll 3d1+1", 'You roll 3d1+1: (1+1+1)+1 = 4')
145+
@willie.module.example(".roll 3d1v2+1", 'You roll 3d1v2+1: (1[+1+1])+1 = 2')
146+
@willie.module.example(".roll 2d4", re='You roll 2d4: \(\d\+\d\) = \d')
147+
@willie.module.example(".roll 100d1", re='[^:]*: \(100x1\) = 100')
148+
@willie.module.example(".roll 1001d1", 'I only have 1000 dice. =(')
149+
@willie.module.example(".roll 1d1 + 1d1", 'You roll 1d1 + 1d1: (1) + (1) = 2')
150+
@willie.module.example(".roll 1d1+1d1", 'You roll 1d1+1d1: (1)+(1) = 2')
151+
def roll(bot, trigger):
152+
""".dice XdY[vZ][+N], rolls dice and reports the result.
153+
154+
X is the number of dice. Y is the number of faces in the dice. Z is the
155+
number of lowest dice to be dropped from the result. N is the constant to
156+
be applied to the end result.
157+
"""
158+
# This regexp is only allowed to have one captured group, because having
159+
# more would alter the output of re.findall.
160+
dice_regexp = r"\d*d\d+(?:v\d+)?"
161+
162+
# Get a list of all dice expressions, evaluate them and then replace the
163+
# expressions in the original string with the results. Replacing is done
164+
# using string formatting, so %-characters must be escaped.
165+
arg_str = trigger.group(2)
166+
dice_expressions = re.findall(dice_regexp, arg_str)
167+
arg_str = arg_str.replace("%", "%%")
168+
arg_str = re.sub(dice_regexp, "%s", arg_str)
169+
dice = map(_roll_dice, dice_expressions)
170+
if None in dice:
171+
bot.reply("I only have 1000 dice. =(")
172+
return
173+
174+
def _get_eval_str(dice):
175+
return "(%d)" % (dice.get_sum(),)
176+
177+
def _get_pretty_str(dice):
178+
if dice.num <= 10:
179+
return dice.get_simple_string()
180+
elif dice.get_number_of_faces() <= 10:
181+
return dice.get_compressed_string()
182+
else:
183+
return "(...)"
184+
185+
eval_str = arg_str % (tuple(map(_get_eval_str, dice)))
186+
pretty_str = arg_str % (tuple(map(_get_pretty_str, dice)))
187+
188+
# Showing the actual error will hopefully give a better hint of what is
189+
# wrong with the syntax than a generic error message.
190+
try:
191+
result = eval_equation(eval_str)
192+
except Exception as e:
193+
bot.reply("SyntaxError, eval(%s), %s" % (eval_str, e))
194+
return
162195

163196
bot.reply("You roll %s: %s = %d" % (
164-
trigger.group(2), dice_str, dice.get_sum()))
197+
trigger.group(2), pretty_str, result))
165198

166199

167200
@willie.module.commands("choice")

0 commit comments

Comments
 (0)