PageRenderTime 577ms CodeModel.GetById 52ms RepoModel.GetById 0ms app.codeStats 0ms

/ltg/src/iter_bot.py

https://github.com/cail/icfpc2011-tbd
Python | 189 lines | 152 code | 37 blank | 0 comment | 37 complexity | bf1d79170ae802ce23ab2f4d1f46adee MD5 | raw file
  1. from itertools import *
  2. import random
  3. import sys
  4. import logging
  5. from rules import cards
  6. from simple_bot import Bot
  7. from terms import lambda_to_sequence, parse_sequence, apply_sequences
  8. class Fail(Exception):
  9. pass
  10. class IterBot(Bot):
  11. def __init__(self):
  12. self.it = None
  13. self.log = logging.getLogger('IdleBot')
  14. def get_strategy(self):
  15. 'return iterable'
  16. raise NotImplementedError()
  17. def choose_move(self):
  18. if self.it is None:
  19. self.it = iter(self.get_strategy())
  20. return next(self.it, ('l', 42, cards.zero))
  21. def fail_safe(self, strat, retries=1):
  22. for i in range(retries):
  23. try:
  24. for move in strat:
  25. yield move
  26. return
  27. except Fail as e:
  28. if i == retries-1:
  29. self.log.warning('fail {0} - abort'.format(e))
  30. else:
  31. self.log.warning('fail {0} - retry'.format(e))
  32. def run_seq(self, slot, sequence):
  33. if isinstance(sequence, str):
  34. sequence = parse_sequence(sequence)
  35. prop = self.game.proponent
  36. for card, dir in sequence:
  37. if prop.vitalities[slot] <= 0:
  38. raise Fail('run_seq on dead slot {0}'.format(slot))
  39. yield (dir, slot, card)
  40. def run_term(self, slot, term, dirty=False):
  41. prop = self.game.proponent
  42. if dirty:
  43. if prop.vitalities[slot] <= 0:
  44. raise Fail('run_seq on dead slot {0}'.format(slot))
  45. yield ('l', slot, cards.put)
  46. for card, dir in lambda_to_sequence(term):
  47. if prop.vitalities[slot] <= 0:
  48. raise Fail('run_seq on dead slot {0}'.format(slot))
  49. yield (dir, slot, card)
  50. def assign_term(self, slot, term):
  51. prop = self.game.proponent
  52. return self.run_term(slot, term, dirty=(prop.values[slot] != cards.I))
  53. def apply_to(self, f, arg):
  54. 'slot[f] = slot[f](arg)'
  55. return self.run_seq(f, apply_sequences([], lambda_to_sequence(arg)))
  56. def fresh_slot(self, rng=range(256)):
  57. prop = self.game.proponent
  58. r = list(rng)
  59. random.shuffle(r)
  60. for slot in r:
  61. if prop.vitalities[slot] > 0 and prop.values[slot] == cards.I:
  62. return slot
  63. raise Fail("Can't find fresh slot among {0}".format(rng))
  64. def revive_reflex(self, strat, slots):
  65. prop = self.game.proponent
  66. for move in strat:
  67. yield move
  68. for slot in slots:
  69. if prop.vitalities[slot] <= 0:
  70. t = self.fresh_slot(range(10, 250))
  71. self.log.info('revive reflex {0} at {1}'.format(slot, t))
  72. for m in self.run_term(t, '(revive {0})'.format(slot)):
  73. yield m
  74. def coup_de_gras_reflex(self, strat, slots):
  75. opp = self.game.opponent
  76. for move in strat:
  77. yield move
  78. for slot in slots:
  79. if opp.vitalities[slot] == 1:
  80. t = self.fresh_slot(range(10, 250))
  81. self.log.info('coup de gras reflex {0} at {1}'.format(slot, t))
  82. for m in self.run_term(t, '(dec {0})'.format(255-slot)):
  83. yield m
  84. class ZombieRush(IterBot):
  85. def __init__(self, handicap=0):
  86. self.handicap = handicap
  87. super(type(self), self).__init__()
  88. def ensure_zombie_heal_possible(self, donor, acceptor, strength):
  89. vits = self.game.opponent.vitalities
  90. if vits[donor] < strength:
  91. raise Fail('acceptor is too weak')
  92. if donor == acceptor:
  93. if vits[acceptor] > strength+strength*11//10:
  94. raise Fail('not enough help to kill donor by himself')
  95. else:
  96. if vits[acceptor] > strength*11//10:
  97. raise Fail('not enough help to kill donor')
  98. def rush(self):
  99. self.log.info('rush')
  100. strength = 9216 # because this number will also be used for Z
  101. kill = chain(
  102. self.assign_term(0, str(strength)),
  103. self.assign_term(1, r'(\source. attack source 0 (get 0))'),
  104. self.assign_term(2, r'(get 1)'),
  105. self.apply_to(1,'0'), # attack 0 -> 0
  106. self.apply_to(2, '1'), # attack 1 -> 0
  107. )
  108. for move in kill:
  109. yield move
  110. donor = acceptor = 0
  111. zombie = chain(
  112. self.assign_term(1, '(K (help {0} {1}))'.format(donor, acceptor)),
  113. self.run_seq(0, 'K l'),
  114. self.assign_term(2, '(S (get 1) (get 0)'),
  115. self.assign_term(3, '(zombie 0 (get 2))'),
  116. )
  117. for move in zombie:
  118. self.ensure_zombie_heal_possible(donor, acceptor, strength)
  119. yield move
  120. def generic_zombie(self):
  121. vits = self.game.opponent.vitalities
  122. alive = [i for i in range(256) if vits[i] > 0]
  123. alive += [0, 0]
  124. donor, acceptor = alive[:2]
  125. if vits[donor] < vits[acceptor]:
  126. donor, acceptor = acceptor, donor
  127. strength = vits[donor]
  128. self.log.info('generic zombie (heal {0} {1} {2})'.
  129. format(donor, acceptor, strength))
  130. strat = chain(
  131. self.assign_term(0, str(acceptor)),
  132. self.assign_term(1, '(help {0})'.format(donor)),
  133. self.apply_to(1, '(get 0)'),
  134. self.run_seq(1, 'K l, S l'),
  135. self.assign_term(0, '(K {0})'.format(strength)),
  136. self.apply_to(1, '(get 0)'),
  137. self.assign_term(3, '(zombie 0 (get 1))'),
  138. )
  139. for move in strat:
  140. self.ensure_zombie_heal_possible(donor, acceptor, strength)
  141. yield move
  142. def get_strategy(self):
  143. self.log = logging.getLogger('IterBot.Player{0}'.format(self.game.half_moves%2))
  144. self.log.info('creating strategy')
  145. strat = chain(
  146. [('l', 0, cards.I)]*self.handicap,
  147. self.fail_safe(self.rush()),
  148. chain(*(self.fail_safe(self.generic_zombie()) for _ in xrange(1000))),
  149. )
  150. strat = self.coup_de_gras_reflex(strat, [255])
  151. strat = self.revive_reflex(strat, range(4))
  152. return strat