[45] | 1 | #!/usr/bin/env python |
---|
| 2 | # -*- coding: utf-8 -*- |
---|
| 3 | # $Id: d51d6cbc0b293385b0dccb6e8ed3bd1e15b285dc $ |
---|
| 4 | |
---|
| 5 | import os |
---|
| 6 | import sys |
---|
| 7 | import random |
---|
| 8 | import pycodebattler |
---|
| 9 | |
---|
| 10 | |
---|
| 11 | class DrawGame(Exception): |
---|
| 12 | pass |
---|
| 13 | |
---|
| 14 | |
---|
| 15 | class BattleProto: |
---|
| 16 | HANDLER_COMMAND = 0 |
---|
| 17 | |
---|
| 18 | def __init__(self, warriors, max_turn=sys.maxint - 1): |
---|
| 19 | self._warriors = warriors |
---|
| 20 | self._max_turn = max_turn |
---|
| 21 | self._handlers = {} |
---|
| 22 | |
---|
| 23 | self.register_handler(self.HANDLER_COMMAND, |
---|
| 24 | self._handle_command_random) |
---|
| 25 | |
---|
| 26 | def winner(self): |
---|
| 27 | living = [w for w in self._warriors if not w.is_dead()] |
---|
| 28 | if not living: |
---|
| 29 | raise DrawGame() |
---|
| 30 | if len(living) == 1: |
---|
| 31 | return living[0] |
---|
| 32 | return None |
---|
| 33 | |
---|
| 34 | def loosers(self): |
---|
| 35 | return [w for w in self._warriors if w.is_dead()] |
---|
| 36 | |
---|
| 37 | def battle(self): |
---|
| 38 | for i in xrange(1, self._max_turn + 1): |
---|
| 39 | if self.winner(): |
---|
| 40 | break |
---|
| 41 | yield self.turn() |
---|
| 42 | else: |
---|
| 43 | raise DrawGame() |
---|
| 44 | |
---|
| 45 | def turn(self): |
---|
| 46 | for attacker in self._sorted_by_agility(): |
---|
| 47 | if attacker in self.loosers(): |
---|
| 48 | continue |
---|
| 49 | |
---|
| 50 | targets = [w for w in self._warriors |
---|
| 51 | if w not in self.loosers() + [attacker]] |
---|
| 52 | |
---|
| 53 | yield self._call_handler(self.HANDLER_COMMAND, attacker, targets) |
---|
| 54 | |
---|
| 55 | if self.winner(): |
---|
| 56 | break |
---|
| 57 | |
---|
| 58 | def register_handler(self, id_, handler, *ext_args): |
---|
| 59 | self._handlers[id_] = (handler, ext_args) |
---|
| 60 | |
---|
| 61 | def _call_handler(self, id_, *args): |
---|
| 62 | handler, ext_args = self._handlers[id_] |
---|
| 63 | return handler(self, *(args + ext_args)) |
---|
| 64 | |
---|
| 65 | def _sorted_by_agility(self): |
---|
| 66 | def cmp_agility(w1, w2): |
---|
| 67 | agi1 = w1.agility() |
---|
| 68 | agi2 = w2.agility() |
---|
| 69 | agi1 += random.randint(0 - agi1 / 8, agi1 / 8) |
---|
| 70 | agi2 += random.randint(0 - agi2 / 8, agi2 / 8) |
---|
| 71 | return agi2 - agi1 |
---|
| 72 | return sorted(self._warriors, cmp=cmp_agility) |
---|
| 73 | |
---|
| 74 | @staticmethod |
---|
| 75 | def _handle_command_random(self, attacker, targets): |
---|
| 76 | target = random.choice(targets) |
---|
| 77 | |
---|
| 78 | try: |
---|
| 79 | skname = random.choice(attacker.skill_list()) |
---|
| 80 | except IndexError: |
---|
| 81 | skname = None |
---|
| 82 | |
---|
| 83 | if attacker.can_invoke(skname) and random.randint(1, 10) > 8: |
---|
| 84 | sk = attacker.skill(skname) |
---|
| 85 | tt = sk.selectable(attacker, self._warriors) |
---|
| 86 | |
---|
| 87 | if sk.skilltype() == 'SuicideAttackType': |
---|
| 88 | damages = attacker.invoke(skname, tt, self._warriors) |
---|
| 89 | return attacker, skname, damages |
---|
| 90 | |
---|
| 91 | if tt: |
---|
| 92 | r = min([len(tt), sk.range(attacker)]) |
---|
| 93 | t = random.sample(tt, random.randint(1, r)) |
---|
| 94 | damages = attacker.invoke(skname, t, self._warriors) |
---|
| 95 | return attacker, skname, damages |
---|
| 96 | |
---|
| 97 | dmg, miss, crit = attacker.attack_to(target) |
---|
| 98 | return attacker, None, [(target, dmg, miss, crit)] |
---|
| 99 | |
---|
| 100 | |
---|
| 101 | def _main_entry(): |
---|
| 102 | from optparse import OptionParser |
---|
| 103 | |
---|
| 104 | parser = OptionParser(usage='%prog [options] file1 file2 ...') |
---|
| 105 | parser.add_option('-C', action='store_true', help='no color output') |
---|
| 106 | parser.add_option('-W', action='store_true', help='no wait per each turn') |
---|
| 107 | options, args = parser.parse_args() |
---|
| 108 | |
---|
| 109 | if len(args) <= 1: |
---|
| 110 | parser.print_help() |
---|
| 111 | return |
---|
| 112 | |
---|
| 113 | pycodebattler.util.set_color_enable(not options.C) |
---|
| 114 | |
---|
| 115 | skills = [pycodebattler.skill.Skill(*sk) for sk in [ |
---|
| 116 | ['Chakra', 3, pycodebattler.skill.HealType, 1], |
---|
| 117 | ['Resurrection', 2, pycodebattler.skill.ResurrectionType, 1], |
---|
| 118 | ['Psychokinesis', 13, pycodebattler.skill.SingleAttackType, 2], |
---|
| 119 | ['Flame Radiation', 8, pycodebattler.skill.RangeAttackType, 1], |
---|
| 120 | ['Meltdown', 7, pycodebattler.skill.MultiAttackType, 1], |
---|
| 121 | ['Big Bang', 23, pycodebattler.skill.SuicideAttackType, 1], |
---|
| 122 | ]] |
---|
| 123 | players = [ |
---|
| 124 | pycodebattler.warrior.Warrior( |
---|
| 125 | pycodebattler.util.path2name(f), |
---|
| 126 | open(f), |
---|
| 127 | random.sample(skills, random.randint(1, len(skills))) |
---|
| 128 | ) |
---|
| 129 | for f in args |
---|
| 130 | ] |
---|
| 131 | proto = BattleProto(players) |
---|
| 132 | |
---|
| 133 | def print_warriors(warriors, all=False): |
---|
| 134 | print('Warriors:') |
---|
| 135 | print(pycodebattler.warrior.pformat(players)) |
---|
| 136 | |
---|
| 137 | if not all: |
---|
| 138 | return |
---|
| 139 | |
---|
| 140 | for w in warriors: |
---|
| 141 | print("%s's skill:" % w) |
---|
| 142 | print(pycodebattler.skill.pformat( |
---|
| 143 | [w.skill(n) for n in w.skill_list()])) |
---|
| 144 | |
---|
| 145 | def print_match_result(attacker, skname, damages): |
---|
| 146 | if skname: |
---|
| 147 | print("-> %s invokes %s!!!" % |
---|
| 148 | (attacker, pycodebattler.util.cyan(skname))) |
---|
| 149 | else: |
---|
| 150 | print("-> %s's attack!" % attacker) |
---|
| 151 | |
---|
| 152 | if not damages: |
---|
| 153 | print(' but no effect...\n') |
---|
| 154 | return |
---|
| 155 | |
---|
| 156 | for victim, damage, miss, critical in damages: |
---|
| 157 | if critical: |
---|
| 158 | print(' %s!!!' % pycodebattler.util.red('CRITICAL')) |
---|
| 159 | if miss: |
---|
| 160 | print(' ...but %s!! %s got no damage!' % |
---|
| 161 | (pycodebattler.util.green('MISS'), victim)) |
---|
| 162 | continue |
---|
| 163 | |
---|
| 164 | if damage < 0: |
---|
| 165 | print(' %s is %s cured!' % |
---|
| 166 | (victim, pycodebattler.util.green(0 - damage))) |
---|
| 167 | else: |
---|
| 168 | print(' %s got %s damage!' % |
---|
| 169 | (victim, pycodebattler.util.yellow(damage))) |
---|
| 170 | |
---|
| 171 | if victim.is_dead(): |
---|
| 172 | print(pycodebattler.util.red(' %s is DEAD...' % victim)) |
---|
| 173 | |
---|
| 174 | print('') |
---|
| 175 | |
---|
| 176 | try: |
---|
| 177 | print_warriors(players, True) |
---|
| 178 | |
---|
| 179 | if not options.W: |
---|
| 180 | raw_input('> Hit enter key... ') |
---|
| 181 | print('') |
---|
| 182 | |
---|
| 183 | num_turn = 1 |
---|
| 184 | for turn in proto.battle(): |
---|
| 185 | print(pycodebattler.util.green( |
---|
| 186 | '============= turn %d =============' % num_turn)) |
---|
| 187 | |
---|
| 188 | for attacker, skname, damages in turn: |
---|
| 189 | print_match_result(attacker, skname, damages) |
---|
| 190 | |
---|
| 191 | print_warriors(players) |
---|
| 192 | num_turn += 1 |
---|
| 193 | |
---|
| 194 | if not options.W: |
---|
| 195 | raw_input('> Hit enter key... ') |
---|
| 196 | print('') |
---|
| 197 | |
---|
| 198 | print('') |
---|
| 199 | print(pycodebattler.util.green( |
---|
| 200 | '>>> %s is WINNER!! <<<' % proto.winner())) |
---|
| 201 | |
---|
| 202 | except (KeyboardInterrupt, EOFError, DrawGame): |
---|
| 203 | print('\n') |
---|
| 204 | print(pycodebattler.util.green('>>> Draw Game! <<<')) |
---|
| 205 | |
---|
| 206 | print('') |
---|
| 207 | print_warriors(players) |
---|
| 208 | |
---|
| 209 | |
---|
| 210 | if __name__ == '__main__': |
---|
| 211 | _main_entry() |
---|