public int solve() {

		//make the array to hold our population of individuals
		Individual[] individuals = new Individual[INDIVIDUAL_POPULATION_SIZE];

		//have a vector to hold our population of blocks
		Vector<Block> blocks = new Vector<Block>(); 

		//start off with making a random initial population
		for(int i = 0; i < individuals.length; i++) {
			individuals[i] = new Individual(RANDOM_NUMBER_GENERATOR, this);
		}

		if(DEBUG) {
			System.out.println("Random starting population:");
			for(int i = 0; i < individuals.length; i++) {
				System.out.println(individuals[i]);
			}
			System.out.println();
		}


		//start going through the generations
		for(int generationNumber = 0; generationNumber < MAX_NUM_GENERATIONS; generationNumber++) {
			if(DEBUG) {
				System.out.println("********************************\nStarting generation " + generationNumber + "\n********************************\n");
			}

			//check if we have the optimal solution
			if(foundOptimalSolution(individuals)) {
				return generationNumber;
			}			

			//we already have a population to work with.
			//Select on that population
			individuals = selectIndividuals(individuals);

			if(DEBUG) {
				System.out.println("Fit Individuals");
				for(Individual i : individuals) {
					System.out.println(i);
				}
				System.out.println();
			}

			//Figure out the most probable character at each index of all of the individuals
			ProbChar[] characterProbabilites = determineCharaterProbability(individuals);

			if(DEBUG) {
				System.out.println("Probabilities of characters");
				for(int i = 0; i < characterProbabilites.length; i++) {
					System.out.println("Index " + i + ":\t" + characterProbabilites[i]);
				}
				System.out.println();
			}

			//replace characters that do not have high enough probability with wild characters
			//because we don't "know" with enough certainty that those characters create fit individuals
			characterProbabilites = processCharacterProbabilites(characterProbabilites);

			if(DEBUG) {
				System.out.println("Processed Characters");
				for(int i = 0; i < characterProbabilites.length; i++) {
					System.out.println("Index " + i + ":\t" + characterProbabilites[i]);
				}
				System.out.println();
			}

			//pull out blocks
			//blocks are contiguous groups of character that are not wild.
			Vector<Block> newBlocks = extractBlocks(characterProbabilites);

			if(DEBUG) {
				System.out.println("Extracted blocks:");
				for(Block b : newBlocks) {
					System.out.println(b);
				}
				System.out.println();
			}

			/*TODO: ADD ALL COMBINATION OF NEW BLOCKS???  
			 * Would need to change Individual constructor as well to handle wilds in the middle.
			 */

			//add our new blocks to the Vector
			blocks.addAll(newBlocks);

			//see if we have any blocks at this point
			//if we don't just make a new, random population of individuals and try again
			if(blocks.size() == 0) {
				for(int i = 0; i < INDIVIDUAL_POPULATION_SIZE; i++) {
					individuals[i] = new Individual(RANDOM_NUMBER_GENERATOR, this);
				}
				continue;
			}

			if(DEBUG) {
				System.out.println("All blocks, before selection:");
				for(Block b : blocks) {
					System.out.println(b);
				}
				System.out.println();
			}

			//remove duplicate blocks
			//otherwise, if only a few blocks are extracted in the early generations
			//they dominate the block pool in subsequent generations
			blocks = removeDuplicateBlocks(blocks);

			if(DEBUG) {
				System.out.println("Without duplicates:");
				for(Block b : blocks) {
					System.out.println(b);
				}
				System.out.println();
			}			

			//select on our blocks to keep our population at constant size
			blocks = selectBlocks(blocks);

			if(DEBUG) {
				System.out.println("Selected blocks:");
				for(Block b : blocks) {
					System.out.println(b);
				}
				System.out.println();
			}

			//organize the blocks based on their starting and ending index
			//we need to do this to make creating new individuals easy.
			Vector<Block>[] organizedByStart = Block.organizeBlocksByStartIndex(blocks, this);
			Vector<Block>[] organizedByEnd = Block.organizeBlocksByEndIndex(blocks, this);

			//build the next population of individuals
			individuals = constructNextIndividualPopulation(blocks, organizedByStart, organizedByEnd);

			if(DEBUG) {
				System.out.println("Next Generation of individuals:");
				for(Individual i : individuals) {
					System.out.println(i);
				}
				System.out.println();
			}

			//mutate the individuals for more variety
			individuals = mutateIndividuals(individuals);

			if(DEBUG) {
				System.out.println("Mutated individuals:");
				for(Individual i : individuals) {
					System.out.println(i);
				}
				System.out.println();
			}
		}//end for loop where generations go from 0 to MAX_NUM_GENERATIONS
		return MAX_NUM_GENERATIONS;
	}