def __init__( self, chromosome: Optional[Dict[str, List[np.ndarray]]] = None, x_pos: Optional[int] = settings['init_bird_x_pos'], y_pos: Optional[int] = settings['init_bird_y_pos'], # x_pos: Optional[int] = 100, # y_pos: Optional[int] = 250, y_speed: Optional[int] = 0, hidden_layer_architecture: Optional[List[int]] = [12, 20], hidden_activation: Optional[ActivationFunction] = 'relu', output_activation: Optional[ActivationFunction] = 'sigmoid'): self.Window_Width = settings['Window_Width'] self.Window_Height = settings['Window_Height'] self.x_pos = x_pos self.y_pos = y_pos self.y_speed = y_speed self.bird_surface = pygame.image.load( "assets/bluebird-midflap.png").convert_alpha() self.bird_surface = pygame.transform.scale2x(self.bird_surface) self.bird_rect = self.bird_surface.get_rect(center=(self.x_pos, self.y_pos)) self._fitness = 0 # Overall fitness self.score = 0 self.x_distance_to_next_pipe_center = 0 self.y_distance_to_next_pipe_center = 0 self.is_alive = True self.hidden_layer_architecture = hidden_layer_architecture self.hidden_activation = hidden_activation self.output_activation = output_activation # Setting up network architecture num_inputs = 5 #@TODO: how many self.network_architecture = [num_inputs] # Inputs self.network_architecture.extend( self.hidden_layer_architecture) # Hidden layers self.network_architecture.append(2) # 2 output, jump or not jump self.network = FeedForwardNetwork( self.network_architecture, get_activation_by_name(self.hidden_activation), get_activation_by_name(self.output_activation)) # If chromosome is set, take it if chromosome: # self._chromosome = chromosome self.network.params = chromosome # self.decode_chromosome() else: # self._chromosome = {} # self.encode_chromosome() pass
def __init__(self, board_size: Tuple[int, int], nrGroups: int, nrBlocks: int, chromosome: Optional[Dict[str, List[np.ndarray]]] = None, hidden_layer_architecture: Optional[List[int]] = [20, 9], hidden_activation: Optional[ActivationFunction] = 'relu', output_activation: Optional[ActivationFunction] = 'sigmoid', groups: Optional[List[Group]] = None): self.board_size = board_size self.nrGroups = nrGroups self.nrBlocks = nrBlocks self.finished = False self.settings = settings self._fitness = 1000 self.groups = groups self.hidden_layer_architecture = hidden_layer_architecture self.hidden_activation = hidden_activation self.output_activation = output_activation # Setting up network architecture # Each "Vision" has 3 distances it tracks: wall, apple and self # there are also one-hot encoded direction and one-hot encoded tail direction, # each of which have 4 possibilities. num_inputs = 14 #@TODO: Add one-hot back in self.input_values_as_array: np.ndarray = np.zeros((num_inputs, 1)) self.network_architecture = [num_inputs] # Inputs self.network_architecture.extend( self.hidden_layer_architecture) # Hidden layers self.network_architecture.append(2) # 4 outputs, ['u', 'd', 'l', 'r'] self.network = FeedForwardNetwork( self.network_architecture, get_activation_by_name(self.hidden_activation), get_activation_by_name(self.output_activation)) if chromosome: self.network.params = chromosome else: pass self.target_pos = (int(self.board_size[0] / 2), int(self.board_size[1] / 2)) self.generate_groups()
def __init__(self, board_size: Tuple[int, int], chromosome: Optional[Dict[str, List[np.ndarray]]] = None, start_pos: Optional[Point] = None, apple_seed: Optional[int] = None, initial_velocity: Optional[str] = None, starting_direction: Optional[str] = None, hidden_layer_architecture: Optional[List[int]] = [1123125, 9], hidden_activation: Optional[ActivationFunction] = 'relu', output_activation: Optional[ActivationFunction] = 'sigmoid', lifespan: Optional[Union[int, float]] = np.inf, apple_and_self_vision: Optional[str] = 'binary'): self.lifespan = lifespan self.apple_and_self_vision = apple_and_self_vision.lower() self.score = 0 # Number of apples snake gets self._fitness = 0 # Overall fitness self._frames = 0 # Number of frames that the snake has been alive self._frames_since_last_apple = 0 self.possible_directions = ('u', 'd', 'l', 'r') self.board_size = board_size self.hidden_layer_architecture = hidden_layer_architecture self.hidden_activation = hidden_activation self.output_activation = output_activation if not start_pos: #@TODO: undo this # x = random.randint(10, self.board_size[0] - 9) # y = random.randint(10, self.board_size[1] - 9) x = random.randint(2, self.board_size[0] - 3) y = random.randint(2, self.board_size[1] - 3) start_pos = Point(x, y) self.start_pos = start_pos self._vision_type = VISION_8 self._vision: List[Vision] = [None] * len(self._vision_type) # This is just used so I can draw and is not actually used in the NN self._drawable_vision: List[DrawableVision] = [None] * len( self._vision_type) # Setting up network architecture # Each "Vision" has 3 distances it tracks: wall, apple and self # there are also one-hot encoded direction and one-hot encoded tail direction, # each of which have 4 possibilities. num_inputs = len( self._vision_type) * 3 + 4 + 4 #@TODO: Add one-hot back in self.vision_as_array: np.ndarray = np.zeros((num_inputs, 1)) self.network_architecture = [num_inputs] # Inputs self.network_architecture.extend( self.hidden_layer_architecture) # Hidden layers self.network_architecture.append(4) # 4 outputs, ['u', 'd', 'l', 'r'] self.network = FeedForwardNetwork( self.network_architecture, get_activation_by_name(self.hidden_activation), get_activation_by_name(self.output_activation)) # If chromosome is set, take it if chromosome: # self._chromosome = chromosome self.network.params = chromosome # self.decode_chromosome() else: # self._chromosome = {} # self.encode_chromosome() pass # For creating the next apple if apple_seed is None: apple_seed = np.random.randint(-1000000000, 1000000000) self.apple_seed = apple_seed # Only needed for saving/loading replay self.rand_apple = random.Random(self.apple_seed) self.apple_location = None if starting_direction: starting_direction = starting_direction[0].lower() else: starting_direction = self.possible_directions[random.randint(0, 3)] self.starting_direction = starting_direction # Only needed for saving/loading replay self.init_snake(self.starting_direction) self.initial_velocity = initial_velocity self.init_velocity(self.starting_direction, self.initial_velocity) self.generate_apple()
class Snake(Individual): def __init__(self, board_size: Tuple[int, int], chromosome: Optional[Dict[str, List[np.ndarray]]] = None, start_pos: Optional[Point] = None, apple_seed: Optional[int] = None, initial_velocity: Optional[str] = None, starting_direction: Optional[str] = None, hidden_layer_architecture: Optional[List[int]] = [1123125, 9], hidden_activation: Optional[ActivationFunction] = 'relu', output_activation: Optional[ActivationFunction] = 'sigmoid', lifespan: Optional[Union[int, float]] = np.inf, apple_and_self_vision: Optional[str] = 'binary'): self.lifespan = lifespan self.apple_and_self_vision = apple_and_self_vision.lower() self.score = 0 # Number of apples snake gets self._fitness = 0 # Overall fitness self._frames = 0 # Number of frames that the snake has been alive self._frames_since_last_apple = 0 self.possible_directions = ('u', 'd', 'l', 'r') self.board_size = board_size self.hidden_layer_architecture = hidden_layer_architecture self.hidden_activation = hidden_activation self.output_activation = output_activation if not start_pos: #@TODO: undo this # x = random.randint(10, self.board_size[0] - 9) # y = random.randint(10, self.board_size[1] - 9) x = random.randint(2, self.board_size[0] - 3) y = random.randint(2, self.board_size[1] - 3) start_pos = Point(x, y) self.start_pos = start_pos self._vision_type = VISION_8 self._vision: List[Vision] = [None] * len(self._vision_type) # This is just used so I can draw and is not actually used in the NN self._drawable_vision: List[DrawableVision] = [None] * len( self._vision_type) # Setting up network architecture # Each "Vision" has 3 distances it tracks: wall, apple and self # there are also one-hot encoded direction and one-hot encoded tail direction, # each of which have 4 possibilities. num_inputs = len( self._vision_type) * 3 + 4 + 4 #@TODO: Add one-hot back in self.vision_as_array: np.ndarray = np.zeros((num_inputs, 1)) self.network_architecture = [num_inputs] # Inputs self.network_architecture.extend( self.hidden_layer_architecture) # Hidden layers self.network_architecture.append(4) # 4 outputs, ['u', 'd', 'l', 'r'] self.network = FeedForwardNetwork( self.network_architecture, get_activation_by_name(self.hidden_activation), get_activation_by_name(self.output_activation)) # If chromosome is set, take it if chromosome: # self._chromosome = chromosome self.network.params = chromosome # self.decode_chromosome() else: # self._chromosome = {} # self.encode_chromosome() pass # For creating the next apple if apple_seed is None: apple_seed = np.random.randint(-1000000000, 1000000000) self.apple_seed = apple_seed # Only needed for saving/loading replay self.rand_apple = random.Random(self.apple_seed) self.apple_location = None if starting_direction: starting_direction = starting_direction[0].lower() else: starting_direction = self.possible_directions[random.randint(0, 3)] self.starting_direction = starting_direction # Only needed for saving/loading replay self.init_snake(self.starting_direction) self.initial_velocity = initial_velocity self.init_velocity(self.starting_direction, self.initial_velocity) self.generate_apple() @property def fitness(self): return self._fitness def calculate_fitness(self): # Give positive minimum fitness for roulette wheel selection # Number of frames that the snake has been alive + 2^(apples eaten) - ((0.25 * frames that the snake has been alive)^1.3)*(frames that the snake has been alive for ^1.2) self._fitness = (self._frames) + ((2**self.score) + (self.score**2.1) * 500) - ( ((.25 * self._frames)**1.3) * (self.score**1.2)) # self._fitness = (self._frames) + ((2**self.score) + (self.score**2.1)*500) - (((.25 * self._frames)) * (self.score)) self._fitness = max(self._fitness, .1) @property def chromosome(self): # return self._chromosome pass def encode_chromosome(self): # # L = len(self.network.params) // 2 # L = len(self.network.layer_nodes) # # Encode weights and bias # for layer in range(1, L): # l = str(layer) # self._chromosome['W' + l] = self.network.params['W' + l].flatten() # self._chromosome['b' + l] = self.network.params['b' + l].flatten() pass def decode_chromosome(self): # # L = len(self.network.params) // 2 # L = len(self.network.layer_nodes) # # Decode weights and bias # for layer in range(1, L): # l = str(layer) # w_shape = (self.network_architecture[layer], self.network_architecture[layer-1]) # b_shape = (self.network_architecture[layer], 1) # self.network.params['W' + l] = self._chromosome['W' + l].reshape(w_shape) # self.network.params['b' + l] = self._chromosome['b' + l].reshape(b_shape) pass def look(self): # Look all around for i, slope in enumerate(self._vision_type): vision, drawable_vision = self.look_in_direction(slope) self._vision[i] = vision self._drawable_vision[i] = drawable_vision # Update the input array self._vision_as_input_array() def look_in_direction(self, slope: Slope) -> Tuple[Vision, DrawableVision]: dist_to_wall = None dist_to_apple = np.inf dist_to_self = np.inf wall_location = None apple_location = None self_location = None position = self.snake_array[0].copy() distance = 1.0 total_distance = 0.0 # Can't start by looking at yourself position.x += slope.run position.y += slope.rise total_distance += distance body_found = False # Only need to find the first occurance since it's the closest food_found = False # Although there is only one food, stop looking once you find it # Keep going until the position is out of bounds while self._within_wall(position): if not body_found and self._is_body_location(position): dist_to_self = total_distance self_location = position.copy() body_found = True if not food_found and self._is_apple_location(position): dist_to_apple = total_distance apple_location = position.copy() food_found = True wall_location = position position.x += slope.run position.y += slope.rise total_distance += distance assert (total_distance != 0.0) # @TODO: May need to adjust numerator in case of VISION_16 since step size isn't always going to be on a tile dist_to_wall = 1.0 / total_distance if self.apple_and_self_vision == 'binary': dist_to_apple = 1.0 if dist_to_apple != np.inf else 0.0 dist_to_self = 1.0 if dist_to_self != np.inf else 0.0 elif self.apple_and_self_vision == 'distance': dist_to_apple = 1.0 / dist_to_apple dist_to_self = 1.0 / dist_to_self vision = Vision(dist_to_wall, dist_to_apple, dist_to_self) drawable_vision = DrawableVision(wall_location, apple_location, self_location) return (vision, drawable_vision) def _vision_as_input_array(self) -> None: # Split _vision into np array where rows [0-2] are _vision[0].dist_to_wall, _vision[0].dist_to_apple, _vision[0].dist_to_self, # rows [3-5] are _vision[1].dist_to_wall, _vision[1].dist_to_apple, _vision[1].dist_to_self, etc. etc. etc. for va_index, v_index in zip(range(0, len(self._vision) * 3, 3), range(len(self._vision))): vision = self._vision[v_index] self.vision_as_array[va_index, 0] = vision.dist_to_wall self.vision_as_array[va_index + 1, 0] = vision.dist_to_apple self.vision_as_array[va_index + 2, 0] = vision.dist_to_self i = len(self._vision) * 3 # Start at the end direction = self.direction[0].lower() # One-hot encode direction direction_one_hot = np.zeros((len(self.possible_directions), 1)) direction_one_hot[self.possible_directions.index(direction), 0] = 1 self.vision_as_array[i:i + len(self.possible_directions)] = direction_one_hot i += len(self.possible_directions) # One-hot tail direction tail_direction_one_hot = np.zeros((len(self.possible_directions), 1)) tail_direction_one_hot[ self.possible_directions.index(self.tail_direction), 0] = 1 self.vision_as_array[i:i + len(self.possible_directions )] = tail_direction_one_hot def _within_wall(self, position: Point) -> bool: return position.x >= 0 and position.y >= 0 and \ position.x < self.board_size[0] and \ position.y < self.board_size[1] def generate_apple(self) -> None: width = self.board_size[0] height = self.board_size[1] # Find all possible points where the snake is not currently possibilities = [ divmod(i, height) for i in range(width * height) if divmod(i, height) not in self._body_locations ] if possibilities: loc = self.rand_apple.choice(possibilities) self.apple_location = Point(loc[0], loc[1]) else: # I guess you win? print('you won!') pass def init_snake(self, starting_direction: str) -> None: """ Initialize teh snake. starting_direction: ('u', 'd', 'l', 'r') direction that the snake should start facing. Whatever the direction is, the head of the snake will begin pointing that way. """ head = self.start_pos # Body is below if starting_direction == 'u': snake = [ head, Point(head.x, head.y + 1), Point(head.x, head.y + 2) ] # Body is above elif starting_direction == 'd': snake = [ head, Point(head.x, head.y - 1), Point(head.x, head.y - 2) ] # Body is to the right elif starting_direction == 'l': snake = [ head, Point(head.x + 1, head.y), Point(head.x + 2, head.y) ] # Body is to the left elif starting_direction == 'r': snake = [ head, Point(head.x - 1, head.y), Point(head.x - 2, head.y) ] self.snake_array = deque(snake) self._body_locations = set(snake) self.is_alive = True def update(self): if self.is_alive: self._frames += 1 self.look() self.network.feed_forward(self.vision_as_array) self.direction = self.possible_directions[np.argmax( self.network.out)] return True else: return False def move(self) -> bool: if not self.is_alive: return False direction = self.direction[0].lower() # Is the direction valid? if direction not in self.possible_directions: return False # Find next position # tail = self.snake_array.pop() # Pop tail since we can technically move to the tail head = self.snake_array[0] if direction == 'u': next_pos = Point(head.x, head.y - 1) elif direction == 'd': next_pos = Point(head.x, head.y + 1) elif direction == 'r': next_pos = Point(head.x + 1, head.y) elif direction == 'l': next_pos = Point(head.x - 1, head.y) # Is the next position we want to move valid? if self._is_valid(next_pos): # Tail if next_pos == self.snake_array[-1]: # Pop tail and add next_pos (same as tail) to front # No need to remove tail from _body_locations since it will go back in anyway self.snake_array.pop() self.snake_array.appendleft(next_pos) # Eat the apple elif next_pos == self.apple_location: self.score += 1 self._frames_since_last_apple = 0 # Move head self.snake_array.appendleft(next_pos) self._body_locations.update({next_pos}) # Don't remove tail since the snake grew self.generate_apple() # Normal movement else: # Move head self.snake_array.appendleft(next_pos) self._body_locations.update({next_pos}) # Remove tail tail = self.snake_array.pop() self._body_locations.symmetric_difference_update({tail}) # Figure out which direction the tail is moving p2 = self.snake_array[-2] p1 = self.snake_array[-1] diff = p2 - p1 if diff.x < 0: self.tail_direction = 'l' elif diff.x > 0: self.tail_direction = 'r' elif diff.y > 0: self.tail_direction = 'd' elif diff.y < 0: self.tail_direction = 'u' self._frames_since_last_apple += 1 #@NOTE: If you have different sized grids you may want to change this if self._frames_since_last_apple > 100: self.is_alive = False return False return True else: self.is_alive = False return False def _is_apple_location(self, position: Point) -> bool: return position == self.apple_location def _is_body_location(self, position: Point) -> bool: return position in self._body_locations def _is_valid(self, position: Point) -> bool: """ Determine whether a given position is valid. Return True if the position is on the board and does not intersect the snake. Return False otherwise """ if (position.x < 0) or (position.x > self.board_size[0] - 1): return False if (position.y < 0) or (position.y > self.board_size[1] - 1): return False if position == self.snake_array[-1]: return True # If the position is a body location, not valid. # @NOTE: _body_locations will contain tail, so need to check tail first elif position in self._body_locations: return False # Otherwise you good else: return True def init_velocity(self, starting_direction, initial_velocity: Optional[str] = None) -> None: if initial_velocity: self.direction = initial_velocity[0].lower() # Whichever way the starting_direction is else: self.direction = starting_direction # Tail starts moving the same direction self.tail_direction = self.direction
def __init__( self, board_size: Tuple[int, int], chromosome: Optional[Dict[str, List[np.ndarray]]] = None, start_pos: Optional[Point] = None, apple_seed: Optional[int] = None, initial_velocity: Optional[str] = None, starting_direction: Optional[str] = None, hidden_layer_architecture: Optional[List[int]] = [1123125, 9], hidden_activation: Optional[ ActivationFunction] = 'relu', # các hàm output_activation: Optional[ActivationFunction] = 'sigmoid', lifespan: Optional[Union[int, float]] = np.inf, apple_and_self_vision: Optional[str] = 'binary'): self.lifespan = lifespan self.apple_and_self_vision = apple_and_self_vision.lower() self.score = 0 # Số táo bị rắn ăn self._fitness = 0 # Overall fitness self._frames = 0 # Số khung hình mà con rắn còn sống self._frames_since_last_apple = 0 self.possible_directions = ('u', 'd', 'l', 'r') self.board_size = board_size self.hidden_layer_architecture = hidden_layer_architecture self.hidden_activation = hidden_activation self.output_activation = output_activation if not start_pos: #@TODO: dưới # x = random.randint(10, self.board_size[0] - 9) # y = random.randint(10, self.board_size[1] - 9) x = random.randint(2, self.board_size[0] - 3) y = random.randint(2, self.board_size[1] - 3) start_pos = Point(x, y) self.start_pos = start_pos self._vision_type = VISION_16 ######### Vẽ tầm nhìn ở đây nè !!!! self._vision: List[Vision] = [None] * len(self._vision_type) # Cái này chỉ được sử dụng để có thể vẽ và không thực sự được sử dụng trong NN, ta phải điều chỉnh ở settings.py nữa self._drawable_vision: List[DrawableVision] = [None] * len( self._vision_type) # Thiết lập kiến trúc mạng # Mỗi "Tầm nhìn" có 3 khoảng cách mà nó theo dõi: tường, táo và bản thân nó # cũng có hướng được mã hóa nóng và hướng đuôi được mã hóa nóng, # mỗi trong số đó có 4 khả năng. num_inputs = len( self._vision_type) * 3 + 4 + 4 #@TODO: Add one-hot back in self.vision_as_array: np.ndarray = np.zeros((num_inputs, 1)) self.network_architecture = [num_inputs] # Đầu ra self.network_architecture.extend( self.hidden_layer_architecture) # Lớp ẩn self.network_architecture.append(4) # 4 đầu ra, ['u', 'd', 'l', 'r'] self.network = FeedForwardNetwork( self.network_architecture, get_activation_by_name(self.hidden_activation), get_activation_by_name(self.output_activation)) # Nếu nhiễm sắc thể được thiết lập, hãy lấy nó if chromosome: # self._chromosome = chromosome self.network.params = chromosome # self.decode_chromosome() else: # self._chromosome = {} # self.encode_chromosome() pass # Tạo mồi tiếp theo if apple_seed is None: apple_seed = np.random.randint(-1000000000, 1000000000) #thức ăn self.apple_seed = apple_seed # Chỉ cần để lưu / tải phát lại self.rand_apple = random.Random(self.apple_seed) self.apple_location = None if starting_direction: starting_direction = starting_direction[0].lower() else: starting_direction = self.possible_directions[random.randint(0, 3)] self.starting_direction = starting_direction # Chỉ cần để lưu / tải phát lại self.init_snake(self.starting_direction) self.initial_velocity = initial_velocity self.init_velocity(self.starting_direction, self.initial_velocity) self.generate_apple()
class Snake(Individual): def __init__( self, board_size: Tuple[int, int], chromosome: Optional[Dict[str, List[np.ndarray]]] = None, start_pos: Optional[Point] = None, apple_seed: Optional[int] = None, initial_velocity: Optional[str] = None, starting_direction: Optional[str] = None, hidden_layer_architecture: Optional[List[int]] = [1123125, 9], hidden_activation: Optional[ ActivationFunction] = 'relu', # các hàm output_activation: Optional[ActivationFunction] = 'sigmoid', lifespan: Optional[Union[int, float]] = np.inf, apple_and_self_vision: Optional[str] = 'binary'): self.lifespan = lifespan self.apple_and_self_vision = apple_and_self_vision.lower() self.score = 0 # Số táo bị rắn ăn self._fitness = 0 # Overall fitness self._frames = 0 # Số khung hình mà con rắn còn sống self._frames_since_last_apple = 0 self.possible_directions = ('u', 'd', 'l', 'r') self.board_size = board_size self.hidden_layer_architecture = hidden_layer_architecture self.hidden_activation = hidden_activation self.output_activation = output_activation if not start_pos: #@TODO: dưới # x = random.randint(10, self.board_size[0] - 9) # y = random.randint(10, self.board_size[1] - 9) x = random.randint(2, self.board_size[0] - 3) y = random.randint(2, self.board_size[1] - 3) start_pos = Point(x, y) self.start_pos = start_pos self._vision_type = VISION_16 ######### Vẽ tầm nhìn ở đây nè !!!! self._vision: List[Vision] = [None] * len(self._vision_type) # Cái này chỉ được sử dụng để có thể vẽ và không thực sự được sử dụng trong NN, ta phải điều chỉnh ở settings.py nữa self._drawable_vision: List[DrawableVision] = [None] * len( self._vision_type) # Thiết lập kiến trúc mạng # Mỗi "Tầm nhìn" có 3 khoảng cách mà nó theo dõi: tường, táo và bản thân nó # cũng có hướng được mã hóa nóng và hướng đuôi được mã hóa nóng, # mỗi trong số đó có 4 khả năng. num_inputs = len( self._vision_type) * 3 + 4 + 4 #@TODO: Add one-hot back in self.vision_as_array: np.ndarray = np.zeros((num_inputs, 1)) self.network_architecture = [num_inputs] # Đầu ra self.network_architecture.extend( self.hidden_layer_architecture) # Lớp ẩn self.network_architecture.append(4) # 4 đầu ra, ['u', 'd', 'l', 'r'] self.network = FeedForwardNetwork( self.network_architecture, get_activation_by_name(self.hidden_activation), get_activation_by_name(self.output_activation)) # Nếu nhiễm sắc thể được thiết lập, hãy lấy nó if chromosome: # self._chromosome = chromosome self.network.params = chromosome # self.decode_chromosome() else: # self._chromosome = {} # self.encode_chromosome() pass # Tạo mồi tiếp theo if apple_seed is None: apple_seed = np.random.randint(-1000000000, 1000000000) #thức ăn self.apple_seed = apple_seed # Chỉ cần để lưu / tải phát lại self.rand_apple = random.Random(self.apple_seed) self.apple_location = None if starting_direction: starting_direction = starting_direction[0].lower() else: starting_direction = self.possible_directions[random.randint(0, 3)] self.starting_direction = starting_direction # Chỉ cần để lưu / tải phát lại self.init_snake(self.starting_direction) self.initial_velocity = initial_velocity self.init_velocity(self.starting_direction, self.initial_velocity) self.generate_apple() @property def fitness(self): return self._fitness def calculate_fitness(self): # Cung cấp cho quần thể tối thiểu tích cực để lựa chọn thế hệ sau self._fitness = (self._frames) + ((2**self.score) + (self.score**2.1) * 500) - ( ((.25 * self._frames)**1.3) * (self.score**1.2)) # self._fitness = (self._frames) + ((2**self.score) + (self.score**2.1)*500) - (((.25 * self._frames)) * (self.score)) self._fitness = max(self._fitness, .1) @property def chromosome(self): # return self._chromosome pass def encode_chromosome(self): # # L = len(self.network.params) // 2 # L = len(self.network.layer_nodes) # # Encode weights and bias # for layer in range(1, L): # l = str(layer) # self._chromosome['W' + l] = self.network.params['W' + l].flatten() # self._chromosome['b' + l] = self.network.params['b' + l].flatten() pass def decode_chromosome(self): # # L = len(self.network.params) // 2 # L = len(self.network.layer_nodes) # # Decode weights and bias # for layer in range(1, L): # l = str(layer) # w_shape = (self.network_architecture[layer], self.network_architecture[layer-1]) # b_shape = (self.network_architecture[layer], 1) # self.network.params['W' + l] = self._chromosome['W' + l].reshape(w_shape) # self.network.params['b' + l] = self._chromosome['b' + l].reshape(b_shape) pass def look(self): # Look all around for i, slope in enumerate(self._vision_type): vision, drawable_vision = self.look_in_direction(slope) self._vision[i] = vision self._drawable_vision[i] = drawable_vision # Update the input array self._vision_as_input_array() def look_in_direction(self, slope: Slope) -> Tuple[Vision, DrawableVision]: dist_to_wall = None dist_to_apple = np.inf dist_to_self = np.inf wall_location = None apple_location = None self_location = None position = self.snake_array[0].copy() distance = 1.0 total_distance = 0.0 # Can't start by looking at yourself position.x += slope.run position.y += slope.rise total_distance += distance body_found = False # Chỉ cần tìm sự xuất hiện đầu tiên vì nó là lần gần nhất food_found = False # Mặc dù chỉ có một thức ăn, hãy ngừng tìm kiếm khi tìm thấy nó # Tiếp tục đi cho đến khi vị trí nằm ngoài giới hạn while self._within_wall(position): if not body_found and self._is_body_location(position): dist_to_self = total_distance self_location = position.copy() body_found = True if not food_found and self._is_apple_location(position): dist_to_apple = total_distance apple_location = position.copy() food_found = True wall_location = position position.x += slope.run position.y += slope.rise total_distance += distance assert (total_distance != 0.0) # @TODO: Có thể cần điều chỉnh tử số trong trường hợp VISION_16 vì kích thước bước không phải lúc nào cũng nằm trên ô dist_to_wall = 1.0 / total_distance if self.apple_and_self_vision == 'binary': dist_to_apple = 1.0 if dist_to_apple != np.inf else 0.0 dist_to_self = 1.0 if dist_to_self != np.inf else 0.0 elif self.apple_and_self_vision == 'distance': dist_to_apple = 1.0 / dist_to_apple dist_to_self = 1.0 / dist_to_self vision = Vision(dist_to_wall, dist_to_apple, dist_to_self) drawable_vision = DrawableVision(wall_location, apple_location, self_location) return (vision, drawable_vision) def _vision_as_input_array(self) -> None: # Split _vision into np array where rows [0-2] are _vision[0].dist_to_wall, _vision[0].dist_to_apple, _vision[0].dist_to_self, # rows [3-5] are _vision[1].dist_to_wall, _vision[1].dist_to_apple, _vision[1].dist_to_self for va_index, v_index in zip(range(0, len(self._vision) * 3, 3), range(len(self._vision))): vision = self._vision[v_index] self.vision_as_array[va_index, 0] = vision.dist_to_wall self.vision_as_array[va_index + 1, 0] = vision.dist_to_apple self.vision_as_array[va_index + 2, 0] = vision.dist_to_self i = len(self._vision) * 3 # Bắt đầu ở cuối direction = self.direction[0].lower() # One-hot encode direction direction_one_hot = np.zeros((len(self.possible_directions), 1)) direction_one_hot[self.possible_directions.index(direction), 0] = 1 self.vision_as_array[i:i + len(self.possible_directions)] = direction_one_hot i += len(self.possible_directions) # One-hot tail direction, đuôi rắn tail_direction_one_hot = np.zeros((len(self.possible_directions), 1)) tail_direction_one_hot[ self.possible_directions.index(self.tail_direction), 0] = 1 self.vision_as_array[i:i + len(self.possible_directions )] = tail_direction_one_hot def _within_wall(self, position: Point) -> bool: return position.x >= 0 and position.y >= 0 and \ position.x < self.board_size[0] and \ position.y < self.board_size[1] def generate_apple(self) -> None: width = self.board_size[0] height = self.board_size[1] # Tìm tất cả các điểm có thể mà con rắn hiện không ở đó possibilities = [ divmod(i, height) for i in range(width * height) if divmod(i, height) not in self._body_locations ] if possibilities: loc = self.rand_apple.choice(possibilities) self.apple_location = Point(loc[0], loc[1]) else: print('Bạn thắng!') pass def init_snake(self, starting_direction: str) -> None: """ Khởi tạo con rắn. start_direction: ('u', 'd', 'l', 'r') hướng mà con rắn nên bắt đầu phải đối mặt. Dù là hướng nào, cái đầu của con rắn sẽ bắt đầu chỉ theo cách đó. """ head = self.start_pos # Body is below if starting_direction == 'u': snake = [ head, Point(head.x, head.y + 1), Point(head.x, head.y + 2) ] # Body is above elif starting_direction == 'd': snake = [ head, Point(head.x, head.y - 1), Point(head.x, head.y - 2) ] # Body is to the right elif starting_direction == 'l': snake = [ head, Point(head.x + 1, head.y), Point(head.x + 2, head.y) ] # Body is to the left elif starting_direction == 'r': snake = [ head, Point(head.x - 1, head.y), Point(head.x - 2, head.y) ] self.snake_array = deque(snake) self._body_locations = set(snake) self.is_alive = True def update(self): if self.is_alive: self._frames += 1 self.look() self.network.feed_forward(self.vision_as_array) self.direction = self.possible_directions[np.argmax( self.network.out)] return True else: return False def move(self) -> bool: if not self.is_alive: return False direction = self.direction[0].lower() # Is the direction valid? if direction not in self.possible_directions: return False # Tìm vị trí tiếp theo # tail = self.snake_array.pop() # Pop tail vì kỹ thuật chúng ta có thể di chuyển đến đuôi head = self.snake_array[0] if direction == 'u': next_pos = Point(head.x, head.y - 1) elif direction == 'd': next_pos = Point(head.x, head.y + 1) elif direction == 'r': next_pos = Point(head.x + 1, head.y) elif direction == 'l': next_pos = Point(head.x - 1, head.y) # Là vị trí tiếp theo chúng ta muốn di chuyển hợp lệ? if self._is_valid(next_pos): # Tail if next_pos == self.snake_array[-1]: # Pop tail and add next_pos (same as tail) to front # No need to remove tail from _body_locations since it will go back in anyway self.snake_array.pop() self.snake_array.appendleft(next_pos) # ăn táo elif next_pos == self.apple_location: self.score += 1 self._frames_since_last_apple = 0 # Move head self.snake_array.appendleft(next_pos) self._body_locations.update({next_pos}) # Đừng bỏ đuôi từ khi con rắn lớn lên self.generate_apple() # Normal movement else: # Move head self.snake_array.appendleft(next_pos) self._body_locations.update({next_pos}) # Remove tail tail = self.snake_array.pop() self._body_locations.symmetric_difference_update({tail}) # Chỉ ra hướng di chuyển của đuôi p2 = self.snake_array[-2] p1 = self.snake_array[-1] diff = p2 - p1 if diff.x < 0: self.tail_direction = 'l' elif diff.x > 0: self.tail_direction = 'r' elif diff.y > 0: self.tail_direction = 'd' elif diff.y < 0: self.tail_direction = 'u' self._frames_since_last_apple += 1 #@NOTE: Nếu ta có lưới có kích thước khác nhau, có thể muốn thay đổi điều này if self._frames_since_last_apple > 100: self.is_alive = False return False return True else: self.is_alive = False return False def _is_apple_location(self, position: Point) -> bool: return position == self.apple_location def _is_body_location(self, position: Point) -> bool: return position in self._body_locations def _is_valid(self, position: Point) -> bool: """ Xác định xem một vị trí nhất định có hợp lệ không. Trả về True nếu vị trí nằm trên bảng và không giao nhau với con rắn. Trả về Sai nếu không """ if (position.x < 0) or (position.x > self.board_size[0] - 1): return False if (position.y < 0) or (position.y > self.board_size[1] - 1): return False if position == self.snake_array[-1]: return True # Nếu vị trí là một vị trí cơ thể, không hợp lệ. # @Note: _body_locations sẽ chứa đuôi, vì vậy cần kiểm tra đuôi trước elif position in self._body_locations: return False # Otherwise you good else: return True def init_velocity(self, starting_direction, initial_velocity: Optional[str] = None) -> None: if initial_velocity: self.direction = initial_velocity[0].lower() # Dù cách khởi động là gì else: self.direction = starting_direction # Đuôi bắt đầu di chuyển cùng hướng self.tail_direction = self.direction
def __init__( self, config: Config, chromosome: Optional[Dict[str, np.ndarray]] = None, hidden_layer_architecture: List[int] = [12, 9], hidden_activation: Optional[ActivationFunction] = 'relu', output_activation: Optional[ActivationFunction] = 'sigmoid', lifespan: Union[int, float] = np.inf, name: Optional[str] = None, debug: Optional[bool] = False, ): self.config = config self.lifespan = lifespan self.name = name self.debug = debug self._fitness = 0 # Overall fitness self._frames_since_progress = 0 # Number of frames since Mario has made progress towards the goal self._frames = 0 # Number of frames Mario has been alive self.hidden_layer_architecture = self.config.NeuralNetwork.hidden_layer_architecture self.hidden_activation = self.config.NeuralNetwork.hidden_node_activation self.output_activation = self.config.NeuralNetwork.output_node_activation self.start_row, self.viz_width, self.viz_height = self.config.NeuralNetwork.input_dims if self.config.NeuralNetwork.encode_row: num_inputs = self.viz_width * self.viz_height + self.viz_height else: num_inputs = self.viz_width * self.viz_height # print(f'num inputs:{num_inputs}') self.inputs_as_array = np.zeros((num_inputs, 1)) self.network_architecture = [num_inputs] # Input Nodes self.network_architecture.extend( self.hidden_layer_architecture) # Hidden Layer Ndoes self.network_architecture.append( 6) # 6 Outputs ['u', 'd', 'l', 'r', 'a', 'b'] self.network = FeedForwardNetwork( self.network_architecture, get_activation_by_name(self.hidden_activation), get_activation_by_name(self.output_activation)) # If chromosome is set, take it if chromosome: self.network.params = chromosome self.is_alive = True self.x_dist = None self.game_score = None self.did_win = False # This is mainly just to "see" Mario winning self.allow_additional_time = self.config.Misc.allow_additional_time_for_flagpole self.additional_timesteps = 0 self.max_additional_timesteps = int(60 * 2.5) self._printed = False # Keys correspond with B, NULL, SELECT, START, U, D, L, R, A # index 0 1 2 3 4 5 6 7 8 self.buttons_to_press = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0], np.int8) self.farthest_x = 0
class Mario(Individual): def __init__( self, config: Config, chromosome: Optional[Dict[str, np.ndarray]] = None, hidden_layer_architecture: List[int] = [12, 9], hidden_activation: Optional[ActivationFunction] = 'relu', output_activation: Optional[ActivationFunction] = 'sigmoid', lifespan: Union[int, float] = np.inf, name: Optional[str] = None, debug: Optional[bool] = False, ): self.config = config self.lifespan = lifespan self.name = name self.debug = debug self._fitness = 0 # Overall fitness self._frames_since_progress = 0 # Number of frames since Mario has made progress towards the goal self._frames = 0 # Number of frames Mario has been alive self.hidden_layer_architecture = self.config.NeuralNetwork.hidden_layer_architecture self.hidden_activation = self.config.NeuralNetwork.hidden_node_activation self.output_activation = self.config.NeuralNetwork.output_node_activation self.start_row, self.viz_width, self.viz_height = self.config.NeuralNetwork.input_dims if self.config.NeuralNetwork.encode_row: num_inputs = self.viz_width * self.viz_height + self.viz_height else: num_inputs = self.viz_width * self.viz_height # print(f'num inputs:{num_inputs}') self.inputs_as_array = np.zeros((num_inputs, 1)) self.network_architecture = [num_inputs] # Input Nodes self.network_architecture.extend( self.hidden_layer_architecture) # Hidden Layer Ndoes self.network_architecture.append( 6) # 6 Outputs ['u', 'd', 'l', 'r', 'a', 'b'] self.network = FeedForwardNetwork( self.network_architecture, get_activation_by_name(self.hidden_activation), get_activation_by_name(self.output_activation)) # If chromosome is set, take it if chromosome: self.network.params = chromosome self.is_alive = True self.x_dist = None self.game_score = None self.did_win = False # This is mainly just to "see" Mario winning self.allow_additional_time = self.config.Misc.allow_additional_time_for_flagpole self.additional_timesteps = 0 self.max_additional_timesteps = int(60 * 2.5) self._printed = False # Keys correspond with B, NULL, SELECT, START, U, D, L, R, A # index 0 1 2 3 4 5 6 7 8 self.buttons_to_press = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0], np.int8) self.farthest_x = 0 @property def fitness(self): return self._fitness @property def chromosome(self): pass def decode_chromosome(self): pass def encode_chromosome(self): pass def calculate_fitness(self): frames = self._frames distance = self.x_dist score = self.game_score self._fitness = self.config.GeneticAlgorithm.fitness_func( frames, distance, score, self.did_win) def set_input_as_array(self, ram, tiles) -> None: mario_row, mario_col = SMB.get_mario_row_col(ram) arr = [] for row in range(self.start_row, self.start_row + self.viz_height): for col in range(mario_col, mario_col + self.viz_width): try: t = tiles[(row, col)] if isinstance(t, StaticTileType): if t.value == 0: arr.append(0) else: arr.append(1) elif isinstance(t, EnemyType): arr.append(-1) else: raise Exception("This should never happen") except: t = StaticTileType(0x00) arr.append(0) # Empty self.inputs_as_array[:self.viz_height * self.viz_width, :] = np.array(arr).reshape( (-1, 1)) if self.config.NeuralNetwork.encode_row: # Assign one-hot for mario row row = mario_row - self.start_row one_hot = np.zeros((self.viz_height, 1)) if row >= 0 and row < self.viz_height: one_hot[row, 0] = 1 self.inputs_as_array[self.viz_height * self.viz_width:, :] = one_hot.reshape((-1, 1)) def update(self, ram, tiles, buttons, ouput_to_buttons_map) -> bool: """ The main update call for Mario. Takes in inputs of surrounding area and feeds through the Neural Network Return: True if Mario is alive False otherwise """ if self.is_alive: self._frames += 1 self.x_dist = SMB.get_mario_location_in_level(ram).x self.game_score = SMB.get_mario_score(ram) # Sliding down flag pole if ram[0x001D] == 3: self.did_win = True if not self._printed and self.debug: name = 'Mario ' name += f'{self.name}' if self.name else '' print(f'{name} won') self._printed = True if not self.allow_additional_time: self.is_alive = False return False # If we made it further, reset stats if self.x_dist > self.farthest_x: self.farthest_x = self.x_dist self._frames_since_progress = 0 else: self._frames_since_progress += 1 if self.allow_additional_time and self.did_win: self.additional_timesteps += 1 if self.allow_additional_time and self.additional_timesteps > self.max_additional_timesteps: self.is_alive = False return False elif not self.did_win and self._frames_since_progress > 60 * 3: self.is_alive = False return False else: return False # Did you fly into a hole? if ram[0x0E] in (0x0B, 0x06) or ram[0xB5] == 2: self.is_alive = False return False self.set_input_as_array(ram, tiles) # Calculate the output output = self.network.feed_forward(self.inputs_as_array) threshold = np.where(output > 0.5)[0] self.buttons_to_press.fill(0) # Clear # Set buttons for b in threshold: self.buttons_to_press[ouput_to_buttons_map[b]] = 1 return True
class Bird(Individual): def __init__( self, chromosome: Optional[Dict[str, List[np.ndarray]]] = None, x_pos: Optional[int] = settings['init_bird_x_pos'], y_pos: Optional[int] = settings['init_bird_y_pos'], # x_pos: Optional[int] = 100, # y_pos: Optional[int] = 250, y_speed: Optional[int] = 0, hidden_layer_architecture: Optional[List[int]] = [12, 20], hidden_activation: Optional[ActivationFunction] = 'relu', output_activation: Optional[ActivationFunction] = 'sigmoid'): self.Window_Width = settings['Window_Width'] self.Window_Height = settings['Window_Height'] self.x_pos = x_pos self.y_pos = y_pos self.y_speed = y_speed self.bird_surface = pygame.image.load( "assets/bluebird-midflap.png").convert_alpha() self.bird_surface = pygame.transform.scale2x(self.bird_surface) self.bird_rect = self.bird_surface.get_rect(center=(self.x_pos, self.y_pos)) self._fitness = 0 # Overall fitness self.score = 0 self.x_distance_to_next_pipe_center = 0 self.y_distance_to_next_pipe_center = 0 self.is_alive = True self.hidden_layer_architecture = hidden_layer_architecture self.hidden_activation = hidden_activation self.output_activation = output_activation # Setting up network architecture num_inputs = 5 #@TODO: how many self.network_architecture = [num_inputs] # Inputs self.network_architecture.extend( self.hidden_layer_architecture) # Hidden layers self.network_architecture.append(2) # 2 output, jump or not jump self.network = FeedForwardNetwork( self.network_architecture, get_activation_by_name(self.hidden_activation), get_activation_by_name(self.output_activation)) # If chromosome is set, take it if chromosome: # self._chromosome = chromosome self.network.params = chromosome # self.decode_chromosome() else: # self._chromosome = {} # self.encode_chromosome() pass @property def fitness(self): return self._fitness def calculate_fitness(self): # Give positive minimum fitness for roulette wheel selection # self._fitness = (self._frames) + ((2**self.score) + (self.score**2.1)*500) - (((.25 * self._frames)**1.3) * (self.score**1.2)) # self._fitness = (self._frames) + ((2**self.score) + (self.score**2.1)*500) - (((.25 * self._frames)) * (self.score)) print(self.y_distance_to_next_pipe_center) self._fitness = (2**self.score + self.score * 2.1) * 200 + 1000 - ( self.x_distance_to_next_pipe_center + abs(self.y_distance_to_next_pipe_center)) * 1.2 self._fitness = max(self._fitness, .1) @property def chromosome(self): # return self._chromosome pass def encode_chromosome(self): # # L = len(self.network.params) // 2 # L = len(self.network.layer_nodes) # # Encode weights and bias # for layer in range(1, L): # l = str(layer) # self._chromosome['W' + l] = self.network.params['W' + l].flatten() # self._chromosome['b' + l] = self.network.params['b' + l].flatten() pass def decode_chromosome(self): # # L = len(self.network.params) // 2 # L = len(self.network.layer_nodes) # # Decode weights and bias # for layer in range(1, L): # l = str(layer) # w_shape = (self.network_architecture[layer], self.network_architecture[layer-1]) # b_shape = (self.network_architecture[layer], 1) # self.network.params['W' + l] = self._chromosome['W' + l].reshape(w_shape) # self.network.params['b' + l] = self._chromosome['b' + l].reshape(b_shape) pass # restart the game for this individual def reset(self): self._fitness = 0 self.score = 0 self.x_distance_to_next_pipe_center = 0 self.y_distance_to_next_pipe_center = 0 self.x_pos = settings['init_bird_x_pos'] self.y_pos = settings['init_bird_y_pos'] self.y_speed = 0 self.is_alive = True def update(self, inputs): self.network.feed_forward(inputs) if self.network.out == 0: # do nothing pass elif self.network.out == 1: # jump self.y_speed = settings['bird_jump_speed'] return True def move(self, pipes): self.y_pos += self.y_speed self.y_speed += settings['gravity'] self.score += 2 / 1.2 / 60 self.check_collision(pipes) # self.distance_travelled += abs(self.xspeed) # if self.x_pos < 0: # self.x_pos = 0 # elif self.x_pos > self.board_size[0]-100: # self.x_pos = self.board_size[0]-100 def check_collision(self, pipes): for pipe in pipes: if self.bird_rect.colliderect(pipe): self.is_alive = False if self.bird_rect.top <= -100 or self.bird_rect.bottom >= self.Window_Height - 100: self.is_alive = False def rotate_bird(self, bird_surface): new_bird = pygame.transform.rotozoom(bird_surface, self.y_speed * -3, 1) return new_bird # Draw the bird def draw(self, screen, winner=False, champion=False): if not self.is_alive: return if champion: self.bird_surface = pygame.image.load( "assets/redbird-midflap.png").convert_alpha() self.bird_surface = pygame.transform.scale2x(self.bird_surface) elif winner: self.bird_surface = pygame.image.load( "assets/yellowbird-midflap.png").convert_alpha() self.bird_surface = pygame.transform.scale2x(self.bird_surface) self.bird_rect.centery = self.y_pos rotated_bird_surface = self.rotate_bird(self.bird_surface) screen.blit(rotated_bird_surface, self.bird_rect)
class Puzzle(Individual): def __init__(self, board_size: Tuple[int, int], nrGroups: int, nrBlocks: int, chromosome: Optional[Dict[str, List[np.ndarray]]] = None, hidden_layer_architecture: Optional[List[int]] = [20, 9], hidden_activation: Optional[ActivationFunction] = 'relu', output_activation: Optional[ActivationFunction] = 'sigmoid', groups: Optional[List[Group]] = None): self.board_size = board_size self.nrGroups = nrGroups self.nrBlocks = nrBlocks self.finished = False self.settings = settings self._fitness = 1000 self.groups = groups self.hidden_layer_architecture = hidden_layer_architecture self.hidden_activation = hidden_activation self.output_activation = output_activation # Setting up network architecture # Each "Vision" has 3 distances it tracks: wall, apple and self # there are also one-hot encoded direction and one-hot encoded tail direction, # each of which have 4 possibilities. num_inputs = 14 #@TODO: Add one-hot back in self.input_values_as_array: np.ndarray = np.zeros((num_inputs, 1)) self.network_architecture = [num_inputs] # Inputs self.network_architecture.extend( self.hidden_layer_architecture) # Hidden layers self.network_architecture.append(2) # 4 outputs, ['u', 'd', 'l', 'r'] self.network = FeedForwardNetwork( self.network_architecture, get_activation_by_name(self.hidden_activation), get_activation_by_name(self.output_activation)) if chromosome: self.network.params = chromosome else: pass self.target_pos = (int(self.board_size[0] / 2), int(self.board_size[1] / 2)) self.generate_groups() def generate_groups(self): self.blocks = [] if not self.groups: self.groups = {} for i in range(self.nrGroups): v = random.randint(0, 1000) / 1000 c = (random.randint(20, 255), random.randint(20, 255), random.randint(20, 255)) d = random.randint(1, self.settings["density_max"]) n = Group(i, v, d, c) self.groups[v] = n self.nrGroups = len(self.groups) for j in range(self.nrBlocks): r = random.randint(0, self.nrGroups - 1) b = Block() i = list(self.groups.keys())[r] self.groups[i].add_block(b) self.blocks.append(b) for k in list(self.groups.keys()): if not self.groups[k].blocksLeft: self.groups.pop(k) self.nrGroups = len(self.groups) self.groupsLeft = len(self.groups) random.shuffle(self.blocks) @property def fitness(self): return self._fitness def calculate_fitness(self): distSet = self.settings["distanceSetting"].lower() maxDist = int((self.target_pos[0] + self.target_pos[1]) / 2) minDist = 1 distDiff = maxDist - minDist coherencies = [] distScores = [] spreads = [] sameScore = 0 values = list(self.groups.keys()) for v in values: blocks = self.groups[v].blocksPlaced nrBlocks = len(blocks) if nrBlocks == 0: break density = self.groups[v].density xSpread = 1 ySpread = 1 if nrBlocks <= lowTownValues[0][-1]: sumOptimal = lowTownValues[1][nrBlocks - 1] else: a, b, c, d = functionTownValues[0] sumOptimal = int(a * (nrBlocks**b) + c * (nrBlocks**d)) internalSum = 0 internalX = 0 internalY = 0 sames = 0 if nrBlocks > 1: pairs = list(itertools.combinations(range(nrBlocks), 2)) for p in pairs: a = blocks[p[0]] b = blocks[p[1]] dx = abs(a.x - b.x) / density dy = abs(a.y - b.y) / density internalX += dx internalY += dy if distSet == "manhattan": if dx + dy < density: dist = max((self.board_size[0] / density) * (density - (dx + dy)), 1) internalSum += dist sames += density - (dx + dy) else: internalSum += dx + dy if distSet == "euclidean": internalSum += math.sqrt(dx**2 + dy**2) coherencies.append(abs(internalSum / sumOptimal)) if not internalX == 0 or internalY == 0: spreads.append( max(internalX / internalY, internalY / internalX)) else: spreads.append(100) sameScore += sames else: coherencies.append(1) spreads.append(1) sumDistance = 0 targetDist = v * distDiff + minDist for b in blocks: d = abs(b.x - self.target_pos[0]) + abs(b.y - self.target_pos[1]) sumDistance += d distAverage = sumDistance / nrBlocks distScore = distAverage distScores.append(distScore) coherencyScore = 0 distScore = 0 spreadScore = 0 nrScores = max(1, len(coherencies)) for i in range(nrScores): coherencyScore += coherencies[i] distScore += distScores[i] spreadScore += spreads[i] coherencyScore = coherencyScore / nrScores distScore = distScore / nrScores spreadScore = spreadScore / nrScores #print(coherencyScore, distScore, spreadScore, sameScore) self._fitness = spreadScore + (coherencyScore** 2) + distScore + sameScore return self._fitness def look(self, cell): array = self.input_values_as_array array[0] = self.target_pos[0] / self.board_size[0] array[1] = self.target_pos[1] / self.board_size[1] array[2] = self.groupsLeft / self.nrGroups array[3] = len(self.blocks) / self.nrBlocks array[4] = len(self.groups[cell.value].blocksLeft) / ( len(self.groups[cell.value].blocksLeft) + len(self.groups[cell.value].blocksPlaced)) array[5] = self.groups[cell.value].density / self.board_size[0] array[6] = self.groups[cell.value].density / self.board_size[1] array[7] = self.groups[cell.value].averageX array[8] = self.groups[cell.value].averageY array[9] = cell.value ul = [] ur = [] dl = [] dr = [] for b in self.groups[cell.value].blocksPlaced: if b.x > array[7]: if b.y > array[8]: dr.append(b) else: ur.append(b) else: if b.y > array[8]: dl.append(b) else: ul.append(b) array[10] = len(ul) array[11] = len(ur) array[12] = len(dl) array[13] = len(dr) def update(self): if self.finished: return False else: return True def fill(self) -> bool: if self.finished: return False b = self.blocks.pop() self.placeCell(b) if not self.blocks: self.finished = True return b def placeCell(self, c): self.look(c) self.network.feed_forward(self.input_values_as_array) output = self.network.out x = output[0][0] y = output[1][0] g = self.groups[c.value] c.x = x * self.board_size[0] c.y = y * self.board_size[1] if g.averageX > 0: g.averageX = (g.averageX * len(g.blocksPlaced) + c.x) / len( g.blocksPlaced) g.averageY = (g.averageY * len(g.blocksPlaced) + c.y) / len( g.blocksPlaced) else: g.averageX = c.x g.averageY = c.y g.blocksLeft.remove(c) g.blocksPlaced.append(c)