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-05-28 09:38:21 +00:00
|
|
|
|
from core.utils.text import remove_prefix
|
|
|
|
|
|
2023-01-19 14:22:14 +00:00
|
|
|
|
MAX_DICE_COUNT = 100 # 一次摇动最多的骰子数量
|
|
|
|
|
MAX_ROLL_TIMES = 10 # 一次命令最多的摇动次数
|
|
|
|
|
MAX_MOD_NUMBER = 10000 # 骰子最大加权值
|
|
|
|
|
MIN_MOD_NUMBER = -10000 # 骰子最小加权值
|
|
|
|
|
MAX_OUTPUT_NUM = 50 # 输出的最多数据量
|
|
|
|
|
MAX_OUTPUT_LEN = 200 # 输出的最大长度
|
2023-02-26 04:03:25 +00:00
|
|
|
|
MAX_DETAIL_CNT = 5 # n次摇动的骰子的总量超过该值时将不再显示详细信息
|
2023-02-26 08:51:03 +00:00
|
|
|
|
MAX_ITEM_COUNT = 10 # 骰子多项式最多的项数
|
|
|
|
|
|
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-04-22 04:48:10 +00:00
|
|
|
|
def __init__(self, message):
|
|
|
|
|
self.message = f"语法错误: {message}"
|
2023-01-27 08:18:47 +00:00
|
|
|
|
|
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-04-22 04:48:10 +00:00
|
|
|
|
def __init__(self, message, value=None):
|
2023-04-30 03:30:59 +00:00
|
|
|
|
if value is not None:
|
2023-04-22 04:48:10 +00:00
|
|
|
|
self.message = f"参数错误: 输入为{value},{message} "
|
2023-02-26 04:03:25 +00:00
|
|
|
|
else:
|
2023-04-22 04:48:10 +00:00
|
|
|
|
self.message = f"参数错误: {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-02-26 05:25:51 +00:00
|
|
|
|
def Roll(self):
|
|
|
|
|
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-04-22 04:48:10 +00:00
|
|
|
|
def __init__(self, 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-04-22 04:48:10 +00:00
|
|
|
|
raise DiceValueError(f'无效的调节值项', '+' 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-04-22 04:48:10 +00:00
|
|
|
|
raise DiceValueError(f'调节值不得小于 {MIN_MOD_NUMBER} 或大于 {MIN_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-04-22 04:48:10 +00:00
|
|
|
|
def __init__(self, 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-02-25 14:19:33 +00:00
|
|
|
|
args = self.GetArgs()
|
|
|
|
|
self.count = args[0]
|
|
|
|
|
self.type = args[1]
|
|
|
|
|
self.adv = args[2]
|
|
|
|
|
if self.count <= 0 or self.count > MAX_DICE_COUNT:
|
2023-04-22 04:48:10 +00:00
|
|
|
|
raise DiceValueError(f'骰子数量不得小于 1 或大于 {MAX_DICE_COUNT}', self.count)
|
2023-02-26 08:51:03 +00:00
|
|
|
|
if self.type <= 0:
|
2023-04-22 04:48:10 +00:00
|
|
|
|
raise DiceValueError(f'骰子面数不得小于 2', self.count)
|
2023-02-26 08:51:03 +00:00
|
|
|
|
if self.type == 1:
|
2023-04-22 04:48:10 +00:00
|
|
|
|
raise DiceValueError(f'1 ... 1 面的骰子?')
|
2023-02-25 14:19:33 +00:00
|
|
|
|
if abs(self.adv) > self.count:
|
2023-04-22 04:48:10 +00:00
|
|
|
|
raise DiceValueError(f'优劣势骰数大于总骰子数', self.adv)
|
2023-02-26 08:51:03 +00:00
|
|
|
|
|
2023-04-22 04:48:10 +00:00
|
|
|
|
def GetArgs(self):
|
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-04-22 04:48:10 +00:00
|
|
|
|
raise DiceSyntaxError('骰子语句中存在无法识别的字符')
|
2023-02-25 14:19:33 +00:00
|
|
|
|
if 'D' not in diceCode:
|
2023-04-22 04:48:10 +00:00
|
|
|
|
raise DiceSyntaxError('骰子语句缺失字符 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-04-22 04:48:10 +00:00
|
|
|
|
raise DiceValueError(f'无效的骰子数量', diceCount)
|
2023-02-25 14:19:33 +00:00
|
|
|
|
if not diceType.isdigit():
|
2023-04-22 04:48:10 +00:00
|
|
|
|
raise DiceValueError(f'无效的骰子面数', diceType)
|
2023-02-25 14:19:33 +00:00
|
|
|
|
if not (advantage.isdigit() or (advantage[0] == '-' and advantage[1:].isdigit())):
|
2023-04-22 04:48:10 +00:00
|
|
|
|
raise DiceValueError(f'无效的优劣势', 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-04-22 04:48:10 +00:00
|
|
|
|
def Roll(self):
|
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-02-25 14:19:33 +00:00
|
|
|
|
if self.count >= MAX_OUTPUT_NUM:
|
2023-04-22 04:48:10 +00:00
|
|
|
|
outputBuffer = f"数量过大,已省略 {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-01-19 14:22:14 +00:00
|
|
|
|
if length > MAX_OUTPUT_NUM: # 显示数据含100
|
2023-04-22 04:48:10 +00:00
|
|
|
|
output += f'数量过大,已省略 {length} 个数据'
|
2023-01-19 12:02:49 +00:00
|
|
|
|
for i in range(length):
|
|
|
|
|
result += diceResults[i]
|
2023-01-19 14:22:14 +00:00
|
|
|
|
if length <= MAX_OUTPUT_NUM: # 显示数据含100
|
2023-01-19 12:02:49 +00:00
|
|
|
|
output += str(diceResults[i])
|
2023-01-27 08:18:47 +00:00
|
|
|
|
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-04-22 04:48:10 +00:00
|
|
|
|
output = '输出过长...'
|
2023-02-26 04:03:25 +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-04-21 15:15:20 +00:00
|
|
|
|
if re.search(r'[^0-9+\-DKL]', dices.upper()):
|
2023-06-07 07:56:42 +00:00
|
|
|
|
return DiceSyntaxError(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 07:56:42 +00:00
|
|
|
|
return DiceValueError(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 07:56:42 +00:00
|
|
|
|
return DiceValueError(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-02-26 08:51:03 +00:00
|
|
|
|
d = Dice(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-02-26 08:51:03 +00:00
|
|
|
|
diceList.append(DiceMod(item, isAdd))
|
|
|
|
|
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:
|
|
|
|
|
dice.Roll()
|
|
|
|
|
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)
|
2023-05-28 09:38:21 +00:00
|
|
|
|
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:
|
|
|
|
|
if result > dc:
|
2023-06-07 07:56:42 +00:00
|
|
|
|
outputLine += msg.locale.t('dice.message.dc.success')
|
2023-01-19 12:02:49 +00:00
|
|
|
|
successNum += 1
|
|
|
|
|
else:
|
2023-06-07 07:56:42 +00:00
|
|
|
|
outputLine += msg.locale.t('dice.message.dc.failed')
|
2023-01-19 12:02:49 +00:00
|
|
|
|
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
|