Advertisement
Guest User

SC2022 Tkinter simulator

a guest
May 17th, 2022
21
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import math
  2. import random
  3. from dataclasses import dataclass, field
  4. from enum import Enum
  5. from time import sleep
  6. from tkinter import Tk, Canvas, YES, BOTH
  7. from tkinter.ttk import Label
  8. from typing import Any, List, Tuple
  9.  
  10. from numpy import array, cos, sin, dot
  11. from scipy.linalg import norm
  12.  
  13.  
  14. class EntityType(Enum):
  15.     SPIDER = 0
  16.     HERO = 1
  17.     BASE = 2
  18.  
  19.  
  20. @dataclass
  21. class Action:
  22.     type: str
  23.     target: array = None
  24.     entity_id: int = None
  25.     message: str = None
  26.  
  27.     def __repr__(self):
  28.         if self.type == 'MOVE':
  29.             x, y = self.target
  30.             msg: str = f' {self.message}' if self.message else ''
  31.             return f'MOVE {round(x)} {round(y)}{msg}'
  32.         elif self.type == 'SPELL WIND':
  33.             x, y = self.target
  34.             return f'{self.type} {round(x)} {round(y)}'
  35.         elif self.type == 'SPELL SHIELD':
  36.             return f'{self.type} {self.entity_id}'
  37.         elif self.type == 'SPELL CONTROL':
  38.             x, y = self.target
  39.             return f'{self.type} {self.entity_id} {round(x)} {round(y)}'
  40.  
  41.  
  42. @dataclass
  43. class Entity:
  44.     id: int
  45.     x: int
  46.     y: int
  47.     vx: int
  48.     vy: int
  49.     radius: int
  50.     speed: int
  51.     type: EntityType
  52.     health: int
  53.     action: Action = None
  54.     location: array = field(init=False)  # calculated value after instance creation
  55.     velocity: array = field(init=False)  # calculated value after instance creation
  56.     label: Any = field(init=False)
  57.     circle: Any = field(init=False)
  58.  
  59.     def __post_init__(self):
  60.         self.location = array([self.x, self.y], dtype=float)
  61.         self.velocity = array([self.vx, self.vy], dtype=float)
  62.  
  63.     def dist(self, other):
  64.         return norm(self.location - other.location) if isinstance(other, Entity) else norm(self.location - other)
  65.  
  66.     def move(self, target):
  67.         if isinstance(target, Entity):
  68.             u: array = target.location - self.location
  69.         else:
  70.             u: array = target - self.location
  71.         v: array = u / norm(u) if norm(u) > 0 else 0
  72.         speed: float = min(self.speed, self.dist(target))
  73.         return self.location + speed * v
  74.  
  75.  
  76. @dataclass
  77. class Block:
  78.     canvas: Canvas
  79.     entities: List[int]
  80.  
  81.  
  82. def inside_map(e: Entity) -> bool:
  83.     x, y = e.location
  84.     return 0 <= x < MAP_WIDTH and 0 <= y < MAP_HEIGHT
  85.  
  86.  
  87. def spawn_spiders(entity_counter: int) -> Tuple[Entity, Entity]:
  88.     hp_list = [10, 20, 30]
  89.     hp_spider: int = random.choice(hp_list)
  90.     top_left, top_right = 5000, MAP_WIDTH
  91.     random_x: int = random.randint(top_left, top_right)
  92.     dir: int = random.randint(1, 5)
  93.     theta = dir * math.pi / 6
  94.     # theta = radians(angle)
  95.     c, s = cos(theta), sin(theta)
  96.     R = array(((c, -s), (s, c)))
  97.     u_x: array = array([1, 0])
  98.     vx, vy = 400 * dot(R, u_x)
  99.     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)
  100.     x, y = array([MAP_WIDTH, MAP_HEIGHT]) - spider.location
  101.     vx, vy = -spider.velocity
  102.     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)
  103.     return spider, symmetric_spider
  104.  
  105.  
  106. @dataclass
  107. class Game:
  108.     max_turns: int
  109.     spawn_cooldown: int
  110.     root: Tk = Tk()
  111.     canvas: Canvas = None
  112.     dbg_label: Label = None
  113.     base: array = None
  114.     detect_range: array = None
  115.     map_width: int = None
  116.     map_height: int = None
  117.     reduce = None
  118.     kills = 0
  119.     turns = 1
  120.     entity_counter = 0
  121.     heroes: List[Entity] = field(default_factory=list)
  122.     spiders: List[Entity] = field(default_factory=list)
  123.  
  124.     def init(self):
  125.         self.base = Entity(id=None, health=3, type=EntityType.BASE, x=0, y=0, vx=0, vy=0, radius=300, speed=0)
  126.         self.detect_range = Entity(id=None, health=math.inf, type=EntityType.BASE, x=0, y=0, vx=0, vy=0, radius=5000, speed=0)
  127.         hero_1 = Entity(id=0, health=math.inf, type=EntityType.HERO, x=1414, y=849, vx=0, vy=0, radius=800, speed=800)
  128.         hero_2 = Entity(id=1, health=math.inf, type=EntityType.HERO, x=1131, y=1131, vx=0, vy=0, radius=800, speed=800)
  129.         hero_3 = Entity(id=2, health=math.inf, type=EntityType.HERO, x=849, y=1414, vx=0, vy=0, radius=800, speed=800)
  130.         self.heroes.append(hero_1)
  131.         self.heroes.append(hero_2)
  132.         self.heroes.append(hero_3)
  133.         self.entity_counter = len(self.heroes)
  134.         spider, symmetric_spider = spawn_spiders(entity_counter=self.entity_counter)
  135.         self.entity_counter += 2
  136.         self.spiders.append(spider)
  137.         self.spiders.append(symmetric_spider)
  138.  
  139.     def init_tk(self, map_width: int, map_height: int, zoom: int):
  140.         self.canvas = Canvas(width=map_width * zoom, height=map_height * zoom, bg='white')
  141.         self.canvas.pack(expand=YES, fill=BOTH)
  142.  
  143.         self.dbg_label = Label(self.root, text="Hello World!")
  144.         self.dbg_label.pack(pady=20)
  145.  
  146.         self.reduce = lambda x_list: map(lambda x: x * zoom, x_list)
  147.  
  148.         # Entities
  149.         self.base.circle = self.draw_circle(entity=self.base, color='green')
  150.         self.detect_range.circle = self.draw_circle(entity=self.detect_range, color='orange')
  151.  
  152.         for e in self.heroes + self.spiders:
  153.             e.label = Label(self.root)
  154.             self.update_label(entity=e)
  155.             e.label.pack(pady=20)
  156.             color = 'red' if e.type == EntityType.SPIDER else 'blue'
  157.             e.circle = self.draw_circle(entity=e, color=color)
  158.  
  159.         self.canvas.bind_all("<space>", self.callback)
  160.         self.root.mainloop()
  161.  
  162.     # All of this would do better as a subclass of Canvas with specialize methods
  163.  
  164.     def draw_circle(self, entity: Entity, color: str):
  165.         # changed this to return the ID
  166.         x, y = entity.location
  167.         oval = x - entity.radius, y - entity.radius, x + entity.radius, y + entity.radius
  168.         x0, y0, x1, y1 = self.reduce(oval)
  169.         return self.canvas.create_oval(x0, y0, x1, y1, width=1, outline=color)
  170.  
  171.     def move_circle(self, entity: Entity, new_location: array):
  172.         dx, dy = self.reduce(new_location - entity.location)
  173.         self.canvas.move(entity.circle, dx, dy)
  174.  
  175.     def update_label(self, entity: Entity):
  176.         if entity.type == EntityType.SPIDER:
  177.             x, y = self.reduce(entity.location)
  178.             entity.label.place(x=x, y=y)
  179.             entity.label['text'] = f"{entity.health}"
  180.         elif entity.type == EntityType.HERO:
  181.             x, y = self.reduce(entity.location)
  182.             entity.label.place(x=x, y=y)
  183.             entity.label['text'] = f"{entity.id}"
  184.         else:  # Base
  185.             x, y = self.reduce(entity.location)
  186.             entity.label.place(x=x + 10, y=y + 10)
  187.             entity.label['text'] = f"{entity.health}"
  188.  
  189.     def update(self):
  190.         self.turns += 1
  191.         self.spawn_cooldown -= 1 if self.spawn_cooldown else 0
  192.         for hero in self.heroes:
  193.             if hero.action.type != 'WAIT':
  194.                 self.move_circle(entity=hero, new_location=hero.action.target)
  195.                 hero.location = hero.move(target=hero.action.target)
  196.                 self.update_label(entity=hero)
  197.             for spider in self.spiders:
  198.                 if hero.dist(spider) <= hero.radius:
  199.                     spider.health -= 2
  200.                     if spider.health <= 0:
  201.                         self.canvas.delete(spider.circle)
  202.                         spider.label.destroy()
  203.                         self.spiders.remove(spider)
  204.                         self.kills += 1
  205.  
  206.         for spider in self.spiders:
  207.             new_location = None
  208.             if spider.dist(self.base) > 5000:
  209.                 new_location = spider.location + spider.velocity
  210.                 self.move_circle(entity=spider, new_location=new_location)
  211.                 spider.location = spider.move(target=new_location)
  212.             elif not inside_map(spider):
  213.                 self.canvas.delete(spider.circle)
  214.                 spider.label.destroy()
  215.                 self.spiders.remove(spider)
  216.             elif spider.dist(self.base) <= 300:
  217.                 self.base.health -= 1
  218.                 self.canvas.delete(spider.circle)
  219.                 spider.label.destroy()
  220.                 self.spiders.remove(spider)
  221.             else:
  222.                 spider.location = spider.move(self.base)
  223.             if new_location is not None:
  224.                 self.update_label(entity=spider)
  225.  
  226.         if self.base.health <= 0:
  227.             self.dbg_label['text'] = f'SPIDERS WINS!'
  228.             self.canvas.unbind_all("<space>")
  229.         elif self.turns >= self.max_turns:
  230.             self.dbg_label['text'] = f'HEROES WINS! SPIDERS KILLED = {self.kills}'
  231.             self.canvas.unbind_all("<space>")
  232.         elif not self.spawn_cooldown:
  233.             self.spawn_cooldown = 5
  234.             spider, symmetric_spider = spawn_spiders(entity_counter=self.entity_counter)
  235.             self.entity_counter += 2
  236.             self.spiders.append(spider)
  237.             self.spiders.append(symmetric_spider)
  238.             for s in [spider, symmetric_spider]:
  239.                 s.label = Label(self.root)
  240.                 self.update_label(entity=s)
  241.                 s.label.pack(pady=20)
  242.                 s.circle = self.draw_circle(entity=s, color='red')
  243.  
  244.         self.dbg_label['text'] = f'Round #{self.turns}/{self.max_turns} - SPIDERS Kills = {self.kills}!'
  245.  
  246.         for hero in self.heroes:
  247.             hero.action = None
  248.  
  249.     def callback(self, event):
  250.         """
  251.            Play next turn on click event
  252.        :param event:
  253.        :return:
  254.        """
  255.         spiders = sorted(self.spiders, key=lambda m: m.dist(self.base))
  256.         heroes = self.heroes[:]
  257.         while heroes and spiders:
  258.             spider: Entity = spiders.pop(0)
  259.             hero: Entity = min(heroes, key=lambda h: h.dist(spider))
  260.             heroes.remove(hero)
  261.             hero.action = Action(type='MOVE', target=spider.location)
  262.         for hero in heroes:
  263.             if not hero.action:
  264.                 hero.action = Action(type='WAIT')
  265.         self.update()
  266.  
  267.  
  268. if __name__ == "__main__":
  269.     MAP_WIDTH, MAP_HEIGHT = 17630, 9000
  270.     mac_display_resolution = 1920, 1080
  271.     ZOOM = 0.1
  272.  
  273.     print('Enter max number of rounds: [1-200] ')
  274.     max_turns = input()
  275.     while not isinstance(max_turns, int) and not 1 <= int(max_turns) <= 200:
  276.         print('Enter max number of rounds: [1-200] ')
  277.         max_turns = input()
  278.     game: Game = Game(max_turns=int(max_turns), spawn_cooldown=5)
  279.     game.init()
  280.     game.init_tk(map_width=MAP_WIDTH, map_height=MAP_HEIGHT, zoom=ZOOM)
  281.  
Advertisement
RAW Paste Data Copied
Advertisement