Example #1
0
    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
Example #2
0
    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()
Example #3
0
    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()
Example #4
0
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
Example #5
0
    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()
Example #6
0
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
Example #7
0
    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
Example #8
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
Example #9
0
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)
Example #10
0
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)