Sources of Telegram bot for cryptopotato chat.
https://t.me/devpotato_bot
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
89 lines
2.9 KiB
89 lines
2.9 KiB
import random
|
|
import re
|
|
from typing import List, Tuple, Optional
|
|
|
|
|
|
class ParseError(Exception):
|
|
def __init__(self):
|
|
super().__init__('Roll is not in dice notation')
|
|
|
|
|
|
class ValueRangeError(ValueError):
|
|
def __init__(self, arg_name, value,
|
|
allowed_range: Optional[Tuple[Optional[int], Optional[int]]] = None):
|
|
self.arg_name = arg_name
|
|
self.value = value
|
|
self.allowed_range = allowed_range
|
|
super().__init__({
|
|
'arg_name': arg_name,
|
|
'value': value,
|
|
'allowed_range': allowed_range
|
|
})
|
|
|
|
@property
|
|
def message(self):
|
|
if self.allowed_range is None or self.allowed_range == (None, None):
|
|
return '{arg_name} has unacceptable value: {value}'
|
|
arg_min, arg_max = self.allowed_range
|
|
if arg_max is None:
|
|
return '{arg_name} is less than {allowed_range[0]}: {value}'
|
|
if arg_min is None:
|
|
return '{arg_name} is greater than {allowed_range[1]}: {value}'
|
|
return '{arg_name} is not between {allowed_range[0]} and {allowed_range[1]}: {value}'
|
|
|
|
@property
|
|
def formatted_message(self):
|
|
return self.message.format(**self.args[0])
|
|
|
|
|
|
class Dice:
|
|
__regex = re.compile(
|
|
r'(?P<rolls>\d+)?'
|
|
r'd'
|
|
r'(?P<sides>\d+)'
|
|
r'(?:(?P<modifier_sign>[+-])(?P<modifier>\d+))?'
|
|
)
|
|
BIGGEST_DICE = 120
|
|
ROLL_LIMIT = 100
|
|
|
|
def __init__(self, rolls, sides, modifier=0):
|
|
if not(0 < rolls <= self.ROLL_LIMIT):
|
|
raise ValueRangeError('roll count', rolls, (1, self.ROLL_LIMIT))
|
|
if not(0 < sides <= self.BIGGEST_DICE):
|
|
raise ValueRangeError('sides', sides, (1, self.BIGGEST_DICE))
|
|
self.rolls = rolls
|
|
self.sides = sides
|
|
self.modifier = modifier or 0
|
|
|
|
@staticmethod
|
|
def parse(roll_str):
|
|
match = Dice.__regex.fullmatch(roll_str)
|
|
if not match:
|
|
raise ParseError
|
|
rolls, sides, modifier_sign, modifier = match.groups()
|
|
rolls = int(rolls) if rolls else 1
|
|
sides = int(sides)
|
|
modifier = int(modifier) if modifier else 0
|
|
if modifier and modifier_sign == '-':
|
|
modifier = -modifier
|
|
return Dice(rolls, sides, modifier)
|
|
|
|
def _single_roll(self):
|
|
return random.randint(1, self.sides)
|
|
|
|
def get_result(self, item_limit=None) -> Tuple[int, List[int], bool]:
|
|
total = self.modifier
|
|
items = []
|
|
if item_limit is None:
|
|
item_count = self.rolls
|
|
else:
|
|
item_count = min(self.rolls, item_limit)
|
|
if item_limit < 0:
|
|
raise ValueError('item limit should be non-negative!')
|
|
for i in range(item_count):
|
|
r = self._single_roll()
|
|
total += r
|
|
items.append(r)
|
|
if item_limit is not None:
|
|
total += sum(self._single_roll() for _ in range(item_limit, self.rolls))
|
|
return total, items, item_count < self.rolls
|