import random
-from .log import Logger
+from .log import Logger, BeginGame, EndGame, Killed, StartTurn, DealtCards, \
+ EarnsArmies, SelectTerritory, PlaceArmies, PlayCards, Attack, Conquer
class PlayerError (Exception):
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).
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()
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)
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.
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
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
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):
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):
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.
"""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.