Advertisement
Guest User

Untitled

a guest
Jan 19th, 2020
86
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 12.14 KB | None | 0 0
  1. import math
  2.  
  3. # BUGS:
  4. #  - if someone is dead, don't let heals work on them
  5. #  - some off by ones
  6.  
  7. TARGET_ALL = 1
  8. TARGET_LINE = 2
  9.  
  10. # For defence changes, subtract from 1, and multiply by it.
  11.  
  12. dragon_dir = 1
  13. dragon = {
  14.   'name': 'dragon',
  15.   'ai': map(lambda s: s.lower(), ['Claw', 'Heatwave', 'Claw', 'Claw', 'Claw', 'Claw', 'Claw', 'Claw', 'Move', 'Claw', 'Heatwave', 'Claw', 'Claw', 'Claw', 'Claw', 'Claw', 'Move', 'Wait', 'Wait', 'Flamethrower']),
  16.   'moves': {
  17.     'claw': {'damage': 20, 'stack': ('claw', 5, 5)},
  18.     'heatwave': {'damage': 5, 'dps': (20, 3), 'target': TARGET_ALL},
  19.     'flamethrower': {'damage': 100, 'target': TARGET_LINE},
  20.     'boss': {'damage': 100, 'target': TARGET_ALL},
  21.     'move': {'y': lambda: dragon_dir},
  22.   },
  23.   'state': {
  24.     'hp': 4000,
  25.     'mana': 0,
  26.     'taunt': 0,
  27.     'dps': [],
  28.     'attack': [],
  29.     'defence': [],
  30.     'stack': {},
  31.     'y': 5,
  32.     'move': 0,
  33.     'boss_used': False,
  34.     'pending': None,
  35.     'pending_target': None,
  36.   },
  37.   'max_hp': 4000,
  38.   'max_mana': 0,
  39. }
  40.  
  41. knight = {
  42.   'name': 'knight',
  43.   'moves': {
  44.     'taunt': {'taunt': 5},
  45.     'strike': {'mana': -20, 'damage': 10},
  46.     'pierce': {'mana': 20, 'damage': 10, 'dps': (10, 5)},
  47.     'shieldwall': {'mana': 50, 'defence': (.5, 5), 'target': 'knight'},
  48.     'shieldbash': {'mana': 50, 'damage': 10, 'defence': (1.5, 5), 'target': 'dragon'},
  49.     'phalanx': {'cooldown': 100, 'defence': (0.0, 5), 'target': 'knight'},
  50.     'move:up': {'y': lambda: 1},
  51.     'move:down': {'y': lambda: -1},
  52.   },
  53.   'state': {
  54.     'hp': 400,
  55.     'mana': 0,
  56.     'dps': [],
  57.     'attack': [],
  58.     'defence': [],
  59.     'stack': {},
  60.     'y': 4,
  61.     'cooldown': {
  62.       'phalanx': 0,
  63.     },
  64.     'pending': None,
  65.     'pending_target': None,
  66.     'last_cast': {
  67.       'taunt': -1000,
  68.       'shieldbash': -1000,
  69.       'shieldwall': -1000,
  70.     }
  71.   },
  72.   'ai': [],
  73.   'max_hp': 400,
  74.   'max_mana': 100,
  75. }
  76.  
  77. priest = {
  78.   'name': 'priest',
  79.   'moves': {
  80.     'greaterheal': {'mana': 5, 'turns': 3, 'damage': -100},
  81.     'flashheal': {'mana': 5, 'damage': -35},
  82.     'regen': {'mana': 2, 'damage': -5, 'dps': (-5, 10)},
  83.     'hymn': {'mana': -10, 'turns': 2, 'damage': -15, 'target': TARGET_ALL},
  84.     'divineintervention': {'mana': 10, 'cooldown': 100, 'dps': (-100, 5), 'target': TARGET_ALL},
  85.     'move:up': {'y': lambda: 1},
  86.     'move:down': {'y': lambda: -1},
  87.   },
  88.   'state': {
  89.     'hp': 200,
  90.     'mana': 300,
  91.     'dps': [],
  92.     'attack': [],
  93.     'defence': [],
  94.     'stack': {},
  95.     'y': 3,
  96.     'cooldown': {
  97.       'divineintervention': 0,
  98.     },
  99.     'pending': None,
  100.     'pending_target': None,
  101.   },
  102.   'ai': [],
  103.   'max_hp': 200,
  104.   'max_mana': 300,
  105. }
  106.  
  107. wizard = {
  108.   'name': 'wizard',
  109.   'moves': {
  110.     'iceshard': {'mana': 4, 'turns': 2, 'damage': 20, 'stack': ('shard', 0, 3)},
  111.     'icespear': {'mana': 5, 'stack_damage': ('shard', 50)},
  112.     'frostbite': {'mana': 5, 'cooldown': 20, 'target': 'wizard', 'attack': (1.2, 10)},
  113.     'waterbarrier': {'mana': -5, 'cooldown': 20, 'defence': (0.8, 10)},
  114.     'deluge': {'mana': -20, 'cooldown': 100, 'damage': 200},
  115.     'move:up': {'y': lambda: 1},
  116.     'move:down': {'y': lambda: -1},
  117.   },
  118.   'state': {
  119.     'hp': 200,
  120.     'mana': 200,
  121.     'dps': [],
  122.     'attack': [],
  123.     'defence': [],
  124.     'stack': {},
  125.     'y': 5,
  126.     'cooldown': {
  127.       'deluge': 0,
  128.       'frostbite': 0,
  129.       'waterbarrier': 0,
  130.     },
  131.     'pending': None,
  132.     'pending_target': None,
  133.   },
  134.   'ai': [],
  135.   'max_hp': 200,
  136.   'max_mana': 200,
  137. }
  138.  
  139. def find_d(d, attack, defence):
  140.   n = d
  141.   for x, _ in attack + defence:
  142.     # Only buff damages and not heals.
  143.     if d > 0:
  144.       n += d * (x - 1.0)
  145.  
  146.   return n
  147.  
  148. def use(char, move_name, target=None):
  149.   s = char['state']
  150.   if s['hp'] <= 0:
  151.     print 'skipping as dead'
  152.     print char
  153.     return
  154.  
  155.   # Special case for wait.
  156.   if move_name == 'wait':
  157.     char['ai'].append(('wait', None))
  158.     print '  ', char['name'], 'wait'
  159.     return
  160.  
  161.   # Select move.
  162.   if move_name == 'pending':
  163.     m = s['pending']
  164.     pending_target = s['pending_target']
  165.     assert m['turns'] == 1
  166.     s['pending'] = None
  167.     s['pending_target'] = None
  168.  
  169.   else:
  170.     assert s['pending'] is None
  171.     assert s['pending_target'] is None
  172.     m = char['moves'][move_name]
  173.  
  174.   # Verify + reset cooldown.
  175.   if 'cooldown' in m:
  176.     assert s['cooldown'][move_name] == 0
  177.     s['cooldown'][move_name] = N * m['cooldown']
  178.  
  179.   # Only incur mana after pending move invoked.
  180.   if 'mana' in m and ('turns' not in m or m['turns'] <= 1):
  181.     assert s['mana'] >= m['mana']
  182.     s['mana'] -= m['mana']
  183.  
  184.   # Handle moves.
  185.   if 'y' in m:
  186.     s['y'] += m['y']()
  187.  
  188.   # Finding targets.
  189.   lookup = {
  190.     'wizard': wizard,
  191.     'priest': priest,
  192.     'knight': knight,
  193.     'dragon': dragon,
  194.   }
  195.   ts = []
  196.   t_name = None
  197.   if move_name == 'pending':
  198.     ts = pending_target
  199.   elif 'target' in m:
  200.     if m['target'] == TARGET_ALL:
  201.       ts = [knight, priest, wizard]
  202.  
  203.     elif m['target'] == TARGET_LINE:
  204.       # TODO - work out if this is correct.
  205.       if s['y'] <= 4:
  206.         ts = filter(lambda x: x['state']['y'] <= 4, [knight, priest, wizard])
  207.       else:
  208.         ts = filter(lambda x: x['state']['y'] >= 2, [knight, priest, wizard])
  209.     else:
  210.       ts = [lookup[m['target']]]
  211.       t_name = m['target']
  212.   else:
  213.     # Grab from argument instead.
  214.     assert target
  215.     ts = [lookup[target]]
  216.     t_name = lookup[target]['name']
  217.  
  218.   # Push move to log.
  219.   if char != dragon and move_name != 'pending':
  220.     char['ai'].append((move_name, t_name))
  221.   print '  ', char['name'], move_name, t_name
  222.  
  223.   # Defer multi-turn actions.
  224.   if 'turns' in m and m['turns'] > 1:
  225.     s['pending'] = m.copy()
  226.     s['pending_target'] = ts
  227.     return
  228.  
  229.   # Apply effects: damage, dps, taunt, stack, stack_damage, attack, defence.
  230.   for t in ts:
  231.     if 'damage' in m:
  232.       t['state']['hp'] -= find_d(m['damage'], s['attack'], t['state']['defence'])
  233.     if 'dps' in m:
  234.       # Attack multipliers don't affect DPS.
  235.       t['state']['dps'].append((m['dps'][0], m['dps'][1]*N))
  236.     if 'taunt' in m:
  237.       t['state']['taunt'] = m['taunt']*N
  238.     if 'stack' in m:
  239.       name, dps, max_v = m['stack']
  240.       cur_n, dps2 = t['state']['stack'].get(name, (0, dps))
  241.       assert dps == dps2
  242.       t['state']['stack'][name] = (min(cur_n + 1, max_v), dps)
  243.     if 'stack_damage' in m:
  244.       name, d = m['stack_damage']
  245.       t['state']['hp'] -= find_d(d*t['state']['stack'][name][0], s['attack'], t['state']['defence'])
  246.       del t['state']['stack'][name]
  247.     if 'attack' in m:
  248.       # HACK: needs +1 here and in defence because if you chain buffs every 5 moves, can't lose them.
  249.       t['state']['attack'].append((m['attack'][0], m['attack'][1] * N+1))
  250.     if 'defence' in m:
  251.       t['state']['defence'].append((m['defence'][0], m['defence'][1] * N+1))
  252.  
  253. chars = [knight, priest, wizard, dragon]
  254. N = len(chars)
  255.  
  256. def fix_hp_mana():
  257.   for char in chars:
  258.     char['state']['mana'] = min(char['state']['mana'], char['max_mana'])
  259.     char['state']['hp'] = math.ceil(char['state']['hp'])
  260.     char['state']['hp'] = min(char['state']['hp'], char['max_hp'])
  261.  
  262. # These need to get processed every turn for accuracy.
  263. # They're multiplied by N when set.
  264. def process_any_turn():
  265.   for char in chars:
  266.     new_dps = []
  267.     for dps, turns in char['state']['dps']:
  268.       if turns > 1:
  269.         new_dps.append((dps, turns-1))
  270.     char['state']['dps'] = new_dps
  271.  
  272.     if 'cooldown' in char['state']:
  273.       for move, cd in char['state']['cooldown'].iteritems():
  274.         char['state']['cooldown'][move] = max(0, cd-1)
  275.  
  276.     if 'taunt' in char['state']:
  277.       char['state']['taunt'] = max(char['state']['taunt'] - 1, 0)
  278.  
  279.     if 'attack' in char['state']:
  280.       n = map(lambda x: (x[0], x[1]-1), char['state']['attack'])
  281.       char['state']['attack'] = filter(lambda x: x[1] > 0, n)
  282.  
  283.     if 'defence' in char['state']:
  284.       n = map(lambda x: (x[0], x[1]-1), char['state']['defence'])
  285.       char['state']['defence'] = filter(lambda x: x[1] > 0, n)
  286.  
  287. def process_turn_start(char):
  288.   for dps, turns in char['state']['dps']:
  289.     char['state']['hp'] = char['state']['hp'] - find_d(dps, [], char['state']['defence'])
  290.  
  291.   for num, dps in char['state']['stack'].values():
  292.     char['state']['hp'] = char['state']['hp'] - find_d(num*dps, [], char['state']['defence'])
  293.  
  294.   if char['state']['pending']:
  295.     char['state']['pending']['turns'] -= 1
  296.  
  297.   fix_hp_mana()
  298.  
  299. for turn in xrange(200):
  300.   print 'Turn', turn+1
  301.  
  302.   # Avoiding flamethrower: swap between line 5 and line 1.
  303.  
  304.   # Have each character act in order, and push moves.
  305.   # Knight.
  306.   #  - If flamethrower coming, avoid the line.
  307.   #  - Taunt if needed.
  308.   #  - Shield bash if needed.
  309.   #  - Piece if >= 70 mana.
  310.   #  - Strike.
  311.   process_any_turn()
  312.   process_turn_start(knight)
  313.   if knight['state']['pending']:
  314.     if knight['state']['pending']['turns'] <= 1:
  315.       use(knight, 'pending')
  316.   elif turn - knight['state']['last_cast']['taunt'] >= 5:
  317.     use(knight, 'taunt', 'dragon')
  318.     knight['state']['last_cast']['taunt'] = turn
  319.   elif knight['state']['mana'] >= 50 and turn - knight['state']['last_cast']['shieldbash'] >= 5:
  320.     use(knight, 'shieldbash', 'dragon')
  321.     knight['state']['last_cast']['shieldbash'] = turn
  322.   elif knight['state']['mana'] >= 70:
  323.     use(knight, 'pierce', 'dragon')
  324.   else:
  325.     use(knight, 'strike', 'dragon')
  326.   fix_hp_mana()
  327.  
  328.   # Priest.
  329.   #  - If flamethrower coming, avoid the line.
  330.   #  - If firestorm coming, divine intervention.
  331.   #  - If knight < 200, greater heal it.
  332.   #  - If priest < 100, regen it.
  333.   #  - If wizard < 100, regen it.
  334.   process_any_turn()
  335.   process_turn_start(priest)
  336.   if priest['state']['pending']:
  337.     if priest['state']['pending']['turns'] <= 1:
  338.       use(priest, 'pending')
  339.   elif knight['state']['hp'] < 100 and priest['state']['hp'] < 100 and wizard['state']['hp'] < 100 and priest['state']['cooldown']['divineintervention'] == 0:
  340.     use(priest, 'divineintervention')
  341.   elif knight['state']['hp'] <= 200:
  342.     use(priest, 'greaterheal', 'knight')
  343.   elif priest['state']['hp'] < 100:
  344.     use(priest, 'regen', 'priest')
  345.   elif wizard['state']['hp'] < 100:
  346.     use(priest, 'regen', 'wizard')
  347.   else:
  348.     use(priest, 'hymn')
  349.   fix_hp_mana()
  350.  
  351.   # Wizard.
  352.   #  - If flamethrower coming, avoid the line.
  353.   #  - Frostbite if possible.
  354.   #  - Ice shard x3
  355.   #  - Ice spear
  356.   #  - Deluge if possible.
  357.   #  - Water barrier if possible.
  358.   process_any_turn()
  359.   process_turn_start(wizard)
  360.   if wizard['state']['pending']:
  361.     if wizard['state']['pending']['turns'] <= 1:
  362.       use(wizard, 'pending')
  363.   elif wizard['state']['mana'] >= 5 and wizard['state']['cooldown']['frostbite'] == 0:
  364.     use(wizard, 'frostbite')
  365.   elif wizard['state']['mana'] <= 180 and wizard['state']['cooldown']['deluge'] == 0:
  366.     use(wizard, 'deluge', 'dragon')
  367.   elif wizard['state']['mana'] <= 195 and wizard['state']['cooldown']['waterbarrier'] == 0:
  368.     use(wizard, 'waterbarrier', 'knight')
  369.   elif wizard['state']['mana'] >= 4 and dragon['state']['stack'].get('shard', (0, 0))[0] < 3:
  370.     use(wizard, 'iceshard', 'dragon')
  371.   elif wizard['state']['mana'] >= 5 and dragon['state']['stack'].get('shard', (0, 0))[0] == 3:
  372.     use(wizard, 'icespear', 'dragon')
  373.   else:
  374.     use(wizard, 'wait')
  375.   fix_hp_mana()
  376.  
  377.   # Dragon.
  378.   process_any_turn()
  379.   process_turn_start(dragon)
  380.   if dragon['state']['hp'] <= 2000 and not dragon['state']['boss_used']:
  381.     use(dragon, 'boss')
  382.     dragon['state']['boss_used'] = True
  383.   else:
  384.     # Dragon will attack wizard, priest, knight in preference order.
  385.     target = 'wizard' if dragon['state']['taunt'] == 0 else 'knight'
  386.     move = dragon['ai'][turn % 20]
  387.     use(dragon, dragon['ai'][turn % 20], target)
  388.  
  389.     # Bounce at ends.
  390.     if move == 'move' and dragon['state']['y'] in (0, 6):
  391.       dragon_dir *= -1
  392.   fix_hp_mana()
  393.  
  394.   # Check who is alive.
  395.   for char in chars:
  396.     print '        ', char['name'], char['state']['hp'], char['state']['mana'], char['state']['attack'], char['state']['defence']
  397.  
  398.   if any(c['state']['hp'] <= 0 for c in chars):
  399.     print 'someone died, bailing out'
  400.     break
  401.  
  402. for char in [wizard, knight, priest]:
  403.   print '%s:' % char['name']
  404.   print '  ', ' '.join(map(lambda x: ':'.join(filter(lambda x: x, x)), char['ai']))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement