source: pycodebattler/trunk/pycodebattler/battle.py @ 71

Revision 71, 6.7 KB checked in by atzm, 13 years ago (diff)

add preamble

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