From 36dccc5159fe6b4906ce2df9580b56e5b7299dbd Mon Sep 17 00:00:00 2001 From: ViliamVadocz Date: Thu, 13 Feb 2020 17:21:04 +0100 Subject: [PATCH 1/9] Add numpy utilities --- src/util/np_data.py | 149 ++++++++++++++++++ src/util/np_utils.py | 351 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 500 insertions(+) create mode 100644 src/util/np_data.py create mode 100644 src/util/np_utils.py diff --git a/src/util/np_data.py b/src/util/np_data.py new file mode 100644 index 0000000..4c45ba4 --- /dev/null +++ b/src/util/np_data.py @@ -0,0 +1,149 @@ +'''Rocket League data pre-processing.''' + +from .np_utils import Car, Ball, BoostPad, a3l, a3r, a3v, orient_matrix, turn_r + +import numpy as np + + +def setup(self, packet, field_info): + """Sets up the variables and classes for the agent. + + Arguments: + self {BaseAgent} -- The agent. + packet {GameTickPacket} -- Information about the game. + field_info {FieldInfoPacket} -- Information about the game field. + """ + + # Game info + self.game_time = packet.game_info.seconds_elapsed + self.dt = 1.0 / 120.0 + self.last_time = 0.0 + self.r_active = packet.game_info.is_round_active + self.ko_pause = packet.game_info.is_kickoff_pause + self.m_ended = packet.game_info.is_match_ended + self.gravity = packet.game_info.world_gravity_z + + # Creates Car objects for each car. + self.teammates = [] + self.opponents = [] + for index in range(packet.num_cars): + car = packet.game_cars[index] + if index == self.index: + self.player = Car(self.index, self.team, self.name) + elif car.team == self.team: + self.teammates.append(Car(index, car.team, car.name)) + else: + self.opponents.append(Car(index, car.team, car.name)) + + # Creates a Ball object. + self.ball = Ball() + + # Creates Boostpad objects. + self.l_pads = [] + self.s_pads = [] + for i in range(field_info.num_boosts): + pad = field_info.boost_pads[i] + pad_type = self.l_pads if pad.is_full_boost else self.s_pads + pad_obj = BoostPad(i, a3v(pad.location)) + pad_type.append(pad_obj) + + +def process(self, packet): + """Processes the gametick packet. + Arguments: + self {BaseAgent} -- The agent. + packet {GameTickPacket} -- The game packet being processed. + """ + + # Processing game info. + self.game_time = packet.game_info.seconds_elapsed + self.dt = self.game_time - self.last_time + self.last_time = self.game_time + self.r_active = packet.game_info.is_round_active + self.ko_pause = packet.game_info.is_kickoff_pause + self.m_ended = packet.game_info.is_match_ended + self.gravity = packet.game_info.world_gravity_z + + # Processing player data. + # From packet: + self.player.pos = a3v(packet.game_cars[self.player.index].physics.location) + self.player.rot = a3r(packet.game_cars[self.player.index].physics.rotation) + self.player.vel = a3v(packet.game_cars[self.player.index].physics.velocity) + self.player.ang_vel = a3v(packet.game_cars[self.player.index].physics.angular_velocity) + self.player.dead = packet.game_cars[self.player.index].is_demolished + self.player.wheel_c = packet.game_cars[self.player.index].has_wheel_contact + self.player.sonic = packet.game_cars[self.player.index].is_super_sonic + self.player.jumped = packet.game_cars[self.player.index].jumped + self.player.d_jumped = packet.game_cars[self.player.index].double_jumped + self.player.boost = packet.game_cars[self.player.index].boost + # Calculated: + self.player.orient_m = orient_matrix(self.player.rot, self.player.orient_m) + # self.player.turn_r = turn_r(self.player.vel) + + # Processing teammates. + for teammate in self.teammates: + # From packet: + teammate.pos = a3v(packet.game_cars[teammate.index].physics.location) + # teammate.rot = a3r(packet.game_cars[teammate.index].physics.rotation) + # teammate.vel = a3v(packet.game_cars[teammate.index].physics.velocity) + # teammate.ang_vel = a3v(packet.game_cars[teammate.index].physics.angular_velocity) + # teammate.dead = packet.game_cars[teammate.index].is_demolished + # teammate.wheel_c = packet.game_cars[teammate.index].has_wheel_contact + # teammate.sonic = packet.game_cars[teammate.index].is_super_sonic + # teammate.jumped = packet.game_cars[teammate.index].jumped + # teammate.d_jumped = packet.game_cars[teammate.index].double_jumped + # teammate.boost = packet.game_cars[teammate.index].boost + # Calculated: + #teammate.orient_m = orient_matrix(teammate.rot) + #teammate.turn_r = turn_r(teammate.vel) + + # Processing opponents. + for opponent in self.opponents: + # From packet: + opponent.pos = a3v(packet.game_cars[opponent.index].physics.location) + # opponent.rot = a3r(packet.game_cars[opponent.index].physics.rotation) + # opponent.vel = a3v(packet.game_cars[opponent.index].physics.velocity) + #opponent.ang_vel = a3v(packet.game_cars[opponent.index].physics.angular_velocity) + # opponent.dead = packet.game_cars[opponent.index].is_demolished + #opponent.wheel_c = packet.game_cars[opponent.index].has_wheel_contact + #opponent.sonic = packet.game_cars[opponent.index].is_super_sonic + #opponent.jumped = packet.game_cars[opponent.index].jumped + #opponent.d_jumped = packet.game_cars[opponent.index].double_jumped + #opponent.boost = packet.game_cars[opponent.index].boost + # Calculated: + #opponent.orient_m = orient_matrix(opponent.rot) + #opponent.turn_r = turn_r(opponent.vel) + + # Processing Ball data. + self.ball.pos = a3v(packet.game_ball.physics.location) + self.ball.rot = a3r(packet.game_ball.physics.rotation) + self.ball.vel = a3v(packet.game_ball.physics.velocity) + self.ball.ang_vel = a3v(packet.game_ball.physics.angular_velocity) + self.ball.last_touch = packet.game_ball.latest_touch + + # Processing ball prediction. + ball_prediction_struct = self.get_ball_prediction_struct() + dtype = [ + ('physics', [ + ('pos', ' np.ndarray: + """Converts list to numpy array. + Arguments: + L {list} -- The list to convert containing 3 elements. + Returns: + np.array -- Numpy array with the same contents as the list. + """ + if len(L) == 3: + return np.array(L) + else: + return np.array(L[0], L[1], L[2]) + + +def a3r(R: Rotator) -> np.ndarray: + """Converts rotator to numpy array. + + Arguments: + R {Rotator} -- Rotator class containing pitch, yaw, and roll. + + Returns: + np.ndarray -- Numpy array with the same contents as the rotator. + """ + return np.array([R.pitch, R.yaw, R.roll]) + + +def a3v(V: Vector3) -> np.ndarray: + """Converts vector3 to numpy array. + + Arguments: + V {Vector3} -- Vector3 class containing x, y, and z. + + Returns: + np.ndarray -- Numpy array with the same contents as the vector3. + """ + return np.array([V.x, V.y, V.z]) + + +# ----------------------------------------------------------- + +# USEFUL UTILITY FUNCTIONS: + +def normalise(V: np.ndarray) -> np.ndarray: + """Normalises a vector. + + Arguments: + V {np.ndarray} -- Vector. + + Returns: + np.ndarray -- Normalised vector. + """ + magnitude = np.linalg.norm(V) + if magnitude != 0.0: + return V / magnitude + else: + return V + + +def angle_between_vectors(v1: np.ndarray, v2: np.ndarray) -> float: + """Returns the positive angle in radians between vectors v1 and v2. + + Arguments: + v1 {np.ndarray} -- First vector. + v2 {np.ndarray} -- Second vector + + Returns: + float -- Positive acute angle between the vectors in radians. + """ + v1_u = normalise(v1) + v2_u = normalise(v2) + return np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0)) + +# ----------------------------------------------------------- + +# FUNCTIONS FOR CONVERTING BETWEEN WORLD AND LOCAL COORDINATES: + +def orient_matrix(R: np.ndarray, A: np.ndarray = np.zeros((3, 3))) -> np.ndarray: + """Converts from Euler angles to an orientation matrix. + + Arguments: + R {np.ndarray} -- Pitch, yaw, and roll. + A {np.ndarray} -- Previous orientation matrix. (default: {np.zeros((3, 3))}) + + Returns: + np.ndarray -- Orientation matrix of shape (3, 3). + """ + # Credits to chip https://samuelpmish.github.io/notes/RocketLeague/aerial_control/ + pitch: float = R[0] + yaw: float = R[1] + roll: float = R[2] + + CR: float = np.cos(roll) + SR: float = np.sin(roll) + CP: float = np.cos(pitch) + SP: float = np.sin(pitch) + CY: float = np.cos(yaw) + SY: float = np.sin(yaw) + + A = np.zeros((3, 3)) + + # front direction + A[0, 0] = CP * CY + A[1, 0] = CP * SY + A[2, 0] = SP + + # right direction + A[0, 1] = CY * SP * SR - CR * SY + A[1, 1] = SY * SP * SR + CR * CY + A[2, 1] = -CP * SR + + # up direction + A[0, 2] = -CR * CY * SP - SR * SY + A[1, 2] = -CR * SY * SP + SR * CY + A[2, 2] = CP * CR + + return A + + +def local(A: np.ndarray, p0: np.ndarray, p1: np.ndarray) -> np.ndarray: + """Transforms world coordinates into local coordinates. + + Arguments: + A {np.ndarray} -- The local orientation matrix. + p0 {np.ndarray} -- World x, y, and z coordinates of the start point for the vector. + p1 {np.ndarray} -- World x, y, and z coordinates of the end point for the vector. + + Returns: + np.ndarray -- Local x, y, and z coordinates. + """ + return np.dot(A.T, p1 - p0) + + +def world(A: np.ndarray, p0: np.ndarray, p1: np.ndarray) -> np.ndarray: + """Transforms local into world coordinates. + + Arguments: + A {np.ndarray} -- The local orientation matrix. + p0 {np.ndarray} -- World x, y, and z coordinates of the start point for the vector. + p1 {np.ndarray} -- Local x, y, and z coordinates of the end point for the vector. + + Returns: + np.ndarray -- World x, y, and z coordinates. + """ + return p0 + A * p1 + +# ----------------------------------------------------------- + +# ROCKET LEAGUE SPECIFIC FUNCTIONS: + +def team_sign(team: int) -> int: + """Gives the sign for a calculation based on team. + + Arguments: + team {int} -- 0 if Blue, 1 if Orange. + + Returns: + int -- 1 if Blue, -1 if Orange + """ + # Other creative ways to do it: + + # return (1, -1)[team] + + # return 1 if team == 0 else -1 + + # for i in range(team): + # return 1 + # return -1 + + # return -range(-1, 2, -2)[team] + + return -2 * team + 1 + + +def turn_r(v: np.ndarray) -> float: + """Calculates the minimum turning radius for given velocity. + + Arguments: + v {np.ndarray} -- A velocity vector. + + Returns: + float -- The smallest radius possible for the given velocity. + """ + s = np.linalg.norm(v) + return -6.901E-11 * s**4 + 2.1815E-07 * s**3 - 5.4437E-06 * s**2 + 0.12496671 * s + 157 + + +def linear_predict(start_pos: np.ndarray, start_vel: np.ndarray, start_time: float, seconds: float) -> np.ndarray: + """Predicts motion of object in a straight line. + + Arguments: + start_pos {np.ndarray} -- Current position. + start_vel {np.ndarray} -- Current velocity. + start_time {float} -- Current time. + seconds {float} -- Time for which to predict. + + Returns: + np.ndarray -- linear prediction, 60 tps. + dtype = [ + ('pos', float, (ticks, 3)), + ('vel', float, (ticks, 3)), + ('time', float, (ticks, 1)) + ] + """ + ticks = int(60*seconds) + time = np.linspace(0, seconds, ticks)[:, np.newaxis] + pos = start_pos + time * start_vel + vel = np.ones_like(time) * start_vel + time += start_time + + dtype = [('pos', float, (ticks, 3)), ('vel', float, (ticks, 3)), ('time', float, (ticks, 1))] + prediction = np.array((pos, vel, time), dtype=dtype) + return prediction + +# ----------------------------------------------------------- + +# OTHER: + +def special_sauce(x: float, a: float) -> float: + """Modified sigmoid function. + + Arguments: + x {float} -- The input. + a {float} -- Constant; larger => faster rise. + + Returns: + float -- Output. + """ + # Graph showing how it can be used for steering: + # https://www.geogebra.org/m/udfp2zcy + return 2 / (1 + np.exp(a*x)) - 1 From 0219c68829027e8669a68a712ba9866bae9df105 Mon Sep 17 00:00:00 2001 From: Viliam Vadocz Date: Thu, 13 Feb 2020 19:37:23 +0100 Subject: [PATCH 2/9] Add example np_utils code --- src/bot.py | 19 +++++++++++++++++-- src/util/np_utils.py | 21 ++++++++++++++++++--- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/bot.py b/src/bot.py index 2638d0c..3653e3a 100644 --- a/src/bot.py +++ b/src/bot.py @@ -11,6 +11,8 @@ from util.spikes import SpikeWatcher from util.vec import Vec3 +import numpy as np +from util.np_utils import a3v, closest_to class MyBot(BaseAgent): @@ -59,6 +61,19 @@ def get_output(self, packet: GameTickPacket) -> SimpleControllerState: elif self.spike_watcher.carry_duration > 3: return SimpleControllerState(use_item=True) + # Example to numpy utilities: + # Get all car positions into an array. + car_positions = [] + for car in packet.game_cars[:packet.num_cars]: + car_positions.append(a3v(car.physics.location)) + car_positions_arr = np.vstack(car_positions) + # Get the ball position. + ball_pos = a3v(packet.game_ball.physics.location) + # Find the closest car to the ball. + closest_index = closest_to(ball_pos, car_positions_arr) + # Prepare render message. + closest_text = f'The bot with index {closest_index} is closest to the ball.' + # The rest of this code just ball chases. # Find the direction of our car using the Orientation class car_orientation = Orientation(my_car.physics.rotation) @@ -71,7 +86,7 @@ def get_output(self, packet: GameTickPacket) -> SimpleControllerState: self.controller_state.throttle = 1.0 self.controller_state.steer = -1 if steer_correction_radians > 0 else 1.0 - draw_debug(self.renderer, [goal_text]) + draw_debug(self.renderer, [goal_text, closest_text]) return self.controller_state @@ -97,7 +112,7 @@ def find_correction(current: Vec3, ideal: Vec3) -> float: def draw_debug(renderer, text_lines: List[str]): renderer.begin_rendering() - y = 200 + y = 250 for line in text_lines: renderer.draw_string_2d(50, y, 1, 1, line, renderer.yellow()) y += 20 diff --git a/src/util/np_utils.py b/src/util/np_utils.py index e69ddda..baa3b0e 100644 --- a/src/util/np_utils.py +++ b/src/util/np_utils.py @@ -75,7 +75,7 @@ class Ball: vel {np.ndarray} -- Velocity vector. ang_vel {np.ndarray} -- Angular velocity (x, y, z). Chip's omega. predict {Prediction} -- Ball prediction. - last_touch {dict} -- Last touch information. + last_touch {struct} -- Last touch information. """ __slots__ = [ 'pos', @@ -304,6 +304,9 @@ def turn_r(v: np.ndarray) -> float: s = np.linalg.norm(v) return -6.901E-11 * s**4 + 2.1815E-07 * s**3 - 5.4437E-06 * s**2 + 0.12496671 * s + 157 +# ----------------------------------------------------------- + +# OTHER: def linear_predict(start_pos: np.ndarray, start_vel: np.ndarray, start_time: float, seconds: float) -> np.ndarray: """Predicts motion of object in a straight line. @@ -332,9 +335,21 @@ def linear_predict(start_pos: np.ndarray, start_vel: np.ndarray, start_time: flo prediction = np.array((pos, vel, time), dtype=dtype) return prediction -# ----------------------------------------------------------- -# OTHER: +def closest_to(pos: np.ndarray, others: np.ndarray) -> int: + """Finds the index of the closest point. + + Arguments: + pos {np.ndarray} -- coordinates of the position of interest. + others {np.ndarray} -- Array where each row is a position. + + Returns: + int -- index of the closest position to the position of interest. + """ + vectors = others - pos + distances = np.sqrt(np.einsum('ij,ij->i', vectors, vectors)) + return np.argmin(distances) # returns first if more than one is closest + def special_sauce(x: float, a: float) -> float: """Modified sigmoid function. From c2d03ca5d946433c1848082bb73d8aa08c6702c4 Mon Sep 17 00:00:00 2001 From: Viliam Vadocz Date: Thu, 13 Feb 2020 19:46:35 +0100 Subject: [PATCH 3/9] Added some more example code --- src/bot.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/bot.py b/src/bot.py index 3653e3a..f8361e9 100644 --- a/src/bot.py +++ b/src/bot.py @@ -12,7 +12,7 @@ from util.vec import Vec3 import numpy as np -from util.np_utils import a3v, closest_to +from util.np_utils import a3v, closest_to, linear_predict class MyBot(BaseAgent): @@ -61,7 +61,7 @@ def get_output(self, packet: GameTickPacket) -> SimpleControllerState: elif self.spike_watcher.carry_duration > 3: return SimpleControllerState(use_item=True) - # Example to numpy utilities: + # Example of numpy utilities: # Get all car positions into an array. car_positions = [] for car in packet.game_cars[:packet.num_cars]: @@ -74,6 +74,13 @@ def get_output(self, packet: GameTickPacket) -> SimpleControllerState: # Prepare render message. closest_text = f'The bot with index {closest_index} is closest to the ball.' + # Render the linear prediction of the closest car. + closest_vel = a3v(packet.game_cars[closest_index].physics.velocity) + prediction = linear_predict(car_positions[closest_index], closest_vel, packet.game_info.seconds_elapsed, 2.0) + self.renderer.begin_rendering('prediction') + self.renderer.draw_polyline_3d(prediction['pos'][::30], self.renderer.pink()) + self.renderer.end_rendering() + # The rest of this code just ball chases. # Find the direction of our car using the Orientation class car_orientation = Orientation(my_car.physics.rotation) From 4434d9352885e7329656f1962c3227d17289bc52 Mon Sep 17 00:00:00 2001 From: Viliam Vadocz Date: Thu, 13 Feb 2020 21:45:57 +0100 Subject: [PATCH 4/9] Moved numpy utils out of the way --- src/bot.py | 25 ++-------------------- src/{util/np_data.py => np_util/data.py} | 2 +- src/{util/np_utils.py => np_util/utils.py} | 0 3 files changed, 3 insertions(+), 24 deletions(-) rename src/{util/np_data.py => np_util/data.py} (98%) rename src/{util/np_utils.py => np_util/utils.py} (100%) diff --git a/src/bot.py b/src/bot.py index f8361e9..e092e0c 100644 --- a/src/bot.py +++ b/src/bot.py @@ -11,8 +11,7 @@ from util.spikes import SpikeWatcher from util.vec import Vec3 -import numpy as np -from util.np_utils import a3v, closest_to, linear_predict +# Would you to use numpy utilities? Check out the np_util folder! class MyBot(BaseAgent): @@ -61,26 +60,6 @@ def get_output(self, packet: GameTickPacket) -> SimpleControllerState: elif self.spike_watcher.carry_duration > 3: return SimpleControllerState(use_item=True) - # Example of numpy utilities: - # Get all car positions into an array. - car_positions = [] - for car in packet.game_cars[:packet.num_cars]: - car_positions.append(a3v(car.physics.location)) - car_positions_arr = np.vstack(car_positions) - # Get the ball position. - ball_pos = a3v(packet.game_ball.physics.location) - # Find the closest car to the ball. - closest_index = closest_to(ball_pos, car_positions_arr) - # Prepare render message. - closest_text = f'The bot with index {closest_index} is closest to the ball.' - - # Render the linear prediction of the closest car. - closest_vel = a3v(packet.game_cars[closest_index].physics.velocity) - prediction = linear_predict(car_positions[closest_index], closest_vel, packet.game_info.seconds_elapsed, 2.0) - self.renderer.begin_rendering('prediction') - self.renderer.draw_polyline_3d(prediction['pos'][::30], self.renderer.pink()) - self.renderer.end_rendering() - # The rest of this code just ball chases. # Find the direction of our car using the Orientation class car_orientation = Orientation(my_car.physics.rotation) @@ -93,7 +72,7 @@ def get_output(self, packet: GameTickPacket) -> SimpleControllerState: self.controller_state.throttle = 1.0 self.controller_state.steer = -1 if steer_correction_radians > 0 else 1.0 - draw_debug(self.renderer, [goal_text, closest_text]) + draw_debug(self.renderer, [goal_text]) return self.controller_state diff --git a/src/util/np_data.py b/src/np_util/data.py similarity index 98% rename from src/util/np_data.py rename to src/np_util/data.py index 4c45ba4..c5b8ea9 100644 --- a/src/util/np_data.py +++ b/src/np_util/data.py @@ -1,6 +1,6 @@ '''Rocket League data pre-processing.''' -from .np_utils import Car, Ball, BoostPad, a3l, a3r, a3v, orient_matrix, turn_r +from .utils import Car, Ball, BoostPad, a3l, a3r, a3v, orient_matrix, turn_r import numpy as np diff --git a/src/util/np_utils.py b/src/np_util/utils.py similarity index 100% rename from src/util/np_utils.py rename to src/np_util/utils.py From 7d7fece4bb861688efe00a1fca7f03f216e9b22c Mon Sep 17 00:00:00 2001 From: Viliam Vadocz Date: Thu, 13 Feb 2020 21:47:50 +0100 Subject: [PATCH 5/9] Oops that sentence didn't make sense --- src/bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bot.py b/src/bot.py index e092e0c..088b637 100644 --- a/src/bot.py +++ b/src/bot.py @@ -11,7 +11,7 @@ from util.spikes import SpikeWatcher from util.vec import Vec3 -# Would you to use numpy utilities? Check out the np_util folder! +# Would you like to use numpy utilities? Check out the np_util folder! class MyBot(BaseAgent): From ab7093a8c162d96b9689745a415f6e7bb9226dc7 Mon Sep 17 00:00:00 2001 From: Viliam Vadocz Date: Thu, 13 Feb 2020 22:38:39 +0100 Subject: [PATCH 6/9] Renamed array making funcs --- src/np_util/data.py | 68 +++++++++++++++++++------------------------- src/np_util/utils.py | 13 ++++----- 2 files changed, 36 insertions(+), 45 deletions(-) diff --git a/src/np_util/data.py b/src/np_util/data.py index c5b8ea9..6c23ec2 100644 --- a/src/np_util/data.py +++ b/src/np_util/data.py @@ -1,6 +1,6 @@ '''Rocket League data pre-processing.''' -from .utils import Car, Ball, BoostPad, a3l, a3r, a3v, orient_matrix, turn_r +from .utils import Car, Ball, BoostPad, arr_from_list, arr_from_rot, arr_from_vec, orient_matrix import numpy as np @@ -44,7 +44,7 @@ def setup(self, packet, field_info): for i in range(field_info.num_boosts): pad = field_info.boost_pads[i] pad_type = self.l_pads if pad.is_full_boost else self.s_pads - pad_obj = BoostPad(i, a3v(pad.location)) + pad_obj = BoostPad(i, arr_from_vec(pad.location)) pad_type.append(pad_obj) @@ -66,10 +66,10 @@ def process(self, packet): # Processing player data. # From packet: - self.player.pos = a3v(packet.game_cars[self.player.index].physics.location) - self.player.rot = a3r(packet.game_cars[self.player.index].physics.rotation) - self.player.vel = a3v(packet.game_cars[self.player.index].physics.velocity) - self.player.ang_vel = a3v(packet.game_cars[self.player.index].physics.angular_velocity) + self.player.pos = arr_from_vec(packet.game_cars[self.player.index].physics.location) + self.player.rot = arr_from_rot(packet.game_cars[self.player.index].physics.rotation) + self.player.vel = arr_from_vec(packet.game_cars[self.player.index].physics.velocity) + self.player.ang_vel = arr_from_vec(packet.game_cars[self.player.index].physics.angular_velocity) self.player.dead = packet.game_cars[self.player.index].is_demolished self.player.wheel_c = packet.game_cars[self.player.index].has_wheel_contact self.player.sonic = packet.game_cars[self.player.index].is_super_sonic @@ -78,47 +78,40 @@ def process(self, packet): self.player.boost = packet.game_cars[self.player.index].boost # Calculated: self.player.orient_m = orient_matrix(self.player.rot, self.player.orient_m) - # self.player.turn_r = turn_r(self.player.vel) # Processing teammates. for teammate in self.teammates: # From packet: - teammate.pos = a3v(packet.game_cars[teammate.index].physics.location) - # teammate.rot = a3r(packet.game_cars[teammate.index].physics.rotation) - # teammate.vel = a3v(packet.game_cars[teammate.index].physics.velocity) - # teammate.ang_vel = a3v(packet.game_cars[teammate.index].physics.angular_velocity) - # teammate.dead = packet.game_cars[teammate.index].is_demolished - # teammate.wheel_c = packet.game_cars[teammate.index].has_wheel_contact - # teammate.sonic = packet.game_cars[teammate.index].is_super_sonic - # teammate.jumped = packet.game_cars[teammate.index].jumped - # teammate.d_jumped = packet.game_cars[teammate.index].double_jumped - # teammate.boost = packet.game_cars[teammate.index].boost - # Calculated: - #teammate.orient_m = orient_matrix(teammate.rot) - #teammate.turn_r = turn_r(teammate.vel) + teammate.pos = arr_from_vec(packet.game_cars[teammate.index].physics.location) + teammate.rot = arr_from_rot(packet.game_cars[teammate.index].physics.rotation) + teammate.vel = arr_from_vec(packet.game_cars[teammate.index].physics.velocity) + teammate.ang_vel = arr_from_vec(packet.game_cars[teammate.index].physics.angular_velocity) + teammate.dead = packet.game_cars[teammate.index].is_demolished + teammate.wheel_c = packet.game_cars[teammate.index].has_wheel_contact + teammate.sonic = packet.game_cars[teammate.index].is_super_sonic + teammate.jumped = packet.game_cars[teammate.index].jumped + teammate.d_jumped = packet.game_cars[teammate.index].double_jumped + teammate.boost = packet.game_cars[teammate.index].boost # Processing opponents. for opponent in self.opponents: # From packet: - opponent.pos = a3v(packet.game_cars[opponent.index].physics.location) - # opponent.rot = a3r(packet.game_cars[opponent.index].physics.rotation) - # opponent.vel = a3v(packet.game_cars[opponent.index].physics.velocity) - #opponent.ang_vel = a3v(packet.game_cars[opponent.index].physics.angular_velocity) - # opponent.dead = packet.game_cars[opponent.index].is_demolished - #opponent.wheel_c = packet.game_cars[opponent.index].has_wheel_contact - #opponent.sonic = packet.game_cars[opponent.index].is_super_sonic - #opponent.jumped = packet.game_cars[opponent.index].jumped - #opponent.d_jumped = packet.game_cars[opponent.index].double_jumped - #opponent.boost = packet.game_cars[opponent.index].boost - # Calculated: - #opponent.orient_m = orient_matrix(opponent.rot) - #opponent.turn_r = turn_r(opponent.vel) + opponent.pos = arr_from_vec(packet.game_cars[opponent.index].physics.location) + opponent.rot = arr_from_rot(packet.game_cars[opponent.index].physics.rotation) + opponent.vel = arr_from_vec(packet.game_cars[opponent.index].physics.velocity) + opponent.ang_vel = arr_from_vec(packet.game_cars[opponent.index].physics.angular_velocity) + opponent.dead = packet.game_cars[opponent.index].is_demolished + opponent.wheel_c = packet.game_cars[opponent.index].has_wheel_contact + opponent.sonic = packet.game_cars[opponent.index].is_super_sonic + opponent.jumped = packet.game_cars[opponent.index].jumped + opponent.d_jumped = packet.game_cars[opponent.index].double_jumped + opponent.boost = packet.game_cars[opponent.index].boost # Processing Ball data. - self.ball.pos = a3v(packet.game_ball.physics.location) - self.ball.rot = a3r(packet.game_ball.physics.rotation) - self.ball.vel = a3v(packet.game_ball.physics.velocity) - self.ball.ang_vel = a3v(packet.game_ball.physics.angular_velocity) + self.ball.pos = arr_from_vec(packet.game_ball.physics.location) + self.ball.rot = arr_from_rot(packet.game_ball.physics.rotation) + self.ball.vel = arr_from_vec(packet.game_ball.physics.velocity) + self.ball.ang_vel = arr_from_vec(packet.game_ball.physics.angular_velocity) self.ball.last_touch = packet.game_ball.latest_touch # Processing ball prediction. @@ -146,4 +139,3 @@ def process(self, packet): pad.timer = packet.game_boosts[pad.index].timer if pad.active == True: self.active_pads.append(pad) - \ No newline at end of file diff --git a/src/np_util/utils.py b/src/np_util/utils.py index baa3b0e..99ef742 100644 --- a/src/np_util/utils.py +++ b/src/np_util/utils.py @@ -120,20 +120,19 @@ def __init__(self, index: int, pos: np.ndarray): # FUNCTIONS FOR CONVERTION TO NUMPY ARRAYS: -def a3l(L: list) -> np.ndarray: +def arr_from_list(L: list) -> np.ndarray: """Converts list to numpy array. Arguments: L {list} -- The list to convert containing 3 elements. Returns: np.array -- Numpy array with the same contents as the list. """ - if len(L) == 3: - return np.array(L) - else: - return np.array(L[0], L[1], L[2]) + if len(L) != 3: + raise ValueError('Expected a list of length 3.') + return np.array(L[0], L[1], L[2]) -def a3r(R: Rotator) -> np.ndarray: +def arr_from_rot(R: Rotator) -> np.ndarray: """Converts rotator to numpy array. Arguments: @@ -145,7 +144,7 @@ def a3r(R: Rotator) -> np.ndarray: return np.array([R.pitch, R.yaw, R.roll]) -def a3v(V: Vector3) -> np.ndarray: +def arr_from_vec(V: Vector3) -> np.ndarray: """Converts vector3 to numpy array. Arguments: From 51ca9902fe55f2a67d9e7d98bbf4c3ff15e0c32b Mon Sep 17 00:00:00 2001 From: ViliamVadocz Date: Fri, 14 Feb 2020 08:56:44 +0100 Subject: [PATCH 7/9] lookup speed and quality of life impovements --- src/util/vec.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/util/vec.py b/src/util/vec.py index 6850e4f..055bc93 100644 --- a/src/util/vec.py +++ b/src/util/vec.py @@ -1,4 +1,5 @@ import math +from typing import Union # This is a helper class for vector math. You can extend it or delete if you want. @@ -13,8 +14,14 @@ class Vec3: When in doubt visit the wiki: https://github.com/RLBot/RLBot/wiki/Useful-Game-Values """ - - def __init__(self, x: float or 'Vec3'=0, y: float=0, z: float=0): + # https://docs.python.org/3/reference/datamodel.html#slots + __slots__ = [ + 'x', + 'y', + 'z' + ] + + def __init__(self, x: Union[float, 'Vec3']=0, y: float=0, z: float=0): """ Create a new Vec3. The x component can alternatively be another vector with an x, y, and z component, in which case the created vector is a copy of the given vector and the y and z parameter is ignored. Examples: @@ -58,27 +65,32 @@ def __truediv__(self, scale: float) -> 'Vec3': return self * scale def __str__(self): - return "Vec3(" + str(self.x) + ", " + str(self.y) + ", " + str(self.z) + ")" + return f"Vec3({self.x:.2f}, {self.y:.2f}, {self.z:.2f})" + + def __repr__(self): + return self.__str__() def flat(self): """Returns a new Vec3 that equals this Vec3 but projected onto the ground plane. I.e. where z=0.""" return Vec3(self.x, self.y, 0) + @property def length(self): """Returns the length of the vector. Also called magnitude and norm.""" return math.sqrt(self.x**2 + self.y**2 + self.z**2) def dist(self, other: 'Vec3') -> float: """Returns the distance between this vector and another vector using pythagoras.""" - return (self - other).length() + return (self - other).length + @property def normalized(self): """Returns a vector with the same direction but a length of one.""" - return self / self.length() + return self / self.length def rescale(self, new_len: float) -> 'Vec3': """Returns a vector with the same direction but a different length.""" - return new_len * self.normalized() + return new_len * self.normalized def dot(self, other: 'Vec3') -> float: """Returns the dot product.""" @@ -94,5 +106,5 @@ def cross(self, other: 'Vec3') -> 'Vec3': def ang_to(self, ideal: 'Vec3') -> float: """Returns the angle to the ideal vector. Angle will be between 0 and pi.""" - cos_ang = self.dot(ideal) / (self.length() * ideal.length()) + cos_ang = self.dot(ideal) / (self.length * ideal.length) return math.acos(cos_ang) From 5ec0d00cb822d95eb846ee59f3874ff0a7ec9612 Mon Sep 17 00:00:00 2001 From: ViliamVadocz Date: Fri, 14 Feb 2020 08:57:01 +0100 Subject: [PATCH 8/9] Ignore VSCode files --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 51410e9..b83f8ae 100644 --- a/.gitignore +++ b/.gitignore @@ -108,4 +108,8 @@ ENV/ /build # Gradle files -/.gradle \ No newline at end of file +/.gradle + +# VSCode +.vscode/ +*.code-workspace \ No newline at end of file From 841b698f27f0e0ca6d9f48d94039ea6fe84bc577 Mon Sep 17 00:00:00 2001 From: ViliamVadocz Date: Fri, 14 Feb 2020 09:03:39 +0100 Subject: [PATCH 9/9] Removed parentheses since length is now a property --- src/bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bot.py b/src/bot.py index b428a79..2e73ce4 100644 --- a/src/bot.py +++ b/src/bot.py @@ -46,7 +46,7 @@ def get_output(self, packet: GameTickPacket) -> SimpleControllerState: # Example of using a sequence # This will do a front flip if the car's velocity is between 550 and 600 - if 550 < car_velocity.length() < 600: + if 550 < car_velocity.length < 600: self.active_sequence = Sequence([ ControlStep(0.05, SimpleControllerState(jump=True)), ControlStep(0.05, SimpleControllerState(jump=False)),