From 7113581c2ca444b0d2e8e2deb4bcbeb76856260e Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 25 Mar 2010 19:30:21 -0400 Subject: [PATCH] Added player-specific log reporting --- risk/base.py | 83 ++++++++++++++++++++++++++++++++-------------------- risk/log.py | 6 ++++ 2 files changed, 58 insertions(+), 31 deletions(-) mode change 100755 => 100644 risk/base.py create mode 100644 risk/log.py diff --git a/risk/base.py b/risk/base.py old mode 100755 new mode 100644 index dbff83b..8d8ef33 --- a/risk/base.py +++ b/risk/base.py @@ -1,9 +1,9 @@ -#!/usr/bin/python -# -# A Python engine for Risk-like games +"""A Python engine for Risk-like games +""" import random +from .log import Logger VERSION='0.1' @@ -124,7 +124,7 @@ class World (list, ID_CmpMixin): raise ValueError('%s shared by %s and %s' % (t.short_name, ts[t.short_name], t)) def production(self, player): - ts = list(player.territories(world)) + ts = list(player.territories(self)) production = max(3, len(ts) / 3) continents = set([t.continent.name for t in ts]) for c_name in continents: @@ -240,6 +240,7 @@ class Player (ID_CmpMixin): ID_CmpMixin.__init__(self) self.alive = True self.hand = Hand() + self._message_index = 0 def territories(self, world): for t in world.territories(): if t.player == self: @@ -250,6 +251,10 @@ class Player (ID_CmpMixin): if neighbor.player != self: yield t break + def phase_report(self, world, log): + print 'Reporting for %s:\n %s' \ + % (self, '\n '.join(log[self._message_index:])) + self._message_index = len(log) def phase_select_territory(self, world): """Return the selected territory """ @@ -291,10 +296,11 @@ class Player (ID_CmpMixin): self.hand.extend(cards) class Engine (ID_CmpMixin): - def __init__(self, world, players, deck_class=Deck): + def __init__(self, world, players, deck_class=Deck, logger_class=Logger): ID_CmpMixin.__init__(self) self.world = world self.deck = deck_class(world.territories()) + self.log = logger_class() self.players = players def __str__(self): return '' % (self.world, self.players) @@ -303,10 +309,13 @@ class Engine (ID_CmpMixin): def run(self): self.setup() self.play() + self.log('Game over.') + for p in self.players: + p.phase_report(self.world, self.log) def setup(self): for p in self.players: p.alive = True - random.shuffle(players) + random.shuffle(self.players) self.select_territories() self.place_initial_armies() self.deal() @@ -320,9 +329,10 @@ class Engine (ID_CmpMixin): active_player = (active_player + 1) % living turn += 1 def play_turn(self, player): - print "%s's turn (territory score: %s)" \ - % (player, [(p,len(list(p.territories(self.world)))) - for p in self.players]) + self.log("%s's turn (territory score: %s)" + % (player, [(p,len(list(p.territories(self.world)))) + for p in self.players])) + player.phase_report(self.world, self.log) self.play_cards_and_place_armies(player) captures = self.attack_phase(player) if captures > 0 and len(self.deck) > 0 and len(self.living_players()) > 1: @@ -332,11 +342,12 @@ class Engine (ID_CmpMixin): t.player = None for i in range(len(list(self.world.territories()))): p = self.players[i % len(self.players)] + p.phase_report(self.world, self.log) t = p.phase_select_territory(self.world) if t.player != None: raise PlayerError('Cannot select %s owned by %s' % (t, t.player)) - print '%s selects %s' % (p, t) + self.log('%s selects %s' % (p, t)) t.player = p t.armies = 1 def place_initial_armies(self): @@ -347,10 +358,12 @@ class Engine (ID_CmpMixin): assert min(s) == max(s)-1, 'Min %d, max %d' % (min(s), max(s)) for p,placed in zip(self.players, already_placed): if placed == min(s): + p.phase_report(self.world, self.log) self.player_place_armies(p, remaining, 1) remaining = self.world.initial_armies[len(self.players)] - max(s) while remaining > 0: for p in self.players: + p.phase_report(self.world, self.log) self.player_place_armies(p, remaining, 1) remaining -= 1 def player_place_armies(self, player, remaining=1, this_round=1): @@ -358,16 +371,16 @@ class Engine (ID_CmpMixin): if sum(placements.values()) != this_round: raise PlayerError('Placing more than %d armies' % this_round) for ter_name,armies in placements.items(): - t = world.territory_by_name(ter_name) + t = self.world.territory_by_name(ter_name) if t.player != player: raise PlayerError('Placing armies in %s owned by %s' % (t, t.player)) if armies < 0: raise PlayerError('Placing a negative number of armies (%d) in %s' % (armies, t)) - print '%s places %s' % (player, placements) + self.log('%s places %s' % (player, placements)) for ter_name,armies in placements.items(): - t = world.territory_by_name(ter_name) + t = self.world.territory_by_name(ter_name) t.armies += armies def deal(self): for p in self.players: @@ -375,7 +388,7 @@ class Engine (ID_CmpMixin): for i in range(3): cards.append(self.deck.pop()) p.phase_draw(cards) - print 'Initial hands dealt' + self.log('Initial hands dealt') def play_cards_and_place_armies(self, player, additional_armies=0): cards_required = len(player.hand) >= 5 cards = player.phase_play_cards( @@ -384,11 +397,11 @@ class Engine (ID_CmpMixin): raise PlayerError('You have %d >= 5 cards in your hand, you must play' % len(player.hand)) w_prod,w_terr_prod = self.world.production(player) - print '%s earned %d armies from territories' % (player, w_prod) + self.log('%s earned %d armies from territories' % (player, w_prod)) c_prod,c_terr_prod = self.deck.production(player, cards) if c_prod > 0: - print '%s played %s, earning %d armies' \ - % (player, cards, c_prod+sum(c_terr_prod.values())) + self.log('%s played %s, earning %d armies' + % (player, cards, c_prod+sum(c_terr_prod.values()))) if cards != None: for c in cards: player.hand.remove(c) @@ -399,7 +412,7 @@ class Engine (ID_CmpMixin): w_terr_prod[terr] = prod self.world.place_territory_production(w_terr_prod) if len(w_terr_prod) > 0: - print '%s was required to place %s' % (player, w_terr_prod) + 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_phase(self, player): @@ -443,14 +456,14 @@ class Engine (ID_CmpMixin): target.armies -= t_dead if target.armies == 0: self.takeover(source, target, remaining_attackers=armies-a_dead) - print '%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.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)) assert target.armies > 0, target return True - print '%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('%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)) assert target.armies > 0, target return False def takeover(self, source, target, remaining_attackers): @@ -469,7 +482,10 @@ class Engine (ID_CmpMixin): if len(self.living_players()) > 1: while len(killer.hand) > 5: self.play_cards_and_place_armies(killer) - print '%s killed by %s' % (player, killer) + self.log('%s killed by %s' % (player, killer)) + if len(self.living_players()) > 1: + player.phase_report(self.world, self.log) + # else the game is over, and killed will hear about this then. def living_players(self): return [p for p in self.players if p.alive == True] @@ -540,15 +556,20 @@ def generate_earth(): w._resolve_link_names() return w -if __name__ == '__main__': +def test(): import doctest - import sys - failures,tests = doctest.testmod() - if failures > 0: - sys.exit(1) + return failures +def random_game(): world = generate_earth() - players = [Player('A'), Player('B')] + players = [Player('Alice'), Player('Bob'), Player('Charlie')] e = Engine(world, players) e.run() + +if __name__ == '__main__': + import sys + failures = self.test() + if failures > 0: + sys.exit(1) + self.random_game() diff --git a/risk/log.py b/risk/log.py new file mode 100644 index 0000000..24a8e8e --- /dev/null +++ b/risk/log.py @@ -0,0 +1,6 @@ +"""Define Logger class for saving and formatting game activity. +""" + +class Logger (list): + def __call__(self, message): + self.append(message) -- 2.26.2