Advertisement
philRG

Fantastic bits (Bronze Top 4)

Apr 3rd, 2022
1,739
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 15.90 KB | None | 0 0
  1. #
  2. #   fantastic_bits.py
  3. #   Fantastic bits / Broomstick Flyers
  4. #
  5. #   Created by philRG on 03/04/2022
  6. #   Copyright © 2022 philRG. All rights reserved.
  7. #
  8.  
  9. import random
  10. import sys
  11. from dataclasses import dataclass, field
  12. from typing import List, Tuple
  13.  
  14. import numpy as np
  15. from numpy import array
  16. from scipy.optimize import linear_sum_assignment
  17.  
  18.  
  19. def idebug(*args):
  20.     return
  21.     print(*args, file=sys.stderr)
  22.  
  23.  
  24. def debug(*args):
  25.     # return
  26.     print(*args, file=sys.stderr)
  27.  
  28.  
  29. @dataclass
  30. class Entity:
  31.     id: int
  32.     type: int
  33.     x: int
  34.     y: int
  35.     vx: int
  36.     vy: int
  37.     state: int
  38.     location: array = field(init=False)
  39.     velocity: array = field(init=False)
  40.  
  41.     def __post_init__(self):
  42.         self.location = array([self.x, self.y])
  43.         self.velocity = array([self.vx, self.vy])
  44.  
  45.     def distance(self, other_location: array):
  46.         return np.linalg.norm(other_location - self.location)
  47.  
  48.     def distance_to_goal(self, goal_x: int):
  49.         if self.y < TOP_GOAL_Y:
  50.             top_goal: array = array([goal_x, TOP_GOAL_Y])
  51.             return self.distance(top_goal)
  52.         elif self.y <= BOTTOM_GOAL_Y:
  53.             return abs(goal_x - self.x)
  54.         else:
  55.             bottom_goal: array = array([goal_x, BOTTOM_GOAL_Y])
  56.             return self.distance(bottom_goal)
  57.  
  58.     def is_aligned(self, wizard, target_goal_x: int) -> bool:
  59.         """
  60.            Check if a snaffle is aligned for flipendo
  61.        :param wizard:
  62.        :param target_goal_x:
  63.        :return: True or False
  64.        """
  65.         vect: array = self.location - wizard.location
  66.         """ a * Y + b * X + c = 0
  67.        """
  68.         # debug(f'target goal: {target_goal_x}')
  69.         # debug(f'wizard goal: {wizard.goal_x}')
  70.         if target_goal_x == wizard.goal_x:
  71.             return False
  72.         if wizard.x < self.x < target_goal_x or wizard.x > self.x > target_goal_x:
  73.             a, b = -vect[0], vect[1]
  74.             c = -a * wizard.y - b * wizard.x
  75.             target_y: int = (-b * target_goal_x - c) / a
  76.             if target_y > 0 and TOP_GOAL_Y + 400 < target_y < BOTTOM_GOAL_Y - 400:
  77.                 debug(f'{wizard.type} #{wizard.id} aligned with {self.type} #{self.id} and {"LEFT_GOAL" if goal_x == 0 else "RIGHT_GOAL"}')
  78.                 return True
  79.         return False
  80.  
  81.     def aims_to(self, entity) -> bool:
  82.         u_x, u_y = entity.location - self.location
  83.         v_x, v_y = self.velocity
  84.         return u_x * v_y == u_y * v_x
  85.  
  86.  
  87. @dataclass
  88. class Wizard(Entity):
  89.     goal_x: int
  90.     action: str = field(init=False)
  91.     captured_snaffle: object = field(init=False)
  92.  
  93.     def __post_init__(self):
  94.         super().__post_init__()
  95.         self.action = None
  96.         self.captured_snaffle = None
  97.  
  98.     def throw(self, goal_x: int) -> array:
  99.         # goal_y: int = TOP_GOAL_Y + SNAFFLE_RADIUS if self.y < TOP_GOAL_Y else self.y if self.y <= BOTTOM_GOAL_Y else BOTTOM_GOAL_Y - SNAFFLE_RADIUS
  100.         goal_y: int = int(FIELD_LENGTH_Y / 2)
  101.         return array([goal_x, goal_y])
  102.  
  103.  
  104. def assign_moves(player_wizards: List[Entity], snaffles: List[Entity]) -> List[Tuple[Entity, Entity]]:
  105.     """
  106.        Assign each wizard to a snaffle (not already captured by other wizard)
  107.    :param player_wizards: list of player wizard without snaffle
  108.    :param snaffles: list of available snaffles
  109.    :return: list of (Wizard, Snaffle) assignment
  110.    """
  111.     cost_matrix = np.zeros(shape=(len(player_wizards), len(snaffles)))
  112.     # debug(f'cost: {cost_matrix}')
  113.     for wiz_index, wiz in enumerate(player_wizards):
  114.         other_wiz: Entity = [w for w in player_wizards if w.id != wiz.id][0]
  115.         player_goal_x: int = wiz.goal_x
  116.         op_goal_x: int = LEFT_GOAL_X if player_goal_x == RIGHT_GOAL_X else RIGHT_GOAL_X
  117.         for snaffle_index, snaffle in enumerate(snaffles):
  118.             snaffle_to_other_wizard = snaffle.distance(other_wiz.location) + 1
  119.             snaffle_to_op_goal_distance = snaffle.distance_to_goal(op_goal_x) + 1
  120.             wizard_to_snaffle_distance = wiz.distance(snaffle.location) + 1
  121.             # Version 3.0
  122.             # distance_other_snaffles_to_my_goal = sum([w.distance_to_goal(player_goal_x) for w in op_wizards]) + sum([s.distance_to_goal(player_goal_x) for s in snaffles if s.id != snaffle.id])
  123.             # Version 3.0 (short without op_wizards)
  124.             # distance_other_snaffles_to_my_goal = sum([s.distance(my_goal) for s in snaffles if s.entity_id != snaffle.entity_id])
  125.             # cost_matrix[wiz_index, snaffle_index] = wizard_to_snaffle_distance / (snaffle_to_op_goal_distance * snaffle_to_other_wizard * distance_other_snaffles_to_my_goal)
  126.             # Version 2.0
  127.             # cost_matrix[wiz_index, snaffle_index] = wizard_to_snaffle_distance / snaffle_to_other_wizard
  128.             # Original one: version 1.0
  129.             cost_matrix[wiz_index, snaffle_index] = wizard_to_snaffle_distance / snaffle_to_op_goal_distance
  130.     # debug(f'cost matrix - SciPy: {cost_matrix}')
  131.     row_ind, col_ind = linear_sum_assignment(cost_matrix)
  132.     best_move = tuple(row_ind), tuple(col_ind)
  133.     # debug(f'best_move - SciPy: {best_move}')
  134.     moves_candidates = []
  135.     for i in range(2):
  136.         wiz = my_wizards[i]
  137.         if len(snaffles) > 1:
  138.             index = best_move[i][1]
  139.             snaffle = snaffles[index]
  140.         else:
  141.             snaffle = snaffles[-1]
  142.         moves_candidates.append((wiz, snaffle))
  143.     debug(f'moves_candidates: {moves_candidates}')
  144.     return moves_candidates
  145.  
  146.  
  147. """ BlitzProg:
  148.    My naïve strategy:
  149.        Target the closest snaffle that was not targeted by the other wizard,
  150.        If you have the snaffle, throw it in the direction of the goals.
  151.        If you don’t, if it is in front of the goal (if the straight line [wizard - snaffle]) and you have at least 20 mana, use Flipendo.
  152.        If it is far behind you and you have 40 mana, use Accio
  153.        This naïve strategy only requires some line intersection formula to find out if you can Flipendo and goal,
  154.        but you can find that formula easily on wikipedia and simplify it to your needs.
  155. """
  156.  
  157. """ Remi:
  158.    Une utilisation basique des sorts te fais passer en silver
  159.        en gros, un flipendo si le snaffle est bien aligné avec les buts (faut faire un peu de maths)
  160.        un accio si un adversaire risque de choper le snaffle avant toi (pas sûr que je l'avais fait en bronze)
  161.        
  162.    Remi. 06:20PM
  163.        philRG tu t'es remis à broomstick flyers ? effectivement y a pas de touches et corners, le snaffle rebondit sur les bords ...
  164.        la seule chose nouvelle par rapport à ce que tu as déjà fait, c'est lancer des sorts ^^ ACCIO pour choper la baballe avant ton adversaire,
  165.        et FLIPENDO si tu es bien aligné avec les buts.
  166. """
  167.  
  168. # Grab Snaffles and try to throw them through the opponent's goal!
  169. # Move towards a Snaffle and use your team id to determine where you need to throw it.
  170.  
  171. # constants
  172. FIELD_LENGTH_X = 16001
  173. FIELD_LENGTH_Y = 7501
  174. LEFT_GOAL_X = 0
  175. RIGHT_GOAL_X = 16000
  176. TOP_GOAL_Y = int((FIELD_LENGTH_Y - 4000) / 2) + 300
  177. BOTTOM_GOAL_Y = int((FIELD_LENGTH_Y - 4000) / 2) + 4000 - 300
  178. BLUDGER_RADIUS = 200
  179. WIZARD_RADIUS = 400
  180. POST_RADIUS = 300
  181. SNAFFLE_RADIUS = 150
  182.  
  183. FLIPENDO_COST, ACCIO_COST = 20, 15
  184.  
  185. my_team_id = int(input())  # if 0 you need to score on the right of the map, if 1 you need to score on the left
  186. idebug(my_team_id)
  187.  
  188. my_goal_x, op_goal_x = (LEFT_GOAL_X, RIGHT_GOAL_X) if my_team_id == 0 else (RIGHT_GOAL_X, LEFT_GOAL_X)
  189.  
  190. # game loop
  191. while True:
  192.     my_score, my_magic = [int(i) for i in input().split()]
  193.     idebug(my_score, my_magic)
  194.     opponent_score, opponent_magic = [int(i) for i in input().split()]
  195.     idebug(opponent_score, opponent_magic)
  196.  
  197.     entities_count = int(input())  # number of entities still in game
  198.     idebug(entities_count)
  199.  
  200.     my_wizards: List[Wizard] = []
  201.     op_wizards: List[Wizard] = []
  202.     snaffles: List[Entity] = []
  203.     bludgers: List[Entity] = []
  204.     for i in range(entities_count):
  205.         line = input()
  206.         idebug(line)
  207.         inputs = line.split()
  208.         entity_id = int(inputs[0])  # entity identifier
  209.         entity_type = inputs[1]  # "WIZARD", "OPPONENT_WIZARD" or "SNAFFLE" (or "BLUDGER" after first league)
  210.         x = int(inputs[2])  # position
  211.         y = int(inputs[3])  # position
  212.         vx = int(inputs[4])  # velocity
  213.         vy = int(inputs[5])  # velocity
  214.         state = int(inputs[6])  # 1 if the wizard is holding a Snaffle, 0 otherwise
  215.         if entity_type == 'WIZARD':
  216.             goal_x = my_goal_x
  217.             my_wizards.append(Wizard(id=entity_id, type=entity_type, x=x, y=y, vx=vx, vy=vy, state=state, goal_x=goal_x))
  218.         elif entity_type == 'OPPONENT_WIZARD':
  219.             goal_x = op_goal_x
  220.             op_wizards.append(Wizard(id=entity_id, type=entity_type, x=x, y=y, vx=vx, vy=vy, state=state, goal_x=goal_x))
  221.         else:
  222.             goal_x = None
  223.             if entity_type == 'SNAFFLE':
  224.                 snaffles.append(Entity(id=entity_id, type=entity_type, x=x, y=y, vx=vx, vy=vy, state=state))
  225.             else:
  226.                 bludgers.append(Entity(id=entity_id, type=entity_type, x=x, y=y, vx=vx, vy=vy, state=state))
  227.  
  228.     # Store snaffle's reference in wizard entity if it was captured
  229.     for s in snaffles:
  230.         if s.state == 1:
  231.             for w in my_wizards + op_wizards:
  232.                 if (w.location == s.location).all():
  233.                     w.captured_snaffle = s
  234.  
  235.     actions: List[str] = []
  236.     thrust, power = 150, 500
  237.  
  238.     """ Try to flipendo free snaffles """
  239.     available_wizards: List[Entity] = my_wizards[:]
  240.     flipendo_candidates: List[Tuple[Entity, Entity]] = [(s, w) for s in snaffles for w in available_wizards if s.state == 0 and s.x > w.x and s.is_aligned(wizard=w, target_goal_x=op_goal_x)]
  241.  
  242.     while my_magic > FLIPENDO_COST and available_wizards and flipendo_candidates:
  243.         wizard = available_wizards.pop()
  244.         snaffle_candidates: List[Entity] = [s for s, w in flipendo_candidates if w == wizard]
  245.         if snaffle_candidates:
  246.             # snaffle = max(snaffle_candidates, key=lambda s: s.distance_to_goal(op_goal_x))
  247.             snaffle = max(snaffle_candidates, key=lambda s: s.distance_to_goal(op_goal_x) / s.distance(wizard.location) ** 2)
  248.             snaffle.state = 1  # lock snaffle
  249.             wizard.action = f'FLIPENDO {snaffle.id}'
  250.             my_magic -= FLIPENDO_COST
  251.         flipendo_candidates: List[Tuple[Entity, Entity]] = [(s, w) for s in snaffles for w in my_wizards if s.state == 0 and s.x > w.x and s.is_aligned(wizard=w, target_goal_x=op_goal_x)]
  252.  
  253.     """ Throw captured snaffles """
  254.     throw_candidates: List[Tuple[Entity, array]] = [(w, w.throw(goal_x=op_goal_x)) for w in my_wizards if not w.action and w.state == 1]
  255.     for wizard, throw_dest in throw_candidates:
  256.         wizard.action = f'THROW {throw_dest[0]} {throw_dest[1]} {power}'
  257.     available_snaffles: List[Entity] = [s for s in snaffles if s.state == 0]
  258.  
  259.     # """ Exclude snaffles targeted by opponent wizards in first choice """
  260.     # available_snaffles_targeted_by_opponent: List[Entity] = []
  261.     # op_wizards_lurking_for_snaffles: List[Entity] = [w for w in op_wizards if e.state == 0]
  262.     # for snaffle in available_snaffles:
  263.     #     snaffle_is_targeted = False
  264.     #     for op_wizard in op_wizards_lurking_for_snaffles:
  265.     #         if op_wizard.aims_to(snaffle):
  266.     #             snaffle_is_targeted = True
  267.     #     if snaffle_is_targeted:
  268.     #         available_snaffles_targeted_by_opponent.append(snaffle)
  269.     # available_snaffles = [s for s in available_snaffles if s not in available_snaffles_targeted_by_opponent] if available_snaffles_targeted_by_opponent else available_snaffles
  270.  
  271.     available_wizards: List[Wizard] = [w for w in my_wizards if w.state == 0 and not w.action]
  272.     i = 0
  273.     while available_wizards and available_snaffles:
  274.         # i += 1
  275.         # debug(f'STEP #{i}')
  276.         # for s in available_snaffles:
  277.         #     debug(s)
  278.         if len(available_wizards) == 2:
  279.             moves_candidates: List[Tuple[Entity, Entity]] = assign_moves(player_wizards=my_wizards, snaffles=available_snaffles)
  280.             for w, s in moves_candidates:
  281.                 debug(f'wizard #{w.id} move to snaffle #{s.id}')
  282.             while moves_candidates and available_snaffles:
  283.                 wizard, snaffle = moves_candidates.pop()
  284.                 wizard.action = f'MOVE {snaffle.x} {snaffle.y} {thrust}'
  285.                 # try:
  286.                 #     available_snaffles.remove(snaffle)
  287.                 # except ValueError:
  288.                 #     debug(f'snaffle {snaffle.id} not found in available snaffles')
  289.                 #     for s in available_snaffles:
  290.                 #         debug(s)
  291.         elif len(available_wizards) == 1:
  292.             my_wizard: Entity = available_wizards[0]
  293.             snaffle: Entity = min(available_snaffles, key=lambda s: s.distance(my_wizard.location))
  294.             my_wizard.action = f'MOVE {snaffle.x} {snaffle.y} {thrust}'
  295.             # available_snaffles.remove(snaffle)
  296.         available_snaffles = [s for s in snaffles if s.state == 0]
  297.         available_wizards: List[Entity] = [w for w in my_wizards if w.state == 0 and not w.action]
  298.  
  299.     available_wizards: List[Wizard] = [w for w in my_wizards if not w.action]
  300.     op_wizards_with_snaffles = [w for w in op_wizards if w.state == 1]
  301.     # accio_candidates: List[Tuple[Wizard, Wizard]] = [(op_w, w) for op_w in op_wizards for w in available_wizards if op_w.state == 1 and op_w.distance_to_goal(goal_x=my_goal_x) < w.distance_to_goal(goal_x=my_goal_x)]
  302.     accio_candidates: List[Tuple[Entity, Wizard]] = [(s, w) for s in snaffles for w in available_wizards if s.distance_to_goal(goal_x=my_goal_x) < w.distance_to_goal(goal_x=my_goal_x)]
  303.  
  304.     # op_wizards_accio_targeted: List[Wizard] = []
  305.     snaffles_accio_targeted: List[Entity] = []
  306.     while my_magic > ACCIO_COST and available_wizards and accio_candidates:
  307.         # op_wizard, wizard = min(accio_candidates, key=lambda x: x[0].distance_to_goal(goal_x=my_goal_x))
  308.         snaffle, wizard = min(accio_candidates, key=lambda x: x[0].distance_to_goal(goal_x=my_goal_x))
  309.         wizard.action = f'ACCIO {snaffle.id}'
  310.         my_magic -= ACCIO_COST
  311.         # op_wizards_accio_targeted.append(op_wizard)
  312.         snaffles_accio_targeted.append(snaffle)
  313.         available_wizards: List[Wizard] = [w for w in my_wizards if not w.action]
  314.         # op_wizards_with_snaffles = [w for w in op_wizards if w.state == 1 and w not in op_wizards_accio_targeted]
  315.         # accio_candidates: List[Wizard] = [(op_w, w) for op_w in op_wizards for w in available_wizards if
  316.         #                                   op_w.state == 1 and op_w.distance_to_goal(goal_x=my_goal_x) < w.distance_to_goal(goal_x=my_goal_x)]
  317.         accio_candidates: List[Tuple[Entity, Wizard]] = [(s, w) for s in snaffles for w in available_wizards if s not in snaffles_accio_targeted and s.distance_to_goal(goal_x=my_goal_x) < w.distance_to_goal(goal_x=my_goal_x)]
  318.  
  319.     op_wizards_with_snaffles = [w for w in op_wizards if w.state == 1]
  320.     if available_wizards:
  321.         if op_wizards_with_snaffles:
  322.             for wizard in available_wizards:
  323.                 target: Entity = min(op_wizards_with_snaffles, key=lambda w: w.distance(wizard.location))
  324.                 wizard.action = f'MOVE {target.x} {target.y} {thrust}'
  325.         else:
  326.             for wizard in available_wizards:
  327.                 target_x, target_y = random.randint(0, FIELD_LENGTH_X), random.randint(0, FIELD_LENGTH_Y)
  328.                 wizard.action = f'MOVE {target_x} {target_y} {thrust}'
  329.  
  330.     available_wizards: List[Wizard] = [w for w in my_wizards if not w.action]
  331.     if available_wizards:
  332.         for wizard in available_wizards:
  333.             target_x, target_y = random.randint(0, FIELD_LENGTH_X), random.randint(0, FIELD_LENGTH_Y)
  334.             wizard.action = f'MOVE {target_x} {target_y} {thrust}'
  335.  
  336.     for i in range(2):
  337.         w: Entity = [w for idx, w in enumerate(my_wizards) if idx == i][0]
  338.         print(w.action)
  339.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement