1
2 """ This module implements the (foundation) GameObject Class """
3 import sys
4 from random import randint
5 from base import Base
6 from gameaction import GameAction
7
8
10 """
11 This is the base class for all artifacts, actors, and contexts.
12 The abilities of this base class are:
13 - own a list of objects (which can be added and retrieved)
14 - return a list of GameActions that it enables
15 - accept and process non-ATTACK GameActions
16
17 @ivar objects: list of owned/contained GameObjects
18
19 objects can be I{hidden} in which case they might not be returned
20 """
21 - def __init__(self, name="actor", descr=None):
22 """
23 create a new GameObject
24 @param name: display name of this object
25 @param descr: for players description of this object
26 """
27 super(GameObject, self).__init__(name, descr)
28 self.objects = []
29
31 """
32 return the given name of this object
33 """
34 return self.name
35
37 """
38 return a list of GameObjects contained in/owned by this GameObject
39
40 if an object is hidden (has a positive RESISTANCE.SEARCH) it may not
41 be visible unless
42 - it has been successfully found (SEARCH > 0)
43 - caller specifies that hidden objects should be returned
44
45 @param hidden: return only hidden objects
46 @return: list of discoverd GameOjects
47 """
48 reported = []
49 for thing in self.objects:
50
51 atr = thing.get("RESISTANCE.SEARCH")
52 concealed = atr is not None and atr > 0
53 atr = thing.get("SEARCH")
54 found = atr is not None and atr > 0
55
56 if hidden:
57
58 if concealed and not found:
59 reported.append(thing)
60 else:
61
62 if found or not concealed:
63 reported.append(thing)
64
65 return reported
66
68 """
69 return a named object from my inventory
70
71 @param name: (string) of the desired object
72 @return: first matching object (or None)
73 """
74 for thing in self.objects:
75 if name in thing.name:
76 return thing
77 return None
78
80 """
81 add another object to my C{objects} list (if not already there)
82 """
83 if item not in self.objects:
84 self.objects.append(item)
85
86
88 """
89 called by C{GameAction.act()} to receive GameAction, determine effects
90
91 NOTE: this base class cannot process ATTACK actions.
92 Those are processed by the C{GameActor} sub-class.
93 This base class can only process actions which (if successful),
94 increment the property who's name matches the action verb.
95
96 @param action: GameAction being performed
97 @param actor: GameActor initiating the action
98 @param context: GameContext in which action is occuring
99
100 NOTE: this base class makes no use of the C{actor} or C{context}
101 parameters, but they might be useful to a subc-class that could
102 process actions before passing them down to us.
103
104 @return: <(boolean) success, (string) description of the effect>
105 """
106
107 if '.' in action.verb:
108 base_verb = action.verb.split('.')[0]
109 sub_type = action.verb.split('.')[1]
110 else:
111 base_verb = action.verb
112 sub_type = None
113
114
115 res = self.get("RESISTANCE")
116 resistance = 0 if res is None else int(res)
117
118
119 res = self.get("RESISTANCE." + base_verb)
120 if res is not None:
121 resistance += int(res)
122
123
124 if sub_type is not None:
125 res = self.get("RESISTANCE." + base_verb + "." + sub_type)
126 if res is not None:
127 resistance += int(res)
128
129
130 power = int(action.get("TO_HIT")) - resistance
131 if power <= 0:
132 return (False, "{} resists {} {}"
133 .format(self.name, action.source.name, action.verb))
134
135
136 incoming = abs(int(action.get("TOTAL")))
137 received = 0
138 for _ in range(incoming):
139 roll = randint(1, 100)
140
141 if roll <= power:
142 received += 1
143
144
145
146 sign = 1 if int(action.get("TOTAL")) > 0 else -1
147 if received > 0:
148 have = self.get(action.verb)
149 have = 0 if have is None else int(have)
150
151 if action.verb == "LIFE" and self.get("HP") is not None:
152 max_hp = int(self.get("HP"))
153 if have + sign * received > max_hp:
154 have = max_hp - received
155 self.set(action.verb, have + (sign * received))
156
157
158 return (received > 0,
159 "{} resists {}/{} stacks of {} from {} in {}"
160 .format(self.name, incoming - received, incoming,
161 ("(negative) " if sign < 0 else "") + action.verb,
162 actor, context))
163
164
165
166
168 """
169 return list of C{GameAction}s this object enables
170
171 verbs come from our (comma-separated-verbs) ACTIONS attribute
172 for each C{GameAction}, ACCURACY, DAMAGE, POWER, STACKS are the sum of
173 - our base ACCURACY, DAMAGE, POWER, STACKS
174 - our ACCURACY.verb, DAMAGE.verb, POWER.verb, STACKS.verb,
175
176 @param actor: GameActor initiating the action
177 @param context: GameContext in which the action is taken
178
179 NOTE: this base class makes no use of the C{actor} and C{context}
180 parameters, but a sub-class might want to determine whether or not
181 B{this actor} could perform this action in B{this context}.
182
183 @return: list of possible GameActions
184 """
185
186 actions = []
187 verbs = self.get("ACTIONS")
188 if verbs is None:
189 return []
190
191
192 base_accuracy = self.get("ACCURACY")
193 base_damage = self.get("DAMAGE")
194 base_power = self.get("POWER")
195 base_stacks = self.get("STACKS")
196
197
198 for compound_verb in verbs.split(','):
199 action = GameAction(self, compound_verb)
200
201
202 accuracies = ""
203 damages = ""
204 powers = ""
205 stacks = ""
206
207
208 for verb in compound_verb.split('+'):
209 if verb.startswith("ATTACK"):
210
211 sub_accuracy = None
212 sub_damage = None
213 if verb.startswith('ATTACK.'):
214 sub_type = verb.split('.')[1]
215 sub_accuracy = self.get("ACCURACY." + sub_type)
216 sub_damage = self.get("DAMAGE." + sub_type)
217
218
219 accuracy = 0 if base_accuracy is None \
220 else int(base_accuracy)
221 accuracy += 0 if sub_accuracy is None \
222 else int(sub_accuracy)
223 if accuracies == "":
224 accuracies = str(accuracy)
225 else:
226 accuracies += "," + str(accuracy)
227
228
229 if sub_damage is not None:
230 damage = sub_damage
231 elif base_damage is not None:
232 damage = base_damage
233 else:
234 damage = 0
235 if damages == "":
236 damages = str(damage)
237 else:
238 damages += "," + str(damage)
239 else:
240
241 sub_power = self.get("POWER." + verb)
242 power = 0 if base_power is None else int(base_power)
243 power += 0 if sub_power is None else int(sub_power)
244 if powers == "":
245 powers = str(power)
246 else:
247 powers += "," + str(power)
248
249
250 sub_stacks = self.get("STACKS." + verb)
251 if sub_stacks is not None:
252 stack = sub_stacks
253 elif base_stacks is not None:
254 stack = base_stacks
255 else:
256 stack = 1
257 if stacks == "":
258 stacks = str(stack)
259 else:
260 stacks += "," + str(stack)
261
262
263 if accuracies != "":
264 action.set("ACCURACY", accuracies)
265 if damages != "":
266 action.set("DAMAGE", damages)
267 if powers != "":
268 action.set("POWER", powers)
269 if stacks != "":
270 action.set("STACKS", stacks)
271
272
273 actions.append(action)
274
275 return actions
276
277 - def load(self, filename):
278 """
279 read object definitions from a file
280 - blank lines and lines beginning w/# are ignored
281 - NAME string ... is the name of an object
282 - DESCRIPTION string ... is the description of that object
283 - ACTIONS string ... is the list of supported verbs
284 - OBJECT ... introduces definition of an object in our inventory
285 - anything else is an atribute and value (strings should be quoted)
286
287 NOTE: The object being defined can contain other objects.
288 (e.g. guard has a sword, box contains a scroll)
289 But contained objects cannot, themselves, contain other objects.
290
291 @param filename: name of file to be read
292 """
293 cur_object = self
294
295 try:
296 infile = open(filename, "r")
297 for line in infile:
298
299 (name, value) = _lex(line)
300 if name is None:
301 continue
302
303
304 if name == "NAME":
305 cur_object.name = value
306 elif name == "DESCRIPTION":
307 cur_object.description = value
308 elif name == "OBJECT":
309 cur_object = GameObject()
310 self.add_object(cur_object)
311 else:
312
313 cur_object.set(name, value)
314
315 infile.close()
316 except IOError:
317 sys.stderr.write("Unable to read attributes from {}\n".
318 format(filename))
319
320
322 """
323 helper function to lex a name and (potentially quoted) value from a line
324 - treat (single or double) quoted strings as a single token
325 - if second token is an integer, return it as such, else a string
326
327 @param line: string to be lexed
328 @return: (name, value) ... or (None, None) for blank/comment lines
329 """
330
331 start = 0
332 eol = len(line)
333 while start < eol and line[start].isspace():
334 start += 1
335
336
337 if start >= eol or line[start] == "#":
338 return (None, None)
339
340
341 end = start + 1
342 while end < eol and not line[end].isspace():
343 end += 1
344 name = line[start:end]
345
346
347 start = end
348 while start < eol and line[start].isspace():
349 start += 1
350
351 if start >= eol or line[start] == "#":
352 return (name, None)
353
354
355 if line[start] == '"' or line[start] == "'":
356
357 quote = line[start]
358 start += 1
359 end = start + 1
360 while end < eol and line[end] != quote:
361 end += 1
362 value = line[start:end]
363 else:
364 end = start + 1
365 while end < eol and not line[end].isspace():
366 end += 1
367
368
369 try:
370 value = int(line[start:end])
371 except ValueError:
372 value = line[start:end]
373
374 return (name, value)
375
376
377
379 """
380 basic test GameObject test cases
381 """
382
383 describe = "simple get/set test object"
384 go1 = GameObject("GameObject 1", describe)
385
386
387 actions = go1.possible_actions(None, None)
388 assert (not actions), \
389 "New object returns non-empty action list"
390
391
392 test_actions = "ACTION,SECOND ACTION"
393 go1.set("ACTIONS", test_actions)
394 print("Set actions='{}', possible_actions returns:".format(test_actions))
395 actions = go1.possible_actions(None, None)
396 for action in actions:
397 print(" {}".format(action.verb))
398 assert (len(actions) == 2), \
399 "possible_actions returns wrong number of actions"
400 assert (actions[0].verb == "ACTION"), \
401 "first action not correctly returned"
402 assert (actions[1].verb == "SECOND ACTION"), \
403 "second action not correctly returned"
404 return (3, 3)
405
406
407
409 """
410 test for weapon actions and damage
411 """
412 tried = 0
413 passed = 0
414
415
416 w_0 = GameObject("Null Weapon")
417 tried += 4
418 assert w_0.name == "Null Weapon", \
419 "Incorrect name: expected w_0"
420 assert w_0.get("DAMAGE") is None, \
421 "Incorrect default damage: expected None"
422 assert w_0.get("ACCURACY") is None, \
423 "Incorrect default accuracy, expected None"
424 actions = w_0.possible_actions(None, None)
425 assert not actions, \
426 "incorrect default actions, expected None"
427 passed += 4
428 print("test #1: " + str(w_0) +
429 " ... NO ATTACKS, ACCURACY or DAMAGE - CORRECT")
430
431
432 w_1 = GameObject("Simple Weapon")
433 w_1.set("ACTIONS", "ATTACK")
434 w_1.set("ACCURACY", 66)
435 w_1.set("DAMAGE", "666")
436 tried += 6
437 assert w_1.get("DAMAGE") == "666", \
438 "Incorrect default damage: expected '666'"
439 assert w_1.get("ACCURACY") == 66, \
440 "Incorrect default accuracy, expected 66"
441 actions = w_1.possible_actions(None, None)
442 assert len(actions) == 1, \
443 "incorrect default actions, expected ['ATTACK'], got " + str(actions)
444 assert actions[0].verb == "ATTACK", \
445 "incorrect default action, expected 'ATTACK', got " + str(actions[0])
446 assert actions[0].get("DAMAGE") == "666", \
447 "incorrect base damage, expected '666', got " + str(actions[0])
448 assert actions[0].get("ACCURACY") == "66", \
449 "incorrect base accuracy, expected 66, got " + str(actions[0])
450 passed += 6
451 print("test #2: " + str(w_1) +
452 " ... BASE ATTACK, ACCURACY and DAMAGE - CORRECT")
453
454
455
456 w_2 = GameObject("multi-attack weapon")
457
458 attacks = [
459
460 ("ATTACK", 50, "D5", "50", "D5"),
461 ("ATTACK.60", 10, "D6", "60", "D6"),
462 ("ATTACK.70", 20, "D7", "70", "D7")]
463 verbs = None
464 for (verb, accuracy, damage, exp_acc, exp_dmg) in attacks:
465 if verbs is None:
466 verbs = verb
467 else:
468 verbs += "," + verb
469 if "." in verb:
470 sub_verb = verb.split(".")[1]
471 w_2.set("ACCURACY." + sub_verb, accuracy)
472 w_2.set("DAMAGE." + sub_verb, damage)
473 else:
474 w_2.set("ACCURACY", accuracy)
475 w_2.set("DAMAGE", damage)
476
477 w_2.set("ACTIONS", verbs)
478 actions = w_2.possible_actions(None, None)
479 tried += 1
480 assert len(actions) == 3, \
481 "incorrect actions list, expected 3, got " + str(actions)
482 passed += 1
483
484
485 for index in range(len(actions)):
486 (verb, accuracy, damage, exp_acc, exp_dmg) = attacks[index]
487 action = actions[index]
488 tried += 3
489 assert action.verb == verb, \
490 "action {}, verb={}, expected {}".format(index, action.verb, verb)
491 assert action.get("ACCURACY") == exp_acc, \
492 "action {}, expected ACCURACY={}, got {}". \
493 format(action.verb, exp_acc, action.get("ACCURACY"))
494 assert action.get("DAMAGE") == exp_dmg, \
495 "action {}, expected DAMAGE={}, got {}". \
496 format(action.verb, exp_dmg, action.get("DAMAGE"))
497 passed += 3
498 print("test #3: {} {} ... ACCURACY({}) and DAMAGE({}) - CORRECT".
499 format(w_2.name, action.verb,
500 "base plus sub-type" if "." in verb else "base only",
501 "sub-type only" if "." in verb else "base only"))
502 return (tried, passed)
503
504
505
507 """
508 Test for attribute collection for compound actions
509 """
510 obj = GameObject("Compound Actions w/base attributes")
511 first = "ATTACK.one+CONDITION.two+ATTACK.three+CONDITION.four"
512 second = "ATTACK.five+CONDITION.six"
513 obj.set("ACTIONS", first + "," + second)
514 obj.set("ACCURACY", 10)
515 obj.set("ACCURACY.one", 5)
516 obj.set("DAMAGE", "60")
517 obj.set("DAMAGE.one", 666)
518
519 obj.set("POWER", "20")
520 obj.set("POWER.CONDITION.two", 10)
521 obj.set("STACKS", 3)
522 obj.set("STACKS.CONDITION.two", 6)
523
524 obj.set("ACCURACY.five", 15)
525 obj.set("DAMAGE.five", 55)
526 obj.set("POWER.CONDITION.six", 6)
527 obj.set("STACKS.CONDITION.six", 66)
528
529 tried = 0
530 passed = 0
531
532 actions = obj.possible_actions(None, None)
533 for action in actions:
534 if action.verb == first:
535 tried += 8
536
537 accuracies = action.get("ACCURACY").split(',')
538 assert accuracies[0] == "15", "ACCURACY.one not added"
539 assert accuracies[1] == "10", "base ACCURACY not used"
540 passed += 2
541
542
543 damages = action.get("DAMAGE").split(',')
544 assert damages[0] == "666", "DAMAGE.one not used"
545 assert damages[1] == "60", "base DAMAGE not used"
546 passed += 2
547 print("test #4a: {} {} ...\n\t ACCURACY(S), DAMAGE(S) - CORRECT".
548 format(obj.name, action.verb))
549
550
551 powers = action.get("POWER").split(',')
552 assert powers[0] == "30", "POWER.two not added in"
553 assert powers[1] == "20", "base POWER not used"
554 passed += 2
555
556
557 stacks = action.get("STACKS").split(',')
558 assert stacks[0] == "6", "STACKS.two not used"
559 assert stacks[1] == "3", "base STACKS not used"
560 passed += 2
561 print("test #4b: {} {} ...\n\t POWER(S), STACKS(S) - CORRECT".
562 format(obj.name, action.verb))
563 elif action.verb == second:
564 tried += 4
565 accuracies = action.get("ACCURACY").split(',')
566 assert accuracies[0] == "25", "ACCURACY.five not added"
567 damages = action.get("DAMAGE").split(',')
568 assert damages[0] == "55", "DAMAGE.five not used"
569 passed += 2
570
571 powers = action.get("POWER").split(',')
572 assert powers[0] == "26", "POWER.six not added in"
573 stacks = action.get("STACKS").split(',')
574 assert stacks[0] == "66", "STACKS.six not used"
575 passed += 2
576 print("test #4c: {} {} ... \n\tPOWER, STACKS - CORRECT".
577 format(obj.name, action.verb))
578 else:
579 tried += 1
580 assert False, "Incorrect verb: " + action.verb
581
582
583 obj = GameObject("Compound Actions w/o base attributes")
584 first = "ATTACK.seven+CONDITION.eight+ATTACK.nine+CONDITION.ten"
585 obj.set("ACTIONS", first)
586 obj.set("ACCURACY.seven", 7)
587 obj.set("DAMAGE.seven", "777")
588
589 obj.set("POWER.CONDITION.eight", 8)
590 obj.set("STACKS.CONDITION.eight", "88")
591
592 actions = obj.possible_actions(None, None)
593 assert len(actions) == 1, \
594 "Incorrect actions: expected 1, got {}".format(len(actions))
595 action = actions[0]
596 assert action.verb == first, \
597 "Incorrect action: expected {}, got {}".format(first, action.verb)
598
599
600 accuracies = action.get("ACCURACY").split(',')
601 assert accuracies[0] == "7", "ACCURACY.seven not used"
602 assert accuracies[1] == "0", \
603 "expected ACCURACY=0, got {}".format(accuracies[1])
604
605
606 damages = action.get("DAMAGE").split(',')
607 assert damages[0] == "777", "DAMAGE.seven not used"
608 assert damages[1] == "0", \
609 "expected DAMAGE=0, got {}".format(damages[1])
610
611 print("test #4d: {} {} ... \n\tACCURACY, DAMAGE - CORRECT".
612 format(obj.name, action.verb))
613
614
615 powers = action.get("POWER").split(',')
616 assert powers[0] == "8", "POWER.eight not used"
617 assert powers[1] == "0", \
618 "expected POWER=0, got {}".format(powers[1])
619
620
621 stacks = action.get("STACKS").split(',')
622 assert stacks[0] == "88", "STACKS.eight not used"
623 assert stacks[1] == "1", "default STACKS=1 not used"
624
625 print("test #4e: {} {} ... \n\tPOWER, STACKS - CORRECT".
626 format(obj.name, action.verb))
627
628 return (tried, passed)
629
630
632 """
633 tests for (non-ATTACK) accept_action()
634 """
635 tried = 0
636 passed = 0
637
638
639 initiator = GameObject("tester")
640 target = GameObject("target")
641 arena = GameObject("arena")
642
643
644 action = GameAction(initiator, "50/50")
645 action.set("STACKS", 100)
646 target.set("RESISTANCE", 50)
647 (success, desc) = action.act(initiator, target, arena)
648 tried += 5
649 assert success,\
650 "None of 100 50/50 STACKs got through"
651 passed += 1
652
653
654 resisted = int(desc.split()[2].split('/')[0])
655 expect = "{} resists {}/100 stacks of {} from {} in {}".\
656 format(target.name, resisted, action.verb, initiator.name, arena.name)
657 assert desc == expect, \
658 "Successful 50/50 did not return expected result string"
659 passed += 1
660
661
662 assert resisted >= 35, \
663 "too few 50/50 STACKS got through"
664 assert resisted <= 64, \
665 "too many 50/50 STACKS got through"
666 attribute = target.get(action.verb)
667 assert attribute == (100 - resisted), \
668 "target's {} does not reflect correct 100-{}". \
669 format(action.verb, resisted)
670 passed += 3
671
672 print("test #5a: {} resists {}/100 STACKS of {} ... \n\t{}.{} 100 -> {}".
673 format(target.name, resisted, action.verb, target.name,
674 action.verb, attribute))
675
676
677 action = GameAction(initiator, "SURE-THING")
678 action.set("STACKS", -50)
679 target.set("RESISTANCE", 0)
680 target.set(action.verb, 100)
681 (success, desc) = action.act(initiator, target, arena)
682 tried += 4
683 assert success, \
684 "{} action failed".format(action.verb)
685
686 resisted = int(desc.split()[2].split('/')[0])
687 expect = "{} resists {}/50 stacks of (negative) {} from {} in {}".\
688 format(target.name, resisted, action.verb, initiator.name, arena.name)
689 assert desc == expect, \
690 "Successful SURE-THING did not return expected result string"
691 assert resisted == 0, \
692 "{} resists {} STACKS of {}".format(target.name, resisted, action.verb)
693
694 attribute = target.get(action.verb)
695 assert attribute == (100 - 50), \
696 "100 - 50 STACKS of {} -> {}".format(action.verb, attribute)
697 passed += 4
698
699 print("test #5b: {} delivers -50 STACKS of {} ... \n\t{}.{} 100 -> {}".
700 format(initiator.name, action.verb, target.name, action.verb,
701 attribute))
702
703
704 target.set("RESISTANCE", 100)
705 action = GameAction(initiator, "BASE-RESISTED-ACTION")
706 action.set("STACKS", 100)
707 (success, desc) = action.act(initiator, target, arena)
708 tried += 3
709 assert not success, \
710 "{} action succeeded".format(action.verb)
711 assert desc == "{} resists {} {}". \
712 format(target.name, initiator.name, action.verb), \
713 "{} action does not return correct failure message".format(action.verb)
714 assert target.get(action.verb) is None, \
715 "target property was set by failed {} action".format(action.verb)
716 passed += 3
717
718 print("test #5c: {} delivers 100 STACKS of {} ... \n\t{}".
719 format(initiator.name, action.verb, desc))
720
721
722 target.set("RESISTANCE", None)
723 target.set("RESISTANCE.VERB-RESISTED-ACTION", 100)
724 action = GameAction(initiator, "VERB-RESISTED-ACTION")
725 action.set("STACKS", 100)
726 (success, desc) = action.act(initiator, target, arena)
727 tried += 3
728 assert not success, \
729 "{} action succeeded".format(action.verb)
730 assert desc == "{} resists {} {}". \
731 format(target.name, initiator.name, action.verb), \
732 "{} action does not return correct failure message".format(action.verb)
733 assert target.get(action.verb) is None, \
734 "target property was set by failed {} action".format(action.verb)
735 passed += 3
736
737 print("test #5d: {} delivers 100 STACKS of {} ... \n\t{}".
738 format(initiator.name, action.verb, desc))
739
740
741 target.set("RESISTANCE.VERB-RESISTED-ACTION", None)
742 target.set("RESISTANCE.RESISTED-ACTION.SUBTYPE", 100)
743 action = GameAction(initiator, "RESISTED-ACTION.SUBTYPE")
744 action.set("STACKS", 100)
745 (success, desc) = action.act(initiator, target, arena)
746 tried += 3
747 assert not success, \
748 "{} action succeeded".format(action.verb)
749 assert desc == "{} resists {} {}". \
750 format(target.name, initiator.name, action.verb), \
751 "{} action does not return correct failure message".format(action.verb)
752 assert target.get(action.verb) is None, \
753 "target property was set by failed {} action".format(action.verb)
754 passed += 3
755
756 print("test #5e: {} delivers 100 STACKS of {} ... \n\t{}".
757 format(initiator.name, action.verb, desc))
758
759 target.set("RESISTANCE.RESISTED-ACTION.SUBTYPE", None)
760
761
762 target.set("RESISTANCE.RESISTED-ACTION.SUBTYPE", None)
763 target.set("LIFE", 25)
764 target.set("HP", 50)
765 action = GameAction(initiator, "LIFE")
766 action.set("STACKS", 100)
767 (success, desc) = action.act(initiator, target, arena)
768 tried += 3
769 assert success, \
770 "sure thing {} did not succeed".format(action.verb)
771 life = target.get(action.verb)
772 assert life <= target.get("HP"), \
773 "{} raised above HP {}".format(action.verb, target.get("HP"))
774 assert life == target.get("HP"), \
775 "25/50 + 100 STACKS of {} -> {}".format(action.verb, life)
776 passed += 3
777
778 print("test #5f: 25/50 + 100 STACKS of {} ... \n\t{}.{} 25 -> {}".
779 format(action.verb, target.name, action.verb, life))
780
781 print()
782 return (tried, passed)
783
784
786 """
787 Run all unit-test cases and print out summary of results
788 """
789 (t_1, p_1) = action_test()
790 (t_2, p_2) = weapon_test()
791 (t_3, p_3) = compound_test()
792 (t_4, p_4) = accept_test()
793 tried = t_1 + t_2 + t_3 + t_4
794 passed = p_1 + p_2 + p_3 + p_4
795 if tried == passed:
796 print("Passed all {} GameObject tests".format(passed))
797 else:
798 print("FAILED {}/{} GameObject tests".format(tried-passed, tried))
799
800
801 if __name__ == "__main__":
802 main()
803