Only call Player.report (prev. Player.phase_report) as final contact
[pyrisk.git] / risk / base.py
index 8d8ef33467724fef9e3812f73dfbd72c5b52c5f1..b63e6c2540e14a0b8a3bb4b034adea2b0a73bfed 100644 (file)
@@ -251,26 +251,34 @@ class Player (ID_CmpMixin):
                 if neighbor.player != self:
                     yield t
                     break
-    def phase_report(self, world, log):
+    def report(self, world, log):
+        """Send reports about death and game endings.
+
+        These events mark the end of contact and require no change in
+        player status or response, so they get a special command
+        seperate from the usual phase_* family.  The phase_* commands
+        in Player subclasses can notify the player (possibly by
+        calling report internally) if they feel so inclined.
+        """
         print 'Reporting for %s:\n  %s' \
             % (self, '\n  '.join(log[self._message_index:]))
         self._message_index = len(log)
-    def phase_select_territory(self, world):
+    def phase_select_territory(self, world, log):
         """Return the selected territory
         """
         free_territories = [t for t in world.territories() if t.player == None]
         return random.sample(free_territories, 1)[0]
-    def phase_play_cards(self, world, play_required=True):
+    def phase_play_cards(self, world, log, play_required=True):
         if play_required == True:
             return random.sample(list(self.hand.possible()), 1)[0]
-    def phase_place_armies(self, world, remaining=1, this_round=1):
+    def phase_place_armies(self, world, log, remaining=1, this_round=1):
         """Both during setup and before each turn.
 
         Return {territory_name: num_armies, ...}
         """
         t = random.sample(list(self.border_territories(world)), 1)[0]
         return {t.name: this_round}
-    def phase_attack(self, world):
+    def phase_attack(self, world, log):
         """Return list of (source, target, armies) tuples.  Place None
         in the list to end this phase.
         """
@@ -284,14 +292,14 @@ class Player (ID_CmpMixin):
         if len(possible_attacks) == 0:
             return [None]
         return random.sample(possible_attacks, 1) # + [None]
-    def phase_support_attack(self, world, source, target):
+    def phase_support_attack(self, world, log, source, target):
         return source.armies-1
-    def phase_fortify(self, world):
+    def phase_fortify(self, world, log):
         """Return list of (source, target, armies) tuples.  Place None
         in the list to end this phase.
         """
         return [None]
-    def phase_draw(self, cards=[]):
+    def phase_draw(self, world, log, cards=[]):
         """Only called if you earned a new card (or cards)"""
         self.hand.extend(cards)
 
@@ -311,7 +319,7 @@ class Engine (ID_CmpMixin):
         self.play()
         self.log('Game over.')
         for p in self.players:
-            p.phase_report(self.world, self.log)
+            p.report(self.world, self.log)
     def setup(self):
         for p in self.players:
             p.alive = True
@@ -332,18 +340,16 @@ class Engine (ID_CmpMixin):
         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:
-            player.phase_draw([self.deck.pop()])
+            player.phase_draw(self.world, self.log, [self.deck.pop()])
     def select_territories(self):
         for t in self.world.territories():
             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)
+            t = p.phase_select_territory(self.world, self.log)
             if t.player != None:
                 raise PlayerError('Cannot select %s owned by %s'
                                   % (t, t.player))
@@ -358,16 +364,14 @@ 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):
-        placements = player.phase_place_armies(self.world, remaining, this_round)
+        placements = player.phase_place_armies(self.world, self.log, remaining, this_round)
         if sum(placements.values()) != this_round:
             raise PlayerError('Placing more than %d armies' % this_round)
         for ter_name,armies in placements.items():
@@ -387,12 +391,12 @@ class Engine (ID_CmpMixin):
             cards = []
             for i in range(3):
                 cards.append(self.deck.pop())
-            p.phase_draw(cards)
+            p.phase_draw(self.world, self.log, cards)
         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(
-            self.world, play_required=cards_required)
+            self.world, self.log, play_required=cards_required)
         if cards_required == True and cards == None:
             raise PlayerError('You have %d >= 5 cards in your hand, you must play'
                               % len(player.hand))
@@ -418,7 +422,7 @@ class Engine (ID_CmpMixin):
     def attack_phase(self, player):
         captures = 0
         while True:
-            attacks = player.phase_attack(self.world)
+            attacks = player.phase_attack(self.world, self.log)
             for attack in attacks:
                 if attack == None:
                     return captures
@@ -470,7 +474,7 @@ class Engine (ID_CmpMixin):
         source.armies -= remaining_attackers
         target.armies += remaining_attackers
         target.player = source.player
-        support = source.player.phase_support_attack(self.world, source, target)
+        support = source.player.phase_support_attack(self.world, self.log, source, target)
         if support < 0 or support >= source.armies:
             raise PlayerError('Cannot support from %s to %s with %d armies, only %d available'
                               % (source, target, support, source.armies-1))
@@ -484,7 +488,7 @@ class Engine (ID_CmpMixin):
                 self.play_cards_and_place_armies(killer)
         self.log('%s killed by %s' % (player, killer))
         if len(self.living_players()) > 1:
-            player.phase_report(self.world, self.log)
+            player.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]