import random
from .log import Logger, BeginGame, EndGame, Killed, StartTurn, DealtCards, \
- EarnsArmies, SelectTerritory, PlaceArmies, PlayCards, Attack, Conquer
+ EarnsArmies, SelectTerritory, PlaceArmies, PlayCards, Attack, Conquer, \
+ Fortify
class PlayerError (Exception):
if id(t) == id(other):
return True
return False
+ def border(self):
+ for t in self:
+ if t.player != self.player:
+ return True
+ return False
class Continent (NameMixin, ID_CmpMixin, list):
"""A group of Territories.
for t in world.territories():
if t.player == self:
yield t
- def border_territories(self, world):
- """Iterate through all territories owned by this player which
- border another player's territories.
- """
- for t in self.territories(world):
- for neighbor in t:
- if neighbor.player != self:
- yield t
- break
def report(self, world, log):
"""Send reports about death and game endings.
Return {territory_name: num_armies, ...}
"""
- t = random.sample(list(self.border_territories(world)), 1)[0]
- return {t.name: this_round}
+ terr = random.sample([t for t in self.territories(world)
+ if t.border()], 1)[0]
+ return {terr.name: this_round}
def attack_and_fortify(self, world, log, error=None,
mode='attack'):
"""Return list of (source, target, armies) tuples. Place None
"""
assert mode != 'fortify', mode
possible_attacks = []
- for t in self.border_territories(world):
+ for t in self.territories(world):
+ if not t.border():
+ continue
if t.armies <= 3: #1: # be more conservative, only attack with 3 dice
continue
targets = [border_t for border_t in t if border_t.player != self]
for tg in targets:
- possible_attacks.append((t.name, tg.name, min(3, t.armies-1)))
+ possible_attacks.append(
+ (t.name, tg.name, min(3, t.armies-1)))
if len(possible_attacks) == 0:
- return [None, None] # stop attack phase, then stop fortification phase
- return random.sample(possible_attacks, 1) # + [None]
+ fortifications = []
+ for t in self.territories(world):
+ if t.border() or t.armies == 1:
+ continue
+ targets = list(t)
+ for tg in targets:
+ fortifications.append(
+ (t.name, tg.name, t.armies-1))
+ if len(fortifications) > 1:
+ fortifications = random.sample(fortifications, 1)
+ # stop attack phase, fortify, stop fortification phase
+ return [None] + fortifications + [None]
+ return random.sample(possible_attacks, 1)
def support_attack(self, world, log, error,
source, target):
"""Follow up on a conquest by moving additional armies.
target = self.world.territory_by_name(target_name)
except KeyError:
raise PlayerError('Invalid territory "%s"' % targer_name)
- if not source.borders(target):
- raise PlayerError('Cannot reach %s from %s to %s'
- % (target, source, mode))
if mode == 'attack':
tplayer = target.player
capture = self.attack(source, target, armies)
else:
assert mode == 'fortify', mode
self.fortify(source, target, armies)
+ return captures # only allow one fortification
except PlayerError, error:
continue
def attack(self, source, target, armies):
if armies >= source.armies:
raise PlayerError('%s attacking %s with %d armies, but only %d are available.'
% (source, target, armies, source.armies-1))
+ if not source.borders(target):
+ raise PlayerError('Cannot reach %s from %s to attack'
+ % (target, source))
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))],
continue
source.armies -= support
target.armies += support
+ def fortify(self, source, target, armies):
+ if source.player != target.player:
+ raise PlayerError('%s (%s) cannot fortifiy %s (%s).'
+ % (source, source.player, target, target.player))
+ if armies == 0:
+ return
+ if armies >= source.armies:
+ raise PlayerError('%s fortifying %s with %d armies, but only %d are available.'
+ % (source, target, armies, source.armies-1))
+ if not source.borders(target):
+ raise PlayerError('Cannot reach %s from %s to fortify'
+ % (target, source))
+ source.armies -= armies
+ target.armies += armies
+ self.log(Fortify(source, target, armies))
def player_killed(self, player, killer):
player.alive = False
killer.hand.extend(player.hand)