Advertisement
Guest User

50k en province et TT je signe

a guest
May 26th, 2022
36
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 38.92 KB | None | 0 0
  1. import math
  2. import random
  3. import sys
  4. from dataclasses import dataclass, field
  5. from enum import Enum
  6. from tkinter import Tk, Canvas, YES, BOTH
  7. from tkinter.ttk import Label
  8. from typing import List, Tuple
  9.  
  10. from numpy import array, cos, sin, dot
  11. from scipy.linalg import norm
  12.  
  13.  
  14. def idebug(*args):
  15.     return
  16.     print(*args, file=sys.stderr, flush=True)
  17.  
  18.  
  19. def debug(*args):
  20.     # return
  21.     print(*args, file=sys.stderr, flush=True)
  22.  
  23.  
  24. class EntityType(Enum):
  25.     SPIDER = 0
  26.     PLAYER = 1
  27.     OPPONENT = 2
  28.     PLAYER_BASE = 3
  29.     ENEMY_BASE = 4
  30.  
  31.  
  32. @dataclass
  33. class Action:
  34.     type: str
  35.     target: array = None
  36.     entity_id: int = None
  37.     message: str = None
  38.  
  39.     def __repr__(self):
  40.         if self.type == 'MOVE':
  41.             x, y = self.target
  42.             msg: str = f' {self.message}' if self.message else ''
  43.             return f'MOVE {round(x)} {round(y)}{msg}'
  44.         elif self.type == 'SPELL WIND':
  45.             x, y = self.target
  46.             return f'{self.type} {round(x)} {round(y)}'
  47.         elif self.type == 'SPELL SHIELD':
  48.             return f'{self.type} {self.entity_id}'
  49.         elif self.type == 'SPELL CONTROL':
  50.             x, y = self.target
  51.             return f'{self.type} {self.entity_id} {round(x)} {round(y)}'
  52.         else:
  53.             return 'WAIT'
  54.  
  55.  
  56. @dataclass
  57. class Entity:
  58.     id: int
  59.     x: int
  60.     y: int
  61.     vx: int
  62.     vy: int
  63.     radius: int
  64.     speed: int
  65.     type: EntityType
  66.     health: int
  67.     visible: bool = False
  68.     action: Action = None
  69.     patrol_point_id: int = -1
  70.     patrol_point_dir: int = 1
  71.     is_controlled: bool = False
  72.     shield_life: int = 0
  73.     threat_for: EntityType = field(init=False)
  74.     location: array = field(init=False)  # calculated value after instance creation
  75.     velocity: array = field(init=False)  # calculated value after instance creation
  76.     label: Label = field(init=False)
  77.     circle: int = field(init=False)
  78.     circle_view_range: int = field(init=False)
  79.  
  80.     def __post_init__(self):
  81.         self.location = array([self.x, self.y], dtype=float)
  82.         self.velocity = array([self.vx, self.vy], dtype=float)
  83.         self.threat_for = self.type
  84.         self.label = None
  85.         self.circle = None
  86.         self.circle_view_range = None
  87.  
  88.     def __copy__(self):
  89.         return Entity(id=self.id, x=self.x, y=self.y, vx=self.vx, vy=self.vy, radius=self.radius, speed=self.speed, type=self.type, health=self.health, visible=self.visible, action=self.action)
  90.  
  91.     def get_next_patrol_point(self, patrol_points: List[array]) -> array:
  92.         if self.patrol_point_id == -1:
  93.             target: array = min(patrol_points, key=lambda pp: norm(self.location))
  94.         else:
  95.             if self.patrol_point_dir > 0:
  96.                 if self.patrol_point_id == len(patrol_points) - 1:
  97.                     self.patrol_point_dir *= -1
  98.                     self.patrol_point_id -= 1
  99.                 else:
  100.                     self.patrol_point_id += 1
  101.             else:
  102.                 if self.patrol_point_id == 0:
  103.                     self.patrol_point_id += 1
  104.                     self.patrol_point_dir *= -1
  105.                 else:
  106.                     self.patrol_point_id -= 1
  107.             target: array = patrol_points[self.patrol_point_id]
  108.             # debug(f'pp current = {self.patrol_point_id} - pos = {target}')
  109.         return target
  110.  
  111.     def threat_level(self, base) -> int:
  112.         score = 0
  113.         if self.threat_for == base.type:
  114.             if self.dist(base) <= 5000:
  115.                 score = 1000
  116.             else:
  117.                 score = 500
  118.         score += 500 / (1 + self.dist(base))
  119.         return score
  120.  
  121.     def dist(self, other):
  122.         return norm(self.location - other.location) if isinstance(other, Entity) else norm(self.location - other)
  123.  
  124.     def delete_from(self, canvas: Canvas):
  125.         canvas.delete(self.circle)
  126.         self.label.destroy()
  127.  
  128.     def get_new_location(self, target=None) -> array:
  129.         if target is not None:
  130.             if isinstance(target, Entity):
  131.                 u: array = target.location - self.location
  132.             else:
  133.                 u: array = target - self.location
  134.             v: array = u / norm(u) if norm(u) > 0 else 0
  135.             speed: float = min(self.speed, self.dist(target))
  136.             new_location = self.location + speed * v
  137.         else:
  138.             new_location = self.location + self.velocity
  139.         return new_location
  140.  
  141.     def move(self, canvas: Canvas, target: array = None):
  142.         new_location: array = self.get_new_location(target=target)
  143.         if canvas:
  144.             dx, dy = reduce(new_location - self.location).astype(int)
  145.             # debug(f'dx, dy = {(dx, dy)}')
  146.             # debug(f'circle id = {self.circle}')
  147.             # debug(canvas.find_withtag(self.circle))
  148.             canvas.move(self.circle, dx, dy)
  149.             if self.type in [EntityType.PLAYER, EntityType.OPPONENT]:
  150.                 canvas.move(self.circle_view_range, dx, dy)
  151.             if not self.visible:
  152.                 canvas.itemconfig(self.circle, dash=(1, 2), width=1)
  153.             else:
  154.                 canvas.itemconfig(self.circle, dash=(1, 1), width=2)
  155.             self.update_label()
  156.         self.location = new_location
  157.  
  158.     def create_circle(self, canvas: Canvas, radius: int, color: str, dash: Tuple = None) -> int:
  159.         """
  160.            Create a circle in the canvas object
  161.        :param dash:
  162.        :param canvas: container of the widget
  163.        :param radius: radius of the widget
  164.        :param color: color of the widget
  165.        :return: None
  166.        """
  167.         # changed this to return the ID
  168.         x0, y0 = self.location - radius
  169.         x1, y1 = self.location + radius
  170.         oval = array([x0, y0, x1, y1])
  171.         x0, y0, x1, y1 = reduce(oval)
  172.         if dash:
  173.             return canvas.create_oval(x0, y0, x1, y1, width=1, outline=color, dash=dash)
  174.         else:
  175.             return canvas.create_oval(x0, y0, x1, y1, width=1, outline=color)
  176.  
  177.     def update_label(self, message=None):
  178.         x, y = reduce(self.location)
  179.         self.label.place(x=x, y=y)
  180.         if message:
  181.             self.label['text'] = message
  182.         else:
  183.             if self.type == EntityType.SPIDER:
  184.                 self.label['text'] = f'P{self.health}' if self.threat_for == EntityType.PLAYER_BASE else f'E{self.id}' if self.threat_for == EntityType.ENEMY_BASE else self.health
  185.             else:
  186.                 if self.visible:
  187.                     self.label['text'] = f"{self.health}" if self.type in [EntityType.SPIDER, EntityType.PLAYER_BASE, EntityType.ENEMY_BASE] else f"{self.id}"
  188.                 else:
  189.                     self.label['text'] = self.id
  190.  
  191.  
  192. def inside_map(location: array) -> bool:
  193.     x, y = location
  194.     return 0 <= x <= MAP_WIDTH and 0 <= y <= MAP_HEIGHT
  195.  
  196.  
  197. def spawn_spiders(entity_counter: int, spiders_max_level: int) -> Tuple[Entity, Entity]:
  198.     hp_list = [(i + 1) * 10 for i in range(spiders_max_level)]
  199.     hp_spider: int = random.choice(hp_list)
  200.     top_left, top_right = 5000, MAP_WIDTH
  201.     random_x: int = random.randint(top_left, top_right)
  202.     dir: int = random.randint(1, 5)
  203.     theta = dir * math.pi / 6
  204.     # theta = radians(angle)
  205.     c, s = cos(theta), sin(theta)
  206.     R = array(((c, -s), (s, c)))
  207.     u_x: array = array([1, 0])
  208.     vx, vy = 400 * dot(R, u_x)
  209.     spider: Entity = Entity(id=entity_counter, health=hp_spider, type=EntityType.SPIDER, x=random_x, y=0, vx=vx, vy=vy, radius=400, speed=400)
  210.     x, y = array([MAP_WIDTH, MAP_HEIGHT]) - spider.location
  211.     vx, vy = -spider.velocity
  212.     symmetric_spider: Entity = Entity(id=entity_counter + 1, health=hp_spider, type=EntityType.SPIDER, x=x, y=y, vx=vx, vy=vy, radius=400, speed=400)
  213.     return spider, symmetric_spider
  214.  
  215.  
  216. @dataclass
  217. class Game:
  218.     max_turns: int
  219.     respawn_value: int
  220.     spiders_max_level: int
  221.     root: Tk = Tk()
  222.     canvas: Canvas = None
  223.     console: Label = None
  224.     my_base: array = None
  225.     my_base_detected_range: array = None
  226.     enemy_base: array = None
  227.     enemy_base_detected_range: array = None
  228.     turns = 1
  229.     entity_counter = 0
  230.     spawn_cooldown: int = 0
  231.     my_mana: int = 0
  232.     enemy_mana: int = 0
  233.     my_wild_mana: int = 0
  234.     enemy_wild_mana: int = 0
  235.     heroes: List[Entity] = field(default_factory=list)
  236.     enemies: List[Entity] = field(default_factory=list)
  237.     spiders: List[Entity] = field(default_factory=list)
  238.     pending_spells: List[Action] = field(default_factory=list)
  239.  
  240.     def get_default_positions(self, player: EntityType) -> List[array]:
  241.         """
  242.            Gives idle/camp positions if no action available
  243.        :param player: PLAYER or OPPONENT
  244.        :return: list of 3 positions near player's base
  245.        """
  246.         # Some constants used to affect game's zones for my heroes
  247.         base: Entity = self.my_base if player == EntityType.PLAYER else self.enemy_base
  248.         base_is_top_left = (base.x, base.y) == (0, 0)
  249.         map_size: array = array([MAP_WIDTH, MAP_HEIGHT])
  250.         pos_0_topleft, pos_1_topleft, pos_2_topleft = array([15199, 6225]), array([4706, 2033]), array([2033, 4706])
  251.         pos_0_bottom_right, pos_1_bottom_right, pos_2_bottom_right = map_size - pos_0_topleft, map_size - pos_1_topleft, map_size - pos_2_topleft
  252.         return [pos_0_topleft, pos_1_topleft, pos_2_topleft] if base_is_top_left else [pos_0_bottom_right, pos_1_bottom_right, pos_2_bottom_right]
  253.  
  254.     def get_patrol_points(self, player: EntityType) -> List[array]:
  255.         """
  256.            Gives patrol points to cycle near enemy base (to capture spiders)
  257.        :param player: PLAYER or OPPONENT
  258.        :return: List of 7 positions to patrol
  259.        """
  260.         # patrol points for attacker
  261.         player_base: Entity = self.my_base if player == EntityType.PLAYER else self.enemy_base
  262.         base_is_top_left = (player_base.x, player_base.y) == (0, 0)
  263.         map_size: array = array([MAP_WIDTH, MAP_HEIGHT])
  264.         patrol_points_bottom_right = [(12875, 7455), (13175, 6731), (13585, 6062), (14095, 5465), (14692, 4955), (15361, 4545), (16085, 4245)]
  265.         offset: int = 800
  266.         patrol_points: List[array] = [array(pp, dtype=float) - offset for pp in patrol_points_bottom_right] if base_is_top_left else [map_size - array(pp) + offset for pp in
  267.                                                                                                                                       patrol_points_bottom_right]
  268.         return patrol_points
  269.  
  270.     def threat_for(self, spider: Entity) -> EntityType:
  271.         """
  272.            Determine if a spider is aiming to a base
  273.        :param spider: spider
  274.        :return: base aimed by spider
  275.        """
  276.         base_candidates: List[Entity] = []
  277.         for base in [self.my_base, self.enemy_base]:
  278.             x0, y0 = base.location
  279.             x1, y1 = spider.location
  280.             a, b = spider.velocity
  281.             r = 5000
  282.             # Equation droite
  283.             # x = x1 + a * t
  284.             # y = y1 + b * t
  285.             # Equation cercle
  286.             # (x - x0) ** 2 + (y - y0) ** 2 = r * r
  287.             A = a * a + b * b
  288.             B = 2 * (a * x1 - a * x0 + b * y1 - b * y0)
  289.             C = (x1 - x0) ** 2 + (y1 - y0) ** 2 - r * r
  290.             # Intersection cercle/droite:
  291.             # A * t^2 + B * t + C = 0
  292.             det: float = B * B - 4 * A * C
  293.             if det >= 0:
  294.                 t0 = (-B + math.sqrt(det)) / (2 * A)
  295.                 X0, Y0 = x1 + a * t0, y1 + b * t0
  296.                 t1 = (-B - math.sqrt(det)) / (2 * A)
  297.                 X1, Y1 = x1 + a * t1, y1 + b * t1
  298.                 if inside_map((X0, Y0)) or inside_map((X1, Y1)):
  299.                     base_candidates.append(base)
  300.         if len(base_candidates) == 2:
  301.             new_spider_location = spider.location + spider.velocity
  302.             for base in base_candidates:
  303.                 if norm(new_spider_location - base.location) < spider.dist(base):
  304.                     return base.type
  305.         else:
  306.             return base_candidates[0].type if base_candidates else EntityType.SPIDER
  307.  
  308.     def init(self):
  309.         """
  310.            Initialise les entités de départ dans le jeu
  311.        :return:
  312.        """
  313.         self.heroes.clear()
  314.         self.spiders.clear()
  315.         self.my_base = Entity(id=None, health=3, type=EntityType.PLAYER_BASE, x=0, y=0, vx=0, vy=0, radius=300, speed=0, visible=True)
  316.         self.my_base_detected_range = Entity(id=None, health=math.inf, type=EntityType.PLAYER_BASE, x=0, y=0, vx=0, vy=0, radius=5000, speed=0)
  317.         self.enemy_base = Entity(id=None, health=3, type=EntityType.ENEMY_BASE, x=MAP_WIDTH, y=MAP_HEIGHT, vx=0, vy=0, radius=300, speed=0, visible=True)
  318.         self.enemy_base_detected_range = Entity(id=None, health=math.inf, type=EntityType.ENEMY_BASE, x=MAP_WIDTH, y=MAP_HEIGHT, vx=0, vy=0, radius=5000, speed=0)
  319.         hero_1 = Entity(id=0, health=math.inf, type=EntityType.PLAYER, x=1414, y=849, vx=0, vy=0, radius=800, speed=800, visible=True)
  320.         hero_2 = Entity(id=1, health=math.inf, type=EntityType.PLAYER, x=1131, y=1131, vx=0, vy=0, radius=800, speed=800, visible=True)
  321.         hero_3 = Entity(id=2, health=math.inf, type=EntityType.PLAYER, x=849, y=1414, vx=0, vy=0, radius=800, speed=800, visible=True)
  322.         self.heroes.append(hero_1)
  323.         self.heroes.append(hero_2)
  324.         self.heroes.append(hero_3)
  325.         hero_1 = Entity(id=3, health=math.inf, type=EntityType.OPPONENT, x=MAP_WIDTH - 1414, y=MAP_HEIGHT - 849, vx=0, vy=0, radius=800, speed=800)
  326.         hero_2 = Entity(id=4, health=math.inf, type=EntityType.OPPONENT, x=MAP_WIDTH - 1131, y=MAP_HEIGHT - 1131, vx=0, vy=0, radius=800, speed=800)
  327.         hero_3 = Entity(id=5, health=math.inf, type=EntityType.OPPONENT, x=MAP_WIDTH - 849, y=MAP_HEIGHT - 1414, vx=0, vy=0, radius=800, speed=800)
  328.         self.heroes.append(hero_1)
  329.         self.heroes.append(hero_2)
  330.         self.heroes.append(hero_3)
  331.         self.entity_counter = len(self.heroes)
  332.         spider, symmetric_spider = spawn_spiders(entity_counter=self.entity_counter, spiders_max_level=self.spiders_max_level)
  333.         spider.threat_for = self.threat_for(spider)
  334.         symmetric_spider.threat_for = self.threat_for(symmetric_spider)
  335.         self.entity_counter += 2
  336.         self.spiders.append(spider)
  337.         self.spiders.append(symmetric_spider)
  338.  
  339.     def init_tk(self):
  340.         """
  341.            Initialisation de la partie graphique de l'application et entités de jeu
  342.        :return:
  343.        """
  344.         self.canvas = Canvas(width=MAP_WIDTH * ZOOM, height=MAP_HEIGHT * ZOOM, bg='white', borderwidth=2)
  345.         self.canvas.pack(expand=YES, fill=BOTH)
  346.  
  347.         self.console = Label(self.root, text="Hello World!")
  348.         self.console.pack(pady=20)
  349.  
  350.         dash_1 = (1, 2)
  351.         dash_2 = (1, 4)
  352.         # Entities
  353.         self.my_base.circle = self.my_base.create_circle(canvas=self.canvas, radius=self.my_base.radius, color='blue')
  354.         self.my_base.circle_view_range = self.my_base.create_circle(canvas=self.canvas, radius=6000, color='blue', dash=dash_1)
  355.         self.my_base.label = Label(self.root)
  356.         self.my_base.update_label()
  357.         self.my_base_detected_range.circle = self.my_base_detected_range.create_circle(canvas=self.canvas, radius=self.my_base_detected_range.radius, color='blue', dash=dash_2)
  358.  
  359.         self.enemy_base.circle = self.enemy_base.create_circle(canvas=self.canvas, radius=self.enemy_base.radius, color='green')
  360.         self.enemy_base.circle_view_range = self.enemy_base.create_circle(canvas=self.canvas, radius=6000, color='green', dash=dash_1)
  361.         self.enemy_base.label = Label(self.root)
  362.         self.enemy_base.update_label()
  363.         self.enemy_base_detected_range.circle = self.enemy_base_detected_range.create_circle(canvas=self.canvas, radius=self.enemy_base_detected_range.radius, color='green', dash=dash_2)
  364.  
  365.         for e in self.heroes + self.spiders:
  366.             e.label = Label(self.root)
  367.             e.update_label()
  368.             # e.label.pack(pady=20)
  369.             color = 'red' if e.type == EntityType.SPIDER else 'blue' if e.type == EntityType.PLAYER else 'green'
  370.             e.circle = e.create_circle(canvas=self.canvas, radius=e.radius, color=color)
  371.             if e.type != EntityType.SPIDER:
  372.                 e.circle_view_range = e.create_circle(canvas=self.canvas, radius=2200, color=color, dash=dash_1)
  373.  
  374.         self.canvas.bind_all("<space>", self.callback)
  375.         self.root.mainloop()
  376.  
  377.     # All of this would do better as a subclass of Canvas with specialize methods
  378.  
  379.     def update_player_data(self, player: EntityType):
  380.         """
  381.            Update fog and patrol points position for player
  382.        :param player: player
  383.        :return: None
  384.        """
  385.         player_base: Entity = self.my_base if player == EntityType.PLAYER else self.enemy_base
  386.         player_heroes: List[Entity] = [h for h in self.heroes if h.type == player]
  387.         enemy_heroes: List[Entity] = [h for h in self.heroes if h.type != player]  # only handle visible enemy heroes
  388.  
  389.         for hero in player_heroes:
  390.             hero.visible = True
  391.  
  392.         for spider in self.spiders:
  393.             if spider.dist(player_base) <= 6000 or any([h for h in player_heroes if spider.dist(h) <= 2200]):
  394.                 spider.visible = True
  395.             else:
  396.                 spider.visible = False
  397.  
  398.         for enemy in enemy_heroes:
  399.             if enemy.dist(player_base) <= 6000 or any([h for h in player_heroes if enemy.dist(h) <= 2200]):
  400.                 enemy.visible = True
  401.             else:
  402.                 enemy.visible = False
  403.  
  404.         for hero in player_heroes:
  405.             if hero == min(player_heroes, key=lambda h: h.id):
  406.                 patrol_points: List[array] = self.get_patrol_points(player=hero.type)
  407.                 pp: array = min(patrol_points, key=lambda pp: norm(pp - hero.location))
  408.                 if (pp == hero.location).all():
  409.                     hero.patrol_point_id = [i for i, p in enumerate(patrol_points) if (p == pp).all()][0]
  410.  
  411.     def update_entities_after_wind(self, hero: Entity, target: array):
  412.         """
  413.            Move entities in Wind range of hero
  414.        :param hero: hero casting a Wind spell
  415.        :param target: target direction of spell
  416.        :return:
  417.        """
  418.         u: array = target - hero.location
  419.         v: array = u / norm(u)
  420.         pushed_entities: List[Entity] = []
  421.         for entity in self.heroes + self.spiders:
  422.             if entity.dist(hero) <= 1280 and entity != hero:
  423.                 e = 'S' if entity.type == EntityType.SPIDER else 'H' if entity.type == EntityType.PLAYER else 'E'
  424.                 e += f'{entity.id}'
  425.                 pushed_entities.append(e)
  426.                 new_location = entity.location + 2200 * v
  427.                 entity.move(canvas=self.canvas, target=new_location)
  428.                 if self.canvas:
  429.                     entity.update_label(message="WIND")
  430.         pushed_list: str = ' '.join(map(str, pushed_entities))
  431.         debug(f'SPELL WIND hero #{hero.id} -> {pushed_list}')
  432.  
  433.  
  434.     def get_entity(self, entity_id: int):
  435.         entity_list = [e for e in self.heroes + self.spiders if e.id == entity_id]
  436.         return entity_list[0] if entity_list else None
  437.  
  438.     def update_mana(self, player: EntityType, mana: int):
  439.         if player == EntityType.PLAYER:
  440.             self.my_mana += mana
  441.         else:
  442.             self.enemy_mana += mana
  443.  
  444.     """ Referee SC2022:
  445.        https://github1s.com/CodinGame/SpringChallenge2022/blob/main/src/main/java/com/codingame/game/Referee.java#L1009
  446.        private void performGameUpdate(int turn) {
  447.            doControl();
  448.            doShield();
  449.            moveHeroes();
  450.            Map<Player, Integer[]> manaGain = performCombat();
  451.            doPush();
  452.            moveMobs();
  453.            shieldDecay();
  454.            spawnNewMobs(turn);
  455.  
  456.            manaGain.forEach((player, amount) -> {
  457.                player.gainMana(amount);
  458.            });
  459.        }
  460.    """
  461.  
  462.     def game_update(self) -> Tuple[bool, str]:
  463.         """
  464.            Mise à jour des entités à chaque tour à partir des actions des héros générées par la fonction callback (IA du jeu)
  465.        :return:
  466.        """
  467.         # update game counters
  468.         self.turns += 1
  469.         self.spawn_cooldown -= 1 if self.spawn_cooldown else 0
  470.  
  471.         # perform heroes actions and display of heroes
  472.         # for hero in self.heroes:
  473.         #     debug(f'hero #{hero.id} action = {hero.action}')
  474.  
  475.  
  476.         if self.pending_spells:
  477.             # doControl();
  478.             control_spells: List[Action] = [a for a in self.pending_spells if a.type == 'SPELL CONTROL']
  479.             while control_spells:
  480.                 action: Action = control_spells.pop(0)
  481.                 self.pending_spells.remove(action)
  482.                 entity: Entity = self.get_entity(entity_id=action.entity_id)
  483.                 if not entity:
  484.                     debug(f'Entity #{action.entity_id} does not exists!')
  485.                     continue
  486.                 if entity.shield_life:
  487.                     debug(f'unable to cast SPELL CONTROL on {entity.type} #{entity.id}')
  488.                 else:
  489.                     entity.is_controlled = True
  490.                     entity.move(canvas=self.canvas, target=action.target)
  491.             # doShield();
  492.             shield_spells: List[Action] = [a for a in self.pending_spells if a.type == 'SPELL SHIELD']
  493.             while shield_spells:
  494.                 action: Action = shield_spells.pop(0)
  495.                 self.pending_spells.remove(action)
  496.                 entity: Entity = self.get_entity(entity_id=action.entity_id)
  497.                 if not entity:
  498.                     debug(f'Entity #{action.entity_id} does not exists!')
  499.                     continue
  500.                 entity.shield_life = 12
  501.                 if self.canvas:
  502.                     self.canvas.itemconfig(entity.circle, fill='red')
  503.                 entity.move(canvas=self.canvas, target=action.target)
  504.  
  505.         # Perform action of players and update status and display
  506.         for hero in self.heroes:
  507.             if hero.action.type in ['SPELL CONTROL', 'SPELL SHIELD']:
  508.                 self.pending_spells.append(hero.action)
  509.                 self.update_mana(player=hero.type, mana=-10)
  510.                 entity: Entity = self.get_entity(entity_id=hero.action.entity_id)
  511.                 debug(f'hero #{hero.id} casts {hero.action.type} on {entity.type} #{entity.id}')
  512.  
  513.  
  514.         # moveHeroes();
  515.         for hero in self.heroes:
  516.             if hero.action.type == 'MOVE':
  517.                 hero.move(canvas=self.canvas, target=hero.action.target)
  518.  
  519.         # doPush();
  520.         for hero in self.heroes:
  521.             if hero.action.type == 'SPELL WIND':
  522.                 self.update_entities_after_wind(hero=hero, target=hero.action.target)
  523.                 self.update_mana(player=hero.type, mana=-10)
  524.  
  525.  
  526.         #  moveMobs();
  527.         #  Update spiders (status and display)
  528.         for spider in self.spiders:
  529.             if spider.health > 0:
  530.                 if spider.dist(self.my_base) <= 300:
  531.                     self.my_base.health -= 1
  532.                     if self.canvas:
  533.                         self.my_base.update_label()
  534.                         self.canvas.delete(spider.circle)
  535.                         spider.label.destroy()
  536.                     self.spiders.remove(spider)
  537.                 elif spider.dist(self.enemy_base) <= 300:
  538.                     self.enemy_base.health -= 1
  539.                     if self.canvas:
  540.                         self.enemy_base.update_label()
  541.                         spider.delete_from(canvas=self.canvas)
  542.                     self.spiders.remove(spider)
  543.                 elif spider.dist(self.my_base) <= 5000:
  544.                     spider.move(canvas=self.canvas, target=self.my_base)
  545.                 elif spider.dist(self.enemy_base) <= 5000:
  546.                     spider.move(canvas=self.canvas, target=self.enemy_base)
  547.                 elif not inside_map(spider.location):
  548.                     if self.canvas:
  549.                         spider.delete_from(canvas=self.canvas)
  550.                     self.spiders.remove(spider)
  551.                 else:
  552.                     # debug(self.canvas.find_all())
  553.                     spider.move(canvas=self.canvas)
  554.             else:
  555.                 if self.canvas:
  556.                     spider.delete_from(canvas=self.canvas)
  557.                 self.spiders.remove(spider)
  558.  
  559.         # shieldDecay();
  560.         for e in self.spiders + self.heroes:
  561.             if e.shield_life:
  562.                 e.shield_life -= 1
  563.                 if not e.shield_life and self.canvas:
  564.                     self.canvas.itemconfig(e.circle, fill=None)
  565.  
  566.         # Update mana, wild mana and spiders health
  567.         for hero in self.heroes:
  568.             for spider in self.spiders:
  569.                 if hero.dist(spider) <= hero.radius:
  570.                     self.update_mana(player=hero.type, mana=2)
  571.                     spider.health -= 2
  572.                     if spider.health <= 0 and spider.dist(hero) <= 800:
  573.                         if hero.type == EntityType.PLAYER and hero.dist(self.my_base) > 5000:
  574.                             self.my_wild_mana += 1
  575.                         elif hero.type == EntityType.OPPONENT and hero.dist(self.enemy_base) > 5000:
  576.                             self.enemy_wild_mana += 1
  577.  
  578.  
  579.         # Check state of game
  580.         # If not end game, spawn new spiders
  581.         console_msg: str = ''
  582.         winner: EntityType = None
  583.  
  584.         if self.my_base.health <= 0:
  585.             console_msg = f'OPPONENT WINS after {self.turns} turns!'
  586.             winner = EntityType.OPPONENT
  587.             if self.canvas:
  588.                 self.canvas.unbind_all("<space>")
  589.         elif self.enemy_base.health <= 0:
  590.             console_msg = f'PLAYER WINS after {self.turns} turns!'
  591.             winner = EntityType.PLAYER
  592.             if self.canvas:
  593.                 self.canvas.unbind_all("<space>")
  594.         elif self.turns == self.max_turns:
  595.             if self.enemy_base.health > self.my_base.health:
  596.                 console_msg = f'OPPONENT WINS!'
  597.                 winner = EntityType.OPPONENT
  598.             elif self.enemy_base.health < self.my_base.health:
  599.                 console_msg = f'PLAYER WINS!'
  600.                 winner = EntityType.PLAYER
  601.             else:
  602.                 if self.my_wild_mana == self.enemy_wild_mana:
  603.                     console_msg = f'THIS IS A DRAW!'
  604.                     winner = EntityType.SPIDER
  605.                 else:
  606.                     if self.my_wild_mana < self.enemy_wild_mana:
  607.                         console_msg = f'OPPONENT WINS!'
  608.                         winner = EntityType.OPPONENT
  609.                     elif self.my_wild_mana > self.enemy_wild_mana:
  610.                         console_msg = f'PLAYER WINS!'
  611.                         winner = EntityType.PLAYER
  612.                 console_msg += f'\nWILD MANA | Player = {self.my_wild_mana} - Opponent = {self.enemy_wild_mana}'
  613.             if self.canvas:
  614.                 self.canvas.unbind_all("<space>")
  615.         else:
  616.             if not self.spawn_cooldown:
  617.                 self.spawn_cooldown = self.respawn_value
  618.                 spider, symmetric_spider = spawn_spiders(entity_counter=self.entity_counter, spiders_max_level=self.spiders_max_level)
  619.                 spider.threat_for = self.threat_for(spider)
  620.                 symmetric_spider.threat_for = self.threat_for(symmetric_spider)
  621.                 self.entity_counter += 2
  622.                 self.spiders.append(spider)
  623.                 self.spiders.append(symmetric_spider)
  624.                 if self.canvas:
  625.                     for s in [spider, symmetric_spider]:
  626.                         s.label = Label(self.root)
  627.                         s.update_label()
  628.                         s.circle = s.create_circle(canvas=self.canvas, radius=s.radius, color='red')
  629.             console_msg = f'BASE HEALTH | Player = {self.my_base.health} - Opponent = {self.my_base.health}'
  630.             console_msg += f'\nMANA | Player = {self.my_mana} - Opponent = {self.enemy_mana}'
  631.  
  632.         if self.canvas:
  633.             self.console['text'] = console_msg
  634.             self.root.title(f'Spider Attack - Turn #{self.turns}/{self.max_turns}')
  635.  
  636.         for hero in self.heroes:
  637.             hero.action = None
  638.  
  639.         return winner, console_msg if not self.canvas else None
  640.  
  641.     def wood_2_ia(self, player: EntityType):
  642.         base: Entity = self.enemy_base if player == EntityType.OPPONENT else self.my_base
  643.  
  644.         player_heroes: List[Entity] = [h for h in self.heroes if h.type == player]
  645.  
  646.         visible_spiders: List[Entity] = [s for s in self.spiders if s.visible]
  647.  
  648.         if visible_spiders:
  649.             spiders = sorted(self.spiders, key=lambda s: s.dist(base))
  650.             while player_heroes and spiders:
  651.                 spider: Entity = spiders.pop(0)
  652.                 hero: Entity = min(player_heroes, key=lambda h: h.dist(spider))
  653.                 player_heroes.remove(hero)
  654.                 hero.action = Action(type='MOVE', target=spider.location)
  655.  
  656.         for hero in player_heroes:
  657.             if not hero.action:
  658.                 hero.action = Action(type='WAIT')
  659.  
  660.         for hero in player_heroes:
  661.             for h in self.heroes:
  662.                 if h.id == hero.id:
  663.                     # debug('prout wood 2')
  664.                     h.action = hero.action
  665.  
  666.     def matthis_ia(self, player: EntityType):
  667.         """
  668.            IA for dummies
  669.        """
  670.         player_heroes: List[Entity] = [h for h in self.heroes if h.type == player]
  671.  
  672.         defenders: List[Entity] = player_heroes[1:]
  673.         attacker: [Entity] = player_heroes[0]
  674.  
  675.         # debug(f'attacker pp id = {attacker.patrol_point_id} - location = {str(np.round(attacker.location))}')
  676.  
  677.         spiders: List[Entity] = [s for s in self.spiders if s.visible]
  678.  
  679.         player_base: Entity = self.my_base if player == EntityType.PLAYER else self.enemy_base
  680.         enemy_base: Entity = self.enemy_base if player == EntityType.PLAYER else self.my_base
  681.  
  682.         player_mana, enemy_mana = (self.my_mana, self.enemy_mana) if player == EntityType.PLAYER else (self.enemy_mana, self.my_mana)
  683.  
  684.         """ DEFENSE """
  685.  
  686.         if spiders:
  687.             spiders_near_my_base: List[Entity] = [s for s in spiders if s.dist(player_base) <= 6500]
  688.             ranked_spiders: List[Entity] = sorted(spiders_near_my_base, key=lambda s: s.dist(player_base))
  689.             while ranked_spiders and defenders:
  690.                 spider: Entity = ranked_spiders.pop(0)
  691.                 hero: Entity = min(defenders, key=lambda h: h.dist(spider))
  692.                 hero.action = Action(type='MOVE', target=spider.location + spider.velocity)
  693.                 defenders.remove(hero)
  694.         else:
  695.             default_positions: List[array] = self.get_default_positions(player=player)
  696.             for hero in defenders:
  697.                 hero.action = Action(type='MOVE', target=default_positions[player_heroes.index(hero)])
  698.  
  699.         """ ATTACK """
  700.  
  701.         spiders_near_enemy_base: List[Entity] = [s for s in spiders if s.dist(enemy_base) <= 6000]
  702.         spiders_wind_candidates: List[Entity] = [s for s in spiders_near_enemy_base if player_mana >= 10 and not s.shield_life and s.dist(attacker) <= 1280]
  703.         enemies_wind_candidates: List[Entity] = [h for h in self.heroes if player_mana >= 10 and h.type != player and not h.shield_life and h.dist(attacker) <= 1280]
  704.         enemies_control_candidates: List[Entity] = [h for h in self.heroes if player_mana >= 10 and h.type != player and not h.shield_life and not h.is_controlled and h.dist(attacker) <= 2200]
  705.         spiders_control_candidates: List[Entity] = [s for s in self.spiders if player_mana >= 10 and not s.shield_life and not s.is_controlled and s.dist(attacker) <= 2200]
  706.         spiders_shield_candidates: List[Entity] = [s for s in spiders_near_enemy_base if player_mana >= 10 and not s.shield_life and s.threat_for == EntityType.ENEMY_BASE and s.dist(attacker) <= 2200]
  707.         if enemies_wind_candidates:
  708.             attacker.action = Action(type='SPELL WIND', target=player_base.location)
  709.         elif enemies_control_candidates:
  710.             enemy: Entity = min(enemies_control_candidates, key=lambda s: s.dist(self.enemy_base))
  711.             attacker.action = Action(type='SPELL CONTROL', entity_id=enemy.id, target=player_base.location)
  712.         elif spiders_wind_candidates:
  713.             attacker.action = Action(type='SPELL WIND', target=enemy_base.location)
  714.         # elif spiders_control_candidates:
  715.         #     spider: Entity = max(spiders_control_candidates, key=lambda s: s.dist(self.enemy_base))
  716.         #     attacker.action = Action(type='SPELL CONTROL', entity_id=spider.id, target=enemy_base.location)
  717.         elif spiders_shield_candidates:
  718.             spider: Entity = min(spiders_shield_candidates, key=lambda s: s.dist(self.enemy_base))
  719.             attacker.action = Action(type='SPELL SHIELD', entity_id=spider.id)
  720.         else:
  721.             patrol_points = self.get_patrol_points(player=player)
  722.             # debug(f'patrol points = {patrol_points}')
  723.             target: array = attacker.get_next_patrol_point(patrol_points=patrol_points)
  724.             attacker.action = Action(type='MOVE', target=target, message=round(attacker.dist(enemy_base), 2))
  725.  
  726.         for hero in player_heroes:
  727.             if not hero.action:
  728.                 hero.action = Action(type='WAIT')
  729.  
  730.         for hero in player_heroes:
  731.             for h in self.heroes:
  732.                 if h.id == hero.id:
  733.                     h.action = hero.action
  734.  
  735.     def wood_1_ia(self, player: EntityType):
  736.         # needs debuggin
  737.         # ia_bronze_defense
  738.         # spring-challenge-2022-Silver_570_defense_only
  739.  
  740.         my_health, enemy_health = (self.my_base.health, self.enemy_base.health) if player == EntityType.PLAYER else (self.enemy_base.health, self.my_base.health)
  741.         my_mana, enemy_mana = (self.my_mana, self.enemy_mana) if player == EntityType.PLAYER else (self.enemy_mana, self.my_mana)
  742.         my_base, enemy_base = (self.my_base, self.enemy_base) if player == EntityType.PLAYER else (self.enemy_base, self.my_base)
  743.  
  744.         player_heroes: List[Entity] = [h for h in self.heroes if h.type == player]
  745.  
  746.         spiders: List[Entity] = [s for s in self.spiders if s.visible]
  747.  
  748.         """ DEFENSE ONLY """
  749.         defenders: List[Entity] = player_heroes
  750.  
  751.         if spiders:
  752.             ranked_spiders: List[Entity] = sorted(spiders, key=lambda s: s.threat_level(base=my_base), reverse=True)
  753.             while ranked_spiders and defenders:
  754.                 wind_heroes: List[Entity] = [(h, len([s for s in ranked_spiders if s.dist(my_base) <= 5000 and h.dist(s) <= 1280])) for h in defenders]
  755.                 if my_mana >= 20 and wind_heroes:
  756.                     hero = max(wind_heroes, key=lambda x: x[1])[0]
  757.                     hero.action = Action(type='SPELL WIND', target=enemy_base.location)
  758.                     my_mana -= 10
  759.                 else:
  760.                     spider: Entity = ranked_spiders.pop(0)
  761.                     hero: Entity = min(defenders, key=lambda h: h.dist(spider))
  762.                     hero.action = Action(type='MOVE', target=spider.location + spider.velocity)
  763.                 defenders.remove(hero)
  764.                 ranked_spiders: List[Entity] = sorted([s for s in ranked_spiders if s.health > 0], key=lambda s: s.threat_level(base=my_base), reverse=True)
  765.         else:
  766.             default_positions: List[array] = self.get_default_positions(player=player)
  767.             for hero in defenders:
  768.                 hero.action = Action(type='MOVE', target=default_positions[player_heroes.index(hero)])
  769.  
  770.         for hero in player_heroes:
  771.             if not hero.action:
  772.                 hero.action = Action(type='WAIT')
  773.  
  774.         for hero in player_heroes:
  775.             for h in self.heroes:
  776.                 if h.id == hero.id:
  777.                     h.action = hero.action
  778.  
  779.     def play_turn(self) -> Tuple[bool, str]:
  780.         """
  781.            IA for Dummies (fonction tour par tour de Codingame)
  782.        :return: None
  783.        """
  784.         ia_player_1 = self.wood_2_ia
  785.         ia_player_1 = self.matthis_ia
  786.         ia_player_2 = self.wood_2_ia
  787.  
  788.         self.update_player_data(player=EntityType.OPPONENT)
  789.         ia_player_2(player=EntityType.OPPONENT)
  790.         # self.wood_1_ia(player=EntityType.OPPONENT) # BUGGY :-(
  791.  
  792.         self.update_player_data(player=EntityType.PLAYER)
  793.         ia_player_1(player=EntityType.PLAYER)
  794.  
  795.         winner, console_msg = self.game_update()
  796.  
  797.         return winner, console_msg if not self.canvas else True
  798.  
  799.     def callback(self, event):
  800.         """
  801.            fonction callback utilisé dans le mode graphique
  802.        :param event:
  803.        :return:
  804.        """
  805.         self.play_turn()
  806.  
  807.  
  808. def read_int_value(message: str, max_value: int) -> int:
  809.     while True:
  810.         try:
  811.             print(f'{message} [1-{max_value}] ')
  812.             value = int(input())
  813.             if not value or not 1 <= int(value) <= max_value:
  814.                 raise Exception
  815.             break
  816.         except Exception as e:
  817.             print('Bad value!')
  818.     return int(value)
  819.  
  820.  
  821. def graphic_mode_check(message: str) -> bool:
  822.     while True:
  823.         try:
  824.             print(f'{message}')
  825.             response = input()
  826.             if not response or response not in 'GgBb':
  827.                 raise Exception
  828.             break
  829.         except Exception as e:
  830.             print('Bad response!')
  831.     return True if response in 'Gg' else False
  832.  
  833.  
  834. if __name__ == "__main__":
  835.     MAP_WIDTH, MAP_HEIGHT = 17630, 9000
  836.     mac_display_resolution = 1920, 1080
  837.     ZOOM = 0.1
  838.     SPAWN_COOLDOWN = 2
  839.     # reduce = lambda x_list: map(lambda x: x * ZOOM, x_list)
  840.     reduce = lambda coord_array: coord_array * ZOOM
  841.     player_score = enemy_score = draws = 0
  842.  
  843.     max_turns = read_int_value(message="Enter max number of rounds:", max_value=300)
  844.     respawn_value = read_int_value(message="Frequency of respawn spiders:", max_value=10)
  845.     spiders_max_level = read_int_value(message="Maximum level of spiders:", max_value=10)
  846.     run_in_graphic_mode = graphic_mode_check(message="Run game in Graphic/Batch mode [G/B]:")
  847.     if not run_in_graphic_mode:
  848.         max_games = read_int_value(message="Enter max number of games:", max_value=100)
  849.  
  850.     if run_in_graphic_mode:
  851.         game: Game = Game(max_turns=int(max_turns), respawn_value=respawn_value, spiders_max_level=spiders_max_level)
  852.         game.init()
  853.         game.init_tk()
  854.     else:
  855.         for _ in range(max_games):
  856.             game: Game = Game(max_turns=int(max_turns), respawn_value=respawn_value, spiders_max_level=spiders_max_level)
  857.             game.init()
  858.             winner = None
  859.             while winner is None:
  860.                 winner, console_msg = game.play_turn()
  861.                 if winner:
  862.                     # print(console_msg)
  863.                     if winner == EntityType.PLAYER:
  864.                         player_score += 1
  865.                     elif winner == EntityType.OPPONENT:
  866.                         enemy_score += 1
  867.                     else:
  868.                         draws += 1
  869.         player_score = round(100 * player_score/max_games)
  870.         enemy_score = round(100 * enemy_score/max_games)
  871.         draws = round(100 * draws/max_games)
  872.         print(f'PLAYER: {player_score}% - OPPONENT: {enemy_score}% - DRAWS: {draws}')
  873.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement