/* @author Andrew DeBiase */ private Tuple <Chromosome2, Chromosome2> Crossover(Chromosome2 parentOne, Chromosome2 parentTwo) { int jointsLength = parentOne.jointMovements.Length; int crossPoint = Random.Range(1, jointsLength); for (int i = crossPoint; i < jointsLength; i++) { Tuple <float, Vector3>[] temp = parentOne.jointMovements[i]; parentOne.jointMovements[i] = parentTwo.jointMovements[i]; parentTwo.jointMovements[i] = temp; } return(new Tuple <Chromosome2, Chromosome2>(parentOne, parentTwo)); }
/* * Handles mutating a chromosome passed in by adding a randomly generated value * to the torques of the chromosome * @author Ernest Essuah Mensah */ private Chromosome2 Mutate(Chromosome2 parent) { // Copy torques from parent to mutate Tuple <float, Vector3>[][] jointMovements = parent.jointMovements; int mutationIndex = Random.Range(0, parent.jointMovements.Length); for (int i = 0; i < COMPLEX_CHROMOSOME_LENGTH; i++) { Tuple <float, Vector3> current = parent.jointMovements[mutationIndex][i]; // Pick a random axis to mutate // 0: x-axis; 1: y-axis, 2: z-axis int mutationAxis = Random.Range(0, 2); float mutation = Random.Range(-INIT_TORQUE_MAG, INIT_TORQUE_MAG); Vector3 newMovement; if (mutationAxis == 0) { newMovement = new Vector3(current.Item2.x + mutation, current.Item2.y, current.Item2.z); } else if (mutationAxis == 1) { newMovement = new Vector3(current.Item2.x, current.Item2.y + mutation, current.Item2.z); } else { newMovement = new Vector3(current.Item2.x, current.Item2.y, current.Item2.z + mutation); } // Mutate the time between joint movements float mutationTime = Random.Range(-MAX_TIME_BETWEEN_TORQUES, MAX_TIME_BETWEEN_TORQUES); float newTime = Mathf.Max(current.Item1 + mutationTime, 0); jointMovements[mutationIndex][i] = new Tuple <float, Vector3>(newTime, newMovement); } return(new Chromosome2(jointMovements)); }
// This should be called right after the golfer is instantiated public void InitializeAgent(Chromosome2 newChrom, GolferSettings newSettings, float holeDistOffset = 0) { chrom = newChrom; jointsInUse = new Rigidbody[chrom.jointMovements.Length]; settings = newSettings; // hide the golf hole if we don't need it if (settings.fitnessFunc == GolferSettings.Fitness.drivingDist) { hole.gameObject.SetActive(false); } else { // position the hole based on the distance it's supposed to be from the golfer, maintaining the y-coordinate hole.position = transform.position - Vector3.right * (settings.holeDist + holeDistOffset) + new Vector3(0, hole.position.y, 0); actualHoleDist = Vector3.Distance(transform.position, hole.position); } // Destroy the joint connecting the club to the second hand if we're going one-handed if (settings.clubGrip == GolferSettings.ClubGrip.oneHand) { Destroy(secondHandJoint); } }
// --------------- ACTUAL GENETIC ALGORITHM STUFF BELOW ---------------- /* @author Matthew Graber, Azhdaha Fayyaz, Andrew DeBiase, Vladislav Dozorov */ // Here is where the actual simulations are managed private IEnumerator Simulate() { // ----------- CREATE INITIAL POPULATION, SET UP SIMULATION ------------ float holeDistOffset; // Initialize arrays chroms = new Chromosome2[numAgents]; agents = new GameObject[numAgents]; fitnesses = new float[numAgents]; fitnessTrack = new float[numGens, numAgents]; // 2D array with fitness for each gen // determine the number of joints based on whether we're using the golfer's full body or not int numJoints = (moveableJoints == GolferSettings.MoveableJointsExtent.fullBody ? 12 : 8); // initialize chromosomes with random values for (int i = 0; i < chroms.Length; i++) { // create Vector3 array of random torques to give to the chromosome Tuple <float, Vector3>[][] initJointMovements = new Tuple <float, Vector3> [numJoints][]; for (int j = 0; j < initJointMovements.Length; j++) { initJointMovements[j] = new Tuple <float, Vector3> [COMPLEX_CHROMOSOME_LENGTH]; for (int k = 0; k < COMPLEX_CHROMOSOME_LENGTH; k++) { initJointMovements[j][k] = new Tuple <float, Vector3>(Random.Range(0f, MAX_TIME_BETWEEN_TORQUES), new Vector3(Random.Range(-INIT_TORQUE_MAG, INIT_TORQUE_MAG), Random.Range(-INIT_TORQUE_MAG, INIT_TORQUE_MAG), Random.Range(-INIT_TORQUE_MAG, INIT_TORQUE_MAG))); } } chroms[i] = new Chromosome2(initJointMovements); } // ----------- RUN THE SIMULATION ------------ for (int i = 0; i < numGens; i++) { // display the current generation number currentGenText.text = "" + (i + 1) + " / " + numGens; // Determine the random hole distance offset for this generation // (if holeDistRand == 0 then it will just be 0) holeDistOffset = Random.Range(-holeDistRand, holeDistRand); // have each agent do several swings and average the fitnesses of those swings // clear out fitnesses array fitnesses = new float[numAgents]; for (int swingNum = 0; swingNum < NUM_SWINGS_PER_GEN; swingNum++) { // Instantiate new agents, give them their respective chromosomes, tell them to start swinging their clubs for (int j = 0; j < agents.Length; j++) { agents[j] = Instantiate(agentPrefab, new Vector3(PARALLEL_POSITION_OFFSET, 0, -j * DIST_BETWEEN_AGENTS), Quaternion.identity); agents[j].GetComponent <GolferBrain2>().InitializeAgent(chroms[j], settings, holeDistOffset); agents[j].GetComponent <GolferBrain2>().BeginSwinging(); } // Allow this generation to run for the specified time. // Execution of this code pauses here until time is up, then automatically resumes. yield return(new WaitForSeconds(timePerGen)); // Get the fitnesses of the agents and then clear them out for (int j = 0; j < agents.Length; j++) { fitnesses[j] += agents[j].GetComponent <GolferBrain2>().GetFitness(); Destroy(agents[j]); } } // complete the calculation of the average fitnesses over those three swings for the gen for (int j = 0; j < fitnesses.Length; j++) { fitnesses[j] = fitnesses[j] / NUM_SWINGS_PER_GEN; } // Track fitness // Initialize best chrom & fit as the first one if (i == 0) { bestChrom = chroms[0]; bestFitness = fitnesses[0]; } // Add fitnesses to 2d fitnessTrack array // Assign new best chrom if there is one for (int j = 0; j < agents.Length; j++) { fitnessTrack[i, j] = fitnesses[j]; if (fitnessTrack[i, j] > bestFitness) { bestFitness = fitnessTrack[i, j]; bestChrom = chroms[j]; } } // create new chromosome array Chromosome2[] newChroms = new Chromosome2[chroms.Length]; /* find most fit individuals * by sorting the fitnesses array in descending order, * then taking the first however many biggest fitnesses * and putting their respective chromosomes in the new chromosome array */ float[] sortedFitnesses = fitnesses; Array.Sort(sortedFitnesses); Array.Reverse(sortedFitnesses); for (int j = 0; j < numElites; j++) { try { newChroms[j] = chroms[Array.FindIndex(fitnesses, x => x == sortedFitnesses[j])]; } catch { Debug.LogWarning("something weird happened"); } } // Andrew DeBiase // Crossover selection for (int pair = numElites; pair < chroms.Length; pair += 2) { //Tournament selection with 2 candidates for each parent // first parent: int cand1idx = Random.Range(0, numAgents); int cand2idx = Random.Range(0, numAgents); while (cand1idx == cand2idx) { cand2idx = Random.Range(0, numAgents); } int par1idx = (fitnesses[cand1idx] > fitnesses[cand2idx] ? cand1idx : cand2idx); // second parent cand1idx = Random.Range(0, numAgents); cand2idx = Random.Range(0, numAgents); while (cand1idx == cand2idx) { cand2idx = Random.Range(0, numAgents); } int par2idx = (fitnesses[cand1idx] > fitnesses[cand2idx] ? cand1idx : cand2idx); // determine if we crossover float crossValue = Random.Range(0.0f, 1.0f); if (crossValue < crossoverProb) { // crossover Tuple <Chromosome2, Chromosome2> xresult = Crossover(chroms[par1idx], chroms[par2idx]); newChroms[pair] = xresult.Item1; newChroms[pair + 1] = xresult.Item2; } else { // don't crossover, just send the parents on to the new array newChroms[pair] = chroms[par1idx]; newChroms[pair + 1] = chroms[par2idx]; } } //Vladislav Dozorov //Handle when mutation occurs for (int candmidx = numElites; candmidx < newChroms.Length; candmidx++) { float mutationValue = Random.Range(0.0f, 1.0f); if (mutationValue <= mutationProb) { newChroms[candmidx] = Mutate(newChroms[candmidx]); } } } completionText.SetActive(true); // ExportCSV() GetComponent <Exporter>().FinishedSimulation(); yield break; }