def get_boundary_walls(x_axis: int, y_axis: int): """Get a set of the boundary walls.""" a = Vec2d(0, 0) b = Vec2d(x_axis, 0) c = Vec2d(x_axis, y_axis) d = Vec2d(0, y_axis) return {Line2d(a, b), Line2d(b, c), Line2d(c, d), Line2d(d, a)}
def create_custom_game(cfg: Config, overwrite=False): """ Dummy to create a custom-defined game. """ # Initial parameters game_id = 0 # Create empty Game instance game = Game(config=cfg, game_id=game_id, overwrite=overwrite) # Put the target on a fixed position game.target = Vec2d(0.5, cfg.game.y_axis - 0.5) # Set game path p = dict() # TODO! for x in range(0, cfg.game.x_axis * 10 + 1): # TODO! for y in range(0, cfg.game.x_axis * 10 + 1): # TODO! p[(x / 10, y / 10)] = Line2d(game.target, Vec2d(x / 10, y / 10)).get_length() game.path = p # Create random player game.player = MarXBot(game=game) game.set_player_init_angle(a=np.pi / 2) game.set_player_init_pos(p=Vec2d(cfg.game.x_axis - 0.5, 0.5)) # Check if implemented correctly game.close() game.reset() game.get_blueprint() game.get_observation() game.step(0, 0) # Save the final game game.save()
def __init__(self, game, # Type not specified due to circular imports r: float = 0 ): """ Create a new Robot object. :param game: Reference to the game in which the robot is created [Game] :param r: Radius of the circular robot """ # Game specific parameter self.game = game # Game in which robot runs # Robot specific parameters (Placeholders) self.pos = Vec2d(0, 0) # Current position self.init_pos = Vec2d(0, 0) # Initial position self.prev_pos = Vec2d(0, 0) # Previous current position self.angle: float = 0 # Current angle self.init_angle: float = 0 # Initial angle self.noisy_init_angle: float = 0 # Initial angle with noise self.prev_angle: float = 0 # Previous angle self.radius: float = r if r else game.bot_config.radius # Radius of the bot # Container of all the sensors self.sensors: dict = dict() # Counters for number of sensors used self.n_angular: int = 0 self.n_delta_distance: int = 0 self.n_distance: int = 0 # Create the sensors (fixed order!) self.create_angular_sensors(cfg=game.bot_config) self.create_delta_distance_sensor(cfg=game.bot_config) self.create_distance_sensor(cfg=game.bot_config)
def set_target_random(self): """Put the target on a random location.""" r = random() if r < 1 / 5: # 1/5th chance self.target = Vec2d(self.game_config.x_axis / 2 - 0.5, self.game_config.y_axis - 0.5) # Top center elif r < 2 / 5: # 1/5th chance self.target = Vec2d(0.5, self.game_config.y_axis / 2 - 0.5) # Center left else: # 3/5th chance self.target = Vec2d(0.5, self.game_config.y_axis - 0.5) # Top left
def test_other_angles(self): """> Check if drone cannot force itself through a wall.""" # Folder must be root to load in make_net properly if os.getcwd().split('\\')[-1] == 'tests': os.chdir('..') # Create the lines zero = Vec2d(0, 0) a = Vec2d(1, 1) line1 = Line2d(zero, a) # Tests self.assertTrue(pi / 4 - EPSILON <= line1.get_orientation() % (2 * pi) <= pi / 4 + EPSILON)
def create_empty_game(self): """Create an empty game that only contains the boundary walls.""" # Create random set of walls self.walls = get_boundary_walls(x_axis=self.game_config.x_axis, y_axis=self.game_config.y_axis) self.target = Vec2d(0.5, self.game_config.y_axis - 0.5) self.player = MarXBot(game=self) self.set_player_init_angle(a=pi / 2) self.set_player_init_pos(p=Vec2d(self.game_config.x_axis - 0.5, 0.5)) # Save the new game self.save() if not self.silent: print(f"New game created under id: {self.id}")
def measure(self, close_walls: set = None): """ Get the distance to the closest wall. If all the walls are 'far enough', as determined by self.max_dist, then the maximum sensor-distance is returned. :param close_walls: Walls which fall within ray_distance from the agent, speeds up readings :return: Float expressing the distance to the closest wall, if there is any """ # Start and end point of ray normalized_offset = Vec2d(cos(self.game.player.angle + self.angle), sin(self.game.player.angle + self.angle)) self.start_pos = self.game.player.pos + normalized_offset * self.pos_offset self.end_pos = self.game.player.pos + normalized_offset * ( self.pos_offset + self.max_dist) sensor_line = Line2d(x=self.game.player.pos, y=self.end_pos) # Check if there is a wall intersecting with the sensor and return the closest distance to a wall self.value = self.max_dist for wall in close_walls if close_walls else self.game.walls: inter, pos = line_line_intersection(sensor_line, wall) if inter: new_dist = (pos - self.start_pos).get_length() if self.value > new_dist: self.end_pos = pos self.value = new_dist if self.game.noise: self.value += random.gauss(0, self.game.noise_proximity) self.value = max(0, min(self.value, self.max_dist))
def test_simple_length(self): """> Test for simple line-segments.""" # Folder must be root to load in make_net properly if os.getcwd().split('\\')[-1] == 'tests': os.chdir('..') # Create simple lines a = Vec2d(1, 1) b = Vec2d(1, 2) c = Vec2d(2, 2) line1 = Line2d(a, b) line2 = Line2d(b, c) line3 = Line2d(a, c) # Test the length self.assertTrue(1 - EPSILON <= line1.get_length() <= 1 + EPSILON) self.assertTrue(1 - EPSILON <= line2.get_length() <= 1 + EPSILON) self.assertTrue(sqrt(2) - EPSILON <= line3.get_length() <= sqrt(2) + EPSILON)
def load(self): """ Load in a game, specified by its current id. :return: True: game successfully loaded | False: otherwise """ try: game = load_pickle(f'{self.save_path}{self}') self.player = MarXBot(game=self) # Create a dummy-player to set values on self.set_player_init_angle(game[D_ANGLE]) self.set_player_init_pos(Vec2d(game[D_POS][0], game[D_POS][1])) self.path = {p[0]: p[1] for p in game[D_PATH]} self.target = Vec2d(game[D_TARGET][0], game[D_TARGET][1]) self.walls = {Line2d(Vec2d(w[0][0], w[0][1]), Vec2d(w[1][0], w[1][1])) for w in game[D_WALLS]} if not self.silent: print(f"Existing game loaded with id: {self.id}") return True except FileNotFoundError: return False
def __init__( self, game, # Type not specified due to circular imports r: float = 0): """ Create a new FootBot object. :param game: Reference to the game in which the robot is created [Game] :param r: Radius of the circular robot """ # Game specific parameter self.game = game # Game in which robot runs # Robot specific parameters (Placeholders) self.pos = Vec2d(0, 0) # Current position self.init_pos = Vec2d(0, 0) # Initial position self.prev_pos = Vec2d(0, 0) # Previous current position self.angle: float = 0 # Current angle self.init_angle: float = 0 # Initial angle self.prev_angle: float = 0 # Previous angle self.radius: float = r if r else game.bot_config.radius # Radius of the bot # Container of all the sensors self.sensors: dict = dict() # Counters for number of sensors used self.n_proximity: int = 0 self.n_angular: int = 0 self.n_delta_distance: int = 0 self.n_distance: int = 0 # Create the sensors (fixed order!) self.create_proximity_sensors(cfg=game.bot_config) self.create_angular_sensors(cfg=game.bot_config) self.create_delta_distance_sensor(cfg=game.bot_config) self.add_distance_sensor() # Number of distance-sensors must always be equal to 1 assert self.n_distance == 1 # Set all the sensors as active initially self.active_sensors = set(self.sensors.keys())
def test_quadrant_angles(self): """> Check if drone cannot force itself through a wall.""" # Folder must be root to load in make_net properly if os.getcwd().split('\\')[-1] == 'tests': os.chdir('..') # Create the lines zero = Vec2d(0, 0) a = Vec2d(1, 0) b = Vec2d(0, 1) c = Vec2d(-1, 0) d = Vec2d(0, -1) line1 = Line2d(zero, a) line2 = Line2d(zero, b) line3 = Line2d(zero, c) line4 = Line2d(zero, d) # Tests self.assertTrue(0 - EPSILON <= line1.get_orientation() % (2 * pi) <= 0 + EPSILON) self.assertTrue(pi / 2 - EPSILON <= line2.get_orientation() % (2 * pi) <= pi / 2 + EPSILON) self.assertTrue(pi - EPSILON <= line3.get_orientation() % (2 * pi) <= pi + EPSILON) self.assertTrue(3 * pi / 2 - EPSILON <= line4.get_orientation() % (2 * pi) <= 3 * pi / 2 + EPSILON)
def create_experiment_3(overwrite: bool = True, show: bool = True): """ Experiment 3 tests both the generalization capabilities in adapting to newly spawned targets. In this scenario, new targets are spawned ones the previous are found, with the goal to find as many as possible. TRAINING During training, new targets are spawned based on the agent's current position. Each target will lay between 4 and 8 meters from the agent and will always be positioned inside of the maze. TESTING Testing is done similar as was the case for training, with the main difference that the positions of the targets are defined beforehand, this to ensure a fair comparison between the different evaluations. 10 targets are created, with the idea that it would be impossible for the drone to ever reach this many targets. """ cfg = get_shared_config() # Fixed parameters ROBOT_INIT_ANGLE = pi / 2 # Looking up ROBOT_INIT_POS = Vec2d(cfg.game.x_axis / 2, cfg.game.y_axis / 2) # Initial position of the drone # Create the training game spawn_f_train = SpawnRandom(game_config=cfg.game, train=True) game = Game( config=cfg, player_noise=0, game_id=get_game_id(0, experiment_id=3), overwrite=overwrite, spawn_func=spawn_f_train, stop_if_reached=False, wall_bound=False, ) game.player = Robot(game=game) game.set_player_init_angle(a=ROBOT_INIT_ANGLE) game.set_player_init_pos(p=ROBOT_INIT_POS) check_and_save_game(game, show=show) # Create 20 evaluation games for i in range(1, 21): spawn_f_eval = SpawnRandom(game_config=cfg.game, train=False) game = Game( config=cfg, player_noise=0, game_id=get_game_id(i, experiment_id=3), overwrite=overwrite, spawn_func=spawn_f_eval, stop_if_reached=False, wall_bound=False, ) game.player = Robot(game=game) game.set_player_init_angle(a=ROBOT_INIT_ANGLE) game.set_player_init_pos(p=ROBOT_INIT_POS) check_and_save_game(game, show=show, randomize=False)
def __init__(self): # Initialize imported pygame modules. pygame.mixer.pre_init(44100, 16, 2, 4096) pygame.init() pygame.mixer.init() # Initialize screen object. # All visual game objects will have a refrence to this # screen object so they can draw to it. self.screen = pygame.display.set_mode( (config.screenWidth, config.screenHeight), 0, 32) # Clock is used to track and control the framerate of the game. self.clock = pygame.time.Clock() # Game states self.paused = 0 self.running = 1 self.gameover = 0 # Sound objects self.initSounds() # Game stats self.stats = {'score': 0, 'level': 1} # Game objects initialShipPos = Vec2d( (config.screenWidth / 2, config.screenHeight / 2)) self.ship = Ship(self.screen, initialShipPos) self.shipBulletController = BulletController(self.screen, self.ship, self.bulletSound) self.asteroidController = AsteroidController(self.screen, self.ship) # Powerup controller self.powerupController = PowerupController(self.screen, self.ship, self.shipBulletController) # Event spawner self.eventSpawner = EventSpawner(self.asteroidController, self.powerupController, self.stats) # Screen visuals self.visualsController = VisualsController(self.screen, self.asteroidController, self.shipBulletController, self.ship, self.stats) # Ship explosion self.explosion = None # Will initialize during game over sequence.
def get_wall_coordinates(self): """ :return: Wall coordinates of final maze (excluding boundaries) in original axis-format. """ wall_list = [] # Horizontal segments for x in range(1, self.x_width - 1, 2): for y in range(2, self.y_width, 2): if self.maze[y, x] == -1: wall_list.append( Line2d(Vec2d((x - 1) // 2, y // 2), Vec2d((x + 1) // 2, y // 2))) # Vertical segments for x in range(2, self.x_width, 2): for y in range(1, self.y_width - 1, 2): if self.maze[y, x] == -1: wall_list.append( Line2d(Vec2d(x // 2, (y - 1) // 2), Vec2d(x // 2, (y + 1) // 2))) combine_walls(wall_list) return wall_list
def create_dummy_game(i: int = 0, overwrite: bool = True, show: bool = True): cfg = get_shared_config() ROBOT_INIT_POS = Vec2d(cfg.game.x_axis / 2, cfg.game.y_axis / 2) # Initial position of the drone spawn_f = SpawnSimple(game_config=cfg, train=True) spawn_f.add_location((1, 1)) game = Game( config=cfg, game_id=get_game_id(i, experiment_id=0), overwrite=overwrite, spawn_func=spawn_f, ) game.player = Robot(game=game) game.set_player_init_pos(p=ROBOT_INIT_POS) check_and_save_game(game, show=show)
def test_negative_component(self): """> Test for line-segments with negative components.""" # Folder must be root to load in make_net properly if os.getcwd().split('\\')[-1] == 'tests': os.chdir('..') # Create simple lines a = Vec2d(1, 1) b = Vec2d(1, -1) c = Vec2d(-1, -1) d = Vec2d(-1, 1) line1 = Line2d(a, b) line2 = Line2d(b, c) line3 = Line2d(c, d) line4 = Line2d(d, a) diag1 = Line2d(a, c) diag2 = Line2d(b, d) # Test the length self.assertTrue(2 - EPSILON <= line1.get_length() <= 2 + EPSILON) self.assertTrue(2 - EPSILON <= line2.get_length() <= 2 + EPSILON) self.assertTrue(2 - EPSILON <= line3.get_length() <= 2 + EPSILON) self.assertTrue(2 - EPSILON <= line4.get_length() <= 2 + EPSILON) self.assertTrue(sqrt(8) - EPSILON <= diag1.get_length() <= sqrt(8.) + EPSILON) self.assertTrue(sqrt(8) - EPSILON <= diag2.get_length() <= sqrt(8.) + EPSILON)
def get_game(): cfg = Config() cfg.game.fps = 60 cfg.game.x_axis = 1 cfg.game.y_axis = 1 cfg.update() spawn = SpawnSimple(game_config=cfg) spawn.add_location((10, 10)) # Dummy location game = Game( config=cfg, game_id=0, noise=False, overwrite=True, save_path="tests/games_db/", silent=True, spawn_func=spawn, wall_bound=True, ) game.set_player_init_pos(Vec2d(0.5, 0.5)) game.sample_target() game.reset() return game
def test_remain_in_box(self): """> Set drone in small box in the middle of the game to check if it stays in this box.""" # Folder must be root to load in make_net properly if os.getcwd().split('\\')[-1] == 'tests': os.chdir('..') # Create empty game game = get_game() # Do 10 different initialized tests for _ in range(10): # Set player init positions game.set_player_init_pos(Vec2d(0.5, 0.5)) game.player.angle = random() * np.pi # Run for one twenty seconds for _ in range(20 * game.game_config.fps): l = random() * 1.5 - 0.5 # [-0.5..1] r = random() * 1.5 - 0.5 # [-0.5..1] game.step(l=l, r=r) # Check if bot still in box self.assertTrue(0 <= game.player.pos.x <= 1) self.assertTrue(0 <= game.player.pos.y <= 1)
def create_game( cfg: Config, game_id: int, maze: Maze, overwrite: bool = False, visualize: bool = False, ): """ Create a game based on a list of walls. :param cfg: The game config :param game_id: ID of the game (Integer) :param maze: The maze on which the game is based :param overwrite: Overwrite pre-existing games :param visualize: Visualize the calculations """ # Create empty Game instance game = Game(config=cfg, game_id=game_id, overwrite=overwrite, silent=True) # Add additional walls to the game game.walls.update(set(maze.get_wall_coordinates())) # Set the target on a randomized position game.set_target_random() # App path to the game path_list = maze.get_path_coordinates(target_pos=game.target, visualize=visualize) game.path = {p[0]: p[1] for p in path_list} # Create random player game.player = MarXBot(game=game) game.set_player_init_angle(a=np.pi / 2) game.set_player_init_pos(p=Vec2d(cfg.game.x_axis - 0.5, 0.5)) # Save the final game game.save()
def combine_walls(wall_list: list): """ Combine every two wall-segments (Line2d) together that can be represented by only one line segment. This will increase the performance later on, since the intersection methods must loop over less wall-segments. :param wall_list: List<Line2d> :return: List<Line2d> """ i = 0 while i < len(wall_list) - 1: concat = False # Check if wall at i can be concatenated with wall after i for w in wall_list[i + 1:]: # Check if in same horizontal line if wall_list[i].x.y == wall_list[i].y.y == w.x.y == w.y.y: # Check if at least one point collides if wall_list[i].x.x == w.x.x: wall_list[i] = Line2d(Vec2d(wall_list[i].y.x, w.x.y), Vec2d(w.y.x, w.x.y)) concat = True elif wall_list[i].y.x == w.x.x: wall_list[i] = Line2d(Vec2d(wall_list[i].x.x, w.x.y), Vec2d(w.y.x, w.x.y)) concat = True elif wall_list[i].y.x == w.y.x: wall_list[i] = Line2d(Vec2d(wall_list[i].x.x, w.x.y), Vec2d(w.x.x, w.x.y)) concat = True elif wall_list[i].x.x == w.y.x: wall_list[i] = Line2d(Vec2d(wall_list[i].y.x, w.x.y), Vec2d(w.x.x, w.x.y)) concat = True # Check if in same vertical line elif wall_list[i].x.x == wall_list[i].y.x == w.x.x == w.y.x: # Check if at least one point collides if wall_list[i].x.y == w.x.y: wall_list[i] = Line2d(Vec2d(w.x.x, wall_list[i].y.y), Vec2d(w.x.x, w.y.y)) concat = True elif wall_list[i].y.y == w.x.y: wall_list[i] = Line2d(Vec2d(w.x.x, wall_list[i].x.y), Vec2d(w.x.x, w.y.y)) concat = True elif wall_list[i].y.y == w.y.y: wall_list[i] = Line2d(Vec2d(w.x.x, wall_list[i].x.y), Vec2d(w.x.x, w.x.y)) concat = True elif wall_list[i].x.y == w.y.y: wall_list[i] = Line2d(Vec2d(w.x.x, wall_list[i].y.y), Vec2d(w.x.x, w.x.y)) concat = True # If w concatenated with i'th wall in wall_list, then remove w from list and break for-loop if concat: wall_list.remove(w) break # Current wall cannot be extended, go to next wall if not concat: i += 1
def create_experiment_2(overwrite: bool = True, show: bool = True): """ Experiment 2 tests the generalization capabilities of the genomes based on their initial distance from the target. TRAINING Training is done in a maze where the robot is in the center, and the targets are positioned around the agent. Each run has a single target sampled. The targets all are positioned 6 meters from the robot's initial position. TESTING Testing is done similar as was the case for training. For testing, half of the training's initial target- orientations are used (18 instead of 36). However, each orientation now has two targets, both with a different initial position from the robot's starting position. An inner-ring has distance 4, where the outer-ring has a distance of 8. """ cfg = get_shared_config() # Fixed parameters ROBOT_INIT_ANGLE = pi / 2 # Looking up ROBOT_INIT_POS = Vec2d(cfg.game.x_axis / 2, cfg.game.y_axis / 2) # Initial position of the drone # Create the training game spawn_f_train = SpawnSimple(game_config=cfg, train=True) for i in range(0, 360, 10): # Positions circular in hops of 10 degree angle = i / 180 * pi offset_x = 6 * cos(angle) offset_y = 6 * sin(angle) spawn_f_train.add_location( (ROBOT_INIT_POS[0] + offset_x, ROBOT_INIT_POS[1] + offset_y)) game = Game( config=cfg, player_noise=0, game_id=get_game_id(0, experiment_id=2), overwrite=overwrite, spawn_func=spawn_f_train, stop_if_reached=True, wall_bound=False, ) game.player = Robot(game=game) game.set_player_init_angle(a=ROBOT_INIT_ANGLE) game.set_player_init_pos(p=ROBOT_INIT_POS) check_and_save_game(game, show=show) # Create the inner circle of the evaluation games, each of those only contains one target for i in range(1, 19): spawn_f_eval = SpawnSimple(game_config=cfg, train=False) angle = i / 9 * pi # Hops of 20° offset_x = 4 * cos(angle) offset_y = 4 * sin(angle) spawn_f_eval.add_location( (ROBOT_INIT_POS[0] + offset_x, ROBOT_INIT_POS[1] + offset_y)) game = Game( config=cfg, player_noise=0, game_id=get_game_id(i, experiment_id=2), overwrite=overwrite, spawn_func=spawn_f_eval, stop_if_reached=True, wall_bound=False, ) game.player = Robot(game=game) game.set_player_init_angle(a=ROBOT_INIT_ANGLE) game.set_player_init_pos(p=ROBOT_INIT_POS) check_and_save_game(game, show=show, randomize=False) # Create the outer circle of the evaluation games, each of those only contains one target for i in range(1, 19): spawn_f_eval = SpawnSimple(game_config=cfg, train=False) angle = i / 9 * pi # Hops of 20° offset_x = 8 * cos(angle) offset_y = 8 * sin(angle) spawn_f_eval.add_location( (ROBOT_INIT_POS[0] + offset_x, ROBOT_INIT_POS[1] + offset_y)) game = Game( config=cfg, player_noise=0, game_id=get_game_id(i + 100, experiment_id=2), overwrite=overwrite, spawn_func=spawn_f_eval, stop_if_reached=True, wall_bound=False, ) game.player = Robot(game=game) game.set_player_init_angle(a=ROBOT_INIT_ANGLE) game.set_player_init_pos(p=ROBOT_INIT_POS) check_and_save_game(game, show=show, randomize=False)
def __init__(self, x: Vec2d = None, y: Vec2d = None): self.x: Vec2d = x if x else Vec2d(0, 0) self.y: Vec2d = y if y else Vec2d(0, 0)
""" import pygame from utils.vec2d import Vec2d # Game config screenWidth = 1000 screenHeight = 700 framerate = 60 # Ship config shipHealth = 3 shipTranslationSpeed = 0.2 shipAcceleration = 0.07 shipDecelerationFactor = 0.97 keyToTranslationVector = { pygame.K_j: Vec2d((-shipTranslationSpeed, 0)), # Left pygame.K_l: Vec2d((shipTranslationSpeed, 0)), # Right pygame.K_i: Vec2d((0, -shipTranslationSpeed)), # Up pygame.K_k: Vec2d((0, shipTranslationSpeed)) # Down } shipRotationSpeed = 0.3 keyToRotationAngle = { pygame.K_a: -shipRotationSpeed, # Counterclockwise pygame.K_d: shipRotationSpeed # Clockwise } invincibilityDuration = 3000.0 # BulletController config timeBetweenBullets = 150
def create_experiment_1(overwrite: bool = True, show: bool = True): """ Experiment 1 tests the generalization capabilities of the genomes based on the relative direction the target is placed. TRAINING Training is done in a maze where the robot is in the center, and the targets are positioned around the agent. Each run has a single target sampled. The targets all are positioned 6 meters from the robot's initial position. TESTING Testing is done similar as was the case for training, however, in this scenario only 20 targets are sampled (where there were 36 for training). Only 4 of these 20 sampled target positions overlap with the training set. """ cfg = get_shared_config() # Fixed parameters ROBOT_INIT_ANGLE = pi / 2 # Looking up ROBOT_INIT_POS = Vec2d(cfg.game.x_axis / 2, cfg.game.y_axis / 2) # Initial position of the drone # Create the training game spawn_f_train = SpawnSimple(game_config=cfg, train=True) for i in range(0, 360, 10): # Positions circular in hops of 10 degree angle = i / 180 * pi offset_x = 6 * cos(angle) offset_y = 6 * sin(angle) spawn_f_train.add_location( (ROBOT_INIT_POS[0] + offset_x, ROBOT_INIT_POS[1] + offset_y)) game = Game( config=cfg, player_noise=0, game_id=get_game_id(0, experiment_id=1), overwrite=overwrite, spawn_func=spawn_f_train, stop_if_reached=True, wall_bound=False, ) game.player = Robot(game=game) game.set_player_init_angle(a=ROBOT_INIT_ANGLE) game.set_player_init_pos(p=ROBOT_INIT_POS) check_and_save_game(game, show=show) # Create the evaluation games, each of those only contains one target for i in range(1, 21): spawn_f_eval = SpawnSimple(game_config=cfg, train=False) angle = i / 10 * pi offset_x = 6 * cos(angle) offset_y = 6 * sin(angle) spawn_f_eval.add_location( (ROBOT_INIT_POS[0] + offset_x, ROBOT_INIT_POS[1] + offset_y)) game = Game( config=cfg, player_noise=0, game_id=get_game_id(i, experiment_id=1), overwrite=overwrite, spawn_func=spawn_f_eval, stop_if_reached=True, wall_bound=False, ) game.player = Robot(game=game) game.set_player_init_angle(a=ROBOT_INIT_ANGLE) game.set_player_init_pos(p=ROBOT_INIT_POS) check_and_save_game(game, show=show, randomize=False)
def get_path_coordinates(self, target_pos, visualize: bool = False): """Define all free-positions together with their distance to the target.""" # Expand the maze such that every 10cm gets a tile y_tiles = self.cfg.game.y_axis * 11 + 1 x_tiles = self.cfg.game.x_axis * 11 + 1 maze_expanded = np.zeros((y_tiles, x_tiles)) # Copy tiles from current maze to the extended maze for row in range(y_tiles): for col in range(x_tiles): # Cross-section if row % 11 == 0 and col % 11 == 0: maze_expanded[row, col] = -1 # Horizontal walls elif row % 11 == 0 and self.maze[(row // 11) * 2, (col // 11) * 2 + 1] < 0: maze_expanded[row, col] = -1 # Vertical walls elif col % 11 == 0 and self.maze[(row // 11) * 2 + 1, (col // 11) * 2] < 0: maze_expanded[row, col] = -1 def update(pos_1, pos_2, dist=0.1): """ Update a single position based on its neighbour position. :param pos_1: Current position :param pos_2: Neighbour position that needs to update :param dist: Distance (real-life) between the two positions """ if self.in_maze([pos_2[1], pos_2[0]], maze=maze_expanded) and \ 0 <= maze_expanded[pos_2[1], pos_2[0]] < maze_expanded[pos_1[1], pos_1[0]] - dist: maze_expanded[pos_2[1], pos_2[0]] = maze_expanded[pos_1[1], pos_1[0]] - dist return True return False def update_neighbours(p): updated = set() for i in [-1, 1]: if update(p, [p[0] + i, p[1]]): updated.add((p[0] + i, p[1])) # Horizontal if update(p, [p[0], p[1] + i]): updated.add((p[0], p[1] + i)) # Vertical if update(p, [p[0] + i, p[1] + i], dist=sqrt(0.02)): updated.add((p[0] + i, p[1] + i)) # Diagonal if update(p, [p[0] + i, p[1] - i], dist=sqrt(0.02)): updated.add((p[0] + i, p[1] - i)) # V-shaped return updated # Constant VALUE_START = 100 # Find distance to target if visualize: self.visualize_extend(maze=maze_expanded) if target_pos == Vec2d(maze.cfg.game.x_axis / 2 - 0.5, maze.cfg.game.y_axis - 0.5): # Top center target = (x_tiles // 2 - 6, y_tiles - 6) elif target_pos == Vec2d(0.5, maze.cfg.game.y_axis / 2 - 0.5): # Center left target = (5, y_tiles // 2 - 6) elif target_pos == Vec2d(0.5, maze.cfg.game.y_axis - 0.5): # Top left target = (5, y_tiles - 6) else: raise Exception("Invalid target_pos input") maze_expanded[target[1], target[0]] = VALUE_START updated_pos = {target} # Keep updating all the set_positions' neighbours while change occurs while updated_pos: new_pos = set() for pos in updated_pos: new_updated_pos = update_neighbours(pos) if new_updated_pos: new_pos.update(new_updated_pos) updated_pos = new_pos if visualize: self.visualize_extend(maze=maze_expanded) # Invert values such that distance to target @target equals 0, and distance at start equals |X-VALUE_START| / 2 # Note the /2 since 1m in-game is 2 squared here for row in range(1, y_tiles - 1): for col in range(1, x_tiles - 1): if maze_expanded[row, col] > 0: maze_expanded[row, col] = abs(maze_expanded[row, col] - VALUE_START) # Put values in list values = [] for row in range(1, y_tiles - 1): for col in range(1, x_tiles - 1): if row % 11 == 0 or col % 11 == 0: continue values.append((((row - row // 11) / 10, (col - col // 11) / 10), maze_expanded[col, row])) # Copy the 0.1 to 0 values (normally, a bot is never on a 0.0 path, but just to be sure) for row in range(1, y_tiles - 1): if row % 11 == 0: continue values.append((((row - row // 11) / 10, 0.0), maze_expanded[1, row])) for col in range(1, x_tiles - 1): if col % 11 == 0: continue values.append(((0.0, (col - col // 11) / 10), maze_expanded[col, 1])) values.append(((0.0, 0.0), maze_expanded[1, 1])) if visualize: self.visualize_extend(maze=maze_expanded) print("Coordinate (fitness) values:") for v in values: print(v) return values
def create_experiment_6(overwrite: bool = True, show: bool = True): """ Experiment 6 is another 'simplified' simulation in which the agents are trained on a harder environment than they are tested on. The reason why is to keep a lower threshold for the 'solution' genomes (only interested in those). TRAINING During training, new targets are spawned based on the agent's initial position. Each target will lay between 2 and 10 meters from the agent and will always be positioned inside of the maze. TESTING Testing is done on 18 predefined targets position in a relative angle from the agent of k*20° and a distance of either 4, 6, or 8 meters (sequential alternating). """ cfg = get_shared_config() # Fixed parameters ROBOT_INIT_ANGLE = pi / 2 # Looking up ROBOT_INIT_POS = Vec2d(cfg.game.x_axis / 2, cfg.game.y_axis / 2) # Initial position of the drone # Create the training game spawn_f_train = SpawnRange(game_config=cfg.game, train=True, r_max=8, r_min=4) game = Game( config=cfg, player_noise=0, game_id=get_game_id(0, experiment_id=6), overwrite=overwrite, spawn_func=spawn_f_train, stop_if_reached=True, wall_bound=False, ) game.player = Robot(game=game) game.set_player_init_angle(a=ROBOT_INIT_ANGLE) game.set_player_init_pos(p=ROBOT_INIT_POS) check_and_save_game(game, show=show) # Create 18 evaluation games for i in range(1, 19): spawn_f_eval = SpawnSimple(game_config=cfg.game, train=False) angle = i / 9 * pi # Hops of 20° d = 4 if i % 3 == 0 else 6 if i % 3 == 1 else 8 offset_x = d * cos(angle) offset_y = d * sin(angle) spawn_f_eval.add_location( (ROBOT_INIT_POS[0] + offset_x, ROBOT_INIT_POS[1] + offset_y)) game = Game( config=cfg, player_noise=0, game_id=get_game_id(i, experiment_id=6), overwrite=overwrite, spawn_func=spawn_f_eval, stop_if_reached=True, wall_bound=False, ) game.player = Robot(game=game) game.set_player_init_angle(a=ROBOT_INIT_ANGLE) game.set_player_init_pos(p=ROBOT_INIT_POS) check_and_save_game(game, show=show, randomize=False)