├── README.md ├── start.sh ├── .gitignore ├── base ├── main_player.py ├── formation_dt │ ├── before_kick_off.conf │ ├── goalie_kick_opp_formation.conf │ ├── goalie_kick_our_formation.conf │ ├── kickin_our_formation.conf │ ├── setplay_opp_formation.conf │ └── setplay_our_formation.conf ├── decision.py ├── set_play │ ├── bhv_set_play_before_kick_off.py │ └── bhv_set_play.py ├── bhv_move.py ├── strategy.py ├── strategy_formation.py └── bhv_kick.py ├── lib ├── math │ ├── geom_2d.py │ ├── region_2d.py │ ├── README.md │ ├── size_2d.py │ ├── ray_2d.py │ ├── sector_2d.py │ ├── angle_deg.py │ ├── line_2d.py │ ├── circle_2d.py │ ├── soccer_math.py │ ├── matrix_2d.py │ ├── convex_hull.py │ └── polygon_2d.py ├── rcsc │ ├── game_time.py │ ├── game_mode.py │ ├── types.py │ └── player_type.py ├── player │ ├── object.py │ ├── object_ball.py │ ├── templates.py │ ├── stamina_model.py │ ├── soccer_action.py │ ├── player_agent.py │ ├── object_player.py │ └── world_model.py ├── debug │ ├── level.py │ ├── color.py │ └── logger.py ├── player_command │ ├── player_command_sender.py │ ├── player_command_support.py │ ├── player_command.py │ └── player_command_body.py ├── network │ └── udp_socket.py ├── action │ ├── stop_ball.py │ ├── intercept_table.py │ ├── intercept_info.py │ └── go_to_point.py ├── parser │ ├── parser_message_params.py │ └── parser_message_fullstate_world.py └── formation │ └── delaunay_triangulation.py └── accessor_maker.py /README.md: -------------------------------------------------------------------------------- 1 | # PYRUS 2 | **Py**thon Cy**rus** Base -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export PYTHONPATH="${PYTHONPATH}:`pwd`" 4 | 5 | ./base/main_player.py g & 6 | 7 | i=1 8 | while [ $i -le 11 ] ; do 9 | ./base/main_player.py & 10 | sleep 0.01 11 | 12 | i=`expr $i + 1` 13 | done 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | */__pycache__/ 3 | */__pycache__/* 4 | *.pyc 5 | venv/ 6 | lib/network/__pycache__/udp_socket.cpython-36.pyc 7 | lib/Player/__pycache__/ 8 | lib/math/__pycache__/ 9 | variables 10 | accessors 11 | *.log 12 | main_test.py 13 | todos.py -------------------------------------------------------------------------------- /base/main_player.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | from lib.player.player_agent import PlayerAgent 4 | import sys 5 | 6 | 7 | def main(team_name="Pyrus", goalie=False): 8 | player_agent = PlayerAgent() 9 | player_agent.run(team_name, goalie) 10 | 11 | 12 | if __name__ == "__main__": 13 | goalie = False 14 | if len(sys.argv) > 1 and sys.argv[1] == "g": 15 | goalie = True 16 | main(goalie=goalie) 17 | -------------------------------------------------------------------------------- /lib/math/geom_2d.py: -------------------------------------------------------------------------------- 1 | from lib.math.angle_deg import * 2 | from lib.math.vector_2d import * 3 | from lib.math.line_2d import * 4 | from lib.math.ray_2d import * 5 | from lib.math.sector_2d import * 6 | from lib.math.triangle_2d import * 7 | from lib.math.segment_2d import * 8 | from lib.math.matrix_2d import * # not necessary but optimized for 2d 9 | from lib.math.circle_2d import * 10 | from lib.math.rect_2d import * 11 | from lib.math.polygon_2d import * -------------------------------------------------------------------------------- /base/formation_dt/before_kick_off.conf: -------------------------------------------------------------------------------- 1 | Formation Static 2 | # --------------------------------------------------------- 3 | # move positions when playmode is BeforeKickOff or AfterGoal. 4 | 1 Goalie -49.0 0.0 5 | 2 CenterBack -25.0 -5.0 6 | 3 CenterBack -25.0 5.0 7 | 4 SideBack -25.0 -10.0 8 | 5 SideBack -25.0 10.0 9 | 6 DefensiveHalf -25.0 0.0 10 | 7 OffensiveHalf -15.0 -5.0 11 | 8 OffensiveHalf -15.0 5.0 12 | 9 SideForward -15.0 -10.0 13 | 10 SideForward -15.0 10.0 14 | 11 CenterForward -15.0 0.0 15 | # --------------------------------------------------------- 16 | -------------------------------------------------------------------------------- /base/formation_dt/goalie_kick_opp_formation.conf: -------------------------------------------------------------------------------- 1 | Formation Static 2 | # --------------------------------------------------------- 3 | # for opponent goal kick 4 | 1 Goalie -49.0 0.0 5 | 2 CenterBack 0.0 -5.0 6 | 3 CenterBack 0.0 5.0 7 | 4 SideBack 0.0 -12.0 8 | 5 SideBack 0.0 12.0 9 | 6 DefensiveHalf 10.0 0.0 10 | 7 OffensiveHalf 15.0 -12.0 11 | 8 OffensiveHalf 15.0 12.0 12 | 9 SideForward 31.0 -17.5 13 | 10 SideForward 31.0 17.5 14 | 11 CenterForward 31.0 0.0 15 | # --------------------------------------------------------- 16 | -------------------------------------------------------------------------------- /base/formation_dt/goalie_kick_our_formation.conf: -------------------------------------------------------------------------------- 1 | Formation Static 2 | # --------------------------------------------------------- 3 | # for our goalie catch 4 | 1 Goalie -49.0 0.0 5 | 2 CenterBack -44.5 -4.0 6 | 3 CenterBack -44.5 4.0 7 | 4 SideBack -42.0 -24.0 8 | 5 SideBack -42.0 24.0 9 | 6 DefensiveHalf -32.0 0.0 10 | 7 OffensiveHalf -32.5 -13.0 11 | 8 OffensiveHalf -32.5 13.0 12 | 9 SideForward -13.0 -27.0 13 | 10 SideForward -13.0 27.0 14 | 11 CenterForward -13.0 0.0 15 | # --------------------------------------------------------- 16 | -------------------------------------------------------------------------------- /base/decision.py: -------------------------------------------------------------------------------- 1 | from base.strategy_formation import StrategyFormation 2 | from base.set_play.bhv_set_play import Bhv_SetPlay 3 | from lib.player.templates import * 4 | from base.bhv_kick import BhvKick 5 | from base.bhv_move import BhvMove 6 | 7 | 8 | def get_decision(agent): 9 | wm: WorldModel = agent.world() 10 | st = StrategyFormation().i() 11 | st.update(wm) 12 | 13 | if wm.game_mode().type() != GameModeType.PlayOn: 14 | return Bhv_SetPlay().execute(agent) 15 | if wm.self().is_kickable(): 16 | return BhvKick().execute(agent) 17 | return BhvMove().execute(agent) 18 | -------------------------------------------------------------------------------- /lib/rcsc/game_time.py: -------------------------------------------------------------------------------- 1 | class GameTime: 2 | def __init__(self, cycle: int = 0, stopped_cycle: int = 0): 3 | self._cycle = cycle 4 | self._stopped_cycle = stopped_cycle 5 | 6 | def cycle(self): 7 | return self._cycle 8 | 9 | def stopped_cycle(self): 10 | return self._stopped_cycle 11 | 12 | def add_cycle(self, cycle: int): 13 | self._cycle += cycle 14 | 15 | def add_stopped_cycle(self, stopped_cycle): 16 | self._stopped_cycle += stopped_cycle 17 | 18 | def __repr__(self): 19 | return f"{self.cycle()}" 20 | 21 | def __eq__(self, other): 22 | return self.cycle() == other.cycle() 23 | -------------------------------------------------------------------------------- /lib/math/region_2d.py: -------------------------------------------------------------------------------- 1 | """ 2 | \ file region_2d.h 3 | \ brief abstract 2D region class File. 4 | 5 | """ 6 | 7 | from lib.math.vector_2d import * 8 | 9 | 10 | class Region2D: 11 | """ 12 | \ brief accessible only from derived classes 13 | """ 14 | 15 | def __init__(self): 16 | pass 17 | 18 | """ 19 | \ brief get the area of this region 20 | \ return value of the area 21 | """ 22 | 23 | def area(self): 24 | pass 25 | 26 | """ 27 | \ brief check if this region contains 'point'. 28 | \ param point considered point 29 | \ return true or false 30 | """ 31 | 32 | def contains(self, point: Vector2D): 33 | pass 34 | -------------------------------------------------------------------------------- /base/set_play/bhv_set_play_before_kick_off.py: -------------------------------------------------------------------------------- 1 | from lib.debug.level import Level 2 | from lib.debug.logger import dlog 3 | from lib.math.angle_deg import AngleDeg 4 | from base.strategy_formation import StrategyFormation 5 | 6 | class Bhv_BeforeKickOff: 7 | def __init__(self): 8 | pass 9 | 10 | def execute(self, agent): 11 | unum = agent.world().self().unum() 12 | st = StrategyFormation.i() 13 | target = st.get_pos(unum) 14 | # dlog.add_text(Level.BLOCK, f"unum: {unum}") 15 | # agent.do_move(AngleDeg.cos_deg(AngleDeg.PI / 11 * unum) * 20, AngleDeg.sin_deg(AngleDeg.PI / 11 * unum) * 20) TODO not working :( 16 | agent.do_move(target.x(), target.y()) 17 | return True 18 | -------------------------------------------------------------------------------- /lib/player/object.py: -------------------------------------------------------------------------------- 1 | from lib.math.geom_2d import * 2 | 3 | 4 | class Object: 5 | def __init__(self): 6 | self._pos = Vector2D.invalid() 7 | self._vel = Vector2D.invalid() 8 | 9 | def pos(self) -> Vector2D: 10 | return self._pos.copy() # TODO How it is?!? 11 | 12 | def vel(self) -> Vector2D: 13 | return self._vel.copy() # TODO How it is?!? 14 | 15 | def reverse(self): 16 | self._pos.reverse() 17 | self._vel.reverse() 18 | self.reverse_more() 19 | 20 | def reverse_more(self): 21 | pass 22 | 23 | @staticmethod 24 | def reverse_list(lst): 25 | for i in range(len(lst)): 26 | lst[i].reverse() 27 | 28 | def update_with_world(self, wm): 29 | pass -------------------------------------------------------------------------------- /lib/player/object_ball.py: -------------------------------------------------------------------------------- 1 | from lib.player.object import * 2 | 3 | 4 | class BallObject(Object): 5 | def __init__(self, string=None): 6 | super().__init__() 7 | self._dist_from_self: float = 10000 8 | if string is None: 9 | return 10 | self.init_str(string) 11 | 12 | def init_str(self, string: str): 13 | data = string.split(" ") 14 | self._pos = Vector2D(float(data[0]), float(data[1])) 15 | self._vel = Vector2D(float(data[2]), float(data[3])) 16 | 17 | def update_with_world(self, wm): 18 | self._dist_from_self = wm.self().pos().dist(self._pos) 19 | 20 | def dist_from_self(self): 21 | return self._dist_from_self 22 | 23 | def __repr__(self): 24 | return f"(pos: {self.pos()}) (vel:{self.vel()})" 25 | -------------------------------------------------------------------------------- /accessor_maker.py: -------------------------------------------------------------------------------- 1 | def main(): 2 | file = open("variables", "r") 3 | lines = file.read().split("\n") 4 | 5 | # make list of variables 6 | variables = [] 7 | for line in lines: 8 | if line.find("self") == -1: 9 | continue 10 | if line.find(":") != -1: 11 | if line.find(":") < line.find("="): 12 | variables.append(line.split(":")[0].strip()) 13 | continue 14 | if line.find("=") != -1: 15 | variables.append(line.split("=")[0].strip()) 16 | continue 17 | variables.append(line.strip()) 18 | 19 | # make accessors 20 | accessors = [] 21 | for var in variables: 22 | acc = f"def {var[6:]}(self):\n" \ 23 | f"\treturn {var}" 24 | accessors.append(acc) 25 | 26 | # write in file 27 | file = open("accessors", "w") 28 | for acc in accessors: 29 | file.write(acc + "\n") 30 | 31 | 32 | if __name__ == "__main__": 33 | main() 34 | -------------------------------------------------------------------------------- /lib/math/README.md: -------------------------------------------------------------------------------- 1 | # Geom2D | Soccer_Math 2 | 3 | Geom is a Python library for dealing with math and geometry problems in SS2D. 4 | 5 | ## Installation 6 | 7 | Use the this include to install Geom. 8 | 9 | ```python 10 | from lib.math.geom import * 11 | ``` 12 | 13 | ## Usage 14 | 15 | ```python 16 | from lib.math.soccer_math import * 17 | 18 | vec1 = Vector2D() # create Vector with default value. 19 | vec2 = Vector2D(20,20) # create Vector with 20 , 20 value directly. 20 | a = Line2D(vec1,vec2) # create line from 2 points. 21 | b = Circle2D(3,3,5) # create circle with center point and radius value. 22 | print(b.intersection(a)) # calculate the intersection with straight line. 23 | ``` 24 | 25 | ## Contributing 26 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. 27 | 28 | Please make sure to update tests as appropriate. 29 | 30 | ## TODO 31 | triangulation | 32 | delaunay_triangulation | 33 | composite_region_2d | 34 | voronoi_diagram 35 | -------------------------------------------------------------------------------- /lib/debug/level.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class Level(Enum): 5 | SYSTEM = 0x00000001 6 | SENSOR = 0x00000002 7 | WORLD = 0x00000004 8 | ACTION = 0x00000008 9 | INTERCEPT = 0x00000010 10 | KICK = 0x00000020 11 | HOLD = 0x00000040 12 | DRIBBLE = 0x00000080 13 | PASS = 0x00000100 14 | CROSS = 0x00000200 15 | SHOOT = 0x00000400 16 | CLEAR = 0x00000800 17 | BLOCK = 0x00001000 18 | MARK = 0x00002000 19 | POSITIONING = 0x00004000 20 | ROLE = 0x00008000 21 | PLAN = 0x00010000 22 | TEAM = 0x00020000 23 | COMMUNICATION = 0x00040000 24 | ANALYZER = 0x00080000 25 | ACTION_CHAIN = 0x00100000 26 | LEVEL_ANY = 0xffffffff 27 | 28 | # unused Levels :\ 29 | LEVEL_00 = 0x00000000 30 | LEVEL_22 = 0x00200000 31 | LEVEL_23 = 0x00400000 32 | LEVEL_24 = 0x00800000 33 | LEVEL_25 = 0x01000000 34 | LEVEL_26 = 0x02000000 35 | LEVEL_27 = 0x04000000 36 | LEVEL_28 = 0x08000000 37 | LEVEL_29 = 0x10000000 38 | LEVEL_30 = 0x20000000 39 | LEVEL_31 = 0x40000000 40 | LEVEL_32 = 0x80000000 41 | -------------------------------------------------------------------------------- /lib/debug/color.py: -------------------------------------------------------------------------------- 1 | class Color: 2 | def __init__(self, red: int = 0, green: int = 0, blue: int = 0, string: str = None): 3 | self._hex: str = "" 4 | if string is None: 5 | red = ('0' if red < 16 else "") + hex(red).split("x")[1] 6 | green = ('0' if green < 16 else "") + hex(green).split("x")[1] 7 | blue = ('0' if blue < 16 else "") + hex(blue).split("x")[1] 8 | self._hex = f"#{red}{green}{blue}" 9 | else: 10 | if string[0] == '#': 11 | self._hex = string 12 | elif string == "white": 13 | self.__init__(255, 255, 255) 14 | elif string == "black": 15 | self.__init__(0, 0, 0) 16 | elif string == "red": 17 | self.__init__(255, 0, 0) 18 | elif string == "green": 19 | self.__init__(0, 255, 0) 20 | elif string == "blue": 21 | self.__init__(0, 0, 255) 22 | 23 | def hex(self): 24 | return self._hex 25 | 26 | def color(self): 27 | return self._hex 28 | 29 | def __repr__(self): 30 | return self._hex 31 | -------------------------------------------------------------------------------- /base/bhv_move.py: -------------------------------------------------------------------------------- 1 | from lib.action.go_to_point import * 2 | from base.strategy_formation import * 3 | from lib.debug.logger import * 4 | from lib.player.templates import * 5 | 6 | 7 | class BhvMove: 8 | def __init__(self): 9 | pass 10 | 11 | def execute(self, agent: PlayerAgent): 12 | st = StrategyFormation().i() 13 | wm: WorldModel = agent.world() 14 | dlog.add_line(Level.BLOCK, start=wm.self().pos(), end=wm.ball().pos(), color=Color(string="black")) 15 | dlog.add_text(Level.BLOCK, f"Test {wm.self().pos()}") # Aref come on :))) # HA HA HA HA :D 16 | dlog.add_circle(cicle=Circle2D(wm.self().pos(), 3), color=Color(string="blue")) 17 | target = st.get_pos(agent.world().self().unum()) 18 | min_dist_ball = 1000 19 | nearest_tm = 0 20 | for u in range(1, 12): 21 | tm = wm.our_player(u) 22 | if tm.unum() is not 0: 23 | dist = tm.pos().dist(wm.ball().pos()) 24 | if dist < min_dist_ball: 25 | min_dist_ball = dist 26 | nearest_tm = u 27 | if nearest_tm == wm.self().unum(): 28 | target = wm.ball().pos() 29 | 30 | GoToPoint(target, 1, 100).execute(agent) 31 | return True 32 | -------------------------------------------------------------------------------- /lib/player/templates.py: -------------------------------------------------------------------------------- 1 | from lib.math.angle_deg import AngleDeg 2 | from lib.player.object_ball import BallObject 3 | from lib.player.object_player import PlayerObject 4 | from lib.rcsc.game_mode import GameMode 5 | from lib.rcsc.game_time import GameTime 6 | from lib.rcsc.types import SideID, GameModeType 7 | 8 | class WorldModel: 9 | def ball(self) -> BallObject: ... 10 | 11 | def self(self) -> PlayerObject: ... 12 | 13 | def our_side(self) -> SideID: ... 14 | 15 | def our_player(self, unum): ... 16 | 17 | def their_player(self, unum): ... 18 | 19 | def time(self) -> GameTime: ... 20 | 21 | def team_name(self) -> str: ... 22 | 23 | def game_mode(self) -> GameMode: ... 24 | 25 | def our_goalie_unum(self) -> int: ... 26 | 27 | def _set_our_goalie_unum(self): ... 28 | 29 | def teammates_from_ball(self): ... 30 | 31 | def _set_teammates_from_ball(self): ... 32 | 33 | def last_kicker_side(self) -> SideID: ... 34 | 35 | 36 | class PlayerAgent: 37 | def do_dash(self, power, angle=0) -> bool: ... 38 | 39 | def do_turn(self, angle) -> bool: ... 40 | 41 | def do_move(self, x, y) -> bool: ... 42 | 43 | def do_kick(self, power: float, rel_dir: AngleDeg) -> bool: ... 44 | 45 | def world(self) -> WorldModel: ... 46 | 47 | def full_world(self) -> WorldModel: ... 48 | 49 | def init_dlog(self, message: str): ... 50 | -------------------------------------------------------------------------------- /lib/player_command/player_command_sender.py: -------------------------------------------------------------------------------- 1 | from lib.player_command.player_command import PlayerCommand 2 | from lib.player_command.player_command_body import PlayerBodyCommand 3 | from lib.player_command.player_command_support import PlayerSupportCommand 4 | 5 | 6 | class PlayerSendCommands(PlayerCommand): 7 | @staticmethod 8 | def all_to_str(commands): 9 | commands_msg = "" 10 | last_body_command = PlayerCommand() 11 | for command in commands: 12 | if type(command) in PlayerBodyCommand.body_commands: 13 | last_body_command = command 14 | else: 15 | commands_msg += command.str() 16 | commands_msg = last_body_command.str() + commands_msg 17 | return commands_msg 18 | 19 | 20 | class PlayerCommandReverser(PlayerCommand): 21 | @staticmethod 22 | def reverse(commands): 23 | for i in range(len(commands)): 24 | # if "_dir" in commands[i].__dict__: 25 | # commands[i]._dir = PlayerCommandReverser.reverse_deg(commands[i]._dir) 26 | if "_moment" in commands[i].__dict__: 27 | commands[i]._moment = PlayerCommandReverser.reverse_deg(commands[i]._moment) 28 | 29 | @staticmethod 30 | def reverse_deg(dir): 31 | if dir > 0: 32 | dir = 180 - dir 33 | elif dir < 0: 34 | dir = 180 + dir 35 | return dir 36 | -------------------------------------------------------------------------------- /lib/network/udp_socket.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | MAX_BUFF_SIZE = 8192 4 | 5 | 6 | class IPAddress: 7 | def __init__(self, ip, port): 8 | self._ip = ip 9 | self._port = port 10 | 11 | def tuple(self) -> tuple: 12 | return self.ip(), self.port() 13 | 14 | def __repr__(self): 15 | return self.ip(), self.port() 16 | 17 | def __str__(self): 18 | return f"({self.ip()}:{self.port()}" 19 | 20 | def ip(self): 21 | return self._ip 22 | 23 | def port(self): 24 | return self._port 25 | 26 | 27 | class UDPSocket: 28 | def __init__(self, ip_address: IPAddress): 29 | self._ip: IPAddress = ip_address 30 | self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 31 | self._sock.settimeout(0.01) # TODO isn't this risky?!?!? 32 | 33 | def send_msg(self, msg: str): 34 | if msg[-1] != '\0': 35 | msg += '\0' 36 | self._sock.sendto(msg.encode(), self._ip.tuple()) 37 | 38 | def recieve_msg(self, message_and_address): 39 | try: 40 | message, server_address = self._sock.recvfrom(MAX_BUFF_SIZE) 41 | except: 42 | message = "" 43 | server_address = 0 44 | message_and_address.clear() 45 | message_and_address.append(message) 46 | message_and_address.append(server_address) 47 | 48 | return len(message) 49 | 50 | -------------------------------------------------------------------------------- /lib/action/stop_ball.py: -------------------------------------------------------------------------------- 1 | """ 2 | \ file body_stop_ball.py 3 | \ brief kick the ball to keep a current positional relation. 4 | """ 5 | from lib.player.soccer_action import * 6 | from lib.math.geom_2d import * 7 | 8 | """ 9 | \ class Body_StopBall 10 | \ brief stop the ball, possible as. 11 | """ 12 | 13 | 14 | class StopBall(BodyAction): 15 | """ 16 | \ brief accessible from global. 17 | """ 18 | 19 | def __init__(self): 20 | super().__init__() 21 | 22 | """ 23 | \ brief execute action 24 | \ param agent pointer to the agent itself 25 | \ return True if action is performed 26 | """ 27 | 28 | def execute(self, agent: PlayerAgent): 29 | wm: WorldModel = agent.world() 30 | if not wm.self().is_kickable(): 31 | return False 32 | # if not wm.ball().velValid() nice :) 33 | accel_radius = 0.0 34 | accel_angle = AngleDeg() 35 | 36 | # calcAccel(agent, accel_radius, accel_angle) 37 | 38 | if accel_radius < 0.02: 39 | agent.do_turn(0.0) 40 | return False 41 | kick_power = 0.0 42 | # kick_power = accel_radius / wm.self().kickRate() 43 | # kick_power = min(kick_power, i.maxPower()) 44 | 45 | return agent.do_kick(kick_power, 46 | accel_angle - wm.self().body()) 47 | 48 | # def calcAccel(self, agent, accel_radius: float, accel_angle: AngleDeg): 49 | -------------------------------------------------------------------------------- /base/strategy.py: -------------------------------------------------------------------------------- 1 | from lib.math.geom_2d import * 2 | 3 | 4 | class _Strategy: 5 | def __init__(self): 6 | self._base_poses = [] 7 | self._base_poses.append([Vector2D(-45, 0), 7, 10]) 8 | self._base_poses.append([Vector2D(-30, -10), 7, 10]) 9 | self._base_poses.append([Vector2D(-30, 10), 7, 10]) 10 | self._base_poses.append([Vector2D(-30, -20), 7, 10]) 11 | self._base_poses.append([Vector2D(-30, 20), 7, 10]) 12 | self._base_poses.append([Vector2D(0, -15), 15, 15]) 13 | self._base_poses.append([Vector2D(0, 15), 15, 15]) 14 | self._base_poses.append([Vector2D(0, 0), 15, 15]) 15 | self._base_poses.append([Vector2D(30, -15), 15, 15]) 16 | self._base_poses.append([Vector2D(30, 15), 15, 15]) 17 | self._base_poses.append([Vector2D(30, 0), 15, 15]) 18 | self._poses = [Vector2D(0, 0) for i in range(11)] 19 | 20 | def update(self, wm): 21 | ball_pos = wm.ball().pos() 22 | for p in range(len(self._poses)): 23 | x = ball_pos.x() / 52.5 * self._base_poses[p - 1][1] + self._base_poses[p - 1][0].x() 24 | y = ball_pos.y() / 52.5 * self._base_poses[p - 1][1] + self._base_poses[p - 1][0].y() 25 | self._poses[p - 1] = Vector2D(x, y) 26 | 27 | def get_pos(self, unum): 28 | return self._poses[unum - 1] 29 | 30 | 31 | class Strategy: 32 | _i: _Strategy = _Strategy() 33 | 34 | @staticmethod 35 | def i() -> _Strategy: 36 | return Strategy._i 37 | -------------------------------------------------------------------------------- /lib/parser/parser_message_params.py: -------------------------------------------------------------------------------- 1 | class MessageParamsParser: 2 | def __init__(self): 3 | self._dic = {} 4 | 5 | @staticmethod 6 | def _parse(dic, string: str): 7 | string = string.strip(" ()") 8 | if len(string) < 3: 9 | return 10 | key = string.split(" ")[0].strip("()") 11 | value = string[string.find(" "):] 12 | if not MessageParamsParser.need_dict(value): 13 | if value.find(")") == -1: 14 | dic[key] = value.strip() 15 | else: 16 | dic[key] = value[:value.find(")")].strip() 17 | MessageParamsParser._parse(dic, value[value.find(")"):]) 18 | else: 19 | dic[key] = {} 20 | end_of_dic = MessageParamsParser.end_of_dic(value) 21 | MessageParamsParser._parse(dic[key], value[:end_of_dic]) 22 | MessageParamsParser._parse(dic, value[end_of_dic:]) 23 | 24 | @staticmethod 25 | def need_dict(string): 26 | if string.find("(") == -1: 27 | return False 28 | return string.find(")") > string.find("(") 29 | 30 | @staticmethod 31 | def end_of_dic(string): 32 | k = 1 33 | for i in range(len(string)): 34 | if string[i] == "(": 35 | k += 1 36 | elif string[i] == ")": 37 | k -= 1 38 | if k == 0: 39 | return i 40 | return -1 41 | 42 | def parse(self, string): 43 | MessageParamsParser._parse(self._dic, string) 44 | return self._dic 45 | 46 | def dic(self): 47 | return self._dic 48 | -------------------------------------------------------------------------------- /lib/player_command/player_command_support.py: -------------------------------------------------------------------------------- 1 | from lib.player_command.player_command import PlayerCommand, CommandType 2 | 3 | 4 | class PlayerSupportCommand(PlayerCommand): 5 | def type(self): 6 | pass 7 | 8 | def str(self): 9 | pass 10 | 11 | # def name(self): 12 | # pass 13 | 14 | 15 | class PlayerTurnNeckCommand(PlayerSupportCommand): 16 | def __init__(self, moment): 17 | self._moment = moment 18 | 19 | def str(self): 20 | return f"(turn_neck {self._moment})" 21 | 22 | def type(self): 23 | return CommandType.TURN_NECK 24 | 25 | def moment(self): 26 | return self._moment 27 | 28 | 29 | # TODO kirie in :\ 30 | # class PlayerChangeViewCommand(PlayerSupportCommand): 31 | # def __init__(self): 32 | 33 | 34 | class PlayerSayCommand(PlayerSupportCommand): 35 | def __init__(self, msg: str, version: float): 36 | self._msg = msg 37 | self._version = version 38 | 39 | def str(self): 40 | tmp = self._msg 41 | if self._version >= 8: 42 | tmp = '"' + self._msg + '"' 43 | return f"(say {tmp})" 44 | 45 | def type(self): 46 | return CommandType.SAY 47 | 48 | def message(self): 49 | return self._msg 50 | 51 | def append(self, msg): 52 | self._msg += msg 53 | 54 | 55 | class PlayerPointtoCommand(PlayerSupportCommand): 56 | def __init__(self, dist: float, dir: float, on: bool): 57 | self._dist = dist 58 | self._dir = dir # relative to body angle 59 | self._on = on # on/off switch 60 | 61 | def str(self): 62 | if self._on: 63 | return f"(pointto {self._dist} {self._dir})" 64 | else: 65 | return "(pointto off)" 66 | 67 | def type(self): 68 | return CommandType.POINTTO 69 | #TODO Write other commands ... 70 | 71 | class PlayerDoneCommand(PlayerSupportCommand): 72 | def __init__(self): 73 | pass 74 | 75 | def str(self): 76 | return "(done)" 77 | 78 | def type(self): 79 | return CommandType.DONE 80 | -------------------------------------------------------------------------------- /lib/math/size_2d.py: -------------------------------------------------------------------------------- 1 | """ 2 | \ file size_2d.py 3 | \ brief 2d size class File. 4 | """ 5 | 6 | import math 7 | 8 | 9 | class Size2D: 10 | """ 11 | \ brief constructor with variables 12 | \ param length x range 13 | \ param width y range 14 | Default 0.0 0.0 15 | """ 16 | 17 | def __init__(self, __l=0.0, __w=0.0): 18 | self._length = math.fabs(__l) 19 | self._width = math.fabs(__w) 20 | 21 | """ 22 | \ brief assign range directly. 23 | \ param length X range 24 | \ param width Y range 25 | """ 26 | 27 | def assign(self, length, width): 28 | self._length = math.fabs(length) 29 | self._width = math.fabs(width) 30 | 31 | """ 32 | \ brief set X range 33 | \ param length X range 34 | """ 35 | 36 | def setLength(self, length): 37 | self._length = math.fabs(length) 38 | 39 | """ 40 | \ brief set Y range 41 | \ param width Y range 42 | \ return reference to itself 43 | """ 44 | 45 | def setWidth(self, width): 46 | self._width = math.fabs(width) 47 | 48 | """ 49 | \ brief get the value of X range 50 | \ return value of X range 51 | """ 52 | 53 | def length(self): 54 | return self._length 55 | 56 | """ 57 | \ brief get the value of Y range 58 | \ return value of Y range 59 | """ 60 | 61 | def width(self): 62 | return self._width 63 | 64 | """ 65 | \ brief get the length of diagonal line 66 | \ return length of diagonal line 67 | """ 68 | 69 | def diagonal(self): 70 | return math.sqrt(self._length * self._length + self._width * self._width) 71 | 72 | """ 73 | \ brief check if size is valid or not. 74 | \ return True if the area of self rectangle is not 0. 75 | """ 76 | 77 | def isValid(self): 78 | return self._length > 0.0 and self._width > 0.0 79 | 80 | """ 81 | \ brief make a logical print. 82 | \ return print_able str 83 | """ 84 | 85 | def __repr__(self): 86 | return "[len:{},wid:{}]".format(self._length, self._width) 87 | -------------------------------------------------------------------------------- /lib/player_command/player_command.py: -------------------------------------------------------------------------------- 1 | from enum import Enum, unique, auto 2 | 3 | @unique 4 | class CommandType(Enum): 5 | # connection commands 6 | INIT = auto() # ! < server connection command 7 | RECONNECT = auto() # ! < server reconnection command 8 | BYE = auto() # ! < server disconnection command 9 | 10 | # base commands 11 | MOVE = auto() 12 | DASH = auto() 13 | TURN = auto() 14 | KICK = auto() 15 | CATCH = auto() 16 | TACKLE = auto() 17 | 18 | # support commands 19 | TURN_NECK = auto() 20 | CHANGE_VIEW = auto() 21 | SAY = auto() 22 | POINTTO = auto() 23 | ATTENTIONTO = auto() 24 | 25 | # mode change commands 26 | CLANG = auto() 27 | EAR = auto() 28 | 29 | # other commands 30 | SENSE_BODY = auto() 31 | SCORE = auto() 32 | COMPRESSION = auto() 33 | 34 | # synch_mode command 35 | DONE = auto() 36 | 37 | ILLEGAL = auto() 38 | 39 | 40 | class PlayerCommand: 41 | def type(self): 42 | pass 43 | 44 | def str(self): 45 | return "" 46 | 47 | # def name(self): 48 | # pass 49 | 50 | 51 | class PlayerInitCommand(PlayerCommand): 52 | def __init__(self, team_name: str, version: float = None, golie: bool = False): 53 | self._team_name = team_name 54 | self._version = version 55 | self._goalie = golie 56 | 57 | def str(self): 58 | return f"(init {self._team_name}" + \ 59 | (f" (version {self._version})" if self._version >= 4 else "") + \ 60 | ("(goalie)" if self._goalie else "") + \ 61 | ")" 62 | 63 | def type(self): 64 | return CommandType.INIT 65 | 66 | class PlayerReconnectCommand(PlayerCommand): 67 | def __init__(self, team_name: str, unum: int): 68 | self._team_name = team_name 69 | self._unum = unum 70 | 71 | def str(self): 72 | return f"(reconnect {self._team_name} {self._unum})" 73 | 74 | def type(self): 75 | return CommandType.RECONNECT 76 | 77 | 78 | class PlayerByeCommand: 79 | def __init__(self): 80 | pass 81 | 82 | def str(self): 83 | return "(bye)" 84 | 85 | def type(self): 86 | return CommandType.BYE 87 | -------------------------------------------------------------------------------- /lib/rcsc/game_mode.py: -------------------------------------------------------------------------------- 1 | from lib.rcsc.types import SideID, GameModeType 2 | 3 | 4 | class GameMode: 5 | def __init__(self, game_mode: GameModeType = None): 6 | self._game_mode: GameModeType = game_mode 7 | self._mode_name: str = None 8 | self._side: SideID = None 9 | if game_mode is not None: 10 | self._mode_name = self._set_mode_name() 11 | self._side = self._set_side() 12 | 13 | def type(self) -> GameModeType: 14 | return self._game_mode 15 | 16 | def side(self) -> SideID: 17 | return self._side 18 | 19 | def mode_name(self) -> str: 20 | return self._mode_name 21 | 22 | def _set_side(self) -> SideID: 23 | if self._game_mode.value[-2:] == '_l' or self._game_mode.value[-2:] == '_r': 24 | return SideID(self._game_mode.value[-1]) 25 | return SideID.NEUTRAL 26 | 27 | def _set_mode_name(self) -> str: 28 | if self._game_mode.value[-2:] == '_l' or self._game_mode.value[-2:] == '_r': 29 | return self._game_mode.value[:-2] 30 | return self._game_mode.value 31 | 32 | def set_game_mode(self, play_mode: GameModeType): 33 | self.__init__(play_mode) 34 | 35 | def is_teams_set_play(self, team_side: SideID): 36 | mode_name = self.mode_name() 37 | if mode_name == "kick_off" or \ 38 | mode_name == "kick_in" or \ 39 | mode_name == "corner_kick" or \ 40 | mode_name == "goal_kick" or \ 41 | mode_name == "free_kick" or \ 42 | mode_name == "goalie_catch" or \ 43 | mode_name == "indirect_free_kick": 44 | return self.side() == team_side 45 | elif mode_name == "off_side" or \ 46 | mode_name == "foul_charge" or \ 47 | mode_name == "foul_push" or \ 48 | mode_name == "free_kick_fault" or \ 49 | mode_name == "back_pass" or \ 50 | mode_name == "catch_fault": 51 | return self.side() != team_side 52 | return False 53 | 54 | def is_penalty_kick_mode(self): 55 | if self.type() in [GameModeType.PenaltySetup_Left, GameModeType.PenaltySetup_Right, GameModeType.PenaltyReady_Left, GameModeType.PenaltyReady_Right, GameModeType.PenaltyTaken_Left, GameModeType.PenaltyReady_Right, GameModeType.PenaltyMiss_Left, GameModeType.PenaltyMiss_Right, GameModeType.PenaltyScore_Left, GameModeType.PenaltyScore_Right]: 56 | return True 57 | return False 58 | # todo add PlayMode.PenaltyOnfield, PenaltyFoul_ 59 | 60 | def is_our_set_play(self, our_side: SideID): 61 | return self.is_teams_set_play(our_side) 62 | -------------------------------------------------------------------------------- /lib/action/intercept_table.py: -------------------------------------------------------------------------------- 1 | from lib.action.intercept_self import SelfIntercept 2 | from lib.debug.color import Color 3 | from lib.debug.level import Level 4 | from lib.debug.logger import dlog 5 | from lib.math.vector_2d import Vector2D 6 | from lib.rcsc.game_time import GameTime 7 | from lib.rcsc.server_param import ServerParam 8 | 9 | 10 | class InterceptTable: 11 | def __init__(self): 12 | self._last_update_time: GameTime = GameTime(-10, -100) 13 | self._max_cycle: int = 30 14 | 15 | self._ball_cache = [] 16 | self._self_cache = [] 17 | 18 | def update(self, wm): 19 | # if self._last_update_time == wm.time(): 20 | # dlog.add_text(Level.INTERCEPT, "intercept updated befor :| it called agein") 21 | # 22 | self._last_update_time = wm.time() 23 | self.clear() 24 | 25 | if wm.time().cycle() < 1: # TODO check game mode here 26 | return 27 | 28 | if not wm.self().pos().is_valid() or not wm.ball().pos().is_valid(): 29 | dlog.add_text(Level.INTERCEPT, "self pos or ball pos is not valid") 30 | return 31 | 32 | self.create_ball_cache(wm) 33 | 34 | def clear(self): 35 | self._ball_cache = [] 36 | 37 | def create_ball_cache(self, wm): 38 | SP = ServerParam.i() 39 | pitch_max_x = SP.pitch_half_length() + 5 40 | pitch_max_y = SP.pitch_half_width() + 5 41 | ball_decay = SP.ball_decay() 42 | 43 | ball_pos: Vector2D = wm.ball().pos() 44 | ball_vel: Vector2D = wm.ball().vel() 45 | 46 | self._ball_cache.append(ball_pos) 47 | 48 | for cycle in range(1, self._max_cycle + 1): 49 | # dlog.add_point(Level.INTERCEPT, pos=ball_pos, color=Color(string='blue')) 50 | dlog.add_circle(Level.INTERCEPT, r=0.1, center=ball_pos, fill=True, color=Color(string="blue")) 51 | dlog.add_text(Level.INTERCEPT, f"ballpos: {ball_pos}") 52 | ball_pos += ball_vel 53 | ball_vel *= ball_decay 54 | self._ball_cache.append(ball_pos) 55 | 56 | if cycle >= 5 and ball_vel.r() < 0.01 ** 2: 57 | # ball stopped 58 | break 59 | 60 | if ball_pos.absX() > pitch_max_x or ball_pos.absY() > pitch_max_y: 61 | # out of pitch 62 | break 63 | 64 | # TODO if len == 1 push ball pos again :| why?? 65 | if len(self._ball_cache) == 1: 66 | self._ball_cache.append(ball_pos) 67 | 68 | def predict_self(self, wm): 69 | if wm.self().is_kickable(): 70 | dlog.add_text(Level.INTERCEPT, "Intercept predict self already kickable") 71 | return 72 | 73 | max_cycle = min(self._max_cycle, len(self._ball_cache)) 74 | predictor = SelfIntercept(wm, self._ball_cache) 75 | predictor.predict(max_cycle, self._self_cache) 76 | -------------------------------------------------------------------------------- /lib/player/stamina_model.py: -------------------------------------------------------------------------------- 1 | import copy 2 | 3 | from lib.rcsc.player_type import PlayerType 4 | from lib.rcsc.server_param import ServerParam 5 | 6 | 7 | class StaminaModel: 8 | def __init__(self, stamina=None, effort=None, recovery=None, capacity=None): 9 | SP = ServerParam.i() 10 | self._stamina: float = float(stamina) if stamina else SP.stamina_max() 11 | self._effort: float = float(effort) if effort else SP.effort_init() 12 | self._recovery: float = float(recovery) if recovery else SP.recover_init() 13 | self._capacity: float = float(capacity) if capacity else -1 14 | 15 | def init(self, player_type: PlayerType): 16 | SP = ServerParam.i() 17 | self._stamina = SP.stamina_max() 18 | self._effort = player_type.effort_max() 19 | self._recovery = SP.recover_init() 20 | self._capacity = SP.stamina_capacity() 21 | 22 | def simulate_wait(self, player_type: PlayerType): 23 | SP = ServerParam.i() 24 | 25 | # recovery 26 | if self._stamina <= SP.recover_dec_thr_value(): 27 | if self._recovery > SP.recover_min(): 28 | self._recovery -= SP.recover_dec() 29 | self._recovery = max(self._recovery, SP.recover_min()) 30 | 31 | # effort 32 | if self._stamina <= SP.effort_dec_thr_value(): 33 | if self._effort > player_type.effort_min(): 34 | self._effort -= SP.effort_dec() 35 | self._effort = max(self._effort, player_type.effort_min()) 36 | elif self._stamina >= SP.effort_inc_thr_value(): 37 | if self._effort < player_type.effort_max(): 38 | self._effort += SP.effort_inc() 39 | self._effort = min(self._effort, player_type.effort_max()) 40 | 41 | stamina_inc = min(player_type.stamina_inc_max() * self._recovery, SP.stamina_max() - self._stamina) 42 | if SP.stamina_capacity() >= 0: 43 | self._stamina += min(stamina_inc, self._capacity) 44 | self._capacity -= stamina_inc 45 | self._capacity = max(0, self._capacity) 46 | else: 47 | self._stamina += stamina_inc 48 | self._stamina = min(self._stamina, SP.stamina_max()) # kirm tu in mohasebat :| 49 | 50 | def capacity_is_empty(self) -> bool: 51 | return 0 <= self._capacity <= 1e-5 52 | 53 | def simulate_dash(self, player_type: PlayerType, dash_power: float): 54 | self._stamina -= dash_power if dash_power >= 0 else dash_power * -2 55 | self._stamina = max(0, self._stamina) 56 | self.simulate_wait(player_type) 57 | 58 | def copy(self): 59 | new = copy.deepcopy(self) 60 | return new 61 | 62 | def stamina(self): 63 | return self._stamina 64 | 65 | def effort(self): 66 | return self._effort 67 | 68 | def recovery(self): 69 | return self._recovery 70 | 71 | def capacity(self): 72 | return self._capacity 73 | -------------------------------------------------------------------------------- /lib/player/soccer_action.py: -------------------------------------------------------------------------------- 1 | """ 2 | \ file soccer_action.py 3 | \ brief abstract player actions class File 4 | """ 5 | 6 | from lib.player.player_agent import * 7 | from lib.rcsc.server_param import * 8 | 9 | """ 10 | \ class AbstractAction 11 | \ brief base class of actions 12 | """ 13 | 14 | 15 | class AbstractAction: 16 | S_action_object_counter = 0 17 | 18 | def __init__(self): 19 | AbstractAction.S_action_object_counter += 1 20 | self._action_object_id = AbstractAction.S_action_object_counter 21 | pass 22 | 23 | """ 24 | \ brief pure virtual. set command to the action effector 25 | \ return_value True if action is performed 26 | \ return_value False if action is failed or not needed. 27 | """ 28 | 29 | def execute(self, agent): 30 | pass 31 | 32 | """ 33 | \ brief get ID of action object to identify action instances 34 | \ return ID of action object 35 | """ 36 | 37 | def actionObjectID(self): 38 | return self._action_object_id 39 | 40 | 41 | # ################################# 42 | """ 43 | \ class BodyAction 44 | \ brief abstract body action 45 | """ 46 | 47 | 48 | class BodyAction(AbstractAction): 49 | 50 | def execute(self, agent): 51 | pass 52 | 53 | 54 | # #################################/ 55 | 56 | """ 57 | \ class NeckAction 58 | \ brief abstract turn neck action 59 | """ 60 | 61 | 62 | class NeckAction(AbstractAction): 63 | def __init__(self, NeckActions: list): 64 | super().__init__() 65 | self._NeckAction = NeckActions 66 | 67 | def execute(self, agent): 68 | pass 69 | 70 | """ 71 | \ brief create cloned action object 72 | \ return pointer to the cloned object instance. 73 | """ 74 | 75 | def clone(self, agent): 76 | pass 77 | 78 | 79 | # #################################/ 80 | 81 | """ 82 | \ class ViewAction 83 | \ brief abstract change view action 84 | """ 85 | 86 | 87 | class ViewAction(AbstractAction): 88 | 89 | def __init__(self, ViewActions: list): 90 | super().__init__() 91 | 92 | self._ViewAction = ViewActions 93 | 94 | def execute(self, agent): 95 | pass 96 | 97 | """ 98 | \ brief create cloned action object 99 | \ return pointer to the cloned object instance. 100 | """ 101 | 102 | def clone(self, agent): 103 | pass 104 | 105 | 106 | # #################################/ 107 | 108 | """ 109 | \ class ArmAction 110 | \ brief abstract point to action 111 | """ 112 | 113 | 114 | class ArmAction(AbstractAction): 115 | 116 | def __init__(self, ArmActions: list): 117 | super().__init__() 118 | self._ArmAction = ArmActions 119 | 120 | def execute(self, agent): 121 | pass 122 | 123 | """ 124 | \ brief create cloned action object 125 | \ return pointer to the cloned object instance. 126 | """ 127 | 128 | def clone(self, agent): 129 | pass 130 | 131 | 132 | # #################################/ 133 | 134 | """ 135 | \ class SoccerBehavior 136 | \ brief abstract player behavior. 137 | """ 138 | 139 | 140 | class SoccerBehavior(AbstractAction): 141 | 142 | def execute(self, agent): 143 | pass 144 | -------------------------------------------------------------------------------- /lib/action/intercept_info.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | from lib.math.angle_deg import AngleDeg 4 | from lib.math.vector_2d import Vector2D 5 | 6 | 7 | class InterceptInfo: 8 | class Mode(Enum): 9 | NORMAL = 0 10 | EXHAUST = 100 11 | 12 | def __init__(self, mode: Mode = Mode.EXHAUST, turn_cycle: int = 10000, dash_cycle: int = 10000, 13 | dash_power: float = 100000, dash_angle: float = 0, 14 | self_pos: Vector2D = Vector2D(-10000, 0), ball_dist: float = 100000, stamina: float = 0): 15 | self._valid: bool = True 16 | self._mode: InterceptInfo.Mode = mode 17 | self._turn_cycle: int = turn_cycle 18 | self._dash_cycle: int = dash_cycle 19 | self._dash_power: float = dash_power 20 | self._dash_angle: AngleDeg = AngleDeg(dash_angle) 21 | self._self_pos: Vector2D = self_pos 22 | self._ball_dist: float = ball_dist 23 | self._stamina: float = stamina 24 | if self._mode == InterceptInfo.Mode.EXHAUST and \ 25 | self._turn_cycle == 10000 and \ 26 | self._dash_cycle == 10000 and \ 27 | self._dash_power == 100000.0 and \ 28 | self._dash_angle == AngleDeg(0.0) and \ 29 | self._self_pos == Vector2D(-10000.0, 0.0) and \ 30 | self._ball_dist == 10000000.0 and \ 31 | self._stamina == 0.0: 32 | self._valid = False 33 | 34 | def init(self, mode: Mode = Mode.EXHAUST, turn_cycle: int = 10000, dash_cycle: int = 10000, 35 | dash_power: float = 100000, dash_angle: float = 0, 36 | self_pos: Vector2D = Vector2D(-10000, 0), ball_dist: float = 100000, stamina: float = 0): 37 | self.__init__(mode, turn_cycle, dash_cycle, dash_power, dash_angle, self_pos, ball_dist, stamina) 38 | 39 | def is_valid(self): 40 | return self._valid 41 | 42 | def mode(self): 43 | return self._mode 44 | 45 | def turn_cycle(self): 46 | return self._turn_cycle 47 | 48 | def dash_cycle(self): 49 | return self._dash_cycle 50 | 51 | def reach_cycle(self): 52 | return self._dash_cycle + self._turn_cycle 53 | 54 | def dash_power(self): 55 | return self._dash_power 56 | 57 | def dash_angle(self): 58 | return self._dash_angle 59 | 60 | def self_pos(self): 61 | return self._self_pos 62 | 63 | def ball_dist(self): 64 | return self._ball_dist 65 | 66 | def stamina(self): 67 | return self._stamina 68 | 69 | @staticmethod 70 | def compare(lhs, rhs): 71 | return True if lhs.reach_cycle() < rhs.reach_cycle() else ( 72 | lhs.reach_cycle() == rhs.reach_cycle() and lhs.turn_cycle() < rhs.turn_cycle()) 73 | 74 | def __lt__(self, other): 75 | if self.reach_cycle() < other.reach_cycle(): 76 | return True 77 | return self.reach_cycle() == other.reach_cycle() and self.turn_cycle() < other.turn_cycle 78 | 79 | def __repr__(self): 80 | return (f"{self._mode}," 81 | f"{self.reach_cycle()}," 82 | f"{self._turn_cycle}," 83 | f"{self._dash_cycle}," 84 | f"{self._dash_power}," 85 | f"{self._dash_angle.degree()}," 86 | f"{self._self_pos}," 87 | f"{self._ball_dist}," 88 | f"{self._stamina};") 89 | -------------------------------------------------------------------------------- /lib/player_command/player_command_body.py: -------------------------------------------------------------------------------- 1 | from lib.player_command.player_command import PlayerCommand, CommandType 2 | 3 | 4 | class PlayerBodyCommand(PlayerCommand): 5 | body_commands = [] 6 | 7 | def type(self): 8 | pass 9 | 10 | def str(self): 11 | pass 12 | 13 | # def name(self): 14 | # pass 15 | 16 | 17 | class PlayerMoveCommand(PlayerBodyCommand): 18 | def __init__(self, x, y): 19 | self._x = x 20 | self._y = y 21 | 22 | def str(self): 23 | return f"(move {self._x} {self._y})" 24 | 25 | def __repr__(self): 26 | return "(Move To {}, {})".format(self._x, self._y) 27 | 28 | def type(self): 29 | return CommandType.MOVE 30 | 31 | 32 | class PlayerDashCommand(PlayerBodyCommand): 33 | def __init__(self, power, dir): 34 | self._power = power 35 | self._dir = dir 36 | 37 | def str(self): 38 | return f"(dash {self._power}" + (f" {self._dir})" if self._dir != 0 else ")") 39 | 40 | def __repr__(self): 41 | return "(Dash power:{}, dir:{})".format(self._power, self._dir) 42 | 43 | def type(self): 44 | return CommandType.DASH 45 | 46 | 47 | class PlayerTurnCommand(PlayerBodyCommand): 48 | def __init__(self, moment: float): 49 | self._moment = moment 50 | 51 | def str(self): 52 | return f"(turn {self._moment})" 53 | 54 | def __repr__(self): 55 | return "(Turn moment:{})".format(self._moment) 56 | 57 | def type(self): 58 | return CommandType.TURN 59 | 60 | 61 | class PlayerKickCommand(PlayerBodyCommand): 62 | def __init__(self, power, rel_dir): 63 | self._power = power 64 | self._dir = rel_dir # relative to body angle 65 | 66 | def str(self): 67 | return f"(kick {self._power} {self._dir})" 68 | 69 | def __repr__(self): 70 | return "(Kick power:{}, dir:{})".format(self._power, self._dir) 71 | 72 | def type(self): 73 | return CommandType.KICK 74 | 75 | def kick_power(self): 76 | return self._power 77 | 78 | def kick_dir(self): 79 | return self._dir 80 | 81 | 82 | class PlayerCatchCommand(PlayerBodyCommand): 83 | def __init__(self, dir): 84 | self._dir = dir 85 | 86 | def str(self): 87 | return f"(catch {self._dir})" 88 | 89 | def __repr__(self): 90 | return "(Catch dir:{})".format(self._dir) 91 | 92 | def type(self): 93 | return CommandType.CATCH 94 | 95 | 96 | class PlayerTackleCommand(PlayerBodyCommand): 97 | def __init__(self, power_or_dir, foul: bool = False): 98 | self._power_or_dir = power_or_dir 99 | self._foul = foul 100 | 101 | def str(self): 102 | return f"(tackle {self._power_or_dir}" + (f" on)" if self._foul else ")") 103 | 104 | def __repr__(self): 105 | return "(Tackle power{}, foul{})".format(self._power_or_dir, self._foul) 106 | 107 | def type(self): 108 | return CommandType.TACKLE 109 | 110 | 111 | PlayerBodyCommand.body_commands = [PlayerMoveCommand, 112 | PlayerDashCommand, 113 | PlayerTurnCommand, 114 | PlayerKickCommand, 115 | PlayerCatchCommand, 116 | PlayerTackleCommand] 117 | -------------------------------------------------------------------------------- /lib/parser/parser_message_fullstate_world.py: -------------------------------------------------------------------------------- 1 | from lib.debug.logger import dlog 2 | from lib.parser.parser_message_params import MessageParamsParser 3 | 4 | """" 5 | (fullstate