✨
This commit is contained in:
@ -3,22 +3,33 @@ from gwendolyn.utils import PARAMS as params
|
||||
|
||||
class MiscExtension(Extension):
|
||||
"""Contains the miscellaneous commands."""
|
||||
@slash_command(**params["misc"]["gen_name"])
|
||||
async def gen_name(self, ctx: SlashContext):
|
||||
await self.bot.other.generate_name(ctx)
|
||||
|
||||
@slash_command(**params["misc"]["hello"])
|
||||
async def hello(self, ctx: SlashContext):
|
||||
"""Greet the bot."""
|
||||
await self.other.hello_func(ctx)
|
||||
await self.bot.other.hello_func(ctx)
|
||||
|
||||
@slash_command(**params["misc"]["help"])
|
||||
async def help(self, ctx: SlashContext, command=""):
|
||||
"""Get help for commands."""
|
||||
await self.bot.other.help_func(ctx, command)
|
||||
|
||||
|
||||
@slash_command(**params["misc"]["ping"])
|
||||
async def ping(self, ctx: SlashContext):
|
||||
"""Send the bot's latency."""
|
||||
latency = self.bot.latency
|
||||
if latency > 100:
|
||||
await ctx.send("Cannot measure latency :(")
|
||||
else:
|
||||
await ctx.send(f"Pong!\nLatency is {round(self.bot.latency * 1000)}ms")
|
||||
|
||||
@slash_command(**params["misc"]["roll"])
|
||||
async def roll(self, ctx: SlashContext, dice: str = "1d20"):
|
||||
await self.bot.other.roll_dice(ctx, dice)
|
||||
|
||||
@slash_command(**params["misc"]["thank"])
|
||||
async def thank(self, ctx: SlashContext):
|
||||
"""Thank the bot."""
|
||||
|
44
gwendolyn/funcs/other/name_generator.py
Normal file
44
gwendolyn/funcs/other/name_generator.py
Normal file
@ -0,0 +1,44 @@
|
||||
import random
|
||||
|
||||
class NameGenerator():
|
||||
def __init__(self) -> None:
|
||||
with open("gwendolyn/resources/names.txt", "r", encoding="UTF-8") as file_pointer:
|
||||
self._names = file_pointer.read().lower().split("\n")
|
||||
|
||||
self._gen_letter_dict()
|
||||
|
||||
def _gen_letter_dict(self):
|
||||
list_of_names = []
|
||||
for name in self._names:
|
||||
if (name != ""):
|
||||
list_of_names.append("__" + name + "__")
|
||||
|
||||
dict_of_names = {}
|
||||
for name in list_of_names:
|
||||
for i in range(len(name)-3):
|
||||
combination = name[i:i+2]
|
||||
if combination not in dict_of_names:
|
||||
dict_of_names[combination] = []
|
||||
dict_of_names[combination].append(name[i+2])
|
||||
|
||||
self._letter_dict = dict_of_names
|
||||
|
||||
def generate(self):
|
||||
combination = "__"
|
||||
next_letter = ""
|
||||
result = ""
|
||||
|
||||
while True:
|
||||
number_of_letters = len(self._letter_dict[combination])
|
||||
index = random.randint(0,number_of_letters - 1)
|
||||
next_letter = self._letter_dict[combination][index]
|
||||
if next_letter == "_":
|
||||
break
|
||||
else:
|
||||
result = result + next_letter
|
||||
combination = combination[1] + next_letter
|
||||
|
||||
return result.title()
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(NameGenerator().generate())
|
@ -2,12 +2,18 @@ import datetime # Used in hello_func
|
||||
|
||||
from interactions import SlashContext, Embed, SlashCommand
|
||||
|
||||
from .name_generator import NameGenerator
|
||||
|
||||
from .roll import DieRoller
|
||||
|
||||
def gen_help_text(commands: list[SlashCommand]):
|
||||
return '\n'.join([f"`/{i.name}`\t— {i.description}" for i in commands])
|
||||
|
||||
class Other():
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self._name_generator = NameGenerator()
|
||||
self._roller = DieRoller()
|
||||
|
||||
# Responds with a greeting of a time-appropriate maner
|
||||
async def hello_func(self, ctx: SlashContext):
|
||||
@ -48,3 +54,12 @@ class Other():
|
||||
except:
|
||||
await ctx.send(f"Could not find a help file for the command '/{command}'")
|
||||
|
||||
async def generate_name(self, ctx: SlashContext):
|
||||
await ctx.send(self._name_generator.generate())
|
||||
|
||||
async def roll_dice(self, ctx: SlashContext, dice: str):
|
||||
try:
|
||||
roll = self._roller.roll(dice)
|
||||
await ctx.send(f":game_die:Rolling dice `{dice}`\n{roll}")
|
||||
except:
|
||||
await ctx.send("There was an error in the rolling")
|
||||
|
1
gwendolyn/funcs/other/roll/__init__.py
Normal file
1
gwendolyn/funcs/other/roll/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .roll import DieRoller
|
702
gwendolyn/funcs/other/roll/ast_nodes.py
Normal file
702
gwendolyn/funcs/other/roll/ast_nodes.py
Normal file
@ -0,0 +1,702 @@
|
||||
from __future__ import annotations
|
||||
from random import randint
|
||||
|
||||
from rply.token import BaseBox
|
||||
|
||||
def show_exp(exp, vtable):
|
||||
r = exp.show(vtable)
|
||||
|
||||
if type(exp) not in [ExpInt,ExpRoll,ExpVar]:
|
||||
r = f"({r})"
|
||||
|
||||
return r
|
||||
|
||||
class Exp(BaseBox):
|
||||
def eval(self, vtable, *args):
|
||||
return self._eval(vtable.copy(), *args)
|
||||
|
||||
def show(self, vtable, *args):
|
||||
return self._show(vtable.copy(), *args)
|
||||
|
||||
def _eval(self, _):
|
||||
return None
|
||||
|
||||
def _show(self, _):
|
||||
return ""
|
||||
|
||||
def includes(self, _):
|
||||
return False
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "exp()"
|
||||
|
||||
def __eq__(self, other: Exp) -> bool:
|
||||
return False
|
||||
|
||||
class ExpInt(Exp):
|
||||
def __init__(self, value: int):
|
||||
self.value = value
|
||||
|
||||
def _eval(self, _):
|
||||
return self.value
|
||||
|
||||
def _show(self, _):
|
||||
return str(self.value)
|
||||
|
||||
def includes(self, _):
|
||||
return False
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"exp_int({self.value})"
|
||||
|
||||
def __eq__(self, other: Exp) -> bool:
|
||||
return isinstance(other, ExpInt) and self.value == other.value
|
||||
|
||||
class ExpMin(Exp):
|
||||
def __init__(self, exp1: Exp, exp2: Exp):
|
||||
self.exp1 = exp1
|
||||
self.exp2 = exp2
|
||||
|
||||
def _eval(self, vtable):
|
||||
r1 = self.exp1.eval(vtable)
|
||||
r2 = self.exp2.eval(vtable)
|
||||
|
||||
if not (isinstance(r1,int) and isinstance(r2,int)):
|
||||
return None
|
||||
|
||||
return max(r1, r2)
|
||||
|
||||
def _show(self, vtable):
|
||||
r1 = show_exp(self.exp1, vtable)
|
||||
r2 = show_exp(self.exp2, vtable)
|
||||
|
||||
return f"{r1}min{r2}"
|
||||
|
||||
def includes(self, var: str):
|
||||
return self.exp1.includes(var) or self.exp2.includes(var)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"exp_min({self.exp1},{self.exp2})"
|
||||
|
||||
def __eq__(self, other: Exp) -> bool:
|
||||
return (
|
||||
isinstance(other, ExpMin) and
|
||||
self.exp1 == other.exp1 and
|
||||
self.exp2 == other.exp2
|
||||
)
|
||||
|
||||
class ExpMax(Exp):
|
||||
def __init__(self, exp1: Exp, exp2: Exp):
|
||||
self.exp1 = exp1
|
||||
self.exp2 = exp2
|
||||
|
||||
def _eval(self, vtable):
|
||||
r1 = self.exp1.eval(vtable)
|
||||
r2 = self.exp2.eval(vtable)
|
||||
|
||||
if not (isinstance(r1,int) and isinstance(r2,int)):
|
||||
return None
|
||||
|
||||
return min(r1, r2)
|
||||
|
||||
def _show(self, vtable):
|
||||
r1 = show_exp(self.exp1, vtable)
|
||||
r2 = show_exp(self.exp2, vtable)
|
||||
|
||||
return f"{r1}max{r2}"
|
||||
|
||||
def includes(self, var: str):
|
||||
return self.exp1.includes(var) or self.exp2.includes(var)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"exp_max({self.exp1},{self.exp2})"
|
||||
|
||||
def __eq__(self, other: Exp) -> bool:
|
||||
return (
|
||||
isinstance(other, ExpMax) and
|
||||
self.exp1 == other.exp1 and
|
||||
self.exp2 == other.exp2
|
||||
)
|
||||
|
||||
class ExpBinop(Exp):
|
||||
def __init__(self, op: str, exp1: Exp, exp2: Exp):
|
||||
self.op = op
|
||||
self.exp1 = exp1
|
||||
self.exp2 = exp2
|
||||
|
||||
def _eval(self, vtable):
|
||||
r1 = self.exp1.eval(vtable)
|
||||
r2 = self.exp2.eval(vtable)
|
||||
|
||||
if not (isinstance(r1,int) and isinstance(r2,int)):
|
||||
return None
|
||||
|
||||
if self.op == "+":
|
||||
return r1+r2
|
||||
elif self.op == "-":
|
||||
return r1-r2
|
||||
elif self.op == "*":
|
||||
return r1*r2
|
||||
elif self.op == "/":
|
||||
return r1//r2
|
||||
else:
|
||||
raise Exception(f"Unknown binop {self.op}")
|
||||
|
||||
def _show(self, vtable):
|
||||
r1 = show_exp(self.exp1, vtable)
|
||||
r2 = show_exp(self.exp2, vtable)
|
||||
|
||||
return f"{r1}{self.op}{r2}"
|
||||
|
||||
def includes(self, var: str):
|
||||
return self.exp1.includes(var) or self.exp2.includes(var)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"exp_binop({self.op}, {self.exp1}, {self.exp2})"
|
||||
|
||||
def __eq__(self, other: Exp) -> bool:
|
||||
return (
|
||||
isinstance(other, ExpBinop) and
|
||||
self.op == other.op and
|
||||
self.exp1 == other.exp1 and
|
||||
self.exp2 == other.exp2
|
||||
)
|
||||
|
||||
class ExpNeg(Exp):
|
||||
def __init__(self, exp: Exp):
|
||||
self.exp = exp
|
||||
|
||||
def _eval(self, vtable):
|
||||
r = self.exp.eval(vtable)
|
||||
|
||||
if not isinstance(r,int):
|
||||
return None
|
||||
|
||||
return -r
|
||||
|
||||
def _show(self, vtable):
|
||||
r = show_exp(self.exp, vtable)
|
||||
|
||||
return f"-{r}"
|
||||
|
||||
def includes(self, var: str):
|
||||
return self.exp.includes(var)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"exp_neg({self.exp})"
|
||||
|
||||
def __eq__(self, other: Exp) -> bool:
|
||||
return (
|
||||
isinstance(other, ExpNeg) and
|
||||
self.exp == other.exp
|
||||
)
|
||||
|
||||
class ExpLet(Exp):
|
||||
def __init__(self, var: str, exp1: Exp, exp2: Exp):
|
||||
self.var = var
|
||||
self.exp1 = exp1
|
||||
self.exp2 = exp2
|
||||
|
||||
def _eval(self, vtable):
|
||||
r1 = self.exp1.eval(vtable)
|
||||
|
||||
vtable[self.var] = r1
|
||||
|
||||
return self.exp2.eval(vtable)
|
||||
|
||||
def _show(self, vtable):
|
||||
r1 = show_exp(self.exp1, vtable)
|
||||
|
||||
vtable[self.var] = self.exp1.eval(vtable)
|
||||
|
||||
r2 = show_exp(self.exp2, vtable)
|
||||
|
||||
if self.exp2.includes(self.var):
|
||||
return f"let {self.var}={r1} in {r2}"
|
||||
else:
|
||||
return r2
|
||||
|
||||
def includes(self, var: str):
|
||||
return self.exp1.includes(var) or self.exp2.includes(var)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"exp_let({self.var}, {self.exp1}, {self.exp2})"
|
||||
|
||||
def __eq__(self, other: Exp) -> bool:
|
||||
return (
|
||||
isinstance(other, ExpLet) and
|
||||
self.var == other.var and
|
||||
self.exp1 == other.exp1 and
|
||||
self.exp2 == other.exp2
|
||||
)
|
||||
|
||||
class ExpIf(Exp):
|
||||
def __init__(self, exp1: Exp, exp2: Exp, exp3: Exp):
|
||||
self.exp1 = exp1
|
||||
self.exp2 = exp2
|
||||
self.exp3 = exp3
|
||||
|
||||
def _eval(self, vtable):
|
||||
r1 = self.exp1.eval(vtable)
|
||||
|
||||
if r1 > 0:
|
||||
return self.exp2.eval(vtable)
|
||||
else:
|
||||
return self.exp3.eval(vtable)
|
||||
|
||||
def _show(self, vtable):
|
||||
r1 = show_exp(self.exp1, vtable)
|
||||
r2 = show_exp(self.exp2, vtable)
|
||||
r3 = self.exp3.show(vtable)
|
||||
|
||||
return f"if {r1} then {r2} else {r3}"
|
||||
|
||||
def includes(self, var: str):
|
||||
return self.exp1.includes(var) or self.exp2.includes(var) or self.exp3.includes(var)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"exp_if({self.exp1}, {self.exp2}, {self.exp3})"
|
||||
|
||||
def __eq__(self, other: Exp) -> bool:
|
||||
return (
|
||||
isinstance(other, ExpIf) and
|
||||
self.exp1 == other.exp1 and
|
||||
self.exp2 == other.exp2 and
|
||||
self.exp3 == other.exp3
|
||||
)
|
||||
|
||||
class ExpLambda(Exp):
|
||||
def __init__(self, var: str, exp: Exp):
|
||||
self.var = var
|
||||
self.exp = exp
|
||||
|
||||
def _eval(self, _):
|
||||
return (self.exp, self.var)
|
||||
|
||||
def _show(self, vtable):
|
||||
r = show_exp(self.exp, vtable)
|
||||
|
||||
return f"\\{self.var} -> {r}"
|
||||
|
||||
def includes(self, var: str):
|
||||
return self.exp.includes(var)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"exp_lambda({self.var}, {self.exp})"
|
||||
|
||||
def __eq__(self, other: Exp) -> bool:
|
||||
return (
|
||||
isinstance(other, ExpLambda) and
|
||||
self.var == other.var and
|
||||
self.exp == other.exp
|
||||
)
|
||||
|
||||
class ExpApply(Exp):
|
||||
def __init__(self, exp1: Exp, exp2: Exp):
|
||||
self.exp1 = exp1
|
||||
self.exp2 = exp2
|
||||
|
||||
def _eval(self, vtable):
|
||||
r1 = self.exp1.eval(vtable)
|
||||
if isinstance(r1, tuple):
|
||||
r2 = self.exp2.eval(vtable)
|
||||
vtable[r1[1]] = r2
|
||||
return r1[0].eval(vtable)
|
||||
else:
|
||||
return None
|
||||
|
||||
def _show(self, vtable):
|
||||
r1 = show_exp(self.exp1, vtable)
|
||||
r2 = self.exp2.show(vtable)
|
||||
return f"{r1}({r2})"
|
||||
|
||||
def includes(self, var: str):
|
||||
return self.exp1.includes(var) or self.exp2.includes(var)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"exp_apply({self.exp1}, {self.exp2})"
|
||||
|
||||
def __eq__(self, other: Exp) -> bool:
|
||||
return (
|
||||
isinstance(other, ExpApply) and
|
||||
self.exp1 == other.exp1 and
|
||||
self.exp2 == other.exp2
|
||||
)
|
||||
|
||||
class ExpVar(Exp):
|
||||
def __init__(self, var: str):
|
||||
self.var = var
|
||||
|
||||
def _eval(self, vtable):
|
||||
return vtable[self.var] if self.var in vtable else None
|
||||
|
||||
def _show(self, vtable):
|
||||
return self.var
|
||||
|
||||
def includes(self, var: str):
|
||||
return var == self.var
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"exp_var({self.var})"
|
||||
|
||||
def __eq__(self, other: Exp) -> bool:
|
||||
return (
|
||||
isinstance(other, ExpVar) and
|
||||
self.var == other.var
|
||||
)
|
||||
|
||||
class ComparePoint(Exp):
|
||||
def __init__(self, comp_op: str, exp: Exp) -> None:
|
||||
self.comp_op = comp_op
|
||||
self.exp = exp
|
||||
|
||||
def _eval(self, vtable, val: int):
|
||||
r = self.exp.eval(vtable)
|
||||
|
||||
if not isinstance(r,int):
|
||||
return None
|
||||
if self.comp_op == "=":
|
||||
return 1 if val == r else 0
|
||||
if self.comp_op == "<=":
|
||||
return 1 if val <= r else 0
|
||||
else:
|
||||
raise Exception(f"Unknown binop {self.op}")
|
||||
|
||||
def _show(self, vtable):
|
||||
r = show_exp(self.exp, vtable)
|
||||
|
||||
return f"{self.comp_op}{r}"
|
||||
|
||||
def includes(self, var: str):
|
||||
return self.exp.includes(var)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"comp({self.comp_op},{self.exp})"
|
||||
|
||||
def __eq__(self, other: Exp) -> bool:
|
||||
return (
|
||||
isinstance(other, ComparePoint) and
|
||||
self.comp_op == other.comp_op and
|
||||
self.exp == other.exp
|
||||
)
|
||||
|
||||
class ExpTest(Exp):
|
||||
def __init__(self, exp: Exp, comp: ComparePoint) -> None:
|
||||
self.exp = exp
|
||||
self.comp = comp
|
||||
|
||||
def _eval(self, vtable):
|
||||
r = self.exp.eval(vtable)
|
||||
|
||||
return self.comp.eval(vtable, r)
|
||||
|
||||
def _show(self, vtable):
|
||||
r = show_exp(self.exp, vtable)
|
||||
c = self.comp.show(vtable)
|
||||
|
||||
return f"{r}{c}"
|
||||
|
||||
def includes(self, var: str):
|
||||
return self.exp.includes(var) or self.comp.includes(var)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"test({self.exp},{self.comp})"
|
||||
|
||||
def __eq__(self, other: Exp) -> bool:
|
||||
return (
|
||||
isinstance(other, ExpTest) and
|
||||
self.exp == other.exp and
|
||||
self.comp == other.comp
|
||||
)
|
||||
|
||||
|
||||
class ExpRoll(Exp):
|
||||
def __init__(self, roll: Roll):
|
||||
self.roll = roll
|
||||
|
||||
def _eval(self, vtable):
|
||||
return sum(self.roll.eval(vtable))
|
||||
|
||||
def _show(self, vtable):
|
||||
return f"[{','.join(self.roll.show(vtable))}]"
|
||||
|
||||
def includes(self, _):
|
||||
return False
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"sum({self.roll})"
|
||||
|
||||
def __eq__(self, other: Exp) -> bool:
|
||||
return (
|
||||
isinstance(other, ExpRoll) and
|
||||
self.roll == other.roll
|
||||
)
|
||||
|
||||
class Roll(Exp):
|
||||
def __init__(self, exp1: Exp, exp2: Exp):
|
||||
self.exp1 = exp1
|
||||
self.exp2 = exp2
|
||||
self.result = None
|
||||
|
||||
def _eval(self, vtable):
|
||||
if self.result is not None:
|
||||
return self.result
|
||||
|
||||
r1 = self.exp1.eval(vtable)
|
||||
r2 = self.exp2.eval(vtable)
|
||||
|
||||
if not (isinstance(r1,int) and isinstance(r2,int)):
|
||||
return []
|
||||
|
||||
self.die_type = r2
|
||||
|
||||
self.result = [randint(1,r2) for _ in range(r1)]
|
||||
return self.result
|
||||
|
||||
def _show(self, vtable):
|
||||
result = self.eval(vtable)
|
||||
return [str(i) for i in result]
|
||||
|
||||
@property
|
||||
def die(self) -> int:
|
||||
if hasattr(self, "die_type"):
|
||||
return self.die_type
|
||||
elif hasattr(self, "roll"):
|
||||
return self.roll.die_type
|
||||
else:
|
||||
return 0
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"roll({self.exp1},{self.exp2})"
|
||||
|
||||
def __eq__(self, other: Exp) -> bool:
|
||||
return (
|
||||
isinstance(other, Roll) and
|
||||
self.exp1 == other.exp1 and
|
||||
self.exp2 == other.exp2
|
||||
)
|
||||
|
||||
class RollKeepHighest(Roll):
|
||||
def __init__(self, roll: Roll, exp: Exp):
|
||||
self.roll = roll
|
||||
self.exp = exp
|
||||
self.result = None
|
||||
self.show_list = None
|
||||
|
||||
def _eval(self, vtable):
|
||||
if self.result is not None:
|
||||
return self.result
|
||||
|
||||
r1 = self.roll.eval(vtable)
|
||||
r2 = self.exp.eval(vtable)
|
||||
|
||||
if not ((isinstance(r1,list) and all(isinstance(i,int) for i in r1)) and isinstance(r2,int)):
|
||||
return []
|
||||
|
||||
max_indices = []
|
||||
for i, n in enumerate(r1):
|
||||
if len(max_indices) < r2:
|
||||
max_indices.append(i)
|
||||
elif not all([n <= r1[j] for j in max_indices]):
|
||||
max_indices.remove(min(max_indices,key=lambda x: r1[x]))
|
||||
max_indices.append(i)
|
||||
|
||||
self.result = [r1[i] for i in max_indices]
|
||||
self.show_list = [str(n) if i in max_indices else f"~~{n}~~" for i, n in enumerate(r1)]
|
||||
return self.result
|
||||
|
||||
def _show(self, vtable):
|
||||
self.eval(vtable)
|
||||
return self.show_list
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"kep_highest({self.roll},{self.exp})"
|
||||
|
||||
def __eq__(self, other: Exp) -> bool:
|
||||
return (
|
||||
isinstance(other, RollKeepHighest) and
|
||||
self.roll == other.roll and
|
||||
self.exp == other.exp
|
||||
)
|
||||
|
||||
class RollKeepLowest(Roll):
|
||||
def __init__(self, roll: Roll, exp: Exp):
|
||||
self.roll = roll
|
||||
self.exp = exp
|
||||
self.result = None
|
||||
self.show_list = None
|
||||
|
||||
def _eval(self, vtable):
|
||||
if self.result is not None:
|
||||
return self.result
|
||||
|
||||
r1 = self.roll.eval(vtable)
|
||||
r2 = self.exp.eval(vtable)
|
||||
|
||||
if not ((isinstance(r1,list) and all(isinstance(i,int) for i in r1)) and isinstance(r2,int)):
|
||||
return []
|
||||
|
||||
min_indices = []
|
||||
for i, n in enumerate(r1):
|
||||
if len(min_indices) < r2:
|
||||
min_indices.append(i)
|
||||
elif not all([n >= r1[j] for j in min_indices]):
|
||||
min_indices.remove(max(min_indices,key=lambda x: r1[x]))
|
||||
min_indices.append(i)
|
||||
|
||||
self.result = [r1[i] for i in min_indices]
|
||||
self.show_list = [str(n) if i in min_indices else f"~~{n}~~" for i, n in enumerate(r1)]
|
||||
return self.result
|
||||
|
||||
def _show(self, vtable):
|
||||
self.eval(vtable)
|
||||
return self.show_list
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"kep_lowest({self.roll},{self.exp})"
|
||||
|
||||
def __eq__(self, other: Exp) -> bool:
|
||||
return (
|
||||
isinstance(other, RollKeepLowest) and
|
||||
self.roll == other.roll and
|
||||
self.exp == other.exp
|
||||
)
|
||||
|
||||
class RollMin(Roll):
|
||||
def __init__(self, roll: Roll, exp: Exp):
|
||||
self.roll = roll
|
||||
self.exp = exp
|
||||
self.result = None
|
||||
self.show_list = None
|
||||
|
||||
def _eval(self, vtable):
|
||||
if self.result is not None:
|
||||
return self.result
|
||||
|
||||
r1 = self.roll.eval(vtable)
|
||||
r2 = self.exp.eval(vtable)
|
||||
|
||||
if not ((isinstance(r1,list) and all(isinstance(i,int) for i in r1)) and isinstance(r2,int)):
|
||||
return []
|
||||
|
||||
self.show_list = []
|
||||
|
||||
for i in range(len(r1)):
|
||||
if r1[i] < r2:
|
||||
r1[i] = r2
|
||||
self.show_list.append(f"{r2}^")
|
||||
else:
|
||||
self.show_list.append(str(r1[i]))
|
||||
|
||||
self.result = r1
|
||||
return self.result
|
||||
|
||||
def _show(self, vtable):
|
||||
self.eval(vtable)
|
||||
return self.show_list
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"min({self.roll},{self.exp})"
|
||||
|
||||
def __eq__(self, other: Exp) -> bool:
|
||||
return (
|
||||
isinstance(other, RollMin) and
|
||||
self.roll == other.roll and
|
||||
self.exp == other.exp
|
||||
)
|
||||
|
||||
class RollMax(Roll):
|
||||
def __init__(self, roll: Roll, exp: Exp):
|
||||
self.roll = roll
|
||||
self.exp = exp
|
||||
self.result = None
|
||||
self.show_list = None
|
||||
|
||||
def _eval(self, vtable):
|
||||
if self.result is not None:
|
||||
return self.result
|
||||
|
||||
r1 = self.roll.eval(vtable)
|
||||
r2 = self.exp.eval(vtable)
|
||||
|
||||
if not ((isinstance(r1,list) and all(isinstance(i,int) for i in r1)) and isinstance(r2,int)):
|
||||
return []
|
||||
|
||||
self.show_list = []
|
||||
|
||||
for i in range(len(r1)):
|
||||
if r1[i] > r2:
|
||||
r1[i] = r2
|
||||
self.show_list.append(f"{r2}v")
|
||||
else:
|
||||
self.show_list.append(str(r1[i]))
|
||||
|
||||
self.result = r1
|
||||
return self.result
|
||||
|
||||
def _show(self, vtable):
|
||||
self.eval(vtable)
|
||||
return self.show_list
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"max({self.roll},{self.exp})"
|
||||
|
||||
def __eq__(self, other: Exp) -> bool:
|
||||
return (
|
||||
isinstance(other, RollMax) and
|
||||
self.roll == other.roll and
|
||||
self.exp == other.exp
|
||||
)
|
||||
|
||||
class RollExplode(Roll):
|
||||
def __init__(self, roll: Roll, comp: ComparePoint = None):
|
||||
self.roll = roll
|
||||
self.comp = comp
|
||||
self.result = None
|
||||
self.show_list = None
|
||||
|
||||
def _eval(self, vtable):
|
||||
if self.result is not None:
|
||||
return self.result
|
||||
|
||||
r1 = self.roll.eval(vtable)
|
||||
|
||||
if not (isinstance(r1,list) and all(isinstance(i,int) for i in r1)):
|
||||
return []
|
||||
|
||||
|
||||
d = self.die
|
||||
|
||||
if self.comp is None:
|
||||
self.comp = ComparePoint("=", ExpInt(d))
|
||||
|
||||
self.result = []
|
||||
self.show_list = []
|
||||
|
||||
def compare(n):
|
||||
if self.comp.eval(vtable, n):
|
||||
self.result.append(n)
|
||||
self.show_list.append(f"{n}!")
|
||||
compare(randint(1,d))
|
||||
else:
|
||||
self.result.append(n)
|
||||
self.show_list.append(str(n))
|
||||
|
||||
for n in r1:
|
||||
compare(n)
|
||||
|
||||
return self.result
|
||||
|
||||
def _show(self, vtable):
|
||||
self.eval(vtable)
|
||||
return self.show_list
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"max({self.roll},{self.exp})"
|
||||
|
||||
def __eq__(self, other: Exp) -> bool:
|
||||
return (
|
||||
isinstance(other, RollMax) and
|
||||
self.roll == other.roll and
|
||||
self.exp == other.exp
|
||||
)
|
48
gwendolyn/funcs/other/roll/lexer.py
Normal file
48
gwendolyn/funcs/other/roll/lexer.py
Normal file
@ -0,0 +1,48 @@
|
||||
|
||||
from string import ascii_letters, digits
|
||||
from rply import LexerGenerator
|
||||
|
||||
VALID_CHARACTERS = ascii_letters[:3]+ascii_letters[4:]+"_"+digits
|
||||
|
||||
TOKENS = [
|
||||
("ROLL_DIE", r"d"),
|
||||
("ROLL_KEEP_HIGHEST", r"kh"),
|
||||
("ROLL_KEEP_LOWEST", r"kl"),
|
||||
("ROLL_MIN", r"min"),
|
||||
("ROLL_MAX", r"max"),
|
||||
("ROLL_EXPLODE", r"!"),
|
||||
|
||||
("SYMBOL_LE", r"\<\="),
|
||||
# ("SYMBOL_GE", r"\>\="),
|
||||
("SYMBOL_EQUALS", r"\="),
|
||||
("SYMBOL_ARROW", r"\-\>"),
|
||||
("SYMBOL_BACKSLASH", r"\\"),
|
||||
("SYMBOL_LPARENS", r"\("),
|
||||
("SYMBOL_RPARENS", r"\)"),
|
||||
("SYMBOL_PLUS", r"\+"),
|
||||
("SYMBOL_MINUS", r"\-"),
|
||||
("SYMBOL_TIMES", r"\*"),
|
||||
("SYMBOL_DIVIDE", r"\/"),
|
||||
|
||||
("KEYWORD_LET", r"let"),
|
||||
("KEYWORD_IN", r"in"),
|
||||
("KEYWORD_IF", r"if"),
|
||||
("KEYWORD_THEN", r"then"),
|
||||
("KEYWORD_ELSE", r"else"),
|
||||
|
||||
("INT", r"\d+"),
|
||||
("ID", f"[{ascii_letters}][{VALID_CHARACTERS}]*")
|
||||
]
|
||||
|
||||
class Lexer():
|
||||
def __init__(self):
|
||||
self.lexer = LexerGenerator()
|
||||
|
||||
def _add_tokens(self):
|
||||
for token in TOKENS:
|
||||
self.lexer.add(*token)
|
||||
self.lexer.ignore(r"\s+")
|
||||
|
||||
def get_lexer(self):
|
||||
self._add_tokens()
|
||||
return self.lexer.build()
|
134
gwendolyn/funcs/other/roll/parser.py
Normal file
134
gwendolyn/funcs/other/roll/parser.py
Normal file
@ -0,0 +1,134 @@
|
||||
from rply import ParserGenerator
|
||||
|
||||
from lexer import TOKENS
|
||||
from ast_nodes import Exp, ExpInt, ExpBinop, ExpLet, ExpVar, ExpMin, ExpMax, ExpRoll, Roll, RollKeepHighest, RollKeepLowest, RollMin, RollMax, RollExplode, ComparePoint, ExpIf, ExpTest, ExpApply, ExpLambda, ExpNeg
|
||||
|
||||
|
||||
class Parser():
|
||||
def __init__(self):
|
||||
self.pg = ParserGenerator(
|
||||
[i[0] for i in TOKENS],
|
||||
precedence=[
|
||||
('left', ["SYMBOL_BACKSLASH","SYMBOL_ARROW"]),
|
||||
('left', ["KEYWORD_LET", "KEYWORD_IN", "KEYWORD_IF", "KEYWORD_THEN", "KEYWORD_ELSE"]),
|
||||
('left', ["SYMBOL_EQUALS", "SYMBOL_LE"]),
|
||||
('left', ["SYMBOL_PLUS", "SYMBOL_MINUS"]),
|
||||
('left', ["SYMBOL_TIMES", "SYMBOL_DIVIDE"]),
|
||||
('left', ["ROLL_DIE"]),
|
||||
('right', ["ROLL_KEEP_HIGHEST","ROLL_KEEP_LOWEST","ROLL_MIN","ROLL_MAX","ROLL_EXPLODE"])
|
||||
]
|
||||
)
|
||||
self._get_parser()
|
||||
|
||||
def parse(self, token_input):
|
||||
return self.parser.parse(token_input)
|
||||
|
||||
def _get_parser(self) -> Exp:
|
||||
# Expressions
|
||||
@self.pg.production('exp : exp SYMBOL_PLUS exp')
|
||||
@self.pg.production('exp : exp SYMBOL_MINUS exp')
|
||||
@self.pg.production('exp : exp SYMBOL_TIMES exp')
|
||||
@self.pg.production('exp : exp SYMBOL_DIVIDE exp')
|
||||
def exp_mul_div(tokens):
|
||||
return ExpBinop(tokens[1].value, tokens[0], tokens[2])
|
||||
|
||||
@self.pg.production('exp : atom')
|
||||
def exp_atom(tokens):
|
||||
return tokens[0]
|
||||
|
||||
@self.pg.production("exp : SYMBOL_MINUS atom")
|
||||
def exp_neg(tokens):
|
||||
return ExpNeg(tokens[1])
|
||||
|
||||
@self.pg.production('exp : KEYWORD_LET ID SYMBOL_EQUALS exp KEYWORD_IN exp')
|
||||
def exp_let(tokens):
|
||||
return ExpLet(tokens[1].value, tokens[3], tokens[5])
|
||||
|
||||
@self.pg.production('exp : roll')
|
||||
def exp_roll(tokens):
|
||||
return ExpRoll(tokens[0])
|
||||
|
||||
@self.pg.production("exp : atom ROLL_MIN atom")
|
||||
def exp_min(tokens):
|
||||
return ExpMin(tokens[0], tokens[2])
|
||||
|
||||
@self.pg.production("exp : atom ROLL_MAX atom")
|
||||
def exp_max(tokens):
|
||||
return ExpMax(tokens[0], tokens[2])
|
||||
|
||||
@self.pg.production("exp : atom comp")
|
||||
def exp_test(tokens):
|
||||
return ExpTest(tokens[0], tokens[1])
|
||||
|
||||
@self.pg.production("exp : KEYWORD_IF exp KEYWORD_THEN exp KEYWORD_ELSE exp")
|
||||
def exp_if(tokens):
|
||||
return ExpIf(tokens[1],tokens[3],tokens[5])
|
||||
|
||||
@self.pg.production("exp : SYMBOL_BACKSLASH ID SYMBOL_ARROW exp")
|
||||
def exp_lamda(tokens):
|
||||
return ExpLambda(tokens[1].value, tokens[3])
|
||||
|
||||
@self.pg.production("exp : atom atom")
|
||||
def apply(tokens):
|
||||
return ExpApply(tokens[0],tokens[1])
|
||||
|
||||
# Rolls
|
||||
@self.pg.production('roll : roll ROLL_KEEP_HIGHEST atom')
|
||||
def roll_keep_highest(tokens):
|
||||
return RollKeepHighest(tokens[0], tokens[2])
|
||||
|
||||
@self.pg.production('roll : roll ROLL_KEEP_LOWEST atom')
|
||||
def roll_keep_lowest(tokens):
|
||||
return RollKeepLowest(tokens[0], tokens[2])
|
||||
|
||||
@self.pg.production('roll : roll ROLL_MIN atom')
|
||||
def roll_min(tokens):
|
||||
return RollMin(tokens[0], tokens[2])
|
||||
|
||||
@self.pg.production('roll : roll ROLL_MAX atom')
|
||||
def roll_max(tokens):
|
||||
return RollMax(tokens[0], tokens[2])
|
||||
|
||||
@self.pg.production('roll : roll ROLL_EXPLODE ')
|
||||
def roll_explode(tokens):
|
||||
return RollExplode(tokens[0])
|
||||
|
||||
@self.pg.production('roll : roll ROLL_EXPLODE comp')
|
||||
def roll_explode_comp(tokens):
|
||||
return RollExplode(tokens[0], tokens[2])
|
||||
|
||||
@self.pg.production('roll : atom ROLL_DIE atom')
|
||||
def roll(tokens):
|
||||
return Roll(tokens[0], tokens[2])
|
||||
|
||||
@self.pg.production('roll : ROLL_DIE atom')
|
||||
def roll_no_amount(tokens):
|
||||
return Roll(ExpInt(1), tokens[1])
|
||||
|
||||
# Compare Points
|
||||
|
||||
@self.pg.production("comp : SYMBOL_EQUALS atom")
|
||||
@self.pg.production("comp : SYMBOL_LE atom")
|
||||
def comp_point(tokens):
|
||||
return ComparePoint(tokens[0].value,tokens[1])
|
||||
|
||||
# Atoms
|
||||
@self.pg.production("atom : INT")
|
||||
def atom_int(tokens):
|
||||
return ExpInt(int(tokens[0].value))
|
||||
|
||||
@self.pg.production("atom : SYMBOL_LPARENS exp SYMBOL_RPARENS")
|
||||
def atom_exp(tokens):
|
||||
return tokens[1]
|
||||
|
||||
@self.pg.production('atom : ID')
|
||||
def atom_var(tokens):
|
||||
return ExpVar(tokens[0].value)
|
||||
|
||||
## Error Handling ##
|
||||
@self.pg.error
|
||||
def error_handle(token):
|
||||
raise Exception(f"Unexpected token '{token.value}' ({token.name}) at line {token.source_pos.lineno}, column {token.source_pos.colno}.")
|
||||
|
||||
## Finish ##
|
||||
self.parser = self.pg.build()
|
58
gwendolyn/funcs/other/roll/roll.py
Normal file
58
gwendolyn/funcs/other/roll/roll.py
Normal file
@ -0,0 +1,58 @@
|
||||
import os, sys; sys.path.append(os.path.dirname(os.path.realpath(__file__)))
|
||||
|
||||
from lexer import Lexer
|
||||
from parser import Parser
|
||||
from ast_nodes import Exp, ExpInt, ExpBinop, ExpLet, ExpVar, ExpRoll, Roll, RollKeepHighest, RollKeepLowest, RollMin, RollMax, ExpMin, ExpMax
|
||||
|
||||
|
||||
class DieRoller():
|
||||
def __init__(self) -> None:
|
||||
self.lexer = Lexer().get_lexer()
|
||||
self.parser = Parser()
|
||||
|
||||
def parse(self, string: str) -> Exp:
|
||||
tokens = self.lexer.lex(string)
|
||||
expression = self.parser.parse(tokens)
|
||||
return expression
|
||||
|
||||
def roll(self, string: str):
|
||||
exp = self.parse(string)
|
||||
# print(exp)
|
||||
# exit()
|
||||
show = exp.show({})
|
||||
eval = exp.eval({})
|
||||
if show is None or eval is None or isinstance(eval,tuple):
|
||||
raise Exception("Something went wrong")
|
||||
return f"**Result**: {show}\n**Total**: {eval}"
|
||||
|
||||
if __name__ == "__main__":
|
||||
d = DieRoller()
|
||||
test_strings = [
|
||||
("4", ExpInt(4)),
|
||||
("1 + 1", ExpBinop("+",ExpInt(1),ExpInt(1))),
|
||||
("5*5", ExpBinop("*",ExpInt(5),ExpInt(5))),
|
||||
("1+5/5", ExpBinop("+",ExpInt(1),ExpBinop("/",ExpInt(5),ExpInt(5)))),
|
||||
("(1+5)/6", ExpBinop("/",ExpBinop("+",ExpInt(1),ExpInt(5)),ExpInt(6))),
|
||||
("let x=5 in x*2", ExpLet("x",ExpInt(5),ExpBinop("*",ExpVar("x"),ExpInt(2)))),
|
||||
("let x= let y = 2 in y in let z = 5 in x*z", ExpLet("x",ExpLet("y",ExpInt(2),ExpVar("y")),ExpLet("z",ExpInt(5),ExpBinop("*",ExpVar("x"),ExpVar("z"))))),
|
||||
("4d6", ExpRoll(Roll(ExpInt(4),ExpInt(6)))),
|
||||
("d6", ExpRoll(Roll(ExpInt(1),ExpInt(6)))),
|
||||
("(2+2)d(3*2)", ExpRoll(Roll(ExpBinop("+",ExpInt(2),ExpInt(2)),ExpBinop("*",ExpInt(3),ExpInt(2))))),
|
||||
("4d3*2", ExpBinop("*",ExpRoll(Roll(ExpInt(4),ExpInt(3))),ExpInt(2))),
|
||||
("4d6kh3", ExpRoll(RollKeepHighest(Roll(ExpInt(4),ExpInt(6)),ExpInt(3)))),
|
||||
("2d20kl1", ExpRoll(RollKeepLowest(Roll(ExpInt(2),ExpInt(20)),ExpInt(1)))),
|
||||
("11d10kh6kl1", ExpRoll(RollKeepLowest(RollKeepHighest(Roll(ExpInt(11),ExpInt(10)),ExpInt(6)),ExpInt(1)))),
|
||||
("3d6min6", ExpRoll(RollMin(Roll(ExpInt(3),ExpInt(6)),ExpInt(6)))),
|
||||
("3d6max1", ExpRoll(RollMax(Roll(ExpInt(3),ExpInt(6)),ExpInt(1)))),
|
||||
("let x = 1d4 in (x)min3", ExpLet("x",ExpRoll(Roll(ExpInt(1),ExpInt(4))),ExpMin(ExpVar("x"),ExpInt(3)))),
|
||||
("10max(2d10)",ExpMax(ExpInt(10),ExpRoll(Roll(ExpInt(2),ExpInt(10)))))
|
||||
]
|
||||
for s, t in test_strings:
|
||||
r = d.parse(s)
|
||||
correct = r == t
|
||||
print(f"{str(s) : <20}: {'✅' if correct else '❌'} ({r.eval({})})")
|
||||
if not correct:
|
||||
print(f"Expected: {t}")
|
||||
print(f"Actual: {r}\n")
|
||||
|
||||
print(d.roll(input(":")))
|
@ -16,10 +16,26 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"gen_name" : {
|
||||
"name": "gen_name",
|
||||
"description": "Generate a random name"
|
||||
},
|
||||
"ping" : {
|
||||
"name" : "ping",
|
||||
"description" : "Get the Gwendolyn's latency to the server"
|
||||
},
|
||||
"roll" : {
|
||||
"name": "roll",
|
||||
"description": "Roll dice",
|
||||
"options": [
|
||||
{
|
||||
"name": "dice",
|
||||
"description": "The dice to be rolled",
|
||||
"type": 3,
|
||||
"required": "false"
|
||||
}
|
||||
]
|
||||
},
|
||||
"stop" : {
|
||||
"name" : "stop",
|
||||
"description" : "Stop Gwendolyn"
|
||||
|
Reference in New Issue
Block a user