def start_man_calc(self): # manual and calculated self._init() while ExitCtr.gen: if DoFlag.update_frame: Gen.screen.fill(Conf.WHITE) # Reset screen for fresh drawings # Control robot self.controller.manual_control() if DoFlag.auto_calc: self.controller.calculated_control() # Update game state Sprites.every.update() Frames.update() if DoFlag.update_frame: # Update drawing if required # Control loop intervals self.clock.tick(Conf.FPS) # Comment out to remove fps limit # Draw field borders pygame.draw.rect(Gen.screen, Conf.BLACK, self.rect, 1) # Draw everything and update the display Sprites.every.draw(Gen.screen) pygame.display.update() self.exit()
def update(self): # Draw direction lines if requested if DoFlag.show_directions: self.draw_movement_line() # Check if a goal was scored for goal in Sprites.goals: enough_dur = (Frames.time() - goal.last_touch) > 0.1 if self.rect.colliderect(goal) and enough_dur: goal.last_touch = Frames.time() self.score_time = Frames.time() - Gen.last_goal_time if goal.side == Conf.LEFT: self.side = Conf.RIGHT else: self.side = Conf.LEFT self.master.score_goal(self.score_time, self.side) # Check if ball was kicked and move accordingly if self.do_move: dur = Frames.time() - self.kick_time if dur < self.max_time: distance = 0.5 * self.move_dist * (self.max_time - dur) if distance < 2: return self.move(self.move_angle, distance) else: self.do_move = False
def __init__(self, size=Conf.BALL_SIZE, pos=None, color=Conf.COLOR2, master=None): if pos is None: pos = (Conf.WIDTH // 2, Conf.HEIGHT // 2) super().__init__(size, pos=pos, color=color, text="B") self.master = master Gen.last_goal_time = Frames.time() self.move_dist = 0 # Move distance self.move_angle = 0 # Degrees from positive x-axis self.do_move = False self.friction = Physics.FRICTION self.kick_time = 0 self.max_time = 0 self.mass = Physics.BALL_MASS self.f_normal = self.mass * Physics.G self.f_friction = self.f_normal * Physics.MU # Force due to friction self.a_friction = self.f_friction / self.mass # Acceleration || self.side = None # The side the ball scored on self.score_time = None Sprites.balls.add(self)
def move(self, move_dir, distance=None): if move_dir == Conf.FORWARD: # Move in current direction super().move(self.move_angle) elif move_dir == Conf.BACKWARD: # Rotate direction by 180 amd move forward super().move(self.move_angle + 180) elif move_dir == Conf.LEFT: # Prevent excessive updates to angle change for a single press dur = Frames.time() - self.cool_down_l if dur > Conf.CD_TIME: self.move_angle -= Conf.DIR_OFFSET self.cool_down_l = Frames.time() self.place_dir_arrow() self.limit_angle() elif move_dir == Conf.RIGHT: dur = Frames.time() - self.cool_down_r if dur > Conf.CD_TIME: self.move_angle += Conf.DIR_OFFSET self.cool_down_r = Frames.time() self.place_dir_arrow() self.limit_angle()
def update_score(self, side): if side == Conf.LEFT: self.left += 1 elif side == Conf.RIGHT: self.right += 1 self.total = self.left + self.right self.image.fill(Conf.ALPHA_COLOR) # Clear score box self.text = f"LEFT {self.left} : RIGHT {self.right}" # Update text self.text_render = self.font.render(self.text, True, Conf.BLACK) self.text_rect = self.text_render.get_rect() self.text_rect.center = (self.rect.width // 2, self.rect.height // 2) Gen.last_goal_time = Frames.time() # Place text self.image.blit(self.text_render, self.text_rect)
def frame_step(self, action): """ param: action --> tuple( direction --> 0 (left), 1 (right) angle --> 1 - 12 (0 to 180/angle_increment ) distance --> 0 to 60 (0 to 300/move_dist) kick --> 0 (don't kick) or 1 (kick) continue --> 0 (don't continue) or 1 (continue) ) return: x --> 0 to 600 (x coordinate) y --> 0 to 400 (y coordinate) # Ball relative conditions ball_flag --> 0 (not seen) or 1 (seen) ball_theta --> 0 to 360/angle_increment ball_dist --> 0 to 600/move_dist # Own goal relative conditions own_goal_theta --> 0 to 360/angle_increment own_goal_dist --> 0 to 600/move_dist # Opponent goal conditions opp_goal_theta --> 0 to 360/angle_increment opp_goal_dist --> 0 to 600/move_dist # Kick results given 0 or 1 for only one frame is_kick_success --> 0 (null) or 2 (miss) or 1 (hit) is_kick_accurate --> 0 (null) or 2 (bad miss) or 1 (near miss) # State flags is_kicking --> 0 (not kicking) or 1 (is kicking) is_moving --> 0 (not moving) or 1 (is moving) is_ball_moved -- > 0 (not moved recently) or 1 (moved recently) is_goal_scored --> 2 (own goal scored) 0 (not scored) or 1 (scored) time --> time since start (calculated based on frames) """ # print(type(action),action) # # return # Attempt to instantiate which will only be done if not done already self._init() if DoFlag.update_frame: Gen.screen.fill(Conf.WHITE) # Reset screen for fresh drawings NULL = 0 STATE1 = 1 STATE2 = 2 # Reset goal to not scored self._goal_scored(reset=True) # Get action commands # print(type(action),action) direction, theta, dist, kick, cont = list(action) theta *= Conf.DIR_OFFSET dist *= Conf.MOVE_DIST if direction == 1: direction = Conf.RIGHT elif direction == 0: direction = Conf.LEFT else: raise ValueError( f"{direction} is not a valid option. Must be 0 or 1") # Create return away ret_val = np.zeros(fsr.NUM_VAL) ret_val[fsr.ACT_DIR] = action[0] ret_val[fsr.ACT_THETA] = action[1] ret_val[fsr.ACT_DIST] = action[2] ret_val[fsr.ACT_KICK] = action[3] ret_val[fsr.ACT_CONT] = action[4] # Get vector positions (relative positions) to_ball_s, _, _ = self.controller.get_vec() ball_dist, ball_theta = to_ball_s.as_polar() if not cont: # Validate data if ret_val[fsr.ACT_THETA] > 360 / Conf.DIR_OFFSET: print(f"Warning {ret_val[fsr.ACT_THETA]} out of range " f"{360/Conf.DIR_OFFSET}") if ret_val[fsr.ACT_DIST] > Conf.WIDTH / Conf.MOVE_DIST: print(f"Warning {ret_val[fsr.ACT_DIST]} out of range " f"{Conf.WIDTH/Conf.MOVE_DIST}") self.net[Conf.DIRECTION] = direction self.net[Conf.THETA] = theta * Conf.DIR_OFFSET self.net[Conf.DIST] = dist * Conf.MOVE_DIST self.net[Conf.KICK] = kick # In case no kick was performed set to null value ret_val[fsr.IS_KICK_ACCURATE] = ret_val[fsr.IS_KICK_SUCCESS] = NULL if Frames.time() - Gen.last_kick_time < Conf.KICK_COOLDOWN: ret_val[fsr.IS_KICKING] = 1 elif self.net[Conf.KICK] == 1: is_kicked = self.robot.kick() self.net[Conf.KICK] = False Gen.last_kick_time = Frames.time() # If within kick range if is_kicked: ret_val[fsr.IS_KICK_SUCCESS] = STATE1 else: ret_val[fsr.IS_KICK_SUCCESS] = STATE2 elif self.net[Conf.THETA] > 0: self.robot.move(self.net[Conf.DIRECTION]) self.net[Conf.THETA] -= Conf.DIR_OFFSET elif self.net[Conf.DIST] > 0: self.robot.move(Conf.FORWARD) self.net[Conf.DIST] -= Conf.MOVE_DIST # Update game state Sprites.every.update() Frames.update() # Update drawing if required if DoFlag.update_frame: self.clock.tick(Conf.FPS) # Regulate FPS if desired # Draw field borders pygame.draw.rect(Gen.screen, Conf.BLACK, self.rect, 1) # Draw everything and update the display Sprites.every.draw(Gen.screen) pygame.display.update() # Update return Values to_ball, to_goal, to_own_goal = self.controller.get_vec() ret_val[fsr.BALL_DIST], ret_val[fsr.BALL_THETA] = to_ball.as_polar() ret_val[fsr.OPP_GOAL_DIST], ret_val[fsr.OPP_GOAL_THETA] = ( to_goal.as_polar()) ret_val[fsr.OWN_GOAL_DIST], ret_val[fsr.OWN_GOAL_THETA] = ( to_own_goal.as_polar()) if self.robot.move_angle < -90 and ball_theta > 90: # Edge case if self.robot.move_angle + 360 - ball_theta <= Conf.HALF_VIS_THETA: ret_val[fsr.BALL_FLAG] = STATE1 elif self.robot.move_angle > 90 and ball_theta < -90: # Edge case if ball_theta + 360 - self.robot.move_angle <= Conf.HALF_VIS_THETA: ret_val[fsr.BALL_FLAG] = STATE1 elif abs(self.robot.move_angle - ball_theta) <= Conf.HALF_VIS_THETA: # If within field of view ret_val[fsr.BALL_FLAG] = STATE1 # If ball_start_pos != current_ball_pos then ball moving if to_ball != to_ball_s: ret_val[fsr.IS_BALL_MOVED] = STATE1 # If still has angle change or distance change then moving if self.net[Conf.THETA] > 0 or self.net[Conf.DIST] > 0: ret_val[fsr.IS_MOVING] = STATE1 # Check if goal scored this frame if self.is_goal_scored: if ( # If close to opponent goal then scored and accurate self.ball.side == self.robot.side): ret_val[fsr.IS_GOAL_SCORED] = STATE1 ret_val[fsr.IS_KICK_ACCURATE] = STATE1 else: # If not close to target then own goal ret_val[fsr.IS_GOAL_SCORED] = STATE2 ret_val[fsr.IS_KICK_ACCURATE] = NULL elif ( # if ball in wall bounce x location check next condition # If ball is within range of y value self.controller.goal_cen.y - 1 * self.controller.goal.rect.height < self.balls[0].rect.centery < self.controller.goal_cen.y + 1 * self.controller.goal.rect.height): if ( # If hit the wall close to the goal self.ball.rect.centerx > self.ball_bounce_x and self.robot.side == Conf.LEFT): if not self.is_accurate_sent: ret_val[fsr.IS_KICK_ACCURATE] = STATE1 self.is_accurate_sent = True elif (self.ball.rect.centerx < self.ball_bounce_x and self.robot.side == Conf.RIGHT): if not self.is_accurate_sent: ret_val[fsr.IS_KICK_ACCURATE] = STATE1 self.is_accurate_sent = True else: # If not against respective wall accuracy was not sent self.is_accurate_sent = False else: if (self.ball.rect.centerx > self.ball_bounce_x and self.robot.side == Conf.LEFT): if not self.is_accurate_sent: ret_val[fsr.IS_KICK_ACCURATE] = STATE2 self.is_accurate_sent = True elif (self.ball.rect.centerx < self.ball_bounce_x and self.robot.side == Conf.RIGHT): if not self.is_accurate_sent: ret_val[fsr.IS_KICK_ACCURATE] = STATE2 self.is_accurate_sent = True else: self.is_accurate_sent = False ret_val[fsr.X] = self.robot.rect.centerx ret_val[fsr.Y] = self.robot.rect.centery ret_val[fsr.TIME] = int(Frames.time()) return np.array(ret_val)
def get_kicked(self, speed, angle): self.move_dist = speed self.move_angle = angle self.kick_time = Frames.time() self.max_time = speed / self.a_friction self.do_move = True