Advertisement
Guest User

Untitled

a guest
Apr 23rd, 2017
53
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.50 KB | None | 0 0
  1. '''
  2. Created on Dec 25, 2016
  3.  
  4. @author: andrew
  5. '''
  6. from heapq import nlargest, nsmallest
  7. from math import floor
  8. import random
  9. from re import IGNORECASE
  10. import re
  11. import traceback
  12.  
  13. import numexpr
  14.  
  15. from cogs5e import tables
  16. from utils.functions import list_get
  17.  
  18. VALID_OPERATORS = 'k|rr|ro|mi|ma'
  19. VALID_OPERATORS_2 = '|'.join(["({})".format(i) for i in VALID_OPERATORS.split('|')])
  20. VALID_OPERATORS_ARRAY = VALID_OPERATORS.split('|')
  21.  
  22. # Rolls Dice
  23. def d_roller(obj, adv=0, double=False):
  24. res = []
  25. splargs = None
  26. crit = 0
  27. total = 0
  28. # Recognizes dice
  29. args = obj
  30. obj = re.findall('\d+', obj)
  31. obj = [int(x) for x in obj]
  32. numArgs = len(obj)
  33. if numArgs == 1:
  34. if not args.startswith('d'):
  35. raise Exception('Please pass in the value of the dice.')
  36. numDice = 1
  37. diceVal = obj[0]
  38. if adv is not 0 and diceVal == 20:
  39. numDice = 2
  40. splargs = ['k', 'h1'] if adv is 1 else ['k', 'l1']
  41. elif numArgs == 2:
  42. numDice = obj[0]
  43. diceVal = obj[-1]
  44. if adv is not 0 and diceVal == 20:
  45. splargs = ['k', 'h' + str(numDice)] if adv is 1 else ['k', 'l' + str(numDice)]
  46. numDice = numDice * 2
  47. else: # split into xdy and operators
  48. numDice = obj[0]
  49. diceVal = obj[1]
  50. args = re.split('(\d+d\d+)', args)[-1]
  51. splargs = re.split(VALID_OPERATORS_2, args)
  52. splargs = [a for a in splargs if a is not None]
  53.  
  54. if double:
  55. numDice = numDice * 2
  56.  
  57. # dice repair/modification
  58. if numDice > 300 or diceVal < 1 or numDice == 0:
  59. raise Exception('Too many dice rolled.')
  60.  
  61. for die in range(numDice):
  62. try:
  63. randres = random.randrange(1, diceVal + 1)
  64. res.append(randres)
  65. except:
  66. res.append(1)
  67.  
  68. if adv is not 0 and diceVal == 20:
  69. numDice = floor(numDice / 2) # for crit detection
  70.  
  71. rawRes = list(map(str, res))
  72.  
  73. if splargs is not None:
  74. def reroll(rerollList, iterations=250):
  75. for i in range(iterations): # let's only iterate 250 times for sanity
  76. breakCheck = True
  77. for r in rerollList:
  78. if r in res:
  79. breakCheck = False
  80. for r in range(len(res)):
  81. if res[r] in rerollList:
  82. try:
  83. randres = random.randint(1, diceVal)
  84. res[r] = randres
  85. rawRes.append(str(randres))
  86. except:
  87. res[r] = 1
  88. rawRes.append('1')
  89. if breakCheck:
  90. break
  91. rerollList = []
  92.  
  93. rerollList = []
  94. reroll_once = []
  95. keep = None
  96. valid_operators = VALID_OPERATORS_ARRAY
  97. for a in range(len(splargs)):
  98. if splargs[a] == 'rr':
  99. rerollList += parse_selectors([list_get(a + 1, 0, splargs)], res)
  100. elif splargs[a] in valid_operators:
  101. reroll(rerollList)
  102. rerollList = []
  103. if splargs[a] == 'k':
  104. keep = [] if keep is None else keep
  105. keep += parse_selectors([list_get(a + 1, 0, splargs)], res)
  106. elif splargs[a] in valid_operators:
  107. res = keep if keep is not None else res
  108. keep = None
  109. if splargs[a] == 'ro':
  110. reroll_once += parse_selectors([list_get(a + 1, 0, splargs)], res)
  111. elif splargs[a] in valid_operators:
  112. reroll(reroll_once, 1)
  113. reroll_once = []
  114. if splargs[a] == 'mi':
  115. min = list_get(a + 1, 0, splargs)
  116. for i, r in enumerate(res):
  117. if r < int(min):
  118. try:
  119. rawRes[rawRes.index(str(r))] = "{} -> _{}_".format(r, min)
  120. except: pass
  121. res[i] = int(min)
  122. if splargs[a] == 'ma':
  123. max = list_get(a + 1, 0, splargs)
  124. for i, r in enumerate(res):
  125. if r > int(max):
  126. try:
  127. rawRes[rawRes.index(str(r))] = "{} -> _{}_".format(r, max)
  128. except: pass
  129. res[i] = int(max)
  130. reroll(reroll_once, 1)
  131. reroll(rerollList)
  132. res = keep if keep is not None else res
  133.  
  134.  
  135. for r in res:
  136. total += r
  137.  
  138. # check for crits/crails
  139. if numDice == 1 and diceVal == 20 and total == 20:
  140. crit = 1
  141. elif numDice == 1 and diceVal == 20 and total == 1:
  142. crit = 2
  143.  
  144. res = list(map(str, res))
  145. for r in range(len(rawRes)):
  146. toCompare = rawRes[len(rawRes) - (r + 1)]
  147. index = len(rawRes) - (r + 1)
  148. if '->' in toCompare: toCompare = toCompare.split('_')[-2]
  149. if toCompare in res:
  150. res.remove(toCompare)
  151. else:
  152. rawRes[index] = '~~' + rawRes[index] + '~~'
  153.  
  154. # Returns string of answer
  155.  
  156. for r in range(0, len(rawRes)):
  157. if rawRes[r] == '1' or rawRes[r] == str(diceVal):
  158. rawRes[r] = '_' + rawRes[r] + '_'
  159.  
  160. # build the output list
  161. out = DiceResult(total, '(' + ', '.join(rawRes) + ')', crit)
  162. return out
  163.  
  164. # # Dice Roller
  165. def roll(rollStr, adv:int=0, rollFor='', inline=False, double=False, show_blurbs=True):
  166. try:
  167. reply = []
  168. out_set = []
  169. crit = 0
  170. total = 0
  171. # Parses math/dice terms
  172. #dice_temp = rollStr.replace('^', '**')
  173. dice_temp = rollStr
  174. # Splits into sections of ['dice', 'operator', 'dice'...] like ['1d20', '+', '4d6', '+', '5']
  175. dice_set = re.split('([-+*/().<>= ])', dice_temp)
  176. out_set = re.split('([-+*/().<>= ])', dice_temp)
  177. eval_set = re.split('([-+*/().<>= ])', dice_temp)
  178. # print("Dice Set is: " + str(dice_set))
  179.  
  180. # Replaces dice sets with rolled results
  181. stack = []
  182. nextAnno = ''
  183. rollForTemp = ''
  184. for i, t in enumerate(dice_set):
  185. # print("Processing a t: " + t)
  186. # print("Stack: " + str(stack))
  187. # print("NextAnno: " + nextAnno)
  188. breakCheck = False
  189. if t is '':
  190. continue
  191.  
  192. if not 'annotation' in stack:
  193. try: # t looks like: " 1d20[annotation] words"
  194. nextAnno = re.findall(r'\[.*\]', t)[0] # finds any annotation encosed by brackets
  195. t = t.replace(nextAnno, '') # and removes it from the string
  196. except:
  197. nextAnno = '' # no annotation
  198. if '[' in t:
  199. stack.append('annotation')
  200. nextAnno += t
  201. out_set[i] = ''
  202. eval_set[i] = ''
  203. continue
  204. if ']' in t:
  205. if 'annotation' in stack:
  206. t = nextAnno + t
  207. nextAnno = re.findall(r'\[.*\]', t)[0] # finds any annotation encosed by brackets
  208. t = t.replace(nextAnno, '') # and removes it from the string
  209. stack.remove('annotation')
  210. if 'annotation' in stack:
  211. nextAnno += t
  212. out_set[i] = ''
  213. eval_set[i] = ''
  214. continue
  215.  
  216. if re.search('^\s*((\d*(d|' + VALID_OPERATORS + '|padellis)?(h\d|l\d|\d)+)+|([-+*/^().<>= ]))?(\[.*\])?\s*$', t, flags=IGNORECASE):
  217. if 'd' in t:
  218. try:
  219. result = d_roller(t, adv, double=double)
  220. out_set[i] = t + " " + result.result + " " + nextAnno if nextAnno is not '' else t + " " + result.result
  221. eval_set[i] = str(result.plain)
  222. if not result.crit == 0:
  223. crit = result.crit
  224. except Exception as e:
  225. out_set[i] = t + " (ERROR: {}) ".format(str(e)) + nextAnno if nextAnno is not '' else t + " (ERROR: {})".format(str(e))
  226. eval_set[i] = "0"
  227. else:
  228. out_set[i] = t + " " + nextAnno if nextAnno is not '' else t
  229. eval_set[i] = t
  230. nextAnno = ''
  231. else:
  232. rollForTemp = ''.join(dice_set[i:]) # that means the rest of the string isn't part of the roll
  233. rollForTemp = re.sub('(^\s+|\s+$)', '', rollForTemp) # get rid of starting/trailing whitespace
  234. breakCheck = True
  235.  
  236. if breakCheck:
  237. out_set = out_set[:i]
  238. eval_set = eval_set[:i]
  239. break
  240.  
  241. # print("Out Set is: " + str(out_set))
  242. # print("Eval Set is: " + str(eval_set))
  243. total = ''.join(eval_set)
  244. try:
  245. total = numexpr.evaluate(total)
  246. except SyntaxError:
  247. total = 0
  248. return DiceResult(verbose_result="Invalid input: Nothing rolled or missing argument after operator.")
  249.  
  250. rolled = ''.join(out_set).replace('**', '^').replace('_', '**')
  251. totalStr = str(floor(total))
  252.  
  253. if rollFor is '':
  254. rollFor = rollForTemp if rollForTemp is not '' else rollFor
  255.  
  256. skeletonReply = ''
  257. if not inline:
  258. # Builds end result while showing rolls
  259. reply.append(' '.join(out_set) + '\n_Total:_ ' + str(floor(total)))
  260. # Replies to user with message
  261. reply = '\n\n'.join(reply).replace('**', '^').replace('_', '**')
  262. skeletonReply = reply
  263. rollFor = rollFor if rollFor is not '' else 'Result'
  264. reply = '**{}:** '.format(rollFor) + reply
  265. if show_blurbs:
  266. if adv == 1:
  267. reply += '\n**Rolled with Advantage**'
  268. elif adv == -1:
  269. reply += '\n**Rolled with Disadvantage**'
  270. if crit == 1:
  271. critStr = "\n_**Critical Hit!**_ " + tables.getCritMessage()
  272. reply += critStr
  273. elif crit == 2:
  274. critStr = "\n_**Critical Fail!**_ " + tables.getFailMessage()
  275. reply += critStr
  276. else:
  277. # Builds end result while showing rolls
  278. reply.append('' + ' '.join(out_set) + ' = `' + str(floor(total)) + '`')
  279. # Replies to user with message
  280. reply = '\n\n'.join(reply).replace('**', '^').replace('_', '**')
  281. skeletonReply = reply
  282. rollFor = rollFor if rollFor is not '' else 'Result'
  283. reply = '**{}:** '.format(rollFor) + reply
  284. if show_blurbs:
  285. if adv == 1:
  286. reply += '\n**Rolled with Advantage**'
  287. elif adv == -1:
  288. reply += '\n**Rolled with Disadvantage**'
  289. if crit == 1:
  290. critStr = "\n_**Critical Hit!**_ " + tables.getCritMessage()
  291. reply += critStr
  292. elif crit == 2:
  293. critStr = "\n_**Critical Fail!**_ " + tables.getFailMessage()
  294. reply += critStr
  295. reply = re.sub(' +', ' ', reply)
  296. skeletonReply = re.sub(' +', ' ', str(skeletonReply))
  297. return DiceResult(result=floor(total), verbose_result=reply, crit=crit, rolled=rolled, skeleton=skeletonReply)
  298.  
  299. except Exception as ex:
  300. print('Error in roll():')
  301. traceback.print_exc()
  302. return DiceResult(verbose_result="Invalid input: {}".format(ex))
  303.  
  304. def parse_selectors(opts, res):
  305. for o in range(len(opts)):
  306. if opts[o][0] is 'h':
  307. opts[o] = nlargest(int(opts[o].split('h')[1]), res)
  308. if opts[o][0] is 'l':
  309. opts[o] = nsmallest(int(opts[o].split('l')[1]), res)
  310. out = []
  311. for o in opts:
  312. if isinstance(o, list):
  313. out += [int(l) for l in o]
  314. else:
  315. out += [int(o) for a in res if a is int(o)]
  316. return out
  317.  
  318. class DiceResult:
  319. """Class to hold the output of a dice roll."""
  320. def __init__(self, result:int=0, verbose_result:str='', crit:int=0, rolled:str='', skeleton:str=''):
  321. self.plain = result
  322. self.total = result
  323. self.result = verbose_result
  324. self.crit = crit
  325. self.rolled = rolled
  326. self.skeleton = skeleton if skeleton is not '' else verbose_result
  327.  
  328. def __str__(self):
  329. return self.result
  330.  
  331. def __repr__(self):
  332. return '<DiceResult object: total={}>'.format(self.total)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement