Advertisement
philRG

FC 2020 (phil's bfs version from dec 2021 fixed some typo errors) :-)

Apr 5th, 2022
1,147
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.52 KB | None | 0 0
  1. import random
  2. import sys
  3. import time
  4. from collections import deque
  5. from copy import copy
  6. from dataclasses import dataclass
  7. from typing import List, Optional
  8.  
  9. from numpy import array
  10.  
  11.  
  12. def idebug(*args):
  13.     return
  14.     print(*args, file=sys.stderr, flush=True)
  15.  
  16.  
  17. def debug(*args):
  18.     # return
  19.     print(*args, file=sys.stderr, flush=True)
  20.  
  21.  
  22. @dataclass
  23. class Potion:
  24.     id: int
  25.     delta: array
  26.     price: int
  27.  
  28.     def __eq__(self, other):
  29.         return self.id == other.id
  30.  
  31.     def __repr__(self):
  32.         return f'{self.id} {str(self.delta)} {self.price}'
  33.  
  34.  
  35. @dataclass
  36. class Spell:
  37.     id: int
  38.     delta: array
  39.     castable: bool
  40.  
  41.     def __copy__(self):
  42.         return Spell(self.id, self.delta[:], self.castable)
  43.  
  44.     def __hash__(self):
  45.         return hash((str(self.delta), self.castable))
  46.  
  47.     def __repr__(self):
  48.         return f'{self.id} {str(self.delta)} {self.castable}'
  49.  
  50.  
  51. @dataclass
  52. class Action:
  53.     type: str
  54.     id: Optional[int] = None
  55.     delta: Optional[array] = None
  56.  
  57.     def __hash__(self):
  58.         # return hash((self.action_type, self.action_id, tuple(map(tuple, self.delta))))
  59.         return hash((self.type, self.id))
  60.  
  61.     def __repr__(self):
  62.         if self.type == 'CAST':
  63.             return f'CAST {self.id}'
  64.         elif self.type == 'BREW':
  65.             return f'BREW {self.id}'
  66.         else:
  67.             return 'REST'
  68.  
  69.  
  70. def bfs_paths(witch_id: int, potion: Potion):
  71.     if witch_id == 0:
  72.         inv: array = my_inv.copy()
  73.         spells: List = [copy(s) for s in my_spells]
  74.     else:
  75.         inv: array = op_inv.copy()
  76.         spells: List = [copy(s) for s in op_spells]
  77.     queue = deque([(spells, inv, [])])
  78.     nodes_count = 0
  79.     # new_start = time.time()
  80.     visited_states = []
  81.     while queue and (time.time() - start) * 1000 < 50:
  82.         spells, inv, path = queue.popleft()
  83.         actions = [Action(type='CAST', id=s.id, delta=s.delta) for s in spells if s.castable and all((s.delta + inv >= 0))]
  84.         # if not any([s.castable for s in spells]):
  85.         actions.append(Action('REST'))
  86.         # debug(f'actions: {actions}')
  87.         # la petite fourmi se deplace pour chercher son chemin
  88.         actions = random.sample(actions, min(2, len(actions)))
  89.         # actions = random.choice(actions)
  90.         for action in actions:
  91.             new_spells: List = [copy(s) for s in spells]
  92.             if action.type == 'CAST':
  93.                 # if inventory is not full after cast, add new recipe, and set castable flag to False
  94.                 new_inv: array = inv + action.delta
  95.                 if sum(new_inv) > 10:
  96.                     continue
  97.                 for s in new_spells:
  98.                     if action.id == s.id:
  99.                         s.castable = False
  100.             else:  # action.action_type == REST
  101.                 new_inv = inv
  102.                 for s in new_spells:
  103.                     s.castable = True
  104.             # gamestate = hash((action, frozenset(new_spells), str(new_inv)))
  105.             # # gamestate = (action, frozenset(new_spells), str(new_inv))
  106.             # if gamestate in visited_states:
  107.             #     continue
  108.             if all((new_inv + potion.delta >= 0)):
  109.                 debug(f'BFS player {witch_id} - nodes_count = {nodes_count}')
  110.                 # debug(f'remaining time after BFS witch_id {witch_id} = {round((time.time() - new_start) * 1000, 2)} ms')
  111.                 brew_action = Action('BREW', potion.id)
  112.                 # debug(f'{str(new_inv)} + {str(potion.delta)} = {new_inv + potion.delta}')
  113.                 yield path + [action, brew_action]
  114.             else:
  115.                 node = (new_spells, new_inv, path + [action])
  116.                 queue.append(node)
  117.                 # visited_states.append(gamestate)
  118.                 # debug(f'node = {node}')
  119.                 # debug(f'game state = {gamestate}')
  120.                 nodes_count += 1
  121.     debug(f'witch {witch_id} = no solution found!')
  122.  
  123.  
  124. def shortest_path(witch_id: int, potion: Potion):
  125.     try:
  126.         return next(bfs_paths(witch_id, potion))
  127.     except StopIteration:
  128.         return None
  129.  
  130.  
  131. def get_action_ia(inv, potion, last_turn_brewed):
  132.     action = None
  133.     if all((inv + potion.delta >= 0)):
  134.         action = f'BREW {potion.id}'
  135.         last_turn_brewed = True
  136.     elif last_turn_brewed:
  137.         action = 'REST'
  138.         last_turn_brewed = False
  139.     else:
  140.         actions = shortest_path(witch_id=0, potion=potion)
  141.         if actions:
  142.             action = actions[0]
  143.             debug(f'path to potion {potion} = {actions}')
  144.         else:
  145.             action
  146.     return action
  147.  
  148.  
  149. # somme = lambda a, b: [[sum(x) for x in list(zip(a, b))]]
  150.  
  151. last_turn_brewed = False
  152. unbrewable_potions = []
  153. # game loop
  154. while True:
  155.     action_count = int(input())  # the number of spells and recipes in play
  156.     start = time.time()
  157.     idebug(action_count)
  158.     my_spells, op_spells = [], []
  159.     my_inv, op_inv = [], []
  160.     potions = []
  161.     for i in range(action_count):
  162.         line = input()
  163.         idebug(line)
  164.         inputs = line.split()
  165.         action_id = int(inputs[0])  # the unique ID of this spell or recipe
  166.         action_type = inputs[1]  # in the first league: BREW; later: CAST, OPPONENT_CAST, LEARN, BREW
  167.         delta = array([int(inputs[2]), int(inputs[3]), int(inputs[4]), int(inputs[5])])
  168.         price = int(inputs[6])  # the price in rupees if this is a potion
  169.         tome_index = int(inputs[
  170.                              7])  # in the first two leagues: always 0; later: the index in the tome if this is a tome spell, equal to the read-ahead tax; For brews, this is the value of the current urgency bonus
  171.         tax_count = int(inputs[
  172.                             8])  # in the first two leagues: always 0; later: the amount of taxed tier-0 ingredients you gain from learning this spell; For brews, this is how many times you can still gain an urgency bonus
  173.         castable = inputs[9] != "0"  # in the first league: always 0; later: 1 if this is a castable player spell
  174.         repeatable = inputs[10] != "0"  # for the first two leagues: always 0; later: 1 if this is a repeatable player spell
  175.         if action_type == 'CAST':
  176.             my_spells.append(Spell(action_id, delta, castable))
  177.         elif action_type == 'OPPONENT_CAST':
  178.             op_spells.append(Spell(action_id, delta, castable))
  179.         elif action_type == 'BREW':
  180.             potions.append(Potion(action_id, delta, price))
  181.     for i in range(2):
  182.         # inv_0: tier-0 ingredients in inventory
  183.         # score: amount of rupees
  184.         line = input()
  185.         idebug(line)
  186.         inv_0, inv_1, inv_2, inv_3, score = [int(j) for j in line.split()]
  187.         if i == 0:
  188.             my_inv, my_score = array([inv_0, inv_1, inv_2, inv_3]), score
  189.         else:
  190.             op_inv, op_score = array([inv_0, inv_1, inv_2, inv_3]), score
  191.  
  192.     # debug(f'my spells = {my_spells}')
  193.  
  194.     # in the first league: BREW <id> | WAIT; later: BREW <id> | CAST <id> [<times>] | LEARN <id> | REST | WAIT
  195.     # potion_to_brew = random.choice(potions)
  196.     potions_candidates = [p for p in potions if p not in unbrewable_potions]
  197.     if not potions_candidates:
  198.         potions_candidates = potions
  199.     potion_to_brew = min(potions_candidates, key=lambda p: p.price)
  200.  
  201.     debug(f'potion to brew: {potion_to_brew}')
  202.  
  203.     action = get_action_ia(my_inv, potion_to_brew, last_turn_brewed)
  204.  
  205.     if action is None:
  206.         unbrewable_potions.append(potion_to_brew)
  207.  
  208.     print(action if action else Action('REST'))
  209.     debug(f'elapsed time = {round((time.time() - start) * 1000)} ms')
  210.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement