|
6 | 6 |
|
7 | 7 | http://willie.dftba.net/ |
8 | 8 | """ |
9 | | - |
10 | 9 | import random |
11 | | -import willie.module |
12 | 10 | import re |
13 | 11 |
|
| 12 | +import willie.module |
| 13 | +from willie.tools import eval_equation |
| 14 | + |
14 | 15 |
|
15 | 16 | class DicePouch: |
16 | 17 | def __init__(self, num_of_die, type_of_die, addition): |
@@ -108,60 +109,92 @@ def get_number_of_faces(self): |
108 | 109 | return len(self.dice) + len(self.dropped) |
109 | 110 |
|
110 | 111 |
|
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): |
126 | 113 | result = re.search(r""" |
127 | | - (?P<dice_num>\d+) |
| 114 | + (?P<dice_num>\d*) |
128 | 115 | d |
129 | 116 | (?P<dice_type>\d+) |
130 | 117 | (v(?P<drop_lowest>\d+))? |
131 | | - (?P<plus>(-|\+)\d+)? |
132 | 118 | $""", |
133 | | - trigger.group(2), |
| 119 | + dice_expression, |
134 | 120 | re.IGNORECASE | re.VERBOSE) |
135 | | - if not result: |
136 | | - bot.reply("Syntax for rolling dice is XdY[vZ][+N].") |
137 | | - return |
138 | 121 |
|
139 | | - dice_num = int(result.group('dice_num')) |
| 122 | + dice_num = int(result.group('dice_num') or 1) |
140 | 123 | dice_type = int(result.group('dice_type')) |
141 | | - addition = int(result.group('plus') or 0) |
142 | 124 |
|
143 | 125 | # Upper limit for dice should be at most a million. Creating a dict with |
144 | 126 | # more than a million elements already takes a noticeable amount of time |
145 | 127 | # on a fast computer and ~55kB of memory. |
146 | 128 | if dice_num > 1000: |
147 | | - bot.reply("I only have 1000 dice. =(") |
148 | | - return |
| 129 | + return None |
149 | 130 |
|
150 | | - dice = DicePouch(dice_num, dice_type, addition) |
| 131 | + dice = DicePouch(dice_num, dice_type, 0) |
151 | 132 |
|
152 | 133 | if result.group('drop_lowest'): |
153 | 134 | drop = int(result.group('drop_lowest')) |
154 | 135 | dice.drop_lowest(drop) |
155 | 136 |
|
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 |
162 | 195 |
|
163 | 196 | bot.reply("You roll %s: %s = %d" % ( |
164 | | - trigger.group(2), dice_str, dice.get_sum())) |
| 197 | + trigger.group(2), pretty_str, result)) |
165 | 198 |
|
166 | 199 |
|
167 | 200 | @willie.module.commands("choice") |
|
0 commit comments