Advertisement
philRG

FC 2020 (phil's version) BFS like to be continued

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