Archived
1
0
Fork 0
This repository has been archived on 2024-04-26. You can view files and clone it, but cannot push or open issues or pull requests.
akari-bot/modules/dice/dice.py

256 lines
10 KiB
Python
Raw Normal View History

2023-04-19 07:41:39 +00:00
import re
2023-01-19 12:02:49 +00:00
import secrets
2023-04-19 07:41:39 +00:00
2023-01-26 07:16:11 +00:00
import numpy as np
2023-04-22 04:20:29 +00:00
2023-06-07 09:03:00 +00:00
from config import Config
from core.utils.text import remove_prefix
2023-06-07 09:22:46 +00:00
MAX_DICE_COUNT = int(Config('dice_limit')) # 一次摇动最多的骰子数量
MAX_ROLL_TIMES = int(Config('dice_roll_limit')) # 一次命令最多的摇动次数
MAX_MOD_NUMBER = int(Config('dice_mod_max')) # 骰子最大加权值
MIN_MOD_NUMBER = int(Config('dice_mod_min')) # 骰子最小加权值
MAX_OUTPUT_CNT = int(Config('dice_output_cnt')) # 输出的最多数据量
MAX_OUTPUT_LEN = int(Config('dice_output_len')) # 输出的最大长度
MAX_DETAIL_CNT = int(Config('dice_detail_cnt')) # n次摇动的骰子的总量超过该值时将不再显示详细信息
MAX_ITEM_COUNT = int(Config('dice_count_limit')) # 骰子多项式最多的项数
2023-02-26 08:51:03 +00:00
2023-01-19 12:02:49 +00:00
2023-02-25 14:19:33 +00:00
class DiceSyntaxError(Exception):
"""骰子语法错误"""
2023-02-26 08:51:03 +00:00
2023-06-07 08:36:42 +00:00
def __init__(self, session, message):
self.message = session.locale.t("dice.message.error.syntax") + message
2023-02-26 08:51:03 +00:00
2023-02-25 14:19:33 +00:00
class DiceValueError(Exception):
"""骰子参数值错误"""
2023-02-26 08:51:03 +00:00
2023-06-07 08:48:14 +00:00
def __init__(self, session, message, value=None):
2023-04-30 03:30:59 +00:00
if value is not None:
2023-06-07 08:48:14 +00:00
self.message = session.locale.t("dice.message.error.value.invalid", value=value) + message
2023-02-26 04:03:25 +00:00
else:
2023-06-07 08:48:14 +00:00
self.message = session.locale.t("dice.message.error.value") + message
2023-02-25 14:19:33 +00:00
2023-02-26 08:51:03 +00:00
2023-02-25 14:19:33 +00:00
class DiceItemBase(object):
"""骰子项的基类"""
2023-02-26 08:51:03 +00:00
def __init__(self, diceCode: str, postive: bool):
2023-02-25 14:19:33 +00:00
self.postive = postive
self.code = diceCode
self.result = None
self.detail = ''
2023-02-26 08:51:03 +00:00
def GetResult(self, abs=True):
2023-02-26 04:03:25 +00:00
if abs:
return self.result
else:
return self.result if self.postive else -self.result
2023-02-25 14:19:33 +00:00
def GetDetail(self):
return self.detail
2023-02-26 08:51:03 +00:00
2023-06-16 17:06:04 +00:00
def Roll(self, session):
2023-02-26 05:25:51 +00:00
pass
2023-02-25 14:19:33 +00:00
2023-02-26 08:51:03 +00:00
2023-02-25 14:19:33 +00:00
class DiceMod(DiceItemBase):
"""调节值项"""
2023-02-26 08:51:03 +00:00
2023-06-07 08:48:14 +00:00
def __init__(self, session, diceCode: str, postive: bool):
2023-02-26 08:51:03 +00:00
super().__init__(diceCode, postive)
2023-02-25 14:19:33 +00:00
if not diceCode.isdigit():
2023-06-07 08:48:14 +00:00
raise DiceValueError(session,
session.locale.t("dice.message.error.value.M.invalid"),
'+' if self.postive else '-' + diceCode)
2023-01-26 07:16:11 +00:00
else:
2023-02-26 04:03:25 +00:00
self.result = int(diceCode)
if self.result > MAX_MOD_NUMBER or self.result < MIN_MOD_NUMBER:
2023-06-07 08:48:14 +00:00
raise DiceValueError(session,
session.locale.t("dice.message.error.value.M.out_of_range", min=MIN_MOD_NUMBER, max=MAX_MOD_NUMBER),
self.result)
2023-02-26 08:51:03 +00:00
2023-02-25 14:19:33 +00:00
def GetDetail(self):
2023-02-26 04:03:25 +00:00
return self.result
2023-02-26 08:51:03 +00:00
2023-02-25 14:19:33 +00:00
class Dice(DiceItemBase):
"""骰子项"""
2023-02-26 08:51:03 +00:00
2023-06-07 08:48:14 +00:00
def __init__(self, session, diceCode: str, postive: bool):
2023-02-25 14:19:33 +00:00
diceCode = diceCode.replace(' ', '')
2023-02-26 08:51:03 +00:00
super().__init__(diceCode, postive)
2023-06-07 08:53:37 +00:00
args = self.GetArgs(session)
2023-02-25 14:19:33 +00:00
self.count = args[0]
self.type = args[1]
self.adv = args[2]
if self.count <= 0 or self.count > MAX_DICE_COUNT:
2023-06-07 08:48:14 +00:00
raise DiceValueError(session,
session.locale.t("dice.message.error.value.n.out_of_range", max=MAX_DICE_COUNT),
self.count)
2023-02-26 08:51:03 +00:00
if self.type <= 0:
2023-06-07 08:48:14 +00:00
raise DiceValueError(session,
session.locale.t("dice.message.error.value.n.less2"),
self.count)
2023-02-26 08:51:03 +00:00
if self.type == 1:
2023-06-07 08:48:14 +00:00
raise DiceValueError(session, session.locale.t("dice.message.error.value.n.d1"))
2023-02-25 14:19:33 +00:00
if abs(self.adv) > self.count:
2023-06-07 08:48:14 +00:00
raise DiceValueError(session,
session.locale.t("dice.message.error.value.k.out_of_range"),
self.adv)
2023-02-26 08:51:03 +00:00
2023-06-07 08:48:14 +00:00
def GetArgs(self, session):
2023-02-25 14:19:33 +00:00
diceCode = self.code.upper() # 便于识别
diceCount = '1' # 骰子数量
advantage = '0' # 保留的骰子量
2023-02-26 08:51:03 +00:00
if re.search(r'[^0-9DKL]', diceCode):
2023-06-07 08:48:14 +00:00
raise DiceSyntaxError(session, session.locale.t("dice.message.error.syntax.invalid"))
2023-02-25 14:19:33 +00:00
if 'D' not in diceCode:
2023-06-07 08:48:14 +00:00
raise DiceSyntaxError(session, session.locale.t("dice.message.error.syntax.missing_d"))
2023-02-25 14:19:33 +00:00
temp = diceCode.split('D')
2023-02-26 04:03:25 +00:00
if len(temp[0]):
2023-02-25 14:19:33 +00:00
diceCount = temp[0]
else:
2023-02-26 08:51:03 +00:00
diceCount = '1'
2023-02-25 14:19:33 +00:00
midstrs = temp[1].partition('K')
diceType = midstrs[0]
if 'K' in midstrs[1]:
advantage = midstrs[2].replace('L', '-')
if not len(advantage.removeprefix('-')):
2023-02-26 08:51:03 +00:00
advantage += '1' # K/KL后没有值默认为1
2023-02-25 14:19:33 +00:00
# 语法合法检定
if not diceCount.isdigit():
2023-06-07 08:48:14 +00:00
raise DiceValueError(session,
session.locale.t("dice.message.error.value.m.invalid"),
diceCount)
2023-02-25 14:19:33 +00:00
if not diceType.isdigit():
2023-06-07 08:48:14 +00:00
raise DiceValueError(session,
session.locale.t("dice.message.error.value.n.invalid"),
diceType)
2023-02-25 14:19:33 +00:00
if not (advantage.isdigit() or (advantage[0] == '-' and advantage[1:].isdigit())):
2023-06-07 08:48:14 +00:00
raise DiceValueError(session,
session.locale.t("dice.message.error.value.k.invalid"),
advantage)
2023-02-26 08:51:03 +00:00
return (int(diceCount), int(diceType), int(advantage))
2023-01-26 07:16:11 +00:00
2023-06-07 08:48:14 +00:00
def Roll(self, session):
2023-02-25 14:19:33 +00:00
output = ''
2023-01-19 12:02:49 +00:00
result = 0
diceResults = []
2023-02-25 14:19:33 +00:00
adv = self.adv
output += self.code + ' = '
2023-01-19 14:22:14 +00:00
# 生成随机序列
2023-02-25 14:19:33 +00:00
for i in range(self.count):
diceResults.append(secrets.randbelow(int(self.type)) + 1)
if adv != 0:
2023-01-26 07:16:11 +00:00
newResults = []
indexs = np.array(diceResults).argsort()
2023-02-25 14:19:33 +00:00
indexs = indexs[-adv:] if adv > 0 else indexs[:-adv]
2023-01-19 12:02:49 +00:00
output += '( '
2023-01-26 07:16:11 +00:00
outputBuffer = ''
2023-02-25 14:19:33 +00:00
for i in range(self.count):
2023-01-26 07:16:11 +00:00
outputBuffer += str(diceResults[i])
if i in indexs:
newResults.append(diceResults[i])
outputBuffer += '*'
2023-02-25 14:19:33 +00:00
if i < self.count - 1:
2023-01-26 07:16:11 +00:00
outputBuffer += ','
2023-06-07 09:03:00 +00:00
if self.count >= MAX_OUTPUT_CNT:
2023-06-07 08:48:14 +00:00
outputBuffer = session.locale.t("dice.message.output.too_long", length=self.count)
2023-01-26 07:16:11 +00:00
output += outputBuffer + ' ) = '
diceResults = newResults
2023-01-19 14:22:14 +00:00
# 公用加法
2023-01-19 12:02:49 +00:00
length = len(diceResults)
2023-01-19 14:22:14 +00:00
if (length > 1):
2023-01-19 12:02:49 +00:00
output += '[ '
2023-06-07 09:03:00 +00:00
if length > MAX_OUTPUT_CNT: # 显示数据含100
2023-06-07 08:48:14 +00:00
output += session.locale.t("dice.message.output.too_long", length=length)
2023-01-19 12:02:49 +00:00
for i in range(length):
result += diceResults[i]
2023-06-07 09:03:00 +00:00
if length <= MAX_OUTPUT_CNT: # 显示数据含100
2023-01-19 12:02:49 +00:00
output += str(diceResults[i])
if i < length - 1:
2023-01-26 07:16:11 +00:00
output += '+'
2023-01-19 12:02:49 +00:00
output += ' ] = '
else:
2023-02-26 08:51:03 +00:00
result = diceResults[0]
2023-02-25 14:19:33 +00:00
if len(output) > MAX_OUTPUT_LEN:
2023-06-07 08:48:14 +00:00
output = session.locale.t("dice.message.too_long")
2023-07-15 16:45:29 +00:00
self.detail = output + f"{result} "
2023-02-25 14:19:33 +00:00
self.result = result
2023-02-26 08:51:03 +00:00
2023-06-07 07:56:42 +00:00
async def GenerateMessage(msg, dices: str, times: int, dc: int):
2023-07-11 02:35:31 +00:00
if not all([MAX_DICE_COUNT > 0, MAX_ROLL_TIMES > 0, MAX_MOD_NUMBER >= MIN_MOD_NUMBER, MAX_OUTPUT_CNT > 0, MAX_OUTPUT_LEN > 0, MAX_DETAIL_CNT > 0, MAX_ITEM_COUNT > 0]):
2023-08-14 17:14:56 +00:00
raise OverflowError(msg.locale.t("error.config.invalid"))
2023-04-21 15:15:20 +00:00
if re.search(r'[^0-9+\-DKL]', dices.upper()):
2023-06-07 08:36:42 +00:00
return DiceSyntaxError(msg, msg.locale.t('dice.message.error.syntax.invalid')).message
2023-02-25 14:19:33 +00:00
if times > MAX_ROLL_TIMES or times < 1:
2023-06-07 08:48:14 +00:00
return DiceValueError(msg, msg.locale.t('dice.message.error.value.N.out_of_range', max=MAX_ROLL_TIMES), times).message
2023-02-26 04:03:25 +00:00
diceCodeList = re.compile(r'[+-]?[^+-]+').findall(dices)
2023-02-25 14:19:33 +00:00
diceList = []
haveErr = False
output = ""
diceCount = 0
i = 0
2023-02-26 04:03:25 +00:00
if len(diceCodeList) > MAX_ITEM_COUNT:
2023-06-07 08:48:14 +00:00
return DiceValueError(msg, msg.locale.t('dice.message.error.value.too_long'), len(diceCodeList)).message
2023-02-26 08:51:03 +00:00
# 初始化骰子序列
2023-02-25 14:19:33 +00:00
for item in diceCodeList:
i += 1
isAdd = True
if item[0] == '-':
isAdd = False
item = item[1:]
if item[0] == '+':
item = item[1:]
try:
if 'D' in item or 'd' in item:
2023-06-07 08:52:06 +00:00
d = Dice(msg, item, isAdd)
2023-02-26 04:03:25 +00:00
diceList.append(d)
diceCount += d.count
2023-02-25 14:19:33 +00:00
elif item.isdigit():
2023-06-07 08:52:06 +00:00
diceList.append(DiceMod(msg, item, isAdd))
2023-02-26 08:51:03 +00:00
except (DiceSyntaxError, DiceValueError) as ex:
2023-06-07 08:26:44 +00:00
output += '\n' + msg.locale.t('dice.message.error.prompt', i=i) + ex.message
2023-02-25 14:19:33 +00:00
haveErr = True
if haveErr:
2023-06-07 07:56:42 +00:00
return msg.locale.t('dice.message.error') + output
2023-02-25 14:19:33 +00:00
successNum = 0
2023-02-26 04:03:25 +00:00
failNum = 0
2023-06-07 07:56:42 +00:00
output = msg.locale.t('dice.message.output')
2023-06-07 08:26:44 +00:00
# 开始投掷并输出
2023-02-25 14:19:33 +00:00
for i in range(times):
outputLine = ''
result = 0
for dice in diceList:
2023-06-07 09:03:00 +00:00
dice.Roll(msg)
2023-02-25 14:19:33 +00:00
outputLine += '+' if dice.postive else '-'
2023-04-30 03:30:59 +00:00
if isinstance(dice, Dice) and times * diceCount < MAX_DETAIL_CNT:
2023-02-26 04:03:25 +00:00
outputLine += f'( {dice.GetDetail()})'
2023-02-25 14:19:33 +00:00
else:
outputLine += str(dice.GetResult())
2023-02-26 04:03:25 +00:00
result += dice.GetResult(False)
outputLine = remove_prefix(outputLine, '+') # 移除多项式首个+
2023-02-25 14:19:33 +00:00
outputLine += ' = ' + str(result)
2023-01-19 12:02:49 +00:00
if dc != 0:
2023-07-15 17:12:31 +00:00
if msg.data.options.get('dice_dc_reversed'):
if result <= dc:
outputLine += msg.locale.t('dice.message.dc.success')
successNum += 1
else:
outputLine += msg.locale.t('dice.message.dc.failed')
failNum += 1
2023-01-19 12:02:49 +00:00
else:
2023-07-15 17:12:31 +00:00
if result >= dc:
outputLine += msg.locale.t('dice.message.dc.success')
successNum += 1
else:
outputLine += msg.locale.t('dice.message.dc.failed')
failNum += 1
2023-02-26 05:54:35 +00:00
output += f'\n{dices} = {outputLine}'
2023-02-25 14:19:33 +00:00
if dc != 0 and times > 1:
2023-06-07 07:56:42 +00:00
output += '\n' + msg.locale.t('dice.message.dc.check', success=str(successNum), failed=str(failNum))
2023-01-19 14:22:14 +00:00
return output