Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import math
- import random
- import sys
- from dataclasses import dataclass, field
- from enum import Enum
- from tkinter import Tk, Canvas, YES, BOTH
- from tkinter.ttk import Label
- from typing import List, Tuple
- from numpy import array, cos, sin, dot
- from scipy.linalg import norm
- def idebug(*args):
- return
- print(*args, file=sys.stderr, flush=True)
- def debug(*args):
- # return
- print(*args, file=sys.stderr, flush=True)
- class EntityType(Enum):
- SPIDER = 0
- PLAYER = 1
- OPPONENT = 2
- PLAYER_BASE = 3
- ENEMY_BASE = 4
- @dataclass
- class Action:
- type: str
- target: array = None
- entity_id: int = None
- message: str = None
- def __repr__(self):
- if self.type == 'MOVE':
- x, y = self.target
- msg: str = f' {self.message}' if self.message else ''
- return f'MOVE {round(x)} {round(y)}{msg}'
- elif self.type == 'SPELL WIND':
- x, y = self.target
- return f'{self.type} {round(x)} {round(y)}'
- elif self.type == 'SPELL SHIELD':
- return f'{self.type} {self.entity_id}'
- elif self.type == 'SPELL CONTROL':
- x, y = self.target
- return f'{self.type} {self.entity_id} {round(x)} {round(y)}'
- else:
- return 'WAIT'
- @dataclass
- class Entity:
- id: int
- x: int
- y: int
- vx: int
- vy: int
- radius: int
- speed: int
- type: EntityType
- health: int
- visible: bool = False
- action: Action = None
- patrol_point_id: int = -1
- patrol_point_dir: int = 1
- is_controlled: bool = False
- shield_life: int = 0
- threat_for: EntityType = field(init=False)
- location: array = field(init=False) # calculated value after instance creation
- velocity: array = field(init=False) # calculated value after instance creation
- label: Label = field(init=False)
- circle: int = field(init=False)
- circle_view_range: int = field(init=False)
- def __post_init__(self):
- self.location = array([self.x, self.y], dtype=float)
- self.velocity = array([self.vx, self.vy], dtype=float)
- self.threat_for = self.type
- self.label = None
- self.circle = None
- self.circle_view_range = None
- def __copy__(self):
- 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)
- def get_next_patrol_point(self, patrol_points: List[array]) -> array:
- if self.patrol_point_id == -1:
- target: array = min(patrol_points, key=lambda pp: norm(self.location))
- else:
- if self.patrol_point_dir > 0:
- if self.patrol_point_id == len(patrol_points) - 1:
- self.patrol_point_dir *= -1
- self.patrol_point_id -= 1
- else:
- self.patrol_point_id += 1
- else:
- if self.patrol_point_id == 0:
- self.patrol_point_id += 1
- self.patrol_point_dir *= -1
- else:
- self.patrol_point_id -= 1
- target: array = patrol_points[self.patrol_point_id]
- # debug(f'pp current = {self.patrol_point_id} - pos = {target}')
- return target
- def threat_level(self, base) -> int:
- score = 0
- if self.threat_for == base.type:
- if self.dist(base) <= 5000:
- score = 1000
- else:
- score = 500
- score += 500 / (1 + self.dist(base))
- return score
- def dist(self, other):
- return norm(self.location - other.location) if isinstance(other, Entity) else norm(self.location - other)
- def delete_from(self, canvas: Canvas):
- canvas.delete(self.circle)
- self.label.destroy()
- def get_new_location(self, target=None) -> array:
- if target is not None:
- if isinstance(target, Entity):
- u: array = target.location - self.location
- else:
- u: array = target - self.location
- v: array = u / norm(u) if norm(u) > 0 else 0
- speed: float = min(self.speed, self.dist(target))
- new_location = self.location + speed * v
- else:
- new_location = self.location + self.velocity
- return new_location
- def move(self, canvas: Canvas, target: array = None):
- new_location: array = self.get_new_location(target=target)
- if canvas:
- dx, dy = reduce(new_location - self.location).astype(int)
- # debug(f'dx, dy = {(dx, dy)}')
- # debug(f'circle id = {self.circle}')
- # debug(canvas.find_withtag(self.circle))
- canvas.move(self.circle, dx, dy)
- if self.type in [EntityType.PLAYER, EntityType.OPPONENT]:
- canvas.move(self.circle_view_range, dx, dy)
- if not self.visible:
- canvas.itemconfig(self.circle, dash=(1, 2), width=1)
- else:
- canvas.itemconfig(self.circle, dash=(1, 1), width=2)
- self.update_label()
- self.location = new_location
- def create_circle(self, canvas: Canvas, radius: int, color: str, dash: Tuple = None) -> int:
- """
- Create a circle in the canvas object
- :param dash:
- :param canvas: container of the widget
- :param radius: radius of the widget
- :param color: color of the widget
- :return: None
- """
- # changed this to return the ID
- x0, y0 = self.location - radius
- x1, y1 = self.location + radius
- oval = array([x0, y0, x1, y1])
- x0, y0, x1, y1 = reduce(oval)
- if dash:
- return canvas.create_oval(x0, y0, x1, y1, width=1, outline=color, dash=dash)
- else:
- return canvas.create_oval(x0, y0, x1, y1, width=1, outline=color)
- def update_label(self, message=None):
- x, y = reduce(self.location)
- self.label.place(x=x, y=y)
- if message:
- self.label['text'] = message
- else:
- if self.type == EntityType.SPIDER:
- 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
- else:
- if self.visible:
- self.label['text'] = f"{self.health}" if self.type in [EntityType.SPIDER, EntityType.PLAYER_BASE, EntityType.ENEMY_BASE] else f"{self.id}"
- else:
- self.label['text'] = self.id
- def inside_map(location: array) -> bool:
- x, y = location
- return 0 <= x <= MAP_WIDTH and 0 <= y <= MAP_HEIGHT
- def spawn_spiders(entity_counter: int, spiders_max_level: int) -> Tuple[Entity, Entity]:
- hp_list = [(i + 1) * 10 for i in range(spiders_max_level)]
- hp_spider: int = random.choice(hp_list)
- top_left, top_right = 5000, MAP_WIDTH
- random_x: int = random.randint(top_left, top_right)
- dir: int = random.randint(1, 5)
- theta = dir * math.pi / 6
- # theta = radians(angle)
- c, s = cos(theta), sin(theta)
- R = array(((c, -s), (s, c)))
- u_x: array = array([1, 0])
- vx, vy = 400 * dot(R, u_x)
- 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)
- x, y = array([MAP_WIDTH, MAP_HEIGHT]) - spider.location
- vx, vy = -spider.velocity
- 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)
- return spider, symmetric_spider
- @dataclass
- class Game:
- max_turns: int
- respawn_value: int
- spiders_max_level: int
- root: Tk = Tk()
- canvas: Canvas = None
- console: Label = None
- my_base: array = None
- my_base_detected_range: array = None
- enemy_base: array = None
- enemy_base_detected_range: array = None
- turns = 1
- entity_counter = 0
- spawn_cooldown: int = 0
- my_mana: int = 0
- enemy_mana: int = 0
- my_wild_mana: int = 0
- enemy_wild_mana: int = 0
- heroes: List[Entity] = field(default_factory=list)
- enemies: List[Entity] = field(default_factory=list)
- spiders: List[Entity] = field(default_factory=list)
- pending_spells: List[Action] = field(default_factory=list)
- def get_default_positions(self, player: EntityType) -> List[array]:
- """
- Gives idle/camp positions if no action available
- :param player: PLAYER or OPPONENT
- :return: list of 3 positions near player's base
- """
- # Some constants used to affect game's zones for my heroes
- base: Entity = self.my_base if player == EntityType.PLAYER else self.enemy_base
- base_is_top_left = (base.x, base.y) == (0, 0)
- map_size: array = array([MAP_WIDTH, MAP_HEIGHT])
- pos_0_topleft, pos_1_topleft, pos_2_topleft = array([15199, 6225]), array([4706, 2033]), array([2033, 4706])
- 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
- 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]
- def get_patrol_points(self, player: EntityType) -> List[array]:
- """
- Gives patrol points to cycle near enemy base (to capture spiders)
- :param player: PLAYER or OPPONENT
- :return: List of 7 positions to patrol
- """
- # patrol points for attacker
- player_base: Entity = self.my_base if player == EntityType.PLAYER else self.enemy_base
- base_is_top_left = (player_base.x, player_base.y) == (0, 0)
- map_size: array = array([MAP_WIDTH, MAP_HEIGHT])
- patrol_points_bottom_right = [(12875, 7455), (13175, 6731), (13585, 6062), (14095, 5465), (14692, 4955), (15361, 4545), (16085, 4245)]
- offset: int = 800
- 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
- patrol_points_bottom_right]
- return patrol_points
- def threat_for(self, spider: Entity) -> EntityType:
- """
- Determine if a spider is aiming to a base
- :param spider: spider
- :return: base aimed by spider
- """
- base_candidates: List[Entity] = []
- for base in [self.my_base, self.enemy_base]:
- x0, y0 = base.location
- x1, y1 = spider.location
- a, b = spider.velocity
- r = 5000
- # Equation droite
- # x = x1 + a * t
- # y = y1 + b * t
- # Equation cercle
- # (x - x0) ** 2 + (y - y0) ** 2 = r * r
- A = a * a + b * b
- B = 2 * (a * x1 - a * x0 + b * y1 - b * y0)
- C = (x1 - x0) ** 2 + (y1 - y0) ** 2 - r * r
- # Intersection cercle/droite:
- # A * t^2 + B * t + C = 0
- det: float = B * B - 4 * A * C
- if det >= 0:
- t0 = (-B + math.sqrt(det)) / (2 * A)
- X0, Y0 = x1 + a * t0, y1 + b * t0
- t1 = (-B - math.sqrt(det)) / (2 * A)
- X1, Y1 = x1 + a * t1, y1 + b * t1
- if inside_map((X0, Y0)) or inside_map((X1, Y1)):
- base_candidates.append(base)
- if len(base_candidates) == 2:
- new_spider_location = spider.location + spider.velocity
- for base in base_candidates:
- if norm(new_spider_location - base.location) < spider.dist(base):
- return base.type
- else:
- return base_candidates[0].type if base_candidates else EntityType.SPIDER
- def init(self):
- """
- Initialise les entités de départ dans le jeu
- :return:
- """
- self.heroes.clear()
- self.spiders.clear()
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- self.heroes.append(hero_1)
- self.heroes.append(hero_2)
- self.heroes.append(hero_3)
- 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)
- 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)
- 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)
- self.heroes.append(hero_1)
- self.heroes.append(hero_2)
- self.heroes.append(hero_3)
- self.entity_counter = len(self.heroes)
- spider, symmetric_spider = spawn_spiders(entity_counter=self.entity_counter, spiders_max_level=self.spiders_max_level)
- spider.threat_for = self.threat_for(spider)
- symmetric_spider.threat_for = self.threat_for(symmetric_spider)
- self.entity_counter += 2
- self.spiders.append(spider)
- self.spiders.append(symmetric_spider)
- def init_tk(self):
- """
- Initialisation de la partie graphique de l'application et entités de jeu
- :return:
- """
- self.canvas = Canvas(width=MAP_WIDTH * ZOOM, height=MAP_HEIGHT * ZOOM, bg='white', borderwidth=2)
- self.canvas.pack(expand=YES, fill=BOTH)
- self.console = Label(self.root, text="Hello World!")
- self.console.pack(pady=20)
- dash_1 = (1, 2)
- dash_2 = (1, 4)
- # Entities
- self.my_base.circle = self.my_base.create_circle(canvas=self.canvas, radius=self.my_base.radius, color='blue')
- self.my_base.circle_view_range = self.my_base.create_circle(canvas=self.canvas, radius=6000, color='blue', dash=dash_1)
- self.my_base.label = Label(self.root)
- self.my_base.update_label()
- 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)
- self.enemy_base.circle = self.enemy_base.create_circle(canvas=self.canvas, radius=self.enemy_base.radius, color='green')
- self.enemy_base.circle_view_range = self.enemy_base.create_circle(canvas=self.canvas, radius=6000, color='green', dash=dash_1)
- self.enemy_base.label = Label(self.root)
- self.enemy_base.update_label()
- 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)
- for e in self.heroes + self.spiders:
- e.label = Label(self.root)
- e.update_label()
- # e.label.pack(pady=20)
- color = 'red' if e.type == EntityType.SPIDER else 'blue' if e.type == EntityType.PLAYER else 'green'
- e.circle = e.create_circle(canvas=self.canvas, radius=e.radius, color=color)
- if e.type != EntityType.SPIDER:
- e.circle_view_range = e.create_circle(canvas=self.canvas, radius=2200, color=color, dash=dash_1)
- self.canvas.bind_all("<space>", self.callback)
- self.root.mainloop()
- # All of this would do better as a subclass of Canvas with specialize methods
- def update_player_data(self, player: EntityType):
- """
- Update fog and patrol points position for player
- :param player: player
- :return: None
- """
- player_base: Entity = self.my_base if player == EntityType.PLAYER else self.enemy_base
- player_heroes: List[Entity] = [h for h in self.heroes if h.type == player]
- enemy_heroes: List[Entity] = [h for h in self.heroes if h.type != player] # only handle visible enemy heroes
- for hero in player_heroes:
- hero.visible = True
- for spider in self.spiders:
- if spider.dist(player_base) <= 6000 or any([h for h in player_heroes if spider.dist(h) <= 2200]):
- spider.visible = True
- else:
- spider.visible = False
- for enemy in enemy_heroes:
- if enemy.dist(player_base) <= 6000 or any([h for h in player_heroes if enemy.dist(h) <= 2200]):
- enemy.visible = True
- else:
- enemy.visible = False
- for hero in player_heroes:
- if hero == min(player_heroes, key=lambda h: h.id):
- patrol_points: List[array] = self.get_patrol_points(player=hero.type)
- pp: array = min(patrol_points, key=lambda pp: norm(pp - hero.location))
- if (pp == hero.location).all():
- hero.patrol_point_id = [i for i, p in enumerate(patrol_points) if (p == pp).all()][0]
- def update_entities_after_wind(self, hero: Entity, target: array):
- """
- Move entities in Wind range of hero
- :param hero: hero casting a Wind spell
- :param target: target direction of spell
- :return:
- """
- u: array = target - hero.location
- v: array = u / norm(u)
- pushed_entities: List[Entity] = []
- for entity in self.heroes + self.spiders:
- if entity.dist(hero) <= 1280 and entity != hero:
- e = 'S' if entity.type == EntityType.SPIDER else 'H' if entity.type == EntityType.PLAYER else 'E'
- e += f'{entity.id}'
- pushed_entities.append(e)
- new_location = entity.location + 2200 * v
- entity.move(canvas=self.canvas, target=new_location)
- if self.canvas:
- entity.update_label(message="WIND")
- pushed_list: str = ' '.join(map(str, pushed_entities))
- debug(f'SPELL WIND hero #{hero.id} -> {pushed_list}')
- def get_entity(self, entity_id: int):
- entity_list = [e for e in self.heroes + self.spiders if e.id == entity_id]
- return entity_list[0] if entity_list else None
- def update_mana(self, player: EntityType, mana: int):
- if player == EntityType.PLAYER:
- self.my_mana += mana
- else:
- self.enemy_mana += mana
- """ Referee SC2022:
- https://github1s.com/CodinGame/SpringChallenge2022/blob/main/src/main/java/com/codingame/game/Referee.java#L1009
- private void performGameUpdate(int turn) {
- doControl();
- doShield();
- moveHeroes();
- Map<Player, Integer[]> manaGain = performCombat();
- doPush();
- moveMobs();
- shieldDecay();
- spawnNewMobs(turn);
- manaGain.forEach((player, amount) -> {
- player.gainMana(amount);
- });
- }
- """
- def game_update(self) -> Tuple[bool, str]:
- """
- Mise à jour des entités à chaque tour à partir des actions des héros générées par la fonction callback (IA du jeu)
- :return:
- """
- # update game counters
- self.turns += 1
- self.spawn_cooldown -= 1 if self.spawn_cooldown else 0
- # perform heroes actions and display of heroes
- # for hero in self.heroes:
- # debug(f'hero #{hero.id} action = {hero.action}')
- if self.pending_spells:
- # doControl();
- control_spells: List[Action] = [a for a in self.pending_spells if a.type == 'SPELL CONTROL']
- while control_spells:
- action: Action = control_spells.pop(0)
- self.pending_spells.remove(action)
- entity: Entity = self.get_entity(entity_id=action.entity_id)
- if not entity:
- debug(f'Entity #{action.entity_id} does not exists!')
- continue
- if entity.shield_life:
- debug(f'unable to cast SPELL CONTROL on {entity.type} #{entity.id}')
- else:
- entity.is_controlled = True
- entity.move(canvas=self.canvas, target=action.target)
- # doShield();
- shield_spells: List[Action] = [a for a in self.pending_spells if a.type == 'SPELL SHIELD']
- while shield_spells:
- action: Action = shield_spells.pop(0)
- self.pending_spells.remove(action)
- entity: Entity = self.get_entity(entity_id=action.entity_id)
- if not entity:
- debug(f'Entity #{action.entity_id} does not exists!')
- continue
- entity.shield_life = 12
- if self.canvas:
- self.canvas.itemconfig(entity.circle, fill='red')
- entity.move(canvas=self.canvas, target=action.target)
- # Perform action of players and update status and display
- for hero in self.heroes:
- if hero.action.type in ['SPELL CONTROL', 'SPELL SHIELD']:
- self.pending_spells.append(hero.action)
- self.update_mana(player=hero.type, mana=-10)
- entity: Entity = self.get_entity(entity_id=hero.action.entity_id)
- debug(f'hero #{hero.id} casts {hero.action.type} on {entity.type} #{entity.id}')
- # moveHeroes();
- for hero in self.heroes:
- if hero.action.type == 'MOVE':
- hero.move(canvas=self.canvas, target=hero.action.target)
- # doPush();
- for hero in self.heroes:
- if hero.action.type == 'SPELL WIND':
- self.update_entities_after_wind(hero=hero, target=hero.action.target)
- self.update_mana(player=hero.type, mana=-10)
- # moveMobs();
- # Update spiders (status and display)
- for spider in self.spiders:
- if spider.health > 0:
- if spider.dist(self.my_base) <= 300:
- self.my_base.health -= 1
- if self.canvas:
- self.my_base.update_label()
- self.canvas.delete(spider.circle)
- spider.label.destroy()
- self.spiders.remove(spider)
- elif spider.dist(self.enemy_base) <= 300:
- self.enemy_base.health -= 1
- if self.canvas:
- self.enemy_base.update_label()
- spider.delete_from(canvas=self.canvas)
- self.spiders.remove(spider)
- elif spider.dist(self.my_base) <= 5000:
- spider.move(canvas=self.canvas, target=self.my_base)
- elif spider.dist(self.enemy_base) <= 5000:
- spider.move(canvas=self.canvas, target=self.enemy_base)
- elif not inside_map(spider.location):
- if self.canvas:
- spider.delete_from(canvas=self.canvas)
- self.spiders.remove(spider)
- else:
- # debug(self.canvas.find_all())
- spider.move(canvas=self.canvas)
- else:
- if self.canvas:
- spider.delete_from(canvas=self.canvas)
- self.spiders.remove(spider)
- # shieldDecay();
- for e in self.spiders + self.heroes:
- if e.shield_life:
- e.shield_life -= 1
- if not e.shield_life and self.canvas:
- self.canvas.itemconfig(e.circle, fill=None)
- # Update mana, wild mana and spiders health
- for hero in self.heroes:
- for spider in self.spiders:
- if hero.dist(spider) <= hero.radius:
- self.update_mana(player=hero.type, mana=2)
- spider.health -= 2
- if spider.health <= 0 and spider.dist(hero) <= 800:
- if hero.type == EntityType.PLAYER and hero.dist(self.my_base) > 5000:
- self.my_wild_mana += 1
- elif hero.type == EntityType.OPPONENT and hero.dist(self.enemy_base) > 5000:
- self.enemy_wild_mana += 1
- # Check state of game
- # If not end game, spawn new spiders
- console_msg: str = ''
- winner: EntityType = None
- if self.my_base.health <= 0:
- console_msg = f'OPPONENT WINS after {self.turns} turns!'
- winner = EntityType.OPPONENT
- if self.canvas:
- self.canvas.unbind_all("<space>")
- elif self.enemy_base.health <= 0:
- console_msg = f'PLAYER WINS after {self.turns} turns!'
- winner = EntityType.PLAYER
- if self.canvas:
- self.canvas.unbind_all("<space>")
- elif self.turns == self.max_turns:
- if self.enemy_base.health > self.my_base.health:
- console_msg = f'OPPONENT WINS!'
- winner = EntityType.OPPONENT
- elif self.enemy_base.health < self.my_base.health:
- console_msg = f'PLAYER WINS!'
- winner = EntityType.PLAYER
- else:
- if self.my_wild_mana == self.enemy_wild_mana:
- console_msg = f'THIS IS A DRAW!'
- winner = EntityType.SPIDER
- else:
- if self.my_wild_mana < self.enemy_wild_mana:
- console_msg = f'OPPONENT WINS!'
- winner = EntityType.OPPONENT
- elif self.my_wild_mana > self.enemy_wild_mana:
- console_msg = f'PLAYER WINS!'
- winner = EntityType.PLAYER
- console_msg += f'\nWILD MANA | Player = {self.my_wild_mana} - Opponent = {self.enemy_wild_mana}'
- if self.canvas:
- self.canvas.unbind_all("<space>")
- else:
- if not self.spawn_cooldown:
- self.spawn_cooldown = self.respawn_value
- spider, symmetric_spider = spawn_spiders(entity_counter=self.entity_counter, spiders_max_level=self.spiders_max_level)
- spider.threat_for = self.threat_for(spider)
- symmetric_spider.threat_for = self.threat_for(symmetric_spider)
- self.entity_counter += 2
- self.spiders.append(spider)
- self.spiders.append(symmetric_spider)
- if self.canvas:
- for s in [spider, symmetric_spider]:
- s.label = Label(self.root)
- s.update_label()
- s.circle = s.create_circle(canvas=self.canvas, radius=s.radius, color='red')
- console_msg = f'BASE HEALTH | Player = {self.my_base.health} - Opponent = {self.my_base.health}'
- console_msg += f'\nMANA | Player = {self.my_mana} - Opponent = {self.enemy_mana}'
- if self.canvas:
- self.console['text'] = console_msg
- self.root.title(f'Spider Attack - Turn #{self.turns}/{self.max_turns}')
- for hero in self.heroes:
- hero.action = None
- return winner, console_msg if not self.canvas else None
- def wood_2_ia(self, player: EntityType):
- base: Entity = self.enemy_base if player == EntityType.OPPONENT else self.my_base
- player_heroes: List[Entity] = [h for h in self.heroes if h.type == player]
- visible_spiders: List[Entity] = [s for s in self.spiders if s.visible]
- if visible_spiders:
- spiders = sorted(self.spiders, key=lambda s: s.dist(base))
- while player_heroes and spiders:
- spider: Entity = spiders.pop(0)
- hero: Entity = min(player_heroes, key=lambda h: h.dist(spider))
- player_heroes.remove(hero)
- hero.action = Action(type='MOVE', target=spider.location)
- for hero in player_heroes:
- if not hero.action:
- hero.action = Action(type='WAIT')
- for hero in player_heroes:
- for h in self.heroes:
- if h.id == hero.id:
- # debug('prout wood 2')
- h.action = hero.action
- def matthis_ia(self, player: EntityType):
- """
- IA for dummies
- """
- player_heroes: List[Entity] = [h for h in self.heroes if h.type == player]
- defenders: List[Entity] = player_heroes[1:]
- attacker: [Entity] = player_heroes[0]
- # debug(f'attacker pp id = {attacker.patrol_point_id} - location = {str(np.round(attacker.location))}')
- spiders: List[Entity] = [s for s in self.spiders if s.visible]
- player_base: Entity = self.my_base if player == EntityType.PLAYER else self.enemy_base
- enemy_base: Entity = self.enemy_base if player == EntityType.PLAYER else self.my_base
- player_mana, enemy_mana = (self.my_mana, self.enemy_mana) if player == EntityType.PLAYER else (self.enemy_mana, self.my_mana)
- """ DEFENSE """
- if spiders:
- spiders_near_my_base: List[Entity] = [s for s in spiders if s.dist(player_base) <= 6500]
- ranked_spiders: List[Entity] = sorted(spiders_near_my_base, key=lambda s: s.dist(player_base))
- while ranked_spiders and defenders:
- spider: Entity = ranked_spiders.pop(0)
- hero: Entity = min(defenders, key=lambda h: h.dist(spider))
- hero.action = Action(type='MOVE', target=spider.location + spider.velocity)
- defenders.remove(hero)
- else:
- default_positions: List[array] = self.get_default_positions(player=player)
- for hero in defenders:
- hero.action = Action(type='MOVE', target=default_positions[player_heroes.index(hero)])
- """ ATTACK """
- spiders_near_enemy_base: List[Entity] = [s for s in spiders if s.dist(enemy_base) <= 6000]
- 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]
- 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]
- 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]
- 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]
- 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]
- if enemies_wind_candidates:
- attacker.action = Action(type='SPELL WIND', target=player_base.location)
- elif enemies_control_candidates:
- enemy: Entity = min(enemies_control_candidates, key=lambda s: s.dist(self.enemy_base))
- attacker.action = Action(type='SPELL CONTROL', entity_id=enemy.id, target=player_base.location)
- elif spiders_wind_candidates:
- attacker.action = Action(type='SPELL WIND', target=enemy_base.location)
- # elif spiders_control_candidates:
- # spider: Entity = max(spiders_control_candidates, key=lambda s: s.dist(self.enemy_base))
- # attacker.action = Action(type='SPELL CONTROL', entity_id=spider.id, target=enemy_base.location)
- elif spiders_shield_candidates:
- spider: Entity = min(spiders_shield_candidates, key=lambda s: s.dist(self.enemy_base))
- attacker.action = Action(type='SPELL SHIELD', entity_id=spider.id)
- else:
- patrol_points = self.get_patrol_points(player=player)
- # debug(f'patrol points = {patrol_points}')
- target: array = attacker.get_next_patrol_point(patrol_points=patrol_points)
- attacker.action = Action(type='MOVE', target=target, message=round(attacker.dist(enemy_base), 2))
- for hero in player_heroes:
- if not hero.action:
- hero.action = Action(type='WAIT')
- for hero in player_heroes:
- for h in self.heroes:
- if h.id == hero.id:
- h.action = hero.action
- def wood_1_ia(self, player: EntityType):
- # needs debuggin
- # ia_bronze_defense
- # spring-challenge-2022-Silver_570_defense_only
- 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)
- my_mana, enemy_mana = (self.my_mana, self.enemy_mana) if player == EntityType.PLAYER else (self.enemy_mana, self.my_mana)
- my_base, enemy_base = (self.my_base, self.enemy_base) if player == EntityType.PLAYER else (self.enemy_base, self.my_base)
- player_heroes: List[Entity] = [h for h in self.heroes if h.type == player]
- spiders: List[Entity] = [s for s in self.spiders if s.visible]
- """ DEFENSE ONLY """
- defenders: List[Entity] = player_heroes
- if spiders:
- ranked_spiders: List[Entity] = sorted(spiders, key=lambda s: s.threat_level(base=my_base), reverse=True)
- while ranked_spiders and defenders:
- 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]
- if my_mana >= 20 and wind_heroes:
- hero = max(wind_heroes, key=lambda x: x[1])[0]
- hero.action = Action(type='SPELL WIND', target=enemy_base.location)
- my_mana -= 10
- else:
- spider: Entity = ranked_spiders.pop(0)
- hero: Entity = min(defenders, key=lambda h: h.dist(spider))
- hero.action = Action(type='MOVE', target=spider.location + spider.velocity)
- defenders.remove(hero)
- 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)
- else:
- default_positions: List[array] = self.get_default_positions(player=player)
- for hero in defenders:
- hero.action = Action(type='MOVE', target=default_positions[player_heroes.index(hero)])
- for hero in player_heroes:
- if not hero.action:
- hero.action = Action(type='WAIT')
- for hero in player_heroes:
- for h in self.heroes:
- if h.id == hero.id:
- h.action = hero.action
- def play_turn(self) -> Tuple[bool, str]:
- """
- IA for Dummies (fonction tour par tour de Codingame)
- :return: None
- """
- ia_player_1 = self.wood_2_ia
- ia_player_1 = self.matthis_ia
- ia_player_2 = self.wood_2_ia
- self.update_player_data(player=EntityType.OPPONENT)
- ia_player_2(player=EntityType.OPPONENT)
- # self.wood_1_ia(player=EntityType.OPPONENT) # BUGGY :-(
- self.update_player_data(player=EntityType.PLAYER)
- ia_player_1(player=EntityType.PLAYER)
- winner, console_msg = self.game_update()
- return winner, console_msg if not self.canvas else True
- def callback(self, event):
- """
- fonction callback utilisé dans le mode graphique
- :param event:
- :return:
- """
- self.play_turn()
- def read_int_value(message: str, max_value: int) -> int:
- while True:
- try:
- print(f'{message} [1-{max_value}] ')
- value = int(input())
- if not value or not 1 <= int(value) <= max_value:
- raise Exception
- break
- except Exception as e:
- print('Bad value!')
- return int(value)
- def graphic_mode_check(message: str) -> bool:
- while True:
- try:
- print(f'{message}')
- response = input()
- if not response or response not in 'GgBb':
- raise Exception
- break
- except Exception as e:
- print('Bad response!')
- return True if response in 'Gg' else False
- if __name__ == "__main__":
- MAP_WIDTH, MAP_HEIGHT = 17630, 9000
- mac_display_resolution = 1920, 1080
- ZOOM = 0.1
- SPAWN_COOLDOWN = 2
- # reduce = lambda x_list: map(lambda x: x * ZOOM, x_list)
- reduce = lambda coord_array: coord_array * ZOOM
- player_score = enemy_score = draws = 0
- max_turns = read_int_value(message="Enter max number of rounds:", max_value=300)
- respawn_value = read_int_value(message="Frequency of respawn spiders:", max_value=10)
- spiders_max_level = read_int_value(message="Maximum level of spiders:", max_value=10)
- run_in_graphic_mode = graphic_mode_check(message="Run game in Graphic/Batch mode [G/B]:")
- if not run_in_graphic_mode:
- max_games = read_int_value(message="Enter max number of games:", max_value=100)
- if run_in_graphic_mode:
- game: Game = Game(max_turns=int(max_turns), respawn_value=respawn_value, spiders_max_level=spiders_max_level)
- game.init()
- game.init_tk()
- else:
- for _ in range(max_games):
- game: Game = Game(max_turns=int(max_turns), respawn_value=respawn_value, spiders_max_level=spiders_max_level)
- game.init()
- winner = None
- while winner is None:
- winner, console_msg = game.play_turn()
- if winner:
- # print(console_msg)
- if winner == EntityType.PLAYER:
- player_score += 1
- elif winner == EntityType.OPPONENT:
- enemy_score += 1
- else:
- draws += 1
- player_score = round(100 * player_score/max_games)
- enemy_score = round(100 * enemy_score/max_games)
- draws = round(100 * draws/max_games)
- print(f'PLAYER: {player_score}% - OPPONENT: {enemy_score}% - DRAWS: {draws}')
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement