From: W. Trevor King Date: Sat, 27 Mar 2010 01:30:50 +0000 (-0400) Subject: Added Event classes to pyrisk.log X-Git-Tag: 0.1~19 X-Git-Url: http://git.tremily.us/?p=pyrisk.git;a=commitdiff_plain;h=ec736930138c2d912fdbf74db8c14390f0e38a5b Added Event classes to pyrisk.log This should make logging easier to configure. For example, a Player.report function now has access to the raw data used to compose the string. Well, if the mutables haven't changed ;). If that's an issue for you, stick some copy.deepcopy calls into the Event subclass __init__ definitions. --- diff --git a/pyrisk/base.py b/pyrisk/base.py index 1cdd30f..38d3b23 100644 --- a/pyrisk/base.py +++ b/pyrisk/base.py @@ -19,7 +19,8 @@ import random -from .log import Logger +from .log import Logger, BeginGame, EndGame, Killed, StartTurn, DealtCards, \ + EarnsArmies, SelectTerritory, PlaceArmies, PlayCards, Attack, Conquer class PlayerError (Exception): @@ -422,7 +423,7 @@ class Player (NameMixin, ID_CmpMixin): draw - another notification-only method """ print 'Reporting for %s:\n %s' \ - % (self, '\n '.join(log[self._message_index:])) + % (self, '\n '.join([str(e) for e in log[self._message_index:]])) self._message_index = len(log) def draw(self, world, log, cards=[]): """Only called if you earned a new card (or cards). @@ -510,7 +511,7 @@ class Engine (ID_CmpMixin): for p in self.players: p.alive = True random.shuffle(self.players) - self.log('Game started with %s' % self.players) + self.log(BeginGame(self.players)) self.deck.shuffle() self.select_territories() self.place_initial_armies() @@ -535,15 +536,13 @@ class Engine (ID_CmpMixin): Currently just a notification hook. """ - self.log('Game over.') + self.log(EndGame(self.players)) for p in self.players: p.report(self.world, self.log) def play_turn(self, player): """Work through the phases of player's turn. """ - self.log("%s's turn (territory score: %s)" - % (player, [(p,len(list(p.territories(self.world)))) - for p in self.players])) + self.log(StartTurn(player, self.players, self.world)) self.play_cards_and_place_armies(player) captures = self.attack_and_fortify(player) self.end_of_turn_cards(player, captures) @@ -567,7 +566,7 @@ class Engine (ID_CmpMixin): break except PlayerError, error: continue - self.log('%s selects %s' % (p, t)) + self.log(SelectTerritory(p, t)) t.player = p t.armies = 1 # last player has no choice. @@ -611,7 +610,7 @@ class Engine (ID_CmpMixin): break except PlayerError, error: continue - self.log('%s places %s' % (player, placements)) + self.log(PlaceArmies(player, placements)) for terr_name,armies in placements.items(): t = self.world.territory_by_name(terr_name) t.armies += armies @@ -621,7 +620,7 @@ class Engine (ID_CmpMixin): cards.append(self.deck.pop()) player.hand.extend(cards) player.draw(self.world, self.log, cards) - self.log('%s dealt %d cards' % (player, number)) + self.log(DealtCards(player, number, len(self.deck))) def play_cards_and_place_armies(self, player, additional_armies=0): cards_required = len(player.hand) >= 5 error = None @@ -637,21 +636,17 @@ class Engine (ID_CmpMixin): except PlayerError, error: continue w_prod,w_terr_prod = self.world.production(player) - self.log('%s earned %d armies from territories' % (player, w_prod)) - if c_prod > 0: - self.log('%s played %s, earning %d armies' - % (player, cards, c_prod+sum(c_terr_prod.values()))) + self.log(EarnsArmies(player, w_prod, w_terr_prod)) if cards != None: for c in cards: player.hand.remove(c) + self.log(PlayCards(player, cards, c_prod, c_terr_prod)) for terr,prod in c_terr_prod.items(): if terr in w_terr_prod: w_terr_prod[terr] += prod else: w_terr_prod[terr] = prod self.world.place_territory_production(w_terr_prod) - if len(w_terr_prod) > 0: - self.log('%s was required to place %s' % (player, w_terr_prod)) armies = w_prod + c_prod self.player_place_armies(player, armies, armies) def attack_and_fortify(self, player): @@ -703,29 +698,25 @@ class Engine (ID_CmpMixin): if armies >= source.armies: raise PlayerError('%s attacking %s with %d armies, but only %d are available.' % (source, target, armies, source.armies-1)) - a_dice = sorted([random.randint(1, 6) for i in range(armies)], + s_dice = sorted([random.randint(1, 6) for i in range(armies)], reverse=True) t_dice = sorted([random.randint(1, 6) for i in range(min(2, target.armies))], reverse=True) - a_dead = 0 + s_dead = 0 t_dead = 0 - for a,d in zip(a_dice, t_dice): + for a,d in zip(s_dice, t_dice): if d >= a: - a_dead += 1 + s_dead += 1 else: t_dead += 1 - source.armies -= a_dead + source.armies -= s_dead target.armies -= t_dead if target.armies == 0: - self.takeover(source, target, remaining_attackers=armies-a_dead) - self.log('%s conquered %s from %s with %d:%d. Deaths %d:%d. Remaining %d:%d' - % (source.player, target, source, armies, len(t_dice), - a_dead, t_dead, source.armies, target.armies)) + self.takeover(source, target, remaining_attackers=armies-s_dead) + self.log(Conquer(source, target, s_dice, t_dice, s_dead, t_dead)) assert target.armies > 0, target return True - self.log('%s attacked %s from %s with %d:%d. Deaths %d:%d. Remaining %d:%d' \ - % (source.player, target, source, armies, len(t_dice), - a_dead, t_dead, source.armies, target.armies)) + self.log(Attack(source, target, s_dice, t_dice, s_dead, t_dead)) assert target.armies > 0, target return False def takeover(self, source, target, remaining_attackers): @@ -753,7 +744,7 @@ class Engine (ID_CmpMixin): if len(self.living_players()) > 1: while len(killer.hand) > 5: self.play_cards_and_place_armies(killer) - self.log('%s killed by %s' % (player, killer)) + self.log(Killed(player, killer)) if len(self.living_players()) > 1: player.report(self.world, self.log) # else the game is over, and killed will hear about this then. diff --git a/pyrisk/log.py b/pyrisk/log.py index e795374..b629616 100644 --- a/pyrisk/log.py +++ b/pyrisk/log.py @@ -17,6 +17,146 @@ """Define Logger class for saving and formatting game activity. """ +class Event (object): + """Base class for logged actions. + """ + def __init__(self, message): + self.message = message + def __str__(self): + return self.message + def __repr__(self): + return self.__str__() + +class Announcement (Event): + """A statement about game status. + """ + +class BeginGame (Announcement): + def __init__(self, players=[]): + # perhaps copy the list of players? + self.players = players + Announcement.__init__(self, 'Game started with %s' % self.players) + +class EndGame (Announcement): + def __init__(self, players=[]): + self.players = players + Announcement.__init__(self, 'Game over') + # perhaps add turns-survived ranking + +class Killed (Announcement): + def __init__(self, player, killer): + self.player = player + self.killer = killer + Announcement.__init__( + self, '%s killed by %s' % (self.player, self.killer)) + + +class StartTurn (Announcement): + def __init__(self, player, players=[], world=None): + self.player = player + self.players = players + self.world = world + Announcement.__init__( + self, + "%s's turn (territory score: %s)" + % (self.player, + [(p,len(list(p.territories(self.world)))) + for p in self.players])) + +class DealtCards (Announcement): + """A player recieves cards from the deck. + """ + def __init__(self, player, num_cards, num_remaining): + self.player = player + self.num_cards = num_cards + self.num_remaining = num_remaining + Announcement.__init__( + self, + '%s dealt %d cards (%d remaining)' + % (self.player, self.num_cards, self.num_remaining)) + +class EarnsArmies (Announcement): + """A player recieves armies. + """ + def __init__(self, player, prod=0, terr_prod={}, source='territories'): + self.player = player + self.prod = prod + self.terr_prod = terr_prod + self.source = source + if len(terr_prod) == 0: + msg = 'earns %d free armies from %s' % (self.prod, self.source) + elif self.prod == 0: + msg = 'earns %s territory armies from %s' % (self.terr_prod, self.source) + else: + msg = 'earns %d free armies and %s territory armies from %s' \ + % (self.prod, self.terr_prod, self.source) + Announcement.__init__(self, msg) + + +class PlayerAction (Event): + """Report a player decision and its result. + """ + def __init__(self, player, message): + Event.__init__(self, message) + self.player = player + def __str__(self): + return '%s: %s' % (self.player, self.message) + +class SelectTerritory (PlayerAction): + def __init__(self, player, territory): + self.territory = territory + PlayerAction.__init__( + self, player, 'selects %s' % self.territory) + +class PlaceArmies (PlayerAction): + def __init__(self, player, placements): + self.placements = placements + PlayerAction.__init__( + self, player, 'places %s' % self.placements) + +class PlayCards (PlayerAction, EarnsArmies): + def __init__(self, player, cards, prod=0, terr_prod={}, source='cards'): + self.cards = cards + EarnsArmies.__init__( + self, player, prod, terr_prod, source) + msg = self.message + PlayerAction.__init__(self, player, 'plays %s' % self.cards) + self.message += ', %s' % msg + +class MoveArmies (PlayerAction): + def __init__(self, player, source, target, armies): + self.source = source + self.target = target + self.armies = armies + PlayerAction.__init__( + self, player, 'moves %d from %s to %s' % (armies, source, target)) + +class Attack (MoveArmies): + def __init__(self, source, target, s_dice, t_dice, s_dead, t_dead, armies=None): + if armies == None: + armies = len(s_dice) + MoveArmies.__init__(self, source.player, source, target, armies) + self.s_dice = s_dice + self.t_dice = t_dice + self.s_dead = s_dead + self.t_dead = t_dead + self.message = 'attacked %s from %s with %d:%d. Deaths %d:%d. Remaining %d:%d' \ + % (self.target, self.source, len(self.s_dice), len(self.t_dice), + self.s_dead, self.t_dead, + self.source.armies, self.target.armies) + +class Conquer (Attack): + def __init__(self, source, target, s_dice, t_dice, s_dead, t_dead): + Attack.__init__(self, source, target, s_dice, t_dice, s_dead, t_dead, + armies=target.armies) + self.message = self.message.replace('attacked', 'conquered') + +class Fortify (MoveArmies): + def __init__(self, *arg, **kwarg): + MoveArmies.__init__(self, *arg, **kwarg) + self.message = 'fortifies %s from %s with %d' \ + % (self.target, self.source, self.armies) + class Logger (list): """Log messages generated by risk.base.Engine.