def depth_search_lookahead(protein, ch_score, max_lookahead):
    global best_chain
    global best_score
    chars = protein.amino_string
    chain_length_goal = len(chars)

    # The first char amino is build in the proteine class
    chars = chars [1:]

    # Skips the first char the index.
    while True:

        char = chars[0]
        # Get the location the last amino folded to.
        # Note: an index of -1 gets the last object in a list.
        amino_xy = protein.chain.chain_list[-1].get_fold_coordinates()

        # Last amino always has fold of 0.
        if protein.char_counter + 1 == len(protein.amino_string):
            fold = 0

        # Determine which fold to pick. Ideal chain is returned as true if the full chain is already processed.
        # If ideal_chain is false, the next ideal fold is given.
        else:
            ideal_chain, fold = fold_selector(protein.chain, chars, max_lookahead, chain_length_goal, ch_score)

        # Ideal chain is already found, replace chain with ideal chain and break loop.
        if ideal_chain:

            protein.matrix, protein.chain.chain_list = get_matrix(best_chain)
            break

        # Adds amino to the protein chain.
        protein.chain.chain_list.append(Amino(char, fold, amino_xy))
        protein.chain.update_mirror_status()

        print("Char " + str(len(protein.chain.chain_list)) +"/" + str(len(protein.amino_string)) + ". Beste score: " + str(best_score), file=sys.stdout)
        print("")

        # Pop the first char from the string. That one has been processed now
        chars = chars[1:]

        # Reset the best score and best chain
        best_score = 1
        best_chain = []

    # Update matrix and protein of the chain. Offset happens now.
    protein.matrix, protein.chain.chain_list = get_matrix(protein.chain.chain_list)
    
    best_score = 1
    best_chain = []
Пример #2
0
def depth_search(protein, ch_score):
    char_counter = 1

    # Skips the first char the index.
    while protein.char_counter < len(protein.amino_string):

        char = protein.amino_string[protein.char_counter]
        # Get the location the last amino folded to.
        # Note: an index of -1 gets the last object in a list.
        amino_xy = protein.chain.chain_list[-1].get_fold_coordinates()


        # Last amino always has fold of 0.
        if protein.char_counter + 1 == len(protein.amino_string):
            fold = 0

        # Determine which fold to pick
        else:
            illegal_folds = None
            ideal_chain = fold_selector(amino_xy, char, protein.chain, illegal_folds, protein.amino_string, ch_score)

        # Ideal chain is already found, replace chain with ideal chain and break loop.
        if ideal_chain:
            
            protein.matrix, protein.chain.chain_list = get_matrix(best_chain)
            break

        # Adds amino to the protein chain.
        protein.chain.chain_list.append(Amino(char, fold, amino_xy))
        char_counter += 1
def depth_search_iterative(protein, ch_score):
    char_counter = 1

    # Build a matrix with dimensions of 2 * length of the protein +1
    matrix_dimensions = 2 * len(protein.amino_string) + 1
    for i in range(matrix_dimensions + 1):
        row = []
        for j in range(matrix_dimensions + 1):
            row.append(" ")
        protein.chain.matrix.append(row)

    # Center the first amino's coordinates in the matrix and add it to the matrix.
    protein.chain.chain_list[0].coordinates = [
        len(protein.amino_string) + 1,
        len(protein.amino_string) + 1
    ]
    protein.chain.matrix[len(protein.amino_string) +
                         1][len(protein.amino_string) +
                            1] = protein.chain.chain_list[0]

    # Skips the first char the index.
    while protein.char_counter < len(protein.amino_string):

        char = protein.amino_string[protein.char_counter]
        # Get the location the last amino folded to.
        # Note: an index of -1 gets the last object in a list.
        amino_xy = protein.chain.chain_list[-1].get_fold_coordinates()

        # Last amino always has fold of 0.
        if protein.char_counter + 1 == len(protein.amino_string):
            fold = 0

        # Determine which fold to pick
        else:
            illegal_folds = None
            ideal_chain = fold_selector(amino_xy, char, protein.chain,
                                        illegal_folds, protein.amino_string,
                                        ch_score)

        # Ideal chain is already found, replace chain with ideal chain and break loop.
        if ideal_chain:

            protein.matrix, protein.chain.chain_list = get_matrix(best_chain)
            break

        # Adds amino to the protein chain.
        protein.chain.chain_list.append(Amino(char, fold, amino_xy))
        char_counter += 1
Пример #4
0
def build_straight_protein(protein):
    mode_3d = protein.mode_3d

    if mode_3d:
        protein.chain.chain_list[0].coordinates = [0, 0, 0]

    else:
        protein.chain.chain_list[0].coordinates = [0, 0]

    for index, char in enumerate(protein.amino_string):
        if index == 0:
            continue

        new_amino = Amino(
            char, 2,
            protein.chain.chain_list[index - 1].get_fold_coordinates())
        protein.chain.chain_list.append(new_amino)

    protein.matrix, protein.chain.chain_list = get_matrix(
        protein.chain.chain_list)
Пример #5
0
def branch_and_bound_random(protein, ch_score, best_score_import, p1, p2):
    global best_score
    global p_below_average
    global p_above_average
    p_below_average = p1
    p_above_average = p2

    # Check if unsupported 3d mode.
    check_dimensions(protein.chain.chain_list)

    # You could import a score to start at (if you know the score to be at least that amount).
    best_score = best_score_import

    char_counter = 1

    mode_3d = protein.mode_3d

    # Build up the partial energy list for every depth in the chain.
    global partial_energies
    partial_energies.append([])
    for char in protein.amino_string:
        partial_energies.append([0, 0, 0])

    if mode_3d:
        # Build a matrix with dimensions of 2 * length of the protein + 1.
        matrix_dimensions = 2 * len(protein.amino_string) + 1

        for k in range(matrix_dimensions + 1):
            layer = []
            for i in range(matrix_dimensions + 1):
                row = []
                for j in range(matrix_dimensions + 1):
                    row.append(" ")
                layer.append(row)
            protein.chain.matrix.append(layer)
        protein.chain.chain_list[0].coordinates = [
            len(protein.amino_string) + 1,
            len(protein.amino_string) + 1,
            len(protein.amino_string) + 1
        ]
        protein.chain.matrix[len(protein.amino_string) +
                             1][len(protein.amino_string) +
                                1][len(protein.amino_string) +
                                   1] = protein.chain.chain_list[0]

    # 2D
    else:
        # Build a matrix with dimensions of 2 * length of the protein +1.
        matrix_dimensions = 2 * len(protein.amino_string) + 1
        for i in range(matrix_dimensions + 1):
            row = []
            for j in range(matrix_dimensions + 1):
                row.append(" ")
            protein.chain.matrix.append(row)

        # Center the first amino's coordinates in the matrix and add it to the matrix.
        protein.chain.chain_list[0].coordinates = [
            len(protein.amino_string) + 1,
            len(protein.amino_string) + 1
        ]
        protein.chain.matrix[len(protein.amino_string) +
                             1][len(protein.amino_string) +
                                1] = protein.chain.chain_list[0]

    # Perform all functions to add corrent spots.
    new_score, spots_to_add, spots_to_remove, spots_to_add_C, spots_to_remove_C = get_score_iterative_and_spots(
        protein.chain, protein.chain.matrix, 0)
    protein.chain.add_fold_spots(spots_to_add, "H")
    protein.chain.remove_fold_spots(spots_to_remove, "H")
    protein.chain.add_fold_spots(spots_to_add_C, "C")
    protein.chain.remove_fold_spots(spots_to_remove_C, "C")

    # Skips the first char the index.
    while protein.char_counter < len(protein.amino_string):

        char = protein.amino_string[protein.char_counter]
        # Get the location the last amino folded to.
        # Note: an index of -1 gets the last object in a list.
        amino_xy = protein.chain.chain_list[-1].get_fold_coordinates()

        # Last amino always has fold of 0.
        if protein.char_counter + 1 == len(protein.amino_string):
            fold = 0

        # Determine which fold to pick.
        else:
            illegal_folds = None
            ideal_chain = fold_selector(amino_xy, char, protein.chain,
                                        illegal_folds, protein.amino_string,
                                        ch_score)

        # Ideal chain is already found, replace chain with ideal chain and break loop.
        if ideal_chain:
            protein.matrix, protein.chain.chain_list = get_matrix(best_chain)
            break

        # Adds amino to the protein chain.
        protein.chain.chain_list.append(Amino(char, fold, amino_xy))

        char_counter += 1
def hill_climbing_caterpillar(protein, iterations, max_non_improvements):

    # Check if unsupported 3d mode.
    check_dimensions(protein.chain.chain_list)

    # We start with a straight protein, you could replace this with a search (random for example)
    build_straight_protein(protein)
    # Save the score at every iteration (Not yet implemented)
    scores = []
    total_iterations = 0

    # The amount of turns the score hasnt improved.
    times_not_improved = 0
    times_not_improved_limit = max_non_improvements

    # The overal best score and chain is saved here
    best_score = 1
    best_chain = []

    while total_iterations < iterations:

        # pick random index for chain and that amino.
        max_index = len(protein.amino_string) - 1
        chosen_index = random.randint(1, max_index - 1)

        # get the amino and his fold with the random index
        chosen_amino = protein.chain.chain_list[chosen_index]
        chosen_amino_fold = chosen_amino.fold

        # Save old chain if the random move doesnt turn out to be legal
        old_chain = copy.deepcopy(protein.chain.chain_list)

        # Also pick random move and apply.
        moves = get_legal_moves(chosen_amino.coordinates,
                                protein.chain.chain_list)

        # remove initial move from the moves
        if chosen_amino_fold in moves:
            moves.remove(chosen_amino_fold)

        # if there a no possible new moves for this amino loop again
        if not moves:
            continue

        # choose random fold and adjust fold of amino
        chosen_move = random.choice(moves)
        chosen_amino.fold = chosen_move

        # get the amino thereafter from the chain
        next_amino = protein.chain.chain_list[chosen_index + 1]

        # If the chosen amino is the one before the last only this amino needed to change
        if chosen_index == max_index - 1:
            pass

        # if the there need to be changed only 2 aminos
        elif chosen_move == next_amino.fold or chosen_index + 2 >= max_index:
            next_amino.fold = chosen_amino_fold

        # skip if new move is 180 degrees to the other side, rearanging gets very difficult
        elif chosen_move * -1 == chosen_amino_fold:
            protein.chain.chain_list = old_chain
            continue

        else:
            # change next fold and the fold after that
            next_amino.fold = chosen_amino_fold
            after_next_amino = protein.chain.chain_list[chosen_index + 2]
            after_next_amino.fold = chosen_move * -1

            # pull rest of the chain by changing position of aminos thereafter 2 places
            for i in range(chosen_index + 1, max_index - 2):
                amino_changed = old_chain[i]
                amino_pulled = protein.chain.chain_list[i + 2]
                amino_pulled.fold = amino_changed.fold

        # Make sure last amino gets a fold of 0
        last_amino = protein.chain.chain_list[-1]
        last_amino.fold = 0

        # Rebuild the chain/matrix.
        legal_chain = rebuild_chain(protein, chosen_index + 1)

        # Function returns False if it isnt a legal chain.
        # If illegal chain, load back old_chain
        if not legal_chain:
            protein.chain.chain_list = old_chain
            continue

        total_iterations += 1

        # Load matrix of new chain
        protein.matrix, protein.chain.chain_list = get_matrix(
            protein.chain.chain_list)

        # check for errror and revert to old chain
        if protein.chain.chain_list == False:
            protein.chain.chain_list = old_chain
            print("error: false chain")
            continue

        # Calculate score of new chain
        score = get_score(protein.chain.chain_list, protein.matrix)

        # Continue with new chain if same or better score
        if score <= protein.chain.score:
            if len(best_chain) == 0:
                best_chain = copy.deepcopy(protein.chain.chain_list)
            # New "local" best score
            if score < protein.chain.score:
                print("new best score: ", end="")
                print(score)
                # Reset times not improved
                times_not_improved = 0

                # Actual new best score
                if score < best_score:
                    best_score = score
                    best_chain = copy.deepcopy(protein.chain.chain_list)

            # Score is same so not improved.
            else:
                times_not_improved += 1

            protein.chain.score = score

        # Chain is worse
        else:

            # If times not improved limit is reaced, continue with that chain anyway
            if times_not_improved >= times_not_improved_limit:
                protein.chain.score = score
                times_not_improved = 0

            # abandon that chain.
            else:
                protein.chain.chain_list = old_chain

    # Save the best score and chain in the protein
    protein.chain.chain_list = best_chain
    protein.matrix, protein.chain.chain_list = get_matrix(best_chain)
Пример #7
0
def simulated_annealing(protein, iterations, temp_start, temp_end):
    # We start with a straight protein, you could replace this with a search (random for example)
    build_straight_protein(protein)

    # Save the score at every iteration (Not yet implemented)
    scores = []
    total_iterations = 0

    # The overal best score and chain is saved here
    best_score = 1
    best_chain = []

    temperature_start = temp_start
    temperature_end = temp_end
    temp_step = (temperature_start - temperature_end) / iterations
    temperature = temperature_start

    while total_iterations < iterations:

        # pick random index for chain and that amino.
        chosen_index = random.randint(0, len(protein.amino_string) - 1)
        chosen_amino = protein.chain.chain_list[chosen_index]

        # Save old chain if the random move doesnt turn out to be legal
        old_chain = copy.deepcopy(protein.chain.chain_list)

        # Also pick random move and apply.
        moves = get_legal_moves(chosen_amino.coordinates,
                                protein.chain.chain_list)

        if not moves:
            continue

        chosen_move = random.choice(moves)
        chosen_amino.fold = chosen_move

        # Rebuild chain for that point on.
        legal_chain = rebuild_chain(protein, chosen_index + 1)

        # Function returns False if it isnt a legal chain.
        # If illegal chain, load back old_chain
        if not legal_chain:
            protein.chain.chain_list = old_chain

        total_iterations += 1

        # Load matrix of new chain
        protein.matrix, protein.chain.chain_list = get_matrix(
            protein.chain.chain_list)

        # Calculate score of new chain
        score = get_score(protein.chain.chain_list, protein.matrix)

        # New best score
        if score < best_score:
            if len(best_chain) == 0:
                best_chain = copy.deepcopy(protein.chain.chain_list)
            print("new best score: ", end="")
            print(score)
            best_score = score
            best_chain = copy.deepcopy(protein.chain.chain_list)

        # Calculate if chain should be accepted based on:
        # New score, old score, temperature, random number
        acceptance_rate = (2**(abs(score) -
                               abs(protein.chain.score))) / temperature
        random_number = random.uniform(0, 1)

        # Accept iteration
        if random_number < acceptance_rate:
            protein.chain.score = score

        # Abbandon iteration
        else:
            protein.chain.chain_list = old_chain

        temperature = temperature - temp_step

    protein.chain.chain_list = best_chain
    protein.matrix, protein.chain.chain_list = get_matrix(best_chain)
Пример #8
0
def breadth_search(protein, ch_score):

    # Check if unsupported 3d mode.
    check_dimensions(protein.chain.chain_list)

    # Get chain WITH first amino already in it.
    start_chain = protein.chain
    # Create queue and put the first amino in it
    queue = Queue(maxsize = 0)
    queue.put(start_chain)

    # Finished queues. Is this smart?
    finished_chains = []

    # Go trough the queue.
    while not queue.empty():
        # Get the first chain from the queue.
        chain_actual = queue.get()

        # Get the index from the length of the chain.
        index = len(chain_actual.chain_list)

        # Last amino always has fold of 0.
        if  index + 1 == len(protein.amino_string):

            fold = 0
            atype = protein.amino_string[index]
            coordinates = chain_actual.chain_list[-1].get_fold_coordinates()
            new_amino = Amino(atype, fold, coordinates)
            chain_actual.chain_list.append(new_amino)

            # Save the chain to the finished chain list.
            finished_chains.append(chain_actual)


        # Determine fold and make new chain for every possibility.
        else:
            legal_moves = get_legal_moves(chain_actual.chain_list[-1].get_fold_coordinates(), chain_actual.chain_list)

            # if there are no legal moves chain ends here.
            if legal_moves:
                # Go trough the legal moves and make a new_chain for every move, then put them in the queue.
                for move in legal_moves:

                    atype = protein.amino_string[index]
                    coordinates = chain_actual.chain_list[-1].get_fold_coordinates()
                    # Make a new amino and add it to the a new chain with deepcopy.
                    amino = Amino(atype, move, coordinates)
                    new_chain = copy.deepcopy(chain_actual)
                    new_chain.chain_list.append(amino)
                    # Put the new chain in the queue.
                    queue.put(new_chain)

    # The best score and corresponding chain that has been found.
    best_score = 1
    best_chains = []

    # Goes over all finished chains to find the one with the best score.
    for chain in finished_chains:

        matrix, xy_offset = get_matrix_efficient(chain.chain_list)
        score = get_score_efficient(chain.chain_list, matrix, xy_offset, ch_score)

        # If the score is better than the best score, replace best_chains.
        # if score is equal add chain to best_chains.
        if score < best_score:
            best_score = score
            best_chains = []
            print("New best score: " + str(score))
            best_chains.append(chain)
        elif score == best_score:
            best_chains.append(chain)

    protein.matrix, protein.chain.chain_list = get_matrix(best_chains[0].chain_list)
Пример #9
0
def beam_search(protein, ch_score):

    # Check if unsupported 3d mode.
    check_dimensions(protein.chain.chain_list)

    # Get chain WITH first amino already in it.
    start_chain = protein.chain
    # Create queue and put the first amino in it.
    queue = Queue(maxsize=0)
    queue.put(start_chain)

    # Finished queues. Is this smart?
    finished_chains = []

    # Keeps track of scores in 1 layer.
    scores = []

    # Go trough the queue.
    while not queue.empty():
        # Get the first chain from the queue.
        chain_actual = queue.get()

        # Get the index from the length of the chain.
        index = len(chain_actual.chain_list)

        # get the globals
        global global_index
        global avg_scores

        # check for level change level change by comparing global index with actual index
        if index == global_index + 1:
            # change global index to new level
            global_index = index

            # update global avg score and reset scores
            sum_scores = sum(scores) / len(scores)
            avg_scores = sum_scores
            scores = []

        # Remove chain from queue if score is worse than cutoff score.
        chain_score = chain_actual.score
        if chain_score > avg_scores:
            continue

        # Last amino always has fold of 0.
        if index + 1 == len(protein.amino_string):

            fold = 0
            atype = protein.amino_string[index]
            coordinates = chain_actual.chain_list[-1].get_fold_coordinates()

            new_amino = Amino(atype, fold, coordinates)
            chain_actual.chain_list.append(new_amino)

            finished_chains.append(chain_actual)

        # Determine fold and make new chain for every possibility.
        else:
            legal_moves = get_legal_moves(
                chain_actual.chain_list[-1].get_fold_coordinates(),
                chain_actual.chain_list)

            # If there are no legal moves chain ends here.
            if legal_moves:
                # Go trough the legal moves and make a new_chain for every move, then put them in the queue.
                for move in legal_moves:
                    atype = protein.amino_string[index]
                    coordinates = chain_actual.chain_list[
                        -1].get_fold_coordinates()

                    # Make a new amino and add it to the a new chain with deepcopy.
                    amino = Amino(atype, move, coordinates)
                    new_chain = copy.deepcopy(chain_actual)
                    new_chain.chain_list.append(amino)

                    # Put the new chain in the queue, set chain's score variable to its score, and add score to this layer's score list.

                    matrix, offset = get_matrix_efficient(new_chain.chain_list)
                    score = get_score_efficient(new_chain.chain_list, matrix,
                                                offset, 1)
                    new_chain.score = score
                    queue.put(new_chain)

                    # add score to the list which tracks all scores in this level
                    scores.append(score)

    # The best score and corresponding chain that has been found.
    best_score = 1
    best_chains = []

    # Goes over all finished chains to find the one with the best score.
    for chain in finished_chains:
        protein1 = Protein(protein.amino_string, "2d")
        protein1.matrix, protein1.chain = get_matrix(
            copy.deepcopy(chain).chain_list)

        matrix, xy_offset = get_matrix_efficient(chain.chain_list)
        score = get_score_efficient(chain.chain_list, matrix, xy_offset,
                                    ch_score)

        # If the score is better than the best score, replace best_chains.
        # If score is equal add chain to best_chains.
        if score < best_score:
            best_score = score
            best_chains = []
            print("New best score: " + str(score))
            best_chains.append(chain)
        elif score == best_score:
            best_chains.append(chain)

    protein.matrix, protein.chain.chain_list = get_matrix(
        best_chains[0].chain_list)
Пример #10
0
def hill_climbing(protein, iterations, max_non_improvements):

    # We start with a straight protein, you could replace this with a search (random for example)
    build_straight_protein(protein)

    # Save the score at every iteration.
    total_iterations = 0

    # The amount of turns the score hasnt improved.
    times_not_improved = 0
    times_not_improved_limit = max_non_improvements

    # The overal best score and chain is saved here
    best_score = 1
    best_chain = []

    while total_iterations < iterations:

        # pick random index for chain and that amino.
        chosen_index = random.randint(0, len(protein.amino_string) - 1)
        chosen_amino = protein.chain.chain_list[chosen_index]

        # Save old chain if the random move doesnt turn out to be legal
        old_chain = copy.deepcopy(protein.chain.chain_list)

        # Also pick random move and apply.
        moves = get_legal_moves(chosen_amino.coordinates,
                                protein.chain.chain_list)

        if not moves:
            continue

        chosen_move = random.choice(moves)
        chosen_amino.fold = chosen_move

        # Rebuild the chain/matrix.
        legal_chain = rebuild_chain(protein, chosen_index + 1)

        # Function returns False if it isnt a legal chain.
        # If illegal chain, load back old_chain
        if not legal_chain:
            protein.chain.chain_list = old_chain

        total_iterations += 1

        # Load matrix of new chain
        protein.matrix, protein.chain.chain_list = get_matrix(
            protein.chain.chain_list)

        # Calculate score of new chain
        score = get_score(protein.chain.chain_list, protein.matrix)

        # Continue with new chain if same or better score
        if score <= protein.chain.score:
            if len(best_chain) == 0:
                best_chain = copy.deepcopy(protein.chain.chain_list)

            # New "local" best score
            if score < protein.chain.score:
                # Reset times not improved
                times_not_improved = 0

                # Actual new best score
                if score < best_score:
                    print("new best score: ", end="")
                    print(score)
                    best_score = score
                    best_chain = copy.deepcopy(protein.chain.chain_list)

            # Score is same so not improved.
            else:
                times_not_improved += 1

            protein.chain.score = score

        # Chain is worse
        else:

            # If times not improved limit is reaced, continue with that chain anyway
            if times_not_improved >= times_not_improved_limit:
                protein.chain.score = score
                times_not_improved = 0

            # abandon that chain.
            else:
                protein.chain.chain_list = old_chain

    # Save the best score and chain in the protein
    protein.chain.chain_list = best_chain
    protein.matrix, protein.chain.chain_list = get_matrix(best_chain)
def branch_and_bound_lookahead(protein, ch_score, best_score_import, max_lookahead):
    global best_score
    global best_chain
    best_score = best_score_import

    mode_3d = is_chain_3d(chain)
 

    if mode_3d:
        matrix_dimensions = 2 * len(protein.amino_string) + 1
        
        for k in range(matrix_dimensions + 1):
            layer = []
            for i in range(matrix_dimensions + 1):
                row = []
                for j in range(matrix_dimensions + 1):
                    row.append(" ")
                layer.append(row)
            protein.matrix.append(layer)

        protein.chain.chain_list[0].coordinates = [len(protein.amino_string) + 1 , len(protein.amino_string) + 1, len(protein.amino_string) + 1]
        protein.chain.matrix[len(protein.amino_string) + 1][len(protein.amino_string) + 1][len(protein.amino_string) + 1] = protein.chain.chain_list[0]


    else:
        # Build a matrix with dimensions of 2 * length of the protein +1
        matrix_dimensions = 2 * len(protein.amino_string) + 1
        for i in range(matrix_dimensions + 1):
            row = []
            for j in range(matrix_dimensions + 1):
                row.append(" ")
            protein.chain.matrix.append(row)
        
        # Center the first amino's coordinates in the matrix and add it to the matrix.
        protein.chain.chain_list[0].coordinates = [len(protein.amino_string) + 1 , len(protein.amino_string) + 1]
        protein.chain.matrix[len(protein.amino_string) + 1][len(protein.amino_string) + 1] = protein.chain.chain_list[0]

    new_score, spots_to_add, spots_to_remove, spots_to_add_C, spots_to_remove_C = get_score_iterative_and_spots(protein.chain, protein.chain.matrix, 0)

    
    protein.chain.add_fold_spots(spots_to_add, "H")
    protein.chain.add_fold_spots(spots_to_add_C, "C")
    
    current_score = 0
    # Skips the first char the index.
    while protein.char_counter < len(protein.amino_string):

        

        # print(str(self.char_counter))
        char = protein.amino_string[protein.char_counter]
        # Get the location the last amino folded to.
        # Note: an index of -1 gets the last object in a list.
        amino_xy = protein.chain.chain_list[-1].get_fold_coordinates()


        # Last amino always has fold of 0.
        if protein.char_counter + 1 == len(protein.amino_string):
            fold = 0

        
        # Determine which fold to pick
        else:
            ideal_chain, fold = fold_selector(amino_xy, char, protein.chain, protein.amino_string[protein.char_counter - 1:], ch_score, max_lookahead, current_score)


        # Ideal chain is already found, replace chain with ideal chain and break loop.
        if ideal_chain:
            for amino in best_chain:
                print(amino)

            protein.matrix, protein.chain.chain_list = get_matrix(best_chain)
            break

        new_amino = Amino(char, fold, amino_xy)
        # Adds amino to the protein chain.
        protein.chain.chain_list.append(new_amino)
        
        protein.chain.update_mirror_status()

        if mode_3d:
            protein.chain.matrix[amino_xy[0]][amino_xy[1]][amino_xy[2]] = new_amino
        else:
            # Also add that amino to the matrix, and update the mirror starus
            protein.chain.matrix[amino_xy[0]][amino_xy[1]] = new_amino
   
        
        # Calculate new score and and/remove the correct fold spots
        new_score, spots_to_add, spots_to_remove, spots_to_add_C, spots_to_remove_C = get_score_iterative_and_spots(protein.chain, protein.chain.matrix, current_score)
        
        current_score = new_score
      
        
        # Remove the spots that are now filled by aminos.
        protein.chain.remove_fold_spots(spots_to_remove, "H")
        protein.chain.remove_fold_spots(spots_to_remove_C, "C")

        # Change odd/even
        protein.chain.odd = not protein.chain.odd
        
        # Add the spots that were newly created.
        if protein.chain.chain_list[-1].atype == "H":
            spots_to_add.append(protein.chain.chain_list[-1].get_fold_coordinates())
        if protein.chain.chain_list[-1].atype == "C":
            spots_to_add_C.append(protein.chain.chain_list[-1].get_fold_coordinates())

        protein.chain.add_fold_spots(spots_to_add, "H")
        protein.chain.add_fold_spots(spots_to_add_C, "C")
        protein.chain.get_max_possible_extra_score(protein.amino_string[protein.char_counter:])


        protein.char_counter += 1

        for amino in protein.chain.chain_list:
            print(amino, end='')
        print()


        best_chain = []
        best_score = 1
        current_lookahead = 0
        
        print(protein.chain.available_bonds_odd_H)
        print(protein.chain.available_bonds_odd_C)
        print(protein.chain.available_bonds_even_H)
        print(protein.chain.available_bonds_even_C)


    
    protein.matrix, protein.chain.chain_list = get_matrix(protein.chain.chain_list)