diff options
Diffstat (limited to 'game.py')
| -rw-r--r-- | game.py | 87 |
1 files changed, 87 insertions, 0 deletions
@@ -0,0 +1,87 @@ +import re +from enum import Enum +from dataclasses import dataclass + +from distribution import Distribution + +def d(edge_count): + res = Distribution() + if edge_count == 0: + res.insert(0, 1) + return res + prob = 1/edge_count + for val in range(1, edge_count+1): + res.insert(val, prob) + return res + + +class Roll: + Fumble = -1 + Fail = 0 + Success = 1 + Crit = 2 + + @staticmethod + def get_natural_success_mod(value): + if value <= 1: + return -1 + if value >= 20: + return 1 + return 0 + + @staticmethod + def check(value, dc, bonus=0): + res = 0 + rolled = value + bonus + if rolled >= dc + 10: + res = Roll.Crit + elif rolled >= dc: + res = Roll.Success + elif rolled <= dc - 10: + res = Roll.Fumble + res += Roll.get_natural_success_mod(value) + return Roll.clamp(res) + + @staticmethod + def clamp(value): + return max(Roll.Fumble, min(Roll.Crit, value)) + + +class Difficulty(Enum): + Terrible = -2 + Low = -1 + Moderate = 0 + High = 1 + Extreme = 2 + + @classmethod + def from_str(cls, name): + name = name.strip().lower() + for member in cls: + if member.name.lower() == name: + return member + raise ValueError(f"{name!r} is not a valid {cls.__name__}") + +@dataclass +class RollInfo: + die_size: int + dice_count: int = 1 + bonus: int = 0 + + @classmethod + def parse(cls, str_data): + # match strings like 2d6+8 (15) + match = re.fullmatch(r"^(\d+)d(\d+)\s*(\+\s*\d+)?\s*(\s*\(\s*\d+\s*\))?", + str_data) + if not match: + raise ValueError(f"{str_data!r} is not a valid RollInfo") + res = RollInfo(int(match.group(2).strip())) + res.dice_count = int(match.group(1).strip()) + bonus = match.group(3) + if bonus: + res.bonus = int(bonus.strip(" +")) + return res + + def distribution(self): + return self.dice_count * d(self.die_size) + self.bonus + |
