-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstbehaviour.py
More file actions
executable file
·602 lines (466 loc) · 30.9 KB
/
Copy pathstbehaviour.py
File metadata and controls
executable file
·602 lines (466 loc) · 30.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
dmflagsList = ('No health spawn', 'No powerup spawn', 'Weapons stay', 'Fall Damage (ZDoom)', 'Fall Damage (Hexen)', ' ', 'Same map', 'Spawn farthest', 'Force respawn', 'No armor spawn', 'No exit', 'Infinite ammo', 'No monsters', 'Respawning monsters', 'Respawning items', 'Fast monsters', 'No jump', 'No freelook', 'Respawning mega-powerups', '90 FOV only', 'Only campaign weapons', 'No crouch', 'Lose inventory on death', 'Lose keys on death', 'Keep weapons on death', 'Lose armor on death', 'Lose powerups on death', 'Lose ammo on death', 'Lose half ammo on death', 'Allow Jump', 'Allow Crouch')
dmflags2List = (' ', 'Drop weapon on death', 'No (normal) runes', 'Instant flag/skull return', 'No team switching', 'Server chooses teams', 'Double ammo', 'Degenerate HP over max', 'BFG aiming', 'Respawning barrels', 'No respawn invuln.', 'Shotgun start', 'Spawn where died', 'Keep team on map change', ' ', ' ', ' ', ' ', 'Forced OpenGL defaults', ' ', 'Point on 100 damage', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'No multiplayer actors')
dmflags3List = (' ')*30
compatflagsList = ('Find shortest texture', 'Buggy stairbuilding', 'Pain Elemental 20 Lost Soul limit', 'Local pickups', 'Infinitely tall actors', 'Silent BFG trick', 'Wallrunning', 'Items dropping on floor', 'Special lines blocking use', 'No door light effect', 'Raven speed scrollers', 'Sector-based sound targeting', 'Max Health bonus limit', ' ', 'No monster movement dropoff', 'Additive scrolling sectors', 'Blursphere not stopping detection', 'Literal air friction', 'Grabbing items through walls', 'Allow instant respawn', 'Tauntless', 'Doom sound curve', 'Old intermission screen', 'Pointless stealth monsters', 'Infinite height explosions', 'No crosshair', 'Forced weapon switch on pickup')
compatflags2List = ('Clientside net scripts', 'Full button info sending', 'No \'land\' command')
eightballpredictions = ('Yes', 'No', 'Maybe', 'Probably', 'Probably not', 'Ask me in a few minutes.', 'Insufficient data', 'No, you moron!', 'Possibly.', 'Yes, if you bribe me.', 'No, if you bribe me.', 'Does 1=1?')
eightballpreps = ('is in', 'is inside', 'is on', 'is on top of', 'is around', 'is outside', 'is below', 'is somewhere near', 'might be at', 'is visiting')
eightballpastpreps = ('was in', 'was inside', 'was on', 'was on top of', 'was around', 'was outside', 'was below', 'was somewhere near', 'might have be at', 'was visiting')
eightballfutpreps = ('will be in', 'will be inside', 'will be on', 'will be on top of', 'will be around', 'will be outside', 'will be below', 'will be somewhere near', 'might be at', 'will be visiting')
eightballfutpreps = ('will be in', 'will be inside', 'will be on', 'will be on top of', 'will be around', 'will be outside', 'will be below', 'will be somewhere near', 'might be at', 'will be visiting')
eightballprepspl = ('are in', 'are inside', 'are on', 'are on top of', 'are around', 'are outside', 'are below', 'are somewhere near', 'might be at', 'are visiting')
eightballpastprepspl = ('were in', 'were inside', 'were on', 'were on top of', 'were around', 'were outside', 'were below', 'were somewhere near', 'might have be at', 'were visiting')
eightballplaces = ('an AOW2 server', 'some place on Earth', 'Hell', 'Purgatory', 'Tichy\'s house, as a hitman', 'the idiot who made this bot', 'the Sun', 'the Moon', 'Betelgeuse')
eightballwhy = ('Because you\'re an idiot', 'Because you stole my pie', 'Because the universe hates you', 'Because AOW2 exists', 'No one really knows for sure', 'You\'ll find out... eventually', 'There\'s a reason, and I don\'t know it', 'Because a \\cfNIGGA STOLE MY BIKE\\c-')
eightballwhenwill = ('Eventually...', 'Yesterday. Yeah, you slept through it', 'In a billion years', 'Tomorrow... I think', 'When Bush dies', 'Some time. That\'s as specific as I\'m getting', 'Rephrase that as \'when can I punch you\' and I\'ll answer it.')
eightballwhendid = ('Some time. That\'s as specific as I\'m getting', 'Tomorrow. Now stop time-travelling!', 'What are you on? That never happened!', 'When I stopped taking crack - ie. never', 'When AOW2 came into existence')
eightballwho = ('a meat popsicle.', 'Adolf Hitler. Sorry.', 'a collection of 1\'s and 0\'s in the infinite hard drive of the universe', 'not clever. Now stop trying to break the bot - that\'s Tichy\'s job.', 'a furry. Here\'s a gun.', 'an idiot.', 'probably missing out on something. Ah well, cyberdemons need to be killed!')
eightballwhoplural = ('meat popsicles.', 'all Adolf Hitler. Sorry.', 'collections of 1\'s and 0\'s in the infinite hard drive of the universe', 'not clever. And they should stop trying to break the bot - that\'s Tichy\'s job.', 'furries. Here\'s an assault rifle.', 'idiots. All idiots.', 'probably missing out on something. Ah well, cyberdemons need to be killed!')
eightballwhoplural2 = ('meat popsicles.', 'all Adolf Hitler. Sorry.', 'collections of 1\'s and 0\'s in the infinite hard drive of the universe', 'not clever. And you should stop trying to break the bot - that\'s Tichy\'s job.', 'furries. Here\'s guns. Go an hero.', 'idiots. All idiots.', 'probably missing out on something. Ah well, cyberdemons need to be killed!')
eightballSelfwho = ('I am the server bot. Not much is known about me, except that I monitor everything.;say Well, possibly. But why clear a mystery? Leaving you in the dark is much more fun.', 'I rule you. And I will make sure you never forget that fact.;say Even if I have to resort to... more drastic measures to prove it.', 'I could speak of myself... but I prefer not to. But if you must know, I\'ll say this - I am your god. Even if you deny it.', 'Why must I answer this question? I think the answer is obvious. But further prod me, if you absolutely cannot figure out.', 'The fact that you don\'t know who I am... dissapoints me.')
eightballwhoPast = ('worry about the past, and for what reason? The past is done and gone - the future is what you want to look at.', 'probably are too nosy for your own good.', 'assume that I, despite my omnipotence, can ascertain the history of a random person based on a name? You flatter me, although that isn\'t necessarily good.', 'should probably already know this, if you\'re asking about their history. I mean, if you care, then why haven\'t you already researched it?')
eightballSelfwhoPast = ('Past? I have no past. How can something, having been in the relative past and future, and travelling at will, have a concept as simple as a past? My influence is not linear. Now speak no more of this.', 'Ah, you assume one who exists at all points in time simultaneously has a past or future. This is simply not true.', 'Past? Speak not of it. It is foolish to speak of something that does not exist, is it not?', 'I concern myself not of relative past and future, but of absolute time. Being able to travel through time at will removes all meaning of \'past\' and \'future\'.')
import re, random
class InvalidDieNumber(BaseException): pass
class InvalidFlags(BaseException): pass
class NotAChatString(BaseException): pass
class NotARoll(BaseException): pass
class InvalidDice(BaseException): pass
class NotANode(BaseException): pass
class DisplayHelp(BaseException): pass
################################################################################
## BaseNode ##
################################################################################
class BaseNode(object):
"""The base of everything in the node tree. Generally the leaves of the tree -
there is no reason this should have branching. If it does, someone's abusing
this code."""
BRANCHCONTTOPSTR = "|--"
BRANCHCONTSTR = "|"
BRANCHENDSTR = "'"
SPACELEN = len(BRANCHCONTTOPSTR)
SPACESTR = ' '*SPACELEN
def __init__(self, value, parent=None, level=1):
self.fixedInit(parent, level)
self.value = value
self.level = level
self.parent = parent
def fixedInit(self, parent, level):
"""Init rules that will always be executed in each subclass."""
self.name = self.__class__.__name__
self.parent = parent
self.level = level
def __repr__(self):
"""Print off what value the node holds. Note - ugly."""
return "{name}({nodeValue})".format(name=self.getName(), nodeValue=repr(self.value))
def __str__(self):
"""Incrementally prints off a tree. Much nicer than __repr__ for seeing what is
going on behind the veil."""
spaces = BaseNode.SPACESTR*(self.level-1)
spaces += BaseNode.BRANCHCONTTOPSTR
return "{spaces}{name}({nodeValue})".format(spaces=spaces, name=self.getName(), nodeValue=repr(self.value))
def getName(self): # don't access self.name directly! >=(
"""Returns the name. This function exists purely for abstracting away name getting."""
return self.name
def get(self):
"""Returns the value the node is attached to."""
return self.value
################################################################################
## NumNode ##
################################################################################
class FloatNode(BaseNode):
"""It's like a BaseNode, but it only stores numbers (floats specifically). Also, it
prints the string representation of its value, so instead of things like 56.8999
99999999999, you get things like 56.9. So yeah, it's an aesthetic thing."""
def __init__(self, value, parent=None, level=1):
self.fixedInit(parent, level)
self.value = float(value)
def __repr__(self):
"""Print off what value the node holds. Note - ugly."""
return "{name}({nodeValue})".format(name=self.getName(), nodeValue=str(self.value))
def __str__(self):
"""Incrementally prints off a tree. Much nicer than __repr__ for seeing what is
going on behind the veil."""
spaces = BaseNode.SPACESTR*(self.level-1)
spaces += BaseNode.BRANCHCONTTOPSTR
return "{spaces}{name}({nodeValue})".format(spaces=spaces, name=self.getName(), nodeValue=str(self.value))
################################################################################
## IntNode ##
################################################################################
class IntNode(BaseNode):
"""It's like a BaseNode, but it only stores numbers (ints specifically)."""
def __init__(self, value, parent=None, level=1):
self.fixedInit(parent, level)
self.value = int(value)
################################################################################
## BaseBranch ##
################################################################################
class BaseBranch(BaseNode):
"""This is what every branch is, basically. If this, or any other branch, is the
value of a BaseNode or subclass, something is seriously wrong (either with my
code, or yours)."""
BRANCHSTR = ". "
def __init__(self, node, node2, parent=None, level=1):
self.fixedInit(parent, level)
self.node = node
self.node2 = node2
self.parseNodes()
def fixedInit(self, parent, level):
"""Init rules that will always be executed in each subclass."""
#####
## Parse Pairs
# wish this could be a class var but sadly, it can't D:
self.PARSEPAIRS = (('^', PowerBranch, 1), ('*', TimesBranch, 0), ('\\', FloorDivBranch, 0), ('/', DivBranch, 0), ('%', ModuloBranch, 0), ('-', MinusBranch, 0), ('+', AddBranch, 0), ('d', DiceBranch, 1), (' ', BaseBranch, 0))
## End Parse Pairs
#####
super(BaseBranch, self).fixedInit(parent, level)
def parseNodes(self):
"""Parse both of the branch nodes. Basically for abstracting, in case a subclass
needs to overload this or something."""
self.node = self.parse(self.node)
self.node2 = self.parse(self.node2)
def parse(self, nodeValue):
"""Takes a tree node that is inherited from or is a BaseNode, parses, and returns
the result of the parsing."""
ret = None
for sep, branch, assoc in self.PARSEPAIRS[::-1]: #reverse so adding is done last
if sep in nodeValue:
if assoc == 1:
parseTuple = nodeValue.split(sep, 1)
else:
parseTuple = nodeValue.rsplit(sep, 1)
ret = branch(*parseTuple, level=self.level+1)
break
else:
if not nodeValue:
raise ValueError("no node value in {name}".format(name=self.getName()))
else:
try:
nodeValue = float(nodeValue)
except:
ret = BaseNode(nodeValue, level=self.level+1)
else:
if int(nodeValue) == nodeValue:
ret = IntNode(nodeValue, level=self.level+1)
else:
ret = FloatNode(nodeValue, level=self.level+1)
return ret
def __repr__(self):
"""Print off what's in the nodes the branch holds. Note - ugly."""
return "{name}({nodeValue}, {node2Value})".format(name=self.getName(), nodeValue=repr(self.node), node2Value=repr(self.node2))
def __str__(self):
"""Incrementally prints off a tree. Much nicer than __repr__ for seeing what is
going on behind the veil."""
spaces = BaseNode.SPACESTR*(self.level-1)
replaceLevel = len(spaces)+BaseNode.SPACELEN
spaces += BaseNode.BRANCHCONTTOPSTR
spaces += BaseBranch.BRANCHSTR
spaces2 = BaseNode.SPACESTR*(self.level)
spaces2 += BaseNode.BRANCHCONTSTR
ret = []
selfString = "{spaces}{name}".format(spaces=spaces, name=self.getName())
spacerString = "{spaces}".format(spaces=spaces2)
node1String, node2String = str(self.node), str(self.node2)
node1Lines, node2Lines = node1String.split('\n'), node2String.split('\n')
newNode1Lines, newNode2Lines = [], []
for line in node1Lines:
line = line[:replaceLevel] + BaseNode.BRANCHCONTSTR + line[replaceLevel+len(BaseNode.BRANCHCONTSTR):]
newNode1Lines.append(line)
curveLine = node2Lines[0]
curveLine = curveLine[:replaceLevel] + BaseNode.BRANCHENDSTR + curveLine[replaceLevel+len(BaseNode.BRANCHENDSTR):]
newNode2Lines.append(curveLine)
for line in node2Lines[1:]:
newNode2Lines.append(line)
node1String, node2String = '\n'.join(newNode1Lines), '\n'.join(newNode2Lines)
ret.append(selfString)
ret.append(node1String)
ret.append(spacerString)
ret.append(node2String)
ret = '\n'.join(ret)
return ret
def get(self):
"""Return the value of the first node, or second if the first node is empty.
Note: This is just so that putting a BaseBranch in the tree won't screw
things up."""
return self.node.get() or self.node2.get() # placeholder - not tuple because then things could ballsy up easily
class BaseStem(BaseBranch):
"""This is simply for when you don't want to use a branch. Most likely will be used
for grouped statements, but you didn't hear that from me."""
def __init__(self, node, parent=None, level=1):
self.fixedInit(parent, level)
self.node = node
self.parseNodes()
def parseNodes(self):
"""Parse the class' branch nodes. Basically for abstracting, in case a subclass
needs to overload this or something."""
self.node = self.parse(self.node)
def __repr__(self):
"""Print off what's in the nodes the branch holds. Note - ugly."""
return "{name}({nodeValue})".format(name=self.getName(), nodeValue=repr(self.node))
def __str__(self):
"""Incrementally prints off a tree. Much nicer than __repr__ for seeing what is
going on behind the veil."""
spaces = BaseNode.SPACESTR*(self.level-1)
replaceLevel = len(spaces)+BaseNode.SPACELEN
spaces += BaseNode.BRANCHCONTTOPSTR
spaces += BaseBranch.BRANCHSTR
spaces2 = BaseNode.SPACESTR*(self.level)
spaces2 += BaseNode.BRANCHCONTSTR
ret = []
selfString = "{spaces}{name}".format(spaces=spaces, name=self.getName())
spacerString = "{spaces}".format(spaces=spaces2)
nodeString = str(self.node)
nodeLines = nodeString.split('\n')
newNodeLines = []
curveLine = nodeLines[0]
curveLine = curveLine[:replaceLevel] + BaseNode.BRANCHENDSTR + curveLine[replaceLevel+len(BaseNode.BRANCHENDSTR):]
newNodeLines.append(curveLine)
for line in nodeLines[1:]:
newNodeLines.append(line)
nodeString = '\n'.join(newNodeLines)
ret.append(selfString)
ret.append(nodeString)
ret = '\n'.join(ret)
return ret
def get(self):
"""Return the value of the first node, or second if the first node is empty.
Note: This is just so that putting a BaseBranch in the tree won't screw
things up."""
return self.node.get() # placeholder - not tuple because then things could ballsy up easily
################################################################################
## AddBranch
################################################################################
class AddBranch(BaseBranch):
"""It's like a BaseBranch, but it adds the values of its two nodes together.
So, it's basically a BaseBranch with one line of code different. Well, not
really, but it might as well be."""
def get(self):
"""Return the value of the first node plus the value of the second. Note that there
is a failsafe built into this so that if it tries to add a BaseNode value and
NumNode value, it will fall back to concatenation. MinusBranch has no such
luxury."""
try:
return self.node.get() + self.node2.get()
except:
return str(self.node.get()) + str(self.node2.get())
################################################################################
## MinusBranch ##
################################################################################
class MinusBranch(BaseBranch):
"""It's like a BaseBranch, but it subracts the value of its second node from the
value of its first."""
def get(self):
"""Return the value of the first node minus the value of the second.
Obviously, this ballsies up when you don't get NumNodes for both values."""
return self.node.get() - self.node2.get()
################################################################################
## TimesBranch ##
################################################################################
class TimesBranch(BaseBranch):
"""It's a BaseBranch that multiplies the values of its two nodes together. Don't
pass it two strings."""
def get(self):
"""Return the value of the first node times the value of the second. Obviously, it
screws up if you pass it two BaseNodes. So don't do that."""
return self.node.get() * self.node2.get()
################################################################################
## DivBranch ##
################################################################################
class DivBranch(BaseBranch):
"""It's like a BaseBranch, but it divides the value of its first node by the value
of its second. Note that since everything is a float, you will get floats from
dividing. If you don't want that, use a FloorDivBranch."""
def get(self):
"""Return the value of the first node divided by the value of the second.
Obviously, it screws up if you pass it a BaseNode. So don't do that."""
return self.node.get() / self.node2.get()
################################################################################
## FloorDivBranch ##
################################################################################
class FloorDivBranch(BaseBranch):
"""It's basically a DivBranch that floors."""
def get(self):
"""Return the value of the first node divided by the value of the second, floored.
Obviously, it screws up if you pass it a BaseNode. So don't do that."""
return self.node.get() // self.node2.get()
################################################################################
## ModuloBranch ##
################################################################################
class ModuloBranch(BaseBranch):
"""It's like a BaseBranch, but it divides its first node's value by its second
node's value, floors it, then returns the first node's value minus the result of
the floor. So, basically, it modulos. Amazing, right?
Don't pass a BaseNode to this branch."""
def get(self):
"""Return the value of the first node moduloed (lol?) by the value of the second.
Obviously, it screws up if you pass it a BaseNode. So don't do that."""
return self.node.get() % self.node2.get()
################################################################################
## PowerBranch ##
################################################################################
class PowerBranch(BaseBranch):
"""It's a BaseBranch that raises the value of its first nodes by the value of its
second. Obligitory warning for passing it BaseNodes."""
def get(self):
"""Return the value of the first node raised to the value of the second. Obviously,
it screws up if you pass it two BaseNodes. So don't do that."""
return self.node.get() ** self.node2.get()
################################################################################
## DiceBranch ##
################################################################################
class DiceBranch(BaseBranch):
def get(self):
ret = 0
for roll in range(self.node.get()):
ret += random.randrange(1, int(self.node2.get())+1)
return ret
################################################################################
## LSCParserBot
################################################################################
class LSCParserBot(object):
"""What talks to the server. Note: doesn't actually do most of the work - instead,
delegates the work to specialized classes."""
onlyNumRE = re.compile("^.*?(-?\d+).*?$") # ...<(-)[0-9]>...
def __init__(self, chatStrings="", players=None, dmflags=0, dmflags2=0, dmflags3=0, compatflags=0, compatflags2=0):
self.chatStrings = chatStrings.split('\n')
if players is None: self.players = []
else: self.players = players
self.dmflags = dmflags
self.dmflags2 = dmflags2
self.dmflags3 = dmflags3
self.compatflags = compatflags
self.compatflags2 = compatflags2
print [self.chatStrings, self.players, self.dmflags, self.dmflags2, self.dmflags3, self.compatflags, self.compatflags2]
def splitChatString(self, chatStr):
"Splits a raw chat string from standard output into chatter and chat message. Returns a list in the format [chatter, message], with leading and trailing whitespace stripped."
#chatStr = self.stripTimestamp(chatStr)
#print "\"%s\"" % chatStr[:4]
if chatStr[:4] != 'CHAT' or ':' not in chatStr:
raise NotAChatString
return # although we shouldn't get here
else:
chatStr = chatStr[5:]
chatStr0 = chatStr.split(':')
chatStr1 = []
for i in chatStr0:
chatStr1 += [i.strip()]
return chatStr1
def splitChatIntoCommand(self, chatList):
"Takes a list in the format of [chatter, message], and splits the message into a list in the form [word1, [word2, word3...]]. Returns [chatter, [word1, [word2, word3...]]]."
chatStr0 = [chatList[0], chatList[1].split()]
chatStr0[1] = [chatStr0[1][0], chatStr0[1][1:]] # for you lispers out there, (' (car chatStr0) (cdr chatStr0))
return chatStr0
def parseChatLine(self, chatStr):
"Takes a raw chat string, and runs it through the splitChatString and splitChatIntoCommand methods. End result SHOULD BE in the form of [chatter, [word1, [word2, word3...]]]. Should be."
chatStr0 = self.splitChatString(chatStr)
chatStr0 = self.splitChatIntoCommand(chatStr0)
for i in chatStr0[1][1]:
i = i.lower()
if i == 'help':
raise DisplayHelp
return chatStr0
dieRE = re.compile("(\d+)d(\d+)([-+]\d+)?(\*-?\d+)?") # <[0-9]>d<[0-9]>(<[+-][0-9]>)(*[0-9])
baseDiceREsult = ['1', '6', '0', '1']
DEFAULTDICE = [[1, [6, 0]], 1]
INVALIDDICE = [[[InvalidDice(), [InvalidDice(), InvalidDice()]], InvalidDice()]]
def parseDie(self, die):
"Takes a string in the form \"(amount)d(sides)[<+->(increment)]*(multiplier)\", and will attempt to parse it into a list of the form [[amount, [sides, increment]], multiplier]. It returns the list in question if it succeeds, and raises InvalidDice if not."
die = die.lower()
dieREsult = LSCParserBot.dieRE.match(die) # the punnery
dieGroup = dieREsult.groups()
newDieGroup = []
for (num, group) in enumerate(dieGroup):
if group is None:
group = LSCParserBot.baseDiceREsult[num]
numGroup = LSCParserBot.onlyNumRE.search(group)
newDieGroup += [numGroup.group(1)]
try:
die1 = [int(i) for i in newDieGroup]
ret = [[die1[0], [die1[1], die1[2]]], die1[3]]
except ValueError:
raise InvalidDice
return ret
def parseDice(self, dice):
"Takes a list of die strings in the form of [die1, die2...], and will run each through parseDie. Returns [[[amount, sides], multiplier]...] if all die strings are valid, and raises InvalidDice if not. Merely a wrapper method."
newDice = []
for die in dice:
try:
newDice += [self.parseDie(die)]
except:
newDice += LSCParserBot.INVALIDDICE
if not newDice: newDice = [LSCParserBot.DEFAULTDICE]
return newDice
def parseDiceChatString(self, chatStr):
"Takes a raw chat string, and runs it through parseChatLine. Then, it checks whether a lowercased word1 says '!roll', and if so, takes the [word2, word3...] list inside it, and parses them in the format: \"(diceCount)d(sides)*(multiplier)\". Returns [chatter, ['!roll', [die1, die2...]]] if the roll string is valid, InvalidDice if !roll is specified, but the dice syntax is invalid, and NotARoll if word1 just isn't '!roll'."
chatStr0 = self.parseChatLine(chatStr)
if chatStr0[1][0].lower() != '!roll':
raise NotARoll
dice = chatStr0[1][1]
dice = self.parseDice(dice)
chatStr0[1][1] = dice
return chatStr0
def rollDie(self, die):
"Takes a list in the form [[amount, sides], multiplier], and will actually roll the thing, and will return the result of the roll."
#print die
amount = die[0][0]
sides = die[0][1][0]
increment = die[0][1][1]
multiplier = die[1]
# print type(amount), type(sides), type(multiplier), InvalidDice, "\n"
if InvalidDice in (type(amount), type(sides), type(increment), type(multiplier)): return InvalidDice()
result = 0
if sides == 1:
result = ((amount*(1+increment))*multiplier)
elif not multiplier:
result = 0
else:
for i in xrange(amount):
result += random.randrange(1, sides+1) + increment
result *= multiplier
return result
def rollDice(self, chatList):
"Takes a list in the format [chatter, ['!roll', [die1, die2...]]], and will run each die through rollDie and save the output of each one. It returns a list in the form of [result1, result2...]."
dice = chatList[1][1]
results = []
for die in dice:
result = self.rollDie(die)
if result is not None:
results += [result]
return results
def parseDiceAndRoll(self, chatStr):
"Takes a raw chat string, and runs it through parseDiceChatString. Then it runs rollDice, and saves the result. Then it returns a list in the form of [[chatter, ['!roll', [die1, die2...]]], [result1, result2...]]. The input should have been sanitized at this point, so any errors will fall to the Python interpreter and halt the program."
chatStr0 = self.parseDiceChatString(chatStr)
diceResults = self.rollDice(chatStr0)
intResults = [i for i in diceResults if type(i) != InvalidDice]
totalResult = sum(intResults)
return [chatStr0, diceResults, totalResult]
def formatDiceString(self, chatStr):
"""Takes a raw chat string, and runs it through parseDiceAndRoll. Then, it formats the resulting list like "<die> - <result>"... with ' :: 's in between, and returns it - unless the die was considered invalid by parseDie, in which case it spits out "Invalid dice" instead of the "<die> - <result>" format. This is the string that the LSC Bot is going to spit out when someone incants the magical '!roll' command in chat."""
try:
chatList = self.parseDiceAndRoll(chatStr)
except DisplayHelp:
return "Format: #d#([\+-]#)?(\*#)? (or <num>d<num>([+-]<num>)(*<num>) )"
dice = chatList[0][1][1]
results = chatList[1]
strDice = []
for die in dice:
typedie = [type(die[0][0]), type(die[0][1][0]), type(die[0][1][1]), type(die[1])]
if InvalidDice in typedie:
strDice += [InvalidDice()]
continue
if die[0][1][1]:
strDie = "[({0}d{1}){2:+}]*{3}".format(die[0][0], die[0][1][0], die[0][1][1], die[1]) # [(<amount>d<sides>)\[+-\]<increment>]*<mult>
else:
strDie = "({0}d{1})*{2}".format(die[0][0], die[0][1][0], die[1]) # <amount>d<sides>*<mult>
strDice += [strDie]
diceResults = zip(strDice, results)
formatStr = "%s - %s"
outputList = []
for (die, result) in diceResults:
if type(result) == InvalidDice:
outputList += ["Invalid die"]
else:
outputList += [formatStr % (die, result)]
outputList += ["Total - %s" % (chatList[2])]
outputStr = ' :: '.join(outputList)
return outputStr