"""
pass
def select_territory(self, world, log):
- """Return the selected territory
+ """Return the selected territory's name.
"""
free_territories = [t for t in world.territories() if t.player == None]
return random.sample(free_territories, 1)[0].name
source.armies -= remaining_attackers
target.armies += remaining_attackers
target.player = source.player
- support = source.player.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))
- source.armies -= support
- target.armies += support
+ if source.armies > 1:
+ support = source.player.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))
+ source.armies -= support
+ target.armies += support
def player_killed(self, player, killer):
player.alive = False
killer.hand.extend(player.hand)
return failures
def random_game():
+ from player.email import EmailPlayer
world = generate_earth()
- players = [Player('Alice'), Player('Bob'), Player('Charlie')]
+ players = [EmailPlayer('Alice', 'alice@example.com', 'server@example.com'),
+ Player('Bob'), Player('Charlie')]
e = Engine(world, players)
e.run()
--- /dev/null
+"""An email interface for players.
+"""
+
+from ..base import Player
+
+class EmailPlayer (Player):
+ """Human Player with an email interface.
+
+ TODO: details on procmail setup
+ """
+ def __init__(self, name, address, return_address):
+ Player.__init__(self, name)
+ self.address = address
+ self.return_address = return_address
+ def _send_email(self, world, log, subject, body):
+ wpart = self._world_part(world)
+ lpart = self._world_part(log)
+ print 'Subject: %s\n\n%s\n' % (subject, body)
+ def _get_email(self):
+ body = raw_input('response? ')
+ body = body.replace(r'\n', '\n')
+ if len(body) == 0 or body[-1] != '\n':
+ body += '\n'
+ return body
+ def _world_part(self, world):
+ pass
+ def _log_part(self, log):
+ pass
+ 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 action family. The action commands in
+ Player subclasses can notify the player (possibly by calling
+ report internally) if they feel so inclined.
+
+ See also
+ --------
+ draw - another notification-only method
+ """
+ self._send_email(world, log, 'Report', Player.report())
+ def draw(self, world, log, cards=[]):
+ """Only called if you earned a new card (or cards).
+
+ See also
+ --------
+ report - another notification-only method
+ """
+ Player.draw(self, world, log, cards)
+ body = ['New cards:']
+ body.extend([' %s' % c for c in cards])
+ body = ['Current Hand:']
+ for c in self.hand:
+ if c.territory != None and c.territory.player == self:
+ body.append(' %s (owned)' % c)
+ else:
+ body.append(' %s' % c)
+ self._send_email(world, log, 'Drawing cards', '\n'.join(body))
+ def select_territory(self, world, log):
+ """Return the selected territory's name.
+ """
+ body = [
+ 'Reply with first line of the body of your email set',
+ 'to the name (long or short, case insenitive) of the',
+ 'territory you wish to occupy. Available territories',
+ 'are:']
+ for t in world.territories():
+ if t.player == None:
+ body.append(' %s' % t)
+ self._send_email(world, log, 'Select territory', '\n'.join(body))
+ body = self._get_email()
+ name = body.splitlines()[0].strip()
+ return name
+ def play_cards(self, world, log, play_required=True):
+ """Decide whether or not to turn in a set of cards.
+
+ Return a list of cards to turn in or None. If play_required
+ is True, you *must* play.
+ """
+ possibles = list(self.hand.possible())
+ if len(possibles) == 0:
+ return None
+ subject = 'Play cards'
+ if play_required == True:
+ subject += ' (required)'
+ body = [
+ 'Reply with first line of the body of your email set',
+ 'to the number of the set you wish to play (leave body',
+ 'blank to pass). Available sets are:']
+ for i,h in enumerate(possibles):
+ body.append(' %d: %s' % (i, h))
+ self._send_email(world, log, subject, '\n'.join(body))
+ body = self._get_email()
+ text = body.splitlines()[0].strip()
+ if text == '':
+ return None
+ return possibles[int(text)]
+ def place_armies(self, world, log, remaining=1, this_round=1):
+ """Both during setup and before each turn.
+
+ Return {territory_name: num_armies, ...}
+ """
+ subject = 'Place %d of %d armies' % (this_round, remaining)
+ body = [
+ 'You can place %d armies this round (out of %d in'
+ % (this_round, remaining),
+ 'this phase).',
+ '',
+ 'Reply with first line(s) of the body of your email set',
+ 'to "<number_of_armies> : <territory_name>" followed by',
+ 'a blank line. For example',
+ ' 1 : gbr',
+ ' 4 : indo',
+ ' ...',
+ ''
+ 'Your current disposition is:']
+ for t in self.territories(world):
+ body.append(' %d : %s' % (t.armies, t))
+ self._send_email(world, log, subject, '\n'.join(body))
+ body = self._get_email()
+ placements = {}
+ for line in body.splitlines():
+ line = line.strip()
+ if len(line) == 0:
+ break
+ armies,terr_name = [x.strip() for x in line.split(':')]
+ placements[terr_name] = int(armies)
+ return placements
+ def attack_and_fortify(self, world, log, mode='attack'):
+ """Return list of (source, target, armies) tuples. Place None
+ in the list to end this phase.
+ """
+ if mode == 'attack':
+ subject = 'Attack and fortify'
+ body = [
+ 'You can attack as many times as you like, and fortify',
+ 'once at the end of the round. Reply with first line(s)',
+ 'of the body of your email set to',
+ ' "<source_name> : <target_name> : <number_of_armies>',
+ 'When you are done attacking or in place of a',
+ 'fortification, insert the line "Pass". For example',
+ ' gbr : ice : 3',
+ ' jap : chi : 3',
+ ' jap : chi : 3',
+ ' Pass',
+ ' gbr : seu : 7',
+ ' ',
+ 'or',
+ ' jap : chi : 3',
+ ' Pass',
+ ' Pass',
+ ' ']
+ else:
+ assert mode == 'fortify', mode
+ subject = 'Fortify'
+ body = [
+ 'You can fortify once. Reply with first line of the',
+ 'body of your email set to',
+ ' "<source_name> : <target_name> : <number_of_armies>',
+ 'Or, if you choose to pass, either a blank line or',
+ '"Pass". For example',
+ ' gbr : seu : 7',
+ 'or',
+ ' ',
+ 'or',
+ ' Pass']
+ self._send_email(world, log, subject, '\n'.join(body))
+ body = self._get_email()
+ if mode == 'fortify':
+ return [self._parse_attack_or_fortify_line(
+ body.splitlines()[0], mode)]
+ actions = []
+ pass_count = 0
+ for line in body.splitlines():
+ action = self._parse_attack_or_fortify_line(line, mode)
+ if action == None:
+ pass_count += 1
+ if pass_count == 2:
+ break
+ actions.append(action)
+ return actions
+ def _parse_attack_or_fortify_line(self, line, mode):
+ line = line.strip()
+ if line.count(':') == 2:
+ fields = [x.strip() for x in line.split(':')]
+ fields[2] = int(fields[2])
+ return fields
+ elif line.lower() == 'pass' \
+ or (mode == 'fortify' and len(line) == 0):
+ return None
+ def support_attack(self, world, log, source, target):
+ """Follow up on a conquest by moving additional armies.
+ """
+ subject = 'Support conquest of %s by %s' % (target, source)
+ body = [
+ 'You can move up to %d of the %d armies remaining on'
+ % (source.armies - 1, source.armies),
+ '%s to %s following your conquest.'
+ % (source, target),
+ '',
+ 'Reply with first line(s) of the body of your email set',
+ 'to "<number_of_armies>", or leave the first line blank',
+ 'to pass.']
+ self._send_email(world, log, subject, '\n'.join(body))
+ body = self._get_email()
+ text = body.splitlines()[0].strip()
+ if text == '':
+ return 0
+ return int(text)