Skip to content

Commit 520284d

Browse files
committed
Add functionality to specify a source range
Now a range can be specified to pick random numbers from: `!roll 1d{0,9}`, `!roll 2d{3,7}`, `!roll 3d{-5,-1}`.
1 parent 30952b9 commit 520284d

File tree

3 files changed

+49
-21
lines changed

3 files changed

+49
-21
lines changed

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
# dice
2+
23
A [maubot](https://github.com/maubot/maubot) that rolls dice. Has built-in calculator.
34

45
## Usage
5-
The base command is `!roll`. To roll dice, pass `XdY` as an argument, where `X`
6-
is the number of dice (optional) and `Y` is the number of sides in each dice.
6+
7+
The base command is `!roll`.
8+
9+
To roll a dice, pass `XdY` as an argument, where `X` is the number of dice
10+
(optional) and `Y` is the number of sides in each dice. `Y` can be passed as a
11+
specific range as well (for example: `{0,9}`, `{-5,-1}`).
12+
713
Most Python math and bitwise operators and basic `math` module functions are
814
also supported, which means you can roll different kinds of dice and combine
915
the results however you like.

dice.py

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#
1414
# You should have received a copy of the GNU Affero General Public License
1515
# along with this program. If not, see <https://www.gnu.org/licenses/>.
16-
from typing import Match, Union, Any, Type
16+
from typing import Match, Optional, Tuple, Union, Any, Type
1717
import operator
1818
import random
1919
import math
@@ -24,7 +24,7 @@
2424
from maubot import Plugin, MessageEvent
2525
from maubot.handlers import command
2626

27-
pattern_regex = re.compile("([0-9]{0,9})[dD]([0-9]{1,9})")
27+
pattern_regex = re.compile(r"(\d{0,9})d((?:\d|(\{-?\d, *-?\d\})){1,9})", re.IGNORECASE)
2828

2929
_OP_MAP = {
3030
ast.Add: operator.add,
@@ -206,37 +206,58 @@ async def roll(self, evt: MessageEvent, pattern: str) -> None:
206206

207207
individual_rolls = [] if self.show_rolls else None
208208

209-
def randomize(number: int, size: int) -> int:
210-
if size < 0 or number < 0:
209+
def randomize(number: int, size: Union[int, Tuple[int, int]]) -> int:
210+
size_is_int: bool = isinstance(size, int)
211+
choices_range: Tuple[int, int] = (
212+
size if isinstance(size, tuple) else (1, size)
213+
)
214+
if not choices_range[0] <= choices_range[1]:
215+
raise ValueError(
216+
"The range's first element must not be greater than the second"
217+
)
218+
largest: int = choices_range[1]
219+
if size_is_int and largest < 0 or number < 0:
211220
raise ValueError("randomize() only accepts non-negative values")
212-
if size == 0 or number == 0:
221+
if size_is_int and largest == 0 or number == 0:
213222
return 0
214-
elif size == 1:
223+
elif size_is_int and largest == 1:
215224
return number
216225
_result = 0
217226
if number < self.gauss_limit:
218227
individual = [] if self.show_rolls and number < self.show_rolls_limit else None
219-
for i in range(number):
220-
roll = random.randint(1, size)
228+
for _ in range(number):
229+
roll = random.randint(*choices_range)
221230
if individual is not None:
222231
individual.append(roll)
223232
_result += roll
224-
if individual:
225-
individual_rolls.append((number, size, individual))
233+
if individual and individual_rolls is not None:
234+
individual_rolls.append(
235+
(
236+
number,
237+
largest
238+
if size_is_int
239+
else "{" + ", ".join(str(i) for i in choices_range) + "}",
240+
individual
241+
)
242+
)
226243
else:
227-
mean = number * (size + 1) / 2
228-
variance = number * (size ** 2 - 1) / 12
229-
while _result < number or _result > number * size:
244+
mean = number * (largest + 1) / 2
245+
variance = number * (largest ** 2 - 1) / 12
246+
while _result < number or _result > number * largest:
230247
_result = int(random.gauss(mean, math.sqrt(variance)))
231248
return _result
232249

233250
def replacer(match: Match) -> str:
234-
number = int(match.group(1) or "1")
235-
size = int(match.group(2))
251+
number: int = int(match.group(1) or "1")
252+
range: Optional[str] = match.group(3)
253+
size: Union[int, Tuple[int, int]] = (
254+
int(match.group(2))
255+
if not range
256+
else tuple(int(i) for i in range.strip('{}').split(','))
257+
)
236258
return str(randomize(number, size))
237-
238-
pattern = pattern_regex.sub(replacer, pattern)
239259
try:
260+
pattern = pattern_regex.sub(replacer, pattern)
240261
result = Calc.evaluate(pattern)
241262
if self.round_decimals >= 0:
242263
result = round(result, self.round_decimals)
@@ -263,7 +284,8 @@ async def help(self, evt: MessageEvent) -> None:
263284
"The base command is `!roll`. \\\n"
264285
"To roll a dice, pass `XdY` as an argument, where `X` is the "
265286
"number of dices (optional) and `Y` is the number of sides on "
266-
"each dice. \\\n"
287+
"each dice. `Y` can be passed as a specific range as well "
288+
"(for example: `{0,9}`, `{-5,-1}`). \\\n"
267289
"Most Python math and bitwise operators and basic `math` module "
268290
"functions are also supported, which means you can roll different "
269291
"kinds of dice and combine the results however you like."

maubot.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
maubot: 0.1.0
22
id: xyz.maubot.dice
3-
version: 1.1.0
3+
version: 1.2.0
44
license: AGPL-3.0-or-later
55
modules:
66
- dice

0 commit comments

Comments
 (0)