def get_bt_from_root(self):
        """
        Returns bt string (actually a list) from py tree root
        by cleaning the ascii tree from py trees
        Not complete or beautiful by any means but works for many trees
        """
        string = pt.display.ascii_tree(self.root)
        print(string)
        string = string.replace('[o] ', '')
        string = string.replace('\t', '')
        string = string.replace('-->', '')
        string = string.replace('Fallback', 'f(')
        string = string.replace('Sequence', 's(')
        bt = string.split('\n')
        bt = bt[:-1]  #Remove empty element because of final newline

        prev_leading_spaces = 999999
        for i in range(len(bt) - 1, -1, -1):
            leading_spaces = len(bt[i]) - len(bt[i].lstrip(' '))
            bt[i] = bt[i].lstrip(' ')
            if leading_spaces > prev_leading_spaces:
                for _ in range(
                        round((leading_spaces - prev_leading_spaces) / 4)):
                    bt.insert(i + 1, ')')
            prev_leading_spaces = leading_spaces

        bt_obj = behavior_tree.BT(bt)
        bt_obj.close()
        return bt_obj.bt
def mutate_gene(genome, p_add, p_delete):
    """
    Mutate only a single gene.
    """

    if p_add < 0 or p_delete < 0:
        raise Exception("Mutation parameters must not be negative.")

    if p_add + p_delete > 1:
        raise Exception(
            "Sum of the mutation probabilities must be less than 1.")

    mutated_individual = behavior_tree.BT([])
    max_attempts = 100
    attempts = 0
    while (not mutated_individual.is_valid()
           or mutated_individual.bt == genome) and attempts < max_attempts:
        mutated_individual.set(genome)
        index = random.randint(0, len(genome) - 1)
        mutation = random.random()

        # Delete node
        if mutation < p_delete:
            mutated_individual.delete_node(index)

        # Add node
        elif mutation < p_delete + p_add:
            mutated_individual.add_node(index)

        # Mutate node
        else:
            mutated_individual.change_node(index)

        #Close bt accordingly to the change
        mutated_individual.close()
        attempts += 1

    if attempts >= max_attempts and (not mutated_individual.is_valid()
                                     or mutated_individual.bt == genome):
        mutated_individual = behavior_tree.BT([])

    return mutated_individual.bt
def crossover_genome(genome1, genome2):
    """
    Do crossover between genomes at random points
    """
    bt1 = behavior_tree.BT(genome1)
    bt2 = behavior_tree.BT(genome2)
    offspring1 = behavior_tree.BT([])
    offspring2 = behavior_tree.BT([])

    if bt1.is_valid() and bt2.is_valid():
        max_attempts = 100
        attempts = 0
        found = False
        while not found and attempts < max_attempts:
            offspring1.set(bt1.bt)
            offspring2.set(bt2.bt)
            cop1 = -1
            cop2 = -1
            if len(genome1) == 1:
                cop1 = 0  #Change whole tree
            else:
                while not offspring1.is_subtree(cop1):
                    cop1 = random.randint(1, len(genome1) - 1)
            if len(genome2) == 1:
                cop2 = 0  #Change whole tree
            else:
                while not offspring2.is_subtree(cop2):
                    cop2 = random.randint(1, len(genome2) - 1)

            offspring1.swap_subtrees(offspring2, cop1, cop2)
            attempts += 1
            if offspring1.is_valid() and offspring2.is_valid():
                found = True
        if not found:
            offspring1.set([])
            offspring2.set([])

    return offspring1.bt, offspring2.bt
    def __init__(self, string, behaviors, state_machine=None, root=None):
        if root is not None:
            self.root = root
            string = self.get_bt_from_root()
        self.bt = behavior_tree.BT(string)
        self.depth = self.bt.depth()
        self.length = self.bt.length()
        self.state_machine = state_machine
        self.behaviors = behaviors

        if root is None:
            self.root, has_children = behaviors.get_node_from_string(
                string[0], state_machine)
            string.pop(0)
        else:
            has_children = False

        super().__init__(root=self.root)
        if has_children:
            self.create_from_string(string, self.root)
def random_genome(length):
    """
    Returns a random genome
    """
    bt = behavior_tree.BT([])
    return bt.random(length)