- Sacramento - California State University
Transcription
- Sacramento - California State University
GENETIC QWOP EVOLVING A BIPEDAL GAIT IN A TWO-DIMENSIONAL WORLD A Project Presented to the faculty of the Department of Computer Science California State University, Sacramento Submitted in partial satisfaction of the requirements for the degree of MASTER OF SCIENCE in Computer Science by Steven Ray FALL 2013 GENETIC QWOP EVOLVING A BIPEDAL GAIT IN A TWO-DIMENSIONAL WORLD A Project by Steven Ray Approved by: __________________________________, Committee Chair V. Scott Gordon, Ph.D. __________________________________, Second Advisor Du Zhang, Ph.D. ____________________________ Date ii Student: Steven Ray I certify that this student has met the requirements for format contained in the University format manual, and that this project is suitable for shelving in the Library and credit is to be awarded for the project. __________________________, Graduate Coordinator Nikrouz Faroughi, Ph.D. Department of Computer Science iii ___________________ Date Abstract of GENETIC QWOP EVOLVING A BIPEDAL GAIT IN A TWO-DIMENSIONAL WORLD by Steven Ray QWOP is a popular Flash game developed by Bennet Foddy in which the user takes control of an Olympic sprinter running a 100-meter dash. Designed to force the user essentially to re-learn to walk, QWOP has gained notoriety for its difficulty due to an unintuitive control scheme and its ragdoll physics system. This master project seeks to solve QWOP using a genetic algorithm (GA), a search heuristic inspired by biological evolution through natural selection. The GA implemented here aims to solve QWOP by evolving sequences of inputs that, when looped, play the game. This particular implementation, lacking any dynamic sensory feedback with which to adjust the runner’s movements, will essentially learn to solve QWOP blind. In this way, the project may demonstrate a proof of concept for learning a robotic gait in a complex environment with very limited inputs and feedbacks. A Java-based QWOP controller program, developed by Laurent Vaucher, is used to play QWOP using input sequences produced by several different GA implementations. Stable gaits are achieved that consistently converge towards speeds comparable to those of human players. _______________________, Committee Chair V. Scott Gordon, Ph.D. _______________________ Date iv ACKNOWLEDGEMENTS I would like to thank everyone who helped me to finish this project. I would like to thank Dr. V. Scott Gordon, my project advisor, for taking active interest in this experiment. Dr. Gordon’s encouragement and guidance not only persuaded me to extend this project from a semester class project into a master’s project, but also helped me to complete the project. I would also like to thank Dr. Du Zhang for being my project’s second reader. I would like to thank Dr. Nikrouz Faroughi, Graduate Coordinator of the Computer Science Department at Sacramento State, for his support. I would also like to thank Laurent Vaucher for developing and releasing the Qwopper software that made this project possible. I would like to thank my friends and family for their encouragement. Lastly, I would like to thank my wife, Sally, for her unwavering love, support and inspiration throughout this endeavor. v TABLE OF CONTENTS Page Acknowledgements ................................................................................................................... v List of Tables ........................................................................................................................ viii List of Figures .......................................................................................................................... ix Chapter 1. INTRODUCTION .............................................................................................................. 1 1.1 QWOP.................................................................................................................... 1 1.2 Controls ................................................................................................................. 1 1.3 Ragdoll Physics...................................................................................................... 2 1.4 Project Motivation ................................................................................................. 2 2. BACKGROUND ................................................................................................................ 4 2.1 QWOP Research .................................................................................................... 4 2.2 Genetic Algorithms ................................................................................................ 5 2.3 Steady-State Genetic Algorithm ............................................................................ 5 2.4 Cellular Genetic Algorithm.................................................................................... 6 2.5 Evolving Gaits ....................................................................................................... 6 3. SYSTEM ORGANIZATION ............................................................................................. 8 3.1 The QWOP Controller ........................................................................................... 8 3.2 Selection Strategy ................................................................................................ 10 3.3 Evolutionary Model ............................................................................................. 10 3.4 Fitness Function ................................................................................................... 12 3.5 Genetic Encoding ................................................................................................. 12 vi 3.6 Crossover ............................................................................................................. 15 3.7 Mutation ............................................................................................................... 16 3.8 Initial Population Generation ............................................................................... 16 3.9 Measuring Success ............................................................................................... 17 4. RESULTS ......................................................................................................................... 19 4.1 Quantitative Results ............................................................................................. 19 4.2 Genotypic Analysis .............................................................................................. 24 5. DISCUSSION ................................................................................................................... 28 5.1 Implications ......................................................................................................... 28 5.2 On Premature Convergence ................................................................................. 29 6. CONCLUSION AND FUTURE WORK ......................................................................... 30 6.1 Conclusion ........................................................................................................... 30 6.2 Future Work ......................................................................................................... 30 Appendix A. Source Code ...................................................................................................... 32 Appendix B. Sample Evolution Log ....................................................................................... 43 Appendix C. Sample Generation Summary Log .................................................................... 50 Bibliography ........................................................................................................................... 56 vii LIST OF TABLES Tables Page Table 1 – Encoding 2 Alphabet With Input States................. .………………………………. 14 viii LIST OF FIGURES Figures Page Figure 1 – Qwopper system diagram ....................................... .………………………………. 9 Figure 2 - Generational Model Pseudo Code ......................... .………………………………. 10 Figure 3 – Cellular Model Pseudo Code ..................................... ……………………………. 11 Figure 4 – Example Runner Using Encoding 1 .......................... ……………………………. 13 Figure 5 – Example Runner Using Encoding 2 .............................. …………………………. 15 Figure 6 - Single-point “Cut-and-splice” Crossover ....................... …………………………. 15 Figure 7 - Two-point Crossover ...................................................... …………………………. 16 Figure 8 – Genetic Implementation and Qwopper system diagram …………………………. 18 Figure 9 – Config. 1 Average Fitness (9 Generations) ................... …………………………. 20 Figure 10 – Config. 2 Average Fitness (30 Generations) ............... …………………………. 21 Figure 11 – Config. 2 vs. Config. 3, Average Fitness (30 Generations)………...……………22 Figure 12 – Config. 3 Fit vs. Unfit Initial Population ..................... …………………………. 24 Figure 13 – Opening Move Genes ‘QO’ and ‘WP’ ........................ …………………………. 26 Figure 14 – The ‘WO’ Opening Stride and Resulting Stance……….………………………. 26 Figure 15 – Gait involving hopping on one knee while kicking with the free leg………..…. 27 ix 1 Chapter 1 INTRODUCTION 1.1 QWOP QWOP is a popular computer game developed in Adobe Flash by Bennett Foddy, and is available for free on his site Foddy.net [1]. In QWOP, the player takes control of an Olympic sprinter running a 100-meter race. Upon reaching the 100-meter mark, the player wins the game. If the runner's head or one of his hands touches the ground at any point along the way, QWOP considers him fallen, and the game is over. The game quickly became notorious for its difficulty, despite what seems like a simple task. Playing QWOP well has been described as "a ballet of tiny corrections, any of which is likely to throw off the player’s timing and may result in a fatal error." [2] QWOP's difficulty is largely a function of two factors: the control scheme and the physics engine. 1.2 Controls QWOP gets its name from the game's control scheme. To play a game of QWOP, the user controls the runner using only the Q, W, O, and P keys on the keyboard, each of which controls a specific muscle group in the runner’s legs. Q and W move forward the runner’s left and right thighs respectively. The O and P keys each map to his left and right calves. Achieving a realistic bipedal gait in QWOP is often soon abandoned by new players, who tend to resort either to finding a repeatable pattern of inputs sufficient enough to scoot the sprinter to the finish line or to giving up in frustration, amusement, or both. The primary factor contributing to QWOP's difficulty is its unintuitive control scheme. Bennet Foddy designed QWOP to force users to work out mechanically a task that most of us perform every day and to which we do not devote much active thought. Users are precluded from 2 relying on their own experience and knowledge of balancing and walking on two legs, because QWOP reroutes the motor skill of synchronized leg muscle manipulation to the user's fingers via the game controls. Players are also limited with regard to the amount and type of relevant sensory feedback available to tell how well they are doing and make necessary adjustments to their runner's gait to avoid falling, as the only relevant and available sensory feedback is the visual state of the runner. 1.3 Ragdoll Physics The second factor that most complicates QWOP is its ragdoll physics engine. Ragdoll physics is a method for procedurally animating characters based on a skeletal configuration of rigid bodies connected by joints and muscles in a simulated physical environment [3]. The physics system is an approximation of the real world that creates a simplified but reasonably realistic experience for the QWOP runner’s body with regard to physical laws like gravity, friction, and inertia. The player must constantly work not only to complete the race, but also to keep the runner from falling over with each step. Every slight movement of the runner's legs carries with it consequences of momentum and velocity compounded by the game world's gravity and friction. Combined with the unintuitive control scheme, achieving QWOP's seemingly simple goal becomes extremely difficult, as the game is very unforgiving to imprecise and poorly timed movements. 1.4 Project Motivation QWOP is a simple game without an obvious solution. For a game with only four inputs, improving the runner's performance has proven to be a difficult, unintuitive task for humans. The goal of solving a game like QWOP seems well suited to a machine learning algorithm. This 3 project seeks to achieve this goal by optimizing QWOP input loops using a genetic algorithm. Due to the combination of its high difficulty and relatively small search space, QWOP seems well suited to a genetic approach. The significance of solving QWOP genetically using only variations and recombinations of input sequences would demonstrate a proof of concept for robotic bipedal gait learning in a complex space using very few inputs and feedbacks. A secondary goal of this project is to learn how different types of genetic learning impact the algorithm's overall performance. 4 Chapter 2 BACKGROUND 2.1 QWOP Research Interestingly, research into leveraging machine learning algorithms to solve QWOP is not without precedent. In 2012, Gustav Brodman and Ryan Volstad of Stanford used reinforcement learning to achieve bipedal gaits in a stick-figure simulation of QWOP. Their program took a multi-dimensional feedback approach that moved the runner and adjusted his various limbs based on their horizontal, vertical and angular velocities, the body's calculated center of mass, and whether or not at least one foot was touching the ground. Their learning approach was able to achieve a stable "shuffle-like” gait and a less stable but faster gait, depending on the reinforcement learning model used [4]. In March of 2011, a French programmer named Laurent Vaucher attempted to solve QWOP genetically by building a Java-based controller program that could play QWOP through a web browser. Unlike the work of Brodman and Volstad, which used a model of QWOP, Vaucher's program attempted to play QWOP itself. Vaucher's "Qwopper" program was able to interact with the desktop to locate the game display in an open browser window, could read the game's display to parse key information like the score and whether or not the runner had fallen, and was able to send mouse commands to QWOP's on-screen interface to control the game. Vaucher had planned to solve QWOP genetically using his controller, and he made it as far as developing a genetic encoding for the input patterns with which he generated and tested random runners. Before moving on to other projects, Vaucher posted his progress to his blog, slowfrog.blogspot.com, and released his source code to anyone interested in the problem [5]. 5 This project picks up Vaucher's baton and utilizes his QWOP controller to implement a genetic learning algorithm that will converge toward an optimal solution by evolving populations of looped input sequences. 2.2 Genetic Algorithms A genetic algorithm (GA) is a type of search algorithm inspired by the biological processes of evolution through natural selection. GAs are used primarily in search and optimization problems, and work by evolving populations of candidate solutions toward better solutions. The solution space is encoded into some form of genetic representation, which may vary from an array of bits to a set of properties. A population of genetically encoded candidate solutions is initialized. Each member of the population is then evaluated by a fitness function which selects for the best solutions in the population. The fittest candidates in a given generation are selected to "mate" with each other to produce child solutions comprised of parts of each parent through some form of crossover process. The children are then typically mutated before being added to the next generation. The GA iterates through generations in this manner until it reaches some terminating condition, such as a sufficiently optimized solution or a fixed number of generations. 2.3 Steady-State Genetic Algorithm Steady-state GAs are a variation of the algorithm which do not follow a generational model. Instead of populating generation N+1 with the children of selected individuals from generation N, children in the steady-state variant are evaluated against their parents. The best two individuals from the parents and children are inserted back into the population. The population size remains constant, and children that are worse than their parents are discarded to guarantee 6 that the average fitness of the population does not decline. The steady-state model has been shown to yield better performance than the generational model in some problem spaces, and has been shown to benefit from parallelization [6]. 2.4 Cellular Genetic Algorithm Cellular GAs disallow individuals from mating arbitrarily by limiting mating possibilities only to nearby individuals in a structured population. The population exists in a multidimensional grid or graph structure in which each individual is connected to other nearby individuals. In the Cellular GA model, each individual in a population selects a mate from those in its local proximity. This mechanic simulates isolation by distance within the population, and results in the creation of niches of subpopulations that promote overall diversity and allow for broader exploration of the solution space by helping to prevent premature convergence [7]. Like the steady-state model, the cellular model’s performance also benefits from parallelization. 2.5 Evolving Gaits Considerable research has been done with GAs in the area of robotic gait learning. GAs have been explored for learning and optimizing gaits in hexapod [8], quadruped [9], and biped robots [10]. Biped robotic gait learning offers a slightly different problem domain from learning for robots with more than two legs, as walking with only two legs necessarily involves lengths of time during which the robot is supported entirely by only one leg. In this way, bipedal gaits require greater balance than gaits in which multiple legs are supporting the robot at any given moment. The vast majority of research into evolving bipedal gaits uses either physical or simulated robots with relatively high degrees of 7 freedom compared to QWOP’s runner, which has only four degrees of freedom. Evolutionarily learning a bipedal gait in QWOP could demonstrate a proof of concept for learning and optimizing a bipedal gait despite great limitation with regard to the robot’s degrees of freedom and sensory feedback. 8 Chapter 3 SYSTEM ORGANIZATION 3.1 The QWOP Controller Laurent Vaucher's QWOP controller program, named Qwopper, uses the java.awt.Robot class to interact with the screen and play QWOP. It captures screen images and compares them with screenshots and known patterns and colors found in the QWOP interface to locate the game window. Once the game window is located, Qwopper will give it focus by sending a mouse event to the game. Once the game window has focus, QWOP is played entirely using keyboard commands sent by Qwopper. Qwopper monitors the current game score, consisting of the current distance traveled by the runner, by periodically capturing a rectangular image of the location on the game screen where the score is displayed. Qwopper then applies a thresholding function that converts the image to black and white, tokenizes each character in the image, and then compares against a set of reference images of each character to parse the score for each runner. If the runner falls over, Qwopper sends a spacebar command to restart the game. 9 Figure 1 – Qwopper system diagram The genetic implementation uses Voucher's Qwopper software to evaluate the fitness of each runner in a population by playing QWOP according to each runner's genetic sequence, which is looped until the runner crashes, reaches the time limit, or beats the game. Generations of runners evaluated by Qwopper are evolved using a genetic algorithm and logged for analysis. 10 3.2 Selection Strategy The selection strategies examined for this project were 3:2 tournament selection and local selection. 3:2 tournament selection randomly chooses three runners from a population and selects the two with the highest fitness scores to become parents. Local selection restricts the pool of potential mates of a given individual to the individual’s neighbors in the population, and mates the individual with the neighbor with the highest fitness score. 3.3 Evolutionary Model The project examined two different implementations of the evolutionary model: a generational model and a cellular model. The generational model used is described by the following pseudo code: P = generate a population of runners while termination criteria not met { nextGen = new empty population while nextGen is not full { select three random runners from P parent1, parent2 = the two fittest runners of those selected child1, child2 = crossover(parent1, parent2) add child1 and child2 to nextGen } P = nextGen } Figure 2 - Generational Model Pseudo Code 11 The cellular GA implementation used a local selection strategy that restricted the pool of potential mates for a given runner to those nearest to it in the population. Local selection was realized using a two-dimensional borderless “wrap-around” grid structure so that each index would have the same number of neighbors, as well as to avoid introducing an arbitrary boundary around the population. Local selection was introduced to prevent, or at least slow considerably, premature convergence on a suboptimal solution due to loss of genetic diversity in the population. Population size was also increased for this configuration to help prevent premature convergence. The cellular configuration also borrowed from steady-state GAs in how child runners were allowed to advance to the next generations. Children were only allowed to advance to the next generation if they performed better than their parents. This both guaranteed that average population fitness could only increase, and also introduced elitism, whereby good solutions that outperformed their children would themselves survive to the next generation. The pseudo code for the cellular implementation is a follows: P = generate a population of runners in a 2D borderless grid while termination criteria not met { nextGen = new empty borderless grid for each runner in P { parent1 = the current runner parent2 = the fittest neighbor to parent1 in P child1, child2 = crossover(parent1, parent2) best = the fittest runner among parent1, child1, and child2 add best to nextGen at parent1’s index } P = nextGen } Figure 3 – Cellular Model Pseudo Code 12 In the cellular configuration, each runner is allowed to mate with its fittest neighbor. The cellular configuration is synchronous, meaning that the algorithm proceeds from the top left individual through each row until every individual has mated. A temporary population is used to store the best runners produced for each index. After the entire population has mated, the current generation is replaced with the new temporary population. 3.4 Fitness Function The fitness function is informed by two parameters, both of which are collected by Qwopper. The first parameter is the final state of the runner when a run ends, of which there are two: stopped and crashed. If the runner falls over, Qwopper recognizes the game over screen and considers the runner crashed. If the run ends for any other reason, either because a time limit has been reached or the race was completed, the final state is "stopped." The second parameter is the final score achieved when the runner reaches its final state. The duration of each run is also recorded. Fitness functions favoring stability, average speed, max distance traveled, and combinations thereof were each explored through experimentation. 3.5 Genetic Encoding Two different encodings were explored. The first encoding was developed by Vaucher, and was the encoding that Qwopper first used to create and play random runners. A second encoding was developed for this project that addressed some issues discovered when populations created in the original encoding were subjected to crossover and mutation functions during evolution. Vaucher’s encoding ( Encoding 1 ) represented a sequence of QWOP inputs as a string of characters. Each character represents either a key press, a key release, or a delay. An example 13 individual could look like the following: QO+qPW+wpo+QPW+wO+qp+P+Q+++qp+QPW+wo+qp+POQ+q+W+Qp+qwo Figure 4 – Example Runner Using Encoding 1 A capital letter represents pressing that key on the keyboard, a lowercase letter represents a key release, and the ‘+’ represents a delay in which the current state of inputs is maintained for a set length of time. The individual in the above figure translates to “Press Q and O, hold them for 150ms, release Q, press P and W, hold for 150ms, release W, P and O, wait…” and so on. One beneficial feature of Encoding 1 is that it is very easy to read and understand the sequence of inputs represented by the solution candidate. Encoding 1 proved problematic when crossover and mutation were introduced, because the control state at any given point on an individual using this encoding is highly dependent on its context within the sequence. Crossover and mutation operations on individuals defined with Encoding 1 resulted in producing “non-coding DNA” or redundant commands like consecutive capitals or lower-case letters. Encoding 1 was designed such that each letter indicates a change in the current input state. Each crossover and mutation operation had a high probability of rendering one or more characters in the chromosome redundant, which often dramatically altered the input sequence. The second genetic encoding explored ( Encoding 2 ) encodes input sequences using a 16-character alphabet, each letter of which represents one of the possible input combinations in QWOP. 14 Table 1 – Encoding 2 Alphabet With Input States Alphabet Q W O P P D C J B I H N A G F M E L K O 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 QWOP uses only four buttons, so there are 16 possible input combinations that comprise QWOP’s input space. Each letter in Encoding 2 maps to one of these 16 input combinations. Unlike Encoding 1, input sequences built with this alphabet assume a small delay between each letter’s execution, since each letter defines a distinct input state for all four control keys. Encoding 2 also did not suffer from the context-dependence of letters in Encoding 1, since each letter here represents a distinct input state for all four keys. As seen in Figure # above, a key value of 0 indicates a key release command, and the key value 1 indicates a command to press the key. Redundant sequential press commands for a given key will simply continue to hold the pressed key continuously, while redundant release commands will just continue to refrain from pressing that key. An example individual defined using Encoding 2 follows: 15 FGBCHFELMIEFNGJCLHLEMCLJKJLNEKGHDGJDAJLE Figure 5 – Example Runner Using Encoding 2 Each letter in the sequence is separated by an implied wait period of 150ms, like Encoding 1, during which the current input state is maintained, meaning that any keys pressed according to the current letter are held until a letter is encountered that releases that key. This individual translates to “press Q and O, hold for 150ms, press P and release O while continuing to hold Q, wait for 150ms, release Q and O and press W, hold for 150ms, release W and press O, wait…” and so on. 3.6 Crossover This project experimented with two crossover strategies: single-point “cut-and-splice” crossover, and standard two-point crossover. Figure 6 - Single-point “Cut-and-splice” Crossover The “cut-and-splice” strategy selects a different crossover point for each parent, allowing for varying chromosomal lengths to be produced in offspring. The motivation for this approach was that the appropriate length of a good input loop in QWOP is not at all obvious, and this strategy increases the search space of the algorithm with regard to the length of looped input sequences. 16 Figure 7 - Two-point Crossover A two-point crossover strategy creates two crossover points for each parent at the same location on both parent chromosomes, thus maintaining chromosomal length in children and throughout all generations. The average fitness of populations evolved using this and the “cutand-splice” approach were compared to determine whether the length of the sequence was a relevant characteristic. 3.7 Mutation The mutation mechanic used throughout the project for both genetic encodings was to randomly select and alter a single character in every child runner. 3.8 Initial Population Generation A population was initialized in one of two ways. A collection of hundreds of random runners were generated with each of the two encodings and fitness-tested. Each random runner played QWOP for 60 seconds or until it fell down. The first initialization strategy seeded Generation 0 with the best performing randomly generated runners. The second initialization strategy populated Generation 0 with untested random runners. The generational implementation used a population size of 16 individuals, while the hybrid model increased the population size to 17 30 to promote increased genetic diversity and help avoid premature convergence. 3.9 Measuring Success This project seeks to evolve a stable bipedal gait with a maximum average speed using very limited sensory input. Output from variations on the configuration of the GA will be compared as the GA is tuned to improve its performance. The fastest runners, measured by meters traveled per minute, will then be compared against the best QWOP scores achieved by humans. 18 Figure 8 – Genetic Implementation and Qwopper system diagram 19 Chapter 4 RESULTS 4.1 Quantitative Results Three configurations of the GA parameters described above were tested. The first configuration used a generational model, Encoding 1, 3:2 tournament selection, single-point cutand-splice crossover, and a population consisting of the 16 best-performing runners from a pool of 390 randomly generated individuals. As seen in Figure 9, this configuration failed to evolve solutions that played QWOP any better than the best randomly generated ones. This lack of improvement was attributed to a consistently observed high rate of failure in children ( >50% ). Because this GA configuration used a naïve succession policy wherein every child produced was guaranteed to succeed to the next generation, this high failure rate precluded the gene pool from improving with any significance. 20 Configuration 1 - Average Generational Fitness (9 Generations) 30 25 20 Speed 15 (m/min) 10 5 0 1 2 3 4 5 6 7 8 9 Generation Figure 9 – Config. 1 Average Fitness (9 Generations) The second configuration was identical to the first, except that it used a succession policy similar to that of the steady-state model. In this configuration, children only succeeded to the next generation if they outperformed the worst of the three potential parents selected. This single parameter tweak resulted in a GA that produced positive results, as seen in Figure 10. The steady-state succession policy conferred a significant performance improvement over the first configuration’s naïve policy that did not test and weed out children that performed worse than their parents, as both of these configurations were run using the same initial population. Though the second configuration was successful at evolving better runners, average generational fitness did not always improve, as seen in Figure 10. This generational variance was again attributed to the succession policy. Selecting children that performed better than only the potential parent that was not selected to mate left some room for the possibility of generational 21 fitness decline because children could still advance if they performed worse than their parents but better than the weakest potential parent chosen by 3:2 tournament selection. Configuration 2 - Average Generational Fitness (30 Generations) 30 25 20 Speed 15 (m/min) 10 5 0 1 5 10 15 20 25 30 Generation Figure 10 – Config. 2 Average Fitness (30 Generations) The configuration that eventually yielded the most stable and consistently efficient gaits is featured in Figure 11 compared with the Configuration 2 data series from Figure 10. Configuration 3 used the hybrid generational/steady-state grid model, the new 16-letter encoding, local selection, two-point crossover, and an increased population of 30 individuals. This population was also initialized with the best randomly generated runners from a pool of 500 built using Encoding 2. Compared with previous configurations, the fitness curve was smoother, generational fitness never declined, solutions improved more quickly through the early generations, and the configuration converged toward better solutions in fewer generations than other successful configurations. 22 Configuration 3 vs. Configuration 2 (30 Generations) 30 Config 2 Config 3 25 20 Speed 15 (m/min) 10 5 0 1 5 10 15 20 25 30 Generation Figure 11 – Config. 2 vs. Config. 3, Average Fitness (30 Generations) Two pools of random runners were generated. The first pool of random runners was produced by Laurent Vaucher using his original genetic representation of the solution space, Encoding 1. The second pool was generated using Encoding 2, the 16-character encoding developed during this project. All three GA configurations were tested using initial populations consisting of the best-performing candidates taken from these pools of randomly generated runners. Encoding 2 produced, on average, better runners than did Encoding 1. For both genetic encodings, though, the vast majority of randomly generated solutions crashed at or very near the starting line. The top 30 random runners filtered from a pool of 500 using Encoding 2 traveled, on average, over twice as fast as the best 16 runners from a pool of 390 using the Encoding 1. The average fitness of the 30 fastest random runners from Encoding 2 was 5.767 meters per minute, while Encoding 1 averaged 2.033 meters per minute in its 16 fastest random runners. These 23 results constitute a 180% increase in the average fitness of Encoding 2’s best random runners compared to the best random runners from Encoding 1. To control for this performance gap between the best random runners generated by the two different encodings, Configuration 3 was further tested using a different initial population of random runners, of which each runner had crashed outright when first tested by Qwopper. Despite a low average fitness of this initial population at 1.3 meters per minute, the average generational fitness accelerated just as quickly in early generations as did the population seeded with high quality random candidates. Regardless of the fitness of the initial population, Configuration 3 consistently performed better than Configuration 2, suggesting that the other GA parameters were largely responsible for the algorithm’s accelerated evolution toward faster runners compared to the previous configurations. 24 Configuration 3 - Fit vs. Unfit Initial Population Fit Unfit 30 25 20 Avg. Speed 15 (m/min) 10 5 0 0 10 20 30 40 50 60 70 80 Generation Figure 12 – Config. 3 Fit vs. Unfit Initial Population 4.2 Genotypic Analysis The combination of the physics engine and restrictive control scheme make falling over in QWOP very easy, both for humans and, as it turned out, also for the GA implementations described in this paper. Significant selective pressure was placed on finding a stable gait. The fitness function also selected for speed, which necessarily, only ever evolved after stability. Compounding this selective pressure was the fact that some amount of effective randomness exists when the Qwopper program plays QWOP. Because of the ragdoll physics system, when the game starts, the runner is first initialized to a standing state, and then the physics rules are immediately applied, causing his body to “settle in” to the gravity. To illustrate the runner’s instability due to the physics calculations, one can simply start a game and not press any buttons, then watch as the runner eventually begins to wobble and fall forward as the physics calculations appear to create a positive feedback loop that gradually increases the wobble. This introduces 25 some amount of effectively random variability in the performance of any given solution sequence played by the Qwopper program. A single sequence of inputs, run multiple times, will rarely result in the same score. It was often observed in early generations that the same runner could score well in one trial and crash in the next. This is evident in the run logs, as many populations were initialized to the same generation of runners, and in each trial these runners achieved different scores. This inconsistency only increased selective pressure on producing a stable gait, as the best surviving gaits were necessarily those that crashed the least despite this variability. The primary trait that emerged in populations was the stability “gene,” usually expressed as an opening sequence that resulted in a stable stance. There were two button combinations that almost always came to dominate the population. The stable openings were to simultaneously press Q and O or to simultaneously press W and P. The ‘QO’ combination moves the left thigh backward and extends the right calf forward, causing the runner to step forward and drop to his left knee with the right leg extended in front. The ‘WP’ opening performs the inverse operation of bending the right leg back and extending the left leg forward, which drops the runner to his right knee. In both openings, the runner lowers his center of gravity and spreads his legs wide, as if trying to do the splits. 26 ‘QO’ Opening ‘WP’ Opening Figure 13 – Opening Move Genes ‘QO’ and ‘WP’ The ‘QO’ and ‘WP’ openings quickly come to dominate populations, even when using a local selection policy in a large population to slow premature convergence. Notably, the opening consistently used by the fastest human QWOP scores documented is ‘WO’, which causes the runner to push off with the back foot and raise the knee of the other leg. The GA always evolves away from this opening because following it with a stable gait is likely impossible without the visual sensory feedback that the fastest human players enjoy. Figure 14 – The ‘WO’ Opening Stride and Resulting Stance 27 The ‘WO’ opening sequence seems to be the most efficient, as it consistently appears in all of the fastest human “speed-runs” on record. However, this opening does not lead to a stance stable enough to transition out of well. The GA implementation, as a blind input loop, is inherently unable to react to visual feedback to adjust its gait during a run like a human player can, so the ‘WO’ gene is quickly bred out of the population in favor of solutions with more stable openings. Figure 15 – Gait involving hopping on one knee while kicking with the free leg The best-evolved solutions transitioned from one of the two stable openings into a gait that scooted forward while largely maintaining the runner’s stable stance. Later generations gave way to the transition from scooting forward to more of a hopping gait. In these gaits, the runner would typically hop on one knee while kicking his free leg out in front of him to increase forward momentum. Even in the hopping gait, the legs remained spread apart, maintaining stability. 28 Chapter 5 DISCUSSION 5.1 Implications This experiment confirms previous research that has shown that the steady-state evolutionary model outperforms generational models in optimization applications such as this one, where randomly generated strings are usually very weak, and where a persistent population of difficult-to-generate above-average strings is needed in order to make progress. This is intuitive considering the high failure rate of children observed throughout this experiment. Maintaining successful individuals and only replacing the weak ones with children of higher quality both helped Configuration 2 to outperform Configuration 1 and helped Configuration 3 to outperform all other configurations. Interestingly, the best solutions found by the GA bear a striking resemblance to the way many people actually play the game. Most people who play QWOP do not post speed-run videos on YouTube and compete for the world record, which is currently held by Roshan Ramachandra at 51 seconds [11]. Finding a repeatable pattern and scooting to the finish line is a commonlyrecommended strategy in online discussions about how to beat QWOP, which is the same type of solution that the GA consistently achieves. Considering QWOP’s volatile ragdoll physics system, its limited control scheme, and the fact that the GA used no mechanism for direct sensory feedback with which to respond and make adjustments, the gaits achieved by the GA implementations described in this paper serve as a proof of concept for robotic gait learning in a complex space with minimal inputs and feedbacks. 29 5.2 On Premature Convergence Through repeated trials we observed that certain traits, particularly traits that conferred some degree of greater gait stability, consistently emerged and often eventually came to dominate the population. This observation held true even in a Cellular GA implementation which used a local selection policy in conjunction with a larger population size. If an argument can be made that the GA prematurely converged toward only solutions with the most stable opening moves, it is likely a consequence of one of the experiment’s stated goals: to learn to play QWOP without the ability to react to dynamic sensory feedback. This goal inherently limited the solution space that the GA could feasibly search. 30 Chapter 6 CONCLUSION AND FUTURE WORK 6.1 Conclusion This project was undertaken with the hypothesis that QWOP could be solved using a genetic algorithm. This hypothesis is certainly validated by the data, though there is reason to expect that improvements could be made to evolve better solutions more efficiently than have been produced so far. Nevertheless, the results, at the very least, demonstrate a proof of concept for evolving bipedal robotic gaits with very limited inputs and feedbacks in a complex environment. 6.2 Future Work More research could be done that may yield better solutions to QWOP. For instance, long sequences of inputs that are not looped, rather than sequences of looped inputs, could support the evolution of a stable opening move while also enabling the evolution of a gait that, unlike looped sequences, does not necessarily include the opening sequence. Another possible approach would be to evolve both an opening sequence and a main input loop as distinct entities, since it is entirely possible that an optimal input loop may not contain the same sequence that starts the runner from his initial standing position, which is only needed once at the beginning of the race. This project experimented with and compared many different flavors of the genetic algorithm and its components, some of which were demonstrably better suited to solving this particular problem than others. This experiment found the greatest success with a Cellular GA implementation. There is room for further exploration with regard to solving QWOP using other variations of the genetic algorithm that were not explored here. 31 One factor that greatly limited the amount of experimentation we were able to achieve is that solving QWOP requires the GA to evolve populations of solutions that must each play QWOP in real time at least once. The Qwopper program developed by Laurent Vaucher is a bot program that takes over the computer it is run on to play QWOP in an open browser window. A typical population of 30 solution candidates evolved for 30 generations often took over 20 hours of uninterrupted execution for this project. We have described a highly parallelizable genetic implementation, and other well-known parallel implementations exist, but we were not able to leverage this capability on multiple instances due to limited time and resources. This is an area with the potential to greatly reduce the learning time required to converge on an optimal solution. Learning time could very likely be reduced by a factor equal to the number of machines used to process a population in parallel, as long as the number of machines did not exceed the size of the population. 32 APPENDIX A. SOURCE CODE package com.slowfrog.qwop; import java.awt.Robot; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; /** * * @author Laurent Vaucher * Extended by Steven Ray * * Running this class will begin executing the * genetic algorithm on the population. * * For the program to inteface with QWOP and execute * its instructions, the game must fully visible in * your web browser on your monitor * * The results of the execution will be saved to an * output file titled "evoOut.txt." * */ public class Genetic { /** * main method * @param args */ public static void main(String[] args) { Genetic g = new Genetic(); } private Map<String, Individual> population; private WrapGrid<Individual> indivs, nextGen; //private static final String NOTES = "QWOPqwop++"; //Encoding 1 private static final String NOTES = "ABCDEFGHIJKLMNOP"; //Encoding 2 private List<Individual> good; private static final Log LOG = new ConsoleLog(); private float fitSum = 0; private int size; private int RUNTIMELIMIT = 60000; //60 seconds private int runsPerIndiv = 1; private Robot rob; private Qwopper qwop; private PrintStream evoOut, genOut; //evolution log and generation log streams private float genAvgFitness; private boolean crashed; private boolean randomGen0 = true; //set to true is using a random gen stored in runs3.txt 33 public Genetic() { this.population = new HashMap<String, Individual>(); if(randomGen0) { this.readPopulation("runs3.txt"); //file containing the bad generation 0 } else { this.readPopulation("runs2.txt"); } System.out .println("Population: " + this.population.size() + " individuals"); int totalRuns = 0; String logFileName = "evo_log_2013_26_1min_RND.txt"; String genLogFileName = "gen_log_2013_26_1min_RND.txt"; int maxGens = 100; float numRuns = 0; for (Individual indiv : this.population.values()) { totalRuns += indiv.runs.size(); } System.out.println("Total runs: " + totalRuns); if(!randomGen0) //execute this block for an alternate Gen0 { IFilter<RunInfo> twoMetersNotCrashed = new AndFilter<RunInfo>( new MinDistFilter(2), new NotFilter<RunInfo>(new CrashedFilter())); IFilter<Individual> individualFilter = new MinRatioFilter( twoMetersNotCrashed); good = this.filter(individualFilter); System.out.println("Good Runners: "); for(int i=0; i<good.size(); i++) { System.out.println(good.get(i).toString()); System.out.println(good.get(i).str); System.out.println(good.get(i).runs.get(0).distance); } } else { good = new ArrayList<Individual>(); //runs3 contains 30 random runners. if the flag is set, use this as gen0 for (Individual individual : this.population.values()) { good.add(individual); } } //init grid for a population of 30 individuals and 4 connections each indivs = new WrapGrid<Individual>(5, 6, 4); //store each individual's string and average distance traveled for manipulation for(int i=0; i<good.size();i++){ float sumFits = 0; for(int j=0; j<good.get(i).runs.size();j++){ sumFits += good.get(i).runs.get(j).distance; } numRuns = good.get(i).runs.size(); good.get(i).fitness = sumFits/numRuns; //add individual with calculated fitness to the grid 34 indivs.add(good.get(i)); } try { rob = new Robot(); qwop = new Qwopper(rob, LOG); qwop.findRealOrigin(); try { evoOut = new PrintStream(new FileOutputStream(logFileName, true)); genOut = new PrintStream(new FileOutputStream(genLogFileName, true)); size = good.size(); //list Gen 0 evoOut.println("########## Generation 0 ##########"); for(int i =0; i<size; i++){ evoOut.println(i + ") " + indivs.get(i).str + " avgFitness: " + indivs.get(i).fitness); } evoOut.println("###################################"); for(int genCnt = 1; genCnt<=maxGens; genCnt++) { evoOut.println("########## " + "Generation " + genCnt + " ##########"); genOut.println("########## " + "Generation " + genCnt + " ##########\n"); genAvgFitness = 0; for(int i=0; i<size; i++){ evoOut.println("---------- " + "Individual " + (i+1) + " ----------"); //test string specified number of times, average the distance //after generation 1, each individual gets tested during reproduction of the prev generation if(genCnt == 1) { testString(qwop, indivs.get(i).str, runsPerIndiv, logFileName); indivs.get(i).fitness = fitSum/runsPerIndiv; } genOut.println((i+1) + ") " + indivs.get(i).fitness + " | " + indivs.get(i).str); evoOut.println((i+1) + ") " + indivs.get(i).fitness + " | " + indivs.get(i).str); genAvgFitness += indivs.get(i).fitness; //sum up average generational fitness } //log this generation's average fitness LOG.log("Generation " + genCnt + " Average Fitness: " + genAvgFitness/size); evoOut.println("Generation " + genCnt + " Average Fitness: " + genAvgFitness/size); genOut.println("Generation " + genCnt + " Average Fitness: " + genAvgFitness/size + "\n"); //select and mate the parents, mutate the children, and generate the next generation nextGen = crossoverSteadyStateGrid(indivs); indivs = nextGen; }//end generation loop evoOut.flush(); evoOut.close(); } catch (FileNotFoundException e) { 35 e.printStackTrace(); } } catch (Throwable t) { LOG.log("Error", t); } } //copied from MAIN private void testString(Qwopper qwop, String str, int count, String filename) { fitSum = 0; for (int i = 0; i < count; ++i) { //LOG.logf("Run #%d\n", i); qwop.startGame(); RunInfo info = qwop.playOneGame(str, RUNTIMELIMIT); LOG.log(info.toString()); LOG.log(info.marshal()); evoOut.println(info.toString()); evoOut.println(info.marshal()); //time limit in ms //info.distance goes into sumfitness fitSum += info.distance; crashed = info.crashed; } } //tests a child runner a specified number of times and returns the average fitness private float testChild(Qwopper qwop, String str, int count) { float fitness = 0; evoOut.println("Testing child: " + str); LOG.log("Testing child: " + str); for (int i = 0; i < count; ++i) { //currently we only test each child once qwop.startGame(); RunInfo info = qwop.playOneGame(str, RUNTIMELIMIT); LOG.log(info.toString()); LOG.log(info.marshal()); evoOut.println(info.toString()); evoOut.println(info.marshal()); //info.distance goes into sumfitness fitness += info.distance; crashed = info.crashed; } return fitness/count; } private static void saveRunInfo(String filename, RunInfo info) { try { PrintStream out = new PrintStream(new FileOutputStream(filename, true)); try { out.println(info.marshal()); } finally { out.flush(); out.close(); } } catch (IOException ioe) { LOG.log("Error marshalling", ioe); } } 36 public List<Individual> filter(IFilter<Individual> filter) { List<Individual> ret = new ArrayList<Individual>(); for (Individual individual : this.population.values()) { if (filter.matches(individual)) { ret.add(individual); } } return ret; } //finds the fittest of the neighbors of the individual located at curPop[curRow][curCol] public Individual fittestNeighbor(int curRow, int curCol, WrapGrid<Individual> curPop) { Individual up, right, left, down, fittest; up right down left = curPop.get(curRow - 1 = curPop.get(curRow = curPop.get(curRow + 1 = curPop.get(curRow , curCol ); , curCol + 1); , curCol ); , curCol - 1); //set fittest to the neighbor with the highest fitness fittest = up; if (fittest.fitness < right.fitness) fittest = right; if (fittest.fitness < down.fitness) fittest = down; if (fittest.fitness < left.fitness) fittest = left; return fittest; } //perform crossover using a 2D wrap-around array of Individuals public WrapGrid<Individual> crossoverSteadyStateGrid(WrapGrid<Individual> curPop) { WrapGrid<Individual> newPop = new WrapGrid<Individual>(curPop.rows, curPop.cols, curPop.conNum); Random random = new Random(System.currentTimeMillis()); Individual current, mate; for(int i = 0; i < curPop.rows; i++) { for(int j = 0; j < curPop.cols; j++) { current = curPop.get(i, j); //find the mate for this individual mate = fittestNeighbor(i, j, curPop); //update log LOG.log("Crossover: Finding the fittest neighbor"); LOG.log("Runner: " + current.str + "| fitness: " + current.fitness); LOG.log("Fittest Neighbor: " + mate.str + "| fitness: " + mate.fitness); evoOut.println("Crossover: Finding the fittest neighbor"); evoOut.println("Runner: " + current.str + "| fitness: " + current.fitness); evoOut.println("Fittest Neighbor: " + mate.str + "| fitness: " + mate.fitness); //this crossover mechanism should maintain chromosomes to a consistent length int p1 = random.nextInt(current.str.length()/2); int p2 = p1 + random.nextInt(mate.str.length()/2); String child1 = current.str.substring(0, p1) + mate.str.substring(p1, p2) 37 + current.str.substring(p2); String child2 = mate.str.substring(0, p1) + current.str.substring(p1, p2) + mate.str.substring(p2); //mutate children here, before testing them for fitness mutate(child1); mutate(child2); //test each child float child1Fitness = testChild(qwop, child1, 1); boolean child1crashed = crashed; float child2Fitness = testChild(qwop, child2, 1); boolean child2crashed = crashed; //add the best child to the new population if it performs better than the current runner if( !child1crashed && child1Fitness > child2Fitness && child1Fitness > current.fitness ) { evoOut.println("Child 1 fit enough to join next generation: " + child1 + "|" + child1Fitness + "\nReplacing: " + current.str + "|" + current.fitness); LOG.log("Child 1 fit enough to join next generation: " + child1 + "|" + child1Fitness + "\nReplacing: " + current.str + "|" + current.fitness); newPop.add( new Individual(child1, child1Fitness) ); } else if( !child2crashed && child2Fitness > child1Fitness && child2Fitness > current.fitness ) { evoOut.println("Child 2 fit enough to join next generation: " + child2 + "|" + child2Fitness + "\nReplacing: " + current.str + "|" + current.fitness); LOG.log("Child 2 fit enough to join next generation: " + child2 + "|" + child2Fitness + "\nReplacing: " + current.str + "|" + current.fitness); newPop.add( new Individual(child2, child2Fitness) ); } //neither child is fitter than the current runner, therefore the current runner advances to next gen else { newPop.add(current); } } } return newPop; } /** Randomly choose three individuals from the pop, select the best two for crossover, * repeat until a new population of the same size has been generated * * Tournament Selection, one-point "cut and splice" crossover, * and single point mutation for the entire population * * This function was used only in Configurations 1 and 2 **/ /* public void crossover(){ Random random = new Random(System.currentTimeMillis()); int rnd; //reset newpop size newPopCnt = 0; newpop = new String[size]; 38 boolean newPopFull = false; //true when the new generation has been populated String[] inds; float[] fits; String[] sel; int weakest = -1; while(!newPopFull){ //pick three individual strings from current population, select the best two inds = new String[3]; fits = new float[3]; sel = new String[2]; rnd = random.nextInt(size); inds[0] = indivs[rnd]; fits[0] = avgFits[rnd]; rnd = random.nextInt(size); inds[1] = indivs[rnd]; fits[1] = avgFits[rnd]; rnd = random.nextInt(size); inds[2] = indivs[rnd]; fits[2] = avgFits[rnd]; LOG.log("Crossover: 3 Runners picked at random"); LOG.log("1: " + inds[0] + "| fitness: " + fits[0]); LOG.log("2: " + inds[1] + "| fitness: " + fits[1]); LOG.log("3: " + inds[2] + "| fitness: " + fits[2]); evoOut.println("Crossover: 3 Runners picked at random"); evoOut.println("1: " + inds[0] + "| fitness: " + fits[0]); evoOut.println("2: " + inds[1] + "| fitness: " + fits[1]); evoOut.println("3: " + inds[2] + "| fitness: " + fits[2]); if ( (fits[0] >= fits[1]) || (fits[0] >= fits[2])) { if (fits[1] > fits[2]) { sel[0] = inds[0]; sel[1] = inds[1]; weakest = 2;} else { sel[0] = inds[0]; sel[1] = inds[2]; weakest = 1; } } else { if ( (fits[1] >= fits[0]) || (fits[1] >= fits[2])) { if (fits[0] > fits[2]) { sel[0] = inds[1]; sel[1] = inds[0]; weakest = 2; } else { sel[0] = inds[1]; sel[1] = inds[2]; weakest = 0;} } else { if ( (fits[2] >= fits[0]) || (fits[2] >= fits[1])) { if (fits[0] > fits[1]) { sel[0] = inds[2]; sel[1] = inds[0]; weakest = 1;} else { sel[0] = inds[2]; sel[1] = inds[1]; weakest = 0;} } } 39 } LOG.log("Crossover: Parents selected\n"); LOG.log("1: " + sel[0]); LOG.log("2: " + sel[1]); evoOut.println("Crossover: Parents selected\n"); evoOut.println("1: " + sel[0]); evoOut.println("2: " + sel[1]); //best two selected. perform one-point "cut and splice" crossover. //two random numbers, each within the size of a parent int p1 = random.nextInt(sel[0].length()); int p2 = random.nextInt(sel[1].length()); String child1 = sel[0].substring(0, p1) + sel[1].substring(p2); String child2 = sel[1].substring(0, p2) + sel[0].substring(p1); //mutate children here, before adding to newpop mutate(child1); mutate(child2); //test each child once float child1Fitness = testChild(qwop, child1, 1); //add each child to the new population if it doesn't crash at less than 3m, //and if it performs better than the weakest potential parent if( (!crashed || child1Fitness >= 3.0) && child1Fitness > fits[weakest]) { evoOut.println("Child 1 fit enough to join next generation: " + child1 + "|" + child1Fitness + "\nReplacing: " + inds[weakest] + "|" + fits[weakest]); LOG.log("Child 1 fit enough to join next generation: " + child1 + "|" + child1Fitness + "\nReplacing: " + inds[weakest] + "|" + fits[weakest]); newpop[newPopCnt] = child1; newPopCnt++; } //if new population is full, exit loop if(newPopCnt == size) { newPopFull = true; continue; } float child2Fitness = testChild(qwop, child2, 1); if((!crashed || child1Fitness >= 3.0) && child2Fitness > fits[weakest]) { evoOut.println("Child 2 fit enough to join next generation: " + child2 + "|" + child2Fitness + "\nReplacing: " + inds[weakest] + "|" + fits[weakest]); LOG.log("Child 2 fit enough to join next generation: " + child2 + "|" + child2Fitness + "\nReplacing: " + inds[weakest] + "|" + fits[weakest]); newpop[newPopCnt] = child2; newPopCnt++; } //if new population is full, exit loop if(newPopCnt == size) newPopFull = true; } //the next generation has been created indivs = newpop; }*/ 40 public String mutate(String indiv){ Random random = new Random(System.currentTimeMillis()); //mutation location int rnd = random.nextInt(indiv.length()); //the mutation int rnd2 = random.nextInt(NOTES.length()); String k = NOTES.substring(rnd2, rnd2 + 1); return indiv.substring(0, rnd) + k + indiv.substring(rnd+1); } public void readPopulation(String filename) { try { BufferedReader input = new BufferedReader(new FileReader(filename)); try { String line; int linenum = 0; while ((line = input.readLine()) != null) { ++linenum; if (line.length() > 0) { try { RunInfo info = RunInfo.unmarshal(line); Individual indiv = this.population.get(info.string); //gets a string from pop if (indiv == null) { indiv = new Individual(info.string, null); this.population.put(info.string, indiv); } indiv.runs.add(info); } catch (RuntimeException e) { System.out.println("Error on line " + linenum); throw e; } } } } finally { try { input.close(); } catch (IOException e) { throw new RuntimeException("Error closing file: " + filename); } } } catch (FileNotFoundException e) { throw new RuntimeException("File not found: " + filename, e); } catch (IOException e) { throw new RuntimeException("Error reading file: " + filename, e); } } } package com.slowfrog.qwop; import java.util.*; /* * This class describing a borderless "wrap-around" 2D array was modified from * http://stackoverflow.com/questions/9058217/2d-array-class-in-java-with-wrap-around-edges 41 */ public class WrapGrid<T> { ArrayList<T> array; // holds objects in grid int rows; // number of rows in grid int cols; // number of cols in grid int length; // total number of objects in grid int conNum; // number of connections for each point on grid (either 4 or 8) // constructor WrapGrid(int row, int col, int numCon) { rows = row; cols = col; length = rows * cols; conNum = numCon; array = new ArrayList<T>(length); } // returns total size of grid public int len() { return length; } // returns number of rows public int row() { return rows; } // returns number of columns public int col() { return cols; } public void add(T t) { array.add(t); } // sets object i in flattened out array public void set(int i, T t) { array.set(i, t); } // returns object i in flattened out array // for faster access when user just needs to iterate through all objects // in grid without respect to position in 2D grid public T get(int i) { return array.get(i); } // returns the row position of i in grid - adjusted for warp around edges private int modRow(int i) { if(i < 0) return i + rows; else if(i >= rows) return i % rows; else return i; } 42 // returns the column position of j in grid - adjusted for wrap around edges private int modCol(int j) { if(j < 0) return j + cols; else if(j >= cols) return j % cols; else return j; } // sets object at (i,j) value from store adjusted for wrap around edges public void set(int i, int j, T t) { array.set(modRow(i) * cols + modCol(j), t); } // gets object at (i,j) value from store adjusted for wrap around edges public T get(int i, int j) { return array.get(modRow(i) * cols + modCol(j)); } // returns distance on the grid between two objects at (y1,x1) and (y2,x2) public int dist(int y1, int x1, int y2, int x2) { int y = distFirst(y1, y2); int x = distSecond(x1, x2); if (conNum == 4) // taxicab distance { return y + x; } else { //if(conNum == 8) supremum distance return Math.max(y, x); } } // returns distance on the grid between the first coordinates y1 & y2 of two objects public int distFirst(int y1, int y2) { int dist = Math.abs(modRow(y2) - modRow(y1)); return Math.min(dist, rows - dist); } // returns distance on the grid between the second coordinates x1 & x2 of two objects public int distSecond(int x1, int x2) { int dist = Math.abs(modCol(x2) - modCol(x1)); return Math.min(dist, cols - dist); } } 43 APPENDIX B. SAMPLE EVOLUTION LOG //This log contains the first generation of evolution using Configuration 3 with an //initial population of poor quality random runners. This run’s results can be seen in //Fig. 12 as the plot labeled “Unfit” ########## Generation 0 ########## 0) DLMPDJINICNLKLMPHDMHIJNHFOPCOOJMPPODGJGH avgFitness: -0.3 1) LBBANMKDEKMFINFPDIEOOAFMGLHFIOILAFPHDPIC avgFitness: 0.1 2) GBKDPHFADPDJFNNMGINMJCJIEJDOLHLLFEBOAAMC avgFitness: 1.9 3) BBNBGGCKDFNIFFAOOJFNNIFFHNHDNIPOAMOFKIJH avgFitness: -0.5 4) FGBCHFGFEIEJPGHEIJMAKCKJKJCGCFLHDGJDAJLE avgFitness: 0.3 5) HCEJNGFOIIHMIBHEPGFJMLNIHKNIFPCDEHPGHEGF avgFitness: 0.7 6) BOONAKFEKHAKFOEKNHADBNOPOLJFBCFDPLMHLIDG avgFitness: 1.2 7) FMFDKJOPLJHNOKOJMKHFEBIPAFPJFLJINCLEHJID avgFitness: -1.1 8) MHFAMAGMGFDAIDHFBOFABLDPMBOAFNECIIFEDDIH avgFitness: 0.9 9) JBBBKGEJCHNGBAEGLFFCOBDJIKAMJIMMPODINHCI avgFitness: 1.3 10) PJCAMLJLIAJHNFHFIAMEJCOJHILDHPMGBILFDAGB avgFitness: 0.1 11) PDDGIFELMCOPCAJHICAPAJOGHMEFLPIPFGIFPKEE avgFitness: 0.0 12) GHOPIHFBMLEMJJEIMFMEIOLGHHDDLAFPHKMKPENG avgFitness: 1.4 13) ODFPGMPHNHPGODGFLPNDIBFONDOBDPBPCHIOIALD avgFitness: 0.2 14) FOFBBIGGDAHKKKBHEEALBPIEEMCALBLMDLEIIOPA avgFitness: 0.6 15) NCMDJBBOMPECHCDNFNFMENJKILOAECDFCPPHKHKI avgFitness: 0.8 16) MKIGDONODMMKLLPKPNLFEDLCONEOBNFKEDPHJODH avgFitness: 0.0 17) GLEGEKMLIGAGMLLNAAAOPFOOKMKMKBNOIDHOAGMN avgFitness: 0.4 18) LNICNHMBBJKGGOLICDAIKCJFMALNEKGHIDKHPMBD avgFitness: 0.7 19) AHNOGEBHEAOJNGIKBFMPANNFDBEGJADOBJOIBMEK avgFitness: 0.3 20) HLEPGHBFBPHIKHFOLFCILJHPJPKIMMACNLPHFFBN avgFitness: 1.4 21) HMLIIKHLBNJEHNLNNKFFFDDMJBGPEOJBIKOKFHPG avgFitness: -1.1 22) MAIKPBHCMONBCLOIIBEGMFMJCBLHNEOGBIMADIKI avgFitness: -0.1 23) LDGCPHPNKBJIBGNHHBPBCJMPFNBAAMIMAFOLHNFK avgFitness: 0.3 24) PAJOHCDCALGEHJOAMLOGOANGLKPBCGDDINOEKNIO avgFitness: 0.3 25) AFLDPICDAMCALMNAEHGBEBMHJFBCPFKAHKOLNGKB avgFitness: -0.3 26) KDAAJGDBEBLICDECGJONFNMIJOBKPELBPPDIMALF avgFitness: 0.3 27) AIACHFMILCOFKPACJGNDGHBDJOALDPJCMJMMLPBP avgFitness: 0.3 28) EMEIBHBHOAPFNGJCLHEDBOBMHKMKKBLCKFIMDFNA avgFitness: 0.2 29) NNGFPMOLIKMEMHNINGAEMCLMHNACKFOOFNJEKPBI avgFitness: 1.8 ################################### ########## Generation 1 ########## ---------- Individual 1 ---------Ran -0.9m during 2766ms and crashed RunInfo#1|DLMPDJINICNLKLMPHDMHIJNHFOPCOOJMPPODGJGH|150|-0.9|2766|C 1) -0.9 | DLMPDJINICNLKLMPHDMHIJNHFOPCOOJMPPODGJGH ---------- Individual 2 ---------Ran 0.0m during 3450ms and crashed RunInfo#1|LBBANMKDEKMFINFPDIEOOAFMGLHFIOILAFPHDPIC|150|0.0|3450|C 2) 0.0 | LBBANMKDEKMFINFPDIEOOAFMGLHFIOILAFPHDPIC ---------- Individual 3 ---------Ran -0.2m during 5590ms and crashed RunInfo#1|GBKDPHFADPDJFNNMGINMJCJIEJDOLHLLFEBOAAMC|150|-0.2|5590|C 3) -0.2 | GBKDPHFADPDJFNNMGINMJCJIEJDOLHLLFEBOAAMC ---------- Individual 4 ---------Ran -0.1m during 3297ms and crashed RunInfo#1|BBNBGGCKDFNIFFAOOJFNNIFFHNHDNIPOAMOFKIJH|150|-0.1|3297|C 4) -0.1 | BBNBGGCKDFNIFFAOOJFNNIFFHNHDNIPOAMOFKIJH ---------- Individual 5 ---------Ran 14.7m during 60194ms and was stopped RunInfo#1|FGBCHFGFEIEJPGHEIJMAKCKJKJCGCFLHDGJDAJLE|150|14.7|60194|S 5) 14.7 | FGBCHFGFEIEJPGHEIJMAKCKJKJCGCFLHDGJDAJLE ---------- Individual 6 ---------Ran 0.6m during 1951ms and crashed RunInfo#1|HCEJNGFOIIHMIBHEPGFJMLNIHKNIFPCDEHPGHEGF|150|0.6|1951|C 6) 0.6 | HCEJNGFOIIHMIBHEPGFJMLNIHKNIFPCDEHPGHEGF ---------- Individual 7 ---------- 44 Ran 0.8m during 3018ms and crashed RunInfo#1|BOONAKFEKHAKFOEKNHADBNOPOLJFBCFDPLMHLIDG|150|0.8|3018|C 7) 0.8 | BOONAKFEKHAKFOEKNHADBNOPOLJFBCFDPLMHLIDG ---------- Individual 8 ---------Ran 2.3m during 60080ms and was stopped RunInfo#1|FMFDKJOPLJHNOKOJMKHFEBIPAFPJFLJINCLEHJID|150|2.3|60080|S 8) 2.3 | FMFDKJOPLJHNOKOJMKHFEBIPAFPJFLJINCLEHJID ---------- Individual 9 ---------Ran 0.2m during 2568ms and crashed RunInfo#1|MHFAMAGMGFDAIDHFBOFABLDPMBOAFNECIIFEDDIH|150|0.2|2568|C 9) 0.2 | MHFAMAGMGFDAIDHFBOFABLDPMBOAFNECIIFEDDIH ---------- Individual 10 ---------Ran 1.3m during 3066ms and crashed RunInfo#1|JBBBKGEJCHNGBAEGLFFCOBDJIKAMJIMMPODINHCI|150|1.3|3066|C 10) 1.3 | JBBBKGEJCHNGBAEGLFFCOBDJIKAMJIMMPODINHCI ---------- Individual 11 ---------Ran 0.3m during 2829ms and crashed RunInfo#1|PJCAMLJLIAJHNFHFIAMEJCOJHILDHPMGBILFDAGB|150|0.3|2829|C 11) 0.3 | PJCAMLJLIAJHNFHFIAMEJCOJHILDHPMGBILFDAGB ---------- Individual 12 ---------Ran -0.5m during 2567ms and crashed RunInfo#1|PDDGIFELMCOPCAJHICAPAJOGHMEFLPIPFGIFPKEE|150|-0.5|2567|C 12) -0.5 | PDDGIFELMCOPCAJHICAPAJOGHMEFLPIPFGIFPKEE ---------- Individual 13 ---------Ran 1.3m during 3785ms and crashed RunInfo#1|GHOPIHFBMLEMJJEIMFMEIOLGHHDDLAFPHKMKPENG|150|1.3|3785|C 13) 1.3 | GHOPIHFBMLEMJJEIMFMEIOLGHHDDLAFPHKMKPENG ---------- Individual 14 ---------Ran 0.2m during 2884ms and crashed RunInfo#1|ODFPGMPHNHPGODGFLPNDIBFONDOBDPBPCHIOIALD|150|0.2|2884|C 14) 0.2 | ODFPGMPHNHPGODGFLPNDIBFONDOBDPBPCHIOIALD ---------- Individual 15 ---------Ran 0.1m during 4079ms and crashed RunInfo#1|FOFBBIGGDAHKKKBHEEALBPIEEMCALBLMDLEIIOPA|150|0.1|4079|C 15) 0.1 | FOFBBIGGDAHKKKBHEEALBPIEEMCALBLMDLEIIOPA ---------- Individual 16 ---------Ran 0.9m during 2278ms and crashed RunInfo#1|NCMDJBBOMPECHCDNFNFMENJKILOAECDFCPPHKHKI|150|0.9|2278|C 16) 0.9 | NCMDJBBOMPECHCDNFNFMENJKILOAECDFCPPHKHKI ---------- Individual 17 ---------Ran 0.8m during 3179ms and crashed RunInfo#1|MKIGDONODMMKLLPKPNLFEDLCONEOBNFKEDPHJODH|150|0.8|3179|C 17) 0.8 | MKIGDONODMMKLLPKPNLFEDLCONEOBNFKEDPHJODH ---------- Individual 18 ---------Ran 0.6m during 2100ms and crashed RunInfo#1|GLEGEKMLIGAGMLLNAAAOPFOOKMKMKBNOIDHOAGMN|150|0.6|2100|C 18) 0.6 | GLEGEKMLIGAGMLLNAAAOPFOOKMKMKBNOIDHOAGMN ---------- Individual 19 ---------Ran 3.3m during 21604ms and crashed RunInfo#1|LNICNHMBBJKGGOLICDAIKCJFMALNEKGHIDKHPMBD|150|3.3|21604|C 19) 3.3 | LNICNHMBBJKGGOLICDAIKCJFMALNEKGHIDKHPMBD ---------- Individual 20 ---------Ran 0.1m during 7706ms and crashed RunInfo#1|AHNOGEBHEAOJNGIKBFMPANNFDBEGJADOBJOIBMEK|150|0.1|7706|C 20) 0.1 | AHNOGEBHEAOJNGIKBFMPANNFDBEGJADOBJOIBMEK ---------- Individual 21 ---------Ran 0.4m during 2583ms and crashed RunInfo#1|HLEPGHBFBPHIKHFOLFCILJHPJPKIMMACNLPHFFBN|150|0.4|2583|C 21) 0.4 | HLEPGHBFBPHIKHFOLFCILJHPJPKIMMACNLPHFFBN ---------- Individual 22 ---------Ran 1.6m during 4082ms and crashed RunInfo#1|HMLIIKHLBNJEHNLNNKFFFDDMJBGPEOJBIKOKFHPG|150|1.6|4082|C 22) 1.6 | HMLIIKHLBNJEHNLNNKFFFDDMJBGPEOJBIKOKFHPG ---------- Individual 23 ---------Ran 1.2m during 3179ms and crashed 45 RunInfo#1|MAIKPBHCMONBCLOIIBEGMFMJCBLHNEOGBIMADIKI|150|1.2|3179|C 23) 1.2 | MAIKPBHCMONBCLOIIBEGMFMJCBLHNEOGBIMADIKI ---------- Individual 24 ---------Ran -0.6m during 2561ms and crashed RunInfo#1|LDGCPHPNKBJIBGNHHBPBCJMPFNBAAMIMAFOLHNFK|150|-0.6|2561|C 24) -0.6 | LDGCPHPNKBJIBGNHHBPBCJMPFNBAAMIMAFOLHNFK ---------- Individual 25 ---------Ran 0.7m during 2851ms and crashed RunInfo#1|PAJOHCDCALGEHJOAMLOGOANGLKPBCGDDINOEKNIO|150|0.7|2851|C 25) 0.7 | PAJOHCDCALGEHJOAMLOGOANGLKPBCGDDINOEKNIO ---------- Individual 26 ---------Ran 0.1m during 2116ms and crashed RunInfo#1|AFLDPICDAMCALMNAEHGBEBMHJFBCPFKAHKOLNGKB|150|0.1|2116|C 26) 0.1 | AFLDPICDAMCALMNAEHGBEBMHJFBCPFKAHKOLNGKB ---------- Individual 27 ---------Ran 7.4m during 40643ms and crashed RunInfo#1|KDAAJGDBEBLICDECGJONFNMIJOBKPELBPPDIMALF|150|7.4|40643|C 27) 7.4 | KDAAJGDBEBLICDECGJONFNMIJOBKPELBPPDIMALF ---------- Individual 28 ---------Ran 0.9m during 2583ms and crashed RunInfo#1|AIACHFMILCOFKPACJGNDGHBDJOALDPJCMJMMLPBP|150|0.9|2583|C 28) 0.9 | AIACHFMILCOFKPACJGNDGHBDJOALDPJCMJMMLPBP ---------- Individual 29 ---------Ran 0.5m during 2700ms and crashed RunInfo#1|EMEIBHBHOAPFNGJCLHEDBOBMHKMKKBLCKFIMDFNA|150|0.5|2700|C 29) 0.5 | EMEIBHBHOAPFNGJCLHEDBOBMHKMKKBLCKFIMDFNA ---------- Individual 30 ---------Ran 1.2m during 2829ms and crashed RunInfo#1|NNGFPMOLIKMEMHNINGAEMCLMHNACKFOOFNJEKPBI|150|1.2|2829|C 30) 1.2 | NNGFPMOLIKMEMHNINGAEMCLMHNACKFOOFNJEKPBI Generation 1 Average Fitness: 1.3066669 Crossover: Finding the fittest neighbor Runner: DLMPDJINICNLKLMPHDMHIJNHFOPCOOJMPPODGJGH| fitness: -0.9 Fittest Neighbor: BOONAKFEKHAKFOEKNHADBNOPOLJFBCFDPLMHLIDG| fitness: Testing child: DLONAKFEKHAKFOEKNHMHIJNHFOPCOOJMPPODGJGH Ran -0.3m during 3619ms and crashed RunInfo#1|DLONAKFEKHAKFOEKNHMHIJNHFOPCOOJMPPODGJGH|150|-0.3|3619|C Testing child: BOMPDJINICNLKLMPHDADBNOPOLJFBCFDPLMHLIDG Ran -0.4m during 3464ms and crashed RunInfo#1|BOMPDJINICNLKLMPHDADBNOPOLJFBCFDPLMHLIDG|150|-0.4|3464|C Crossover: Finding the fittest neighbor Runner: LBBANMKDEKMFINFPDIEOOAFMGLHFIOILAFPHDPIC| fitness: 0.0 Fittest Neighbor: FMFDKJOPLJHNOKOJMKHFEBIPAFPJFLJINCLEHJID| fitness: Testing child: LBBANMKDEJHNOKOJMKHFEAFMGLHFIOILAFPHDPIC Ran 1.6m during 3034ms and crashed RunInfo#1|LBBANMKDEJHNOKOJMKHFEAFMGLHFIOILAFPHDPIC|150|1.6|3034|C Testing child: FMFDKJOPLKMFINFPDIEOOBIPAFPJFLJINCLEHJID Ran -1.5m during 60166ms and was stopped RunInfo#1|FMFDKJOPLKMFINFPDIEOOBIPAFPJFLJINCLEHJID|150|-1.5|60166|S Crossover: Finding the fittest neighbor Runner: GBKDPHFADPDJFNNMGINMJCJIEJDOLHLLFEBOAAMC| fitness: -0.2 Fittest Neighbor: KDAAJGDBEBLICDECGJONFNMIJOBKPELBPPDIMALF| fitness: Testing child: GBAAJGFADPDJFNNMGINMJCJIEJDOLHLLFEBOAAMC Ran -0.2m during 2418ms and crashed RunInfo#1|GBAAJGFADPDJFNNMGINMJCJIEJDOLHLLFEBOAAMC|150|-0.2|2418|C Testing child: KDKDPHDBEBLICDECGJONFNMIJOBKPELBPPDIMALF Ran -1.8m during 6039ms and crashed RunInfo#1|KDKDPHDBEBLICDECGJONFNMIJOBKPELBPPDIMALF|150|-1.8|6039|C Crossover: Finding the fittest neighbor Runner: BBNBGGCKDFNIFFAOOJFNNIFFHNHDNIPOAMOFKIJH| fitness: -0.1 Fittest Neighbor: FGBCHFGFEIEJPGHEIJMAKCKJKJCGCFLHDGJDAJLE| fitness: Testing child: BBNBGGGFEIEJPGHEIJFNNIFFHNHDNIPOAMOFKIJH Ran -0.9m during 3068ms and crashed RunInfo#1|BBNBGGGFEIEJPGHEIJFNNIFFHNHDNIPOAMOFKIJH|150|-0.9|3068|C Testing child: FGBCHFCKDFNIFFAOOJMAKCKJKJCGCFLHDGJDAJLE Ran 0.0m during 3031ms and crashed 0.8 2.3 7.4 14.7 46 RunInfo#1|FGBCHFCKDFNIFFAOOJMAKCKJKJCGCFLHDGJDAJLE|150|0.0|3031|C Crossover: Finding the fittest neighbor Runner: FGBCHFGFEIEJPGHEIJMAKCKJKJCGCFLHDGJDAJLE| fitness: 14.7 Fittest Neighbor: HCEJNGFOIIHMIBHEPGFJMLNIHKNIFPCDEHPGHEGF| fitness: Testing child: FGBCHFGFEIEJIBHEPGMAKCKJKJCGCFLHDGJDAJLE Ran 0.4m during 3184ms and crashed RunInfo#1|FGBCHFGFEIEJIBHEPGMAKCKJKJCGCFLHDGJDAJLE|150|0.4|3184|C Testing child: HCEJNGFOIIHMPGHEIJFJMLNIHKNIFPCDEHPGHEGF Ran 0.3m during 3940ms and crashed RunInfo#1|HCEJNGFOIIHMPGHEIJFJMLNIHKNIFPCDEHPGHEGF|150|0.3|3940|C Crossover: Finding the fittest neighbor Runner: HCEJNGFOIIHMIBHEPGFJMLNIHKNIFPCDEHPGHEGF| fitness: 0.6 Fittest Neighbor: FGBCHFGFEIEJPGHEIJMAKCKJKJCGCFLHDGJDAJLE| fitness: Testing child: HCEJNGFOIIHMIBHEPGMAKCKJHKNIFPCDEHPGHEGF Ran 0.5m during 9374ms and crashed RunInfo#1|HCEJNGFOIIHMIBHEPGMAKCKJHKNIFPCDEHPGHEGF|150|0.5|9374|C Testing child: FGBCHFGFEIEJPGHEIJFJMLNIKJCGCFLHDGJDAJLE Ran 0.1m during 3652ms and crashed RunInfo#1|FGBCHFGFEIEJPGHEIJFJMLNIKJCGCFLHDGJDAJLE|150|0.1|3652|C Crossover: Finding the fittest neighbor Runner: BOONAKFEKHAKFOEKNHADBNOPOLJFBCFDPLMHLIDG| fitness: 0.8 Fittest Neighbor: FMFDKJOPLJHNOKOJMKHFEBIPAFPJFLJINCLEHJID| fitness: Testing child: BOONAKFELJAKFOEKNHADBNOPOLJFBCFDPLMHLIDG Ran 1.0m during 2843ms and crashed RunInfo#1|BOONAKFELJAKFOEKNHADBNOPOLJFBCFDPLMHLIDG|150|1.0|2843|C Testing child: FMFDKJOPKHHNOKOJMKHFEBIPAFPJFLJINCLEHJID Ran 0.6m during 28440ms and crashed RunInfo#1|FMFDKJOPKHHNOKOJMKHFEBIPAFPJFLJINCLEHJID|150|0.6|28440|C Crossover: Finding the fittest neighbor Runner: FMFDKJOPLJHNOKOJMKHFEBIPAFPJFLJINCLEHJID| fitness: 2.3 Fittest Neighbor: BOONAKFEKHAKFOEKNHADBNOPOLJFBCFDPLMHLIDG| fitness: Testing child: FMFDKJOPLJHNOOEKNHAFEBIPAFPJFLJINCLEHJID Ran -1.2m during 28657ms and crashed RunInfo#1|FMFDKJOPLJHNOOEKNHAFEBIPAFPJFLJINCLEHJID|150|-1.2|28657|C Testing child: BOONAKFEKHAKFKOJMKHDBNOPOLJFBCFDPLMHLIDG Ran 1.1m during 3028ms and crashed RunInfo#1|BOONAKFEKHAKFKOJMKHDBNOPOLJFBCFDPLMHLIDG|150|1.1|3028|C Crossover: Finding the fittest neighbor Runner: MHFAMAGMGFDAIDHFBOFABLDPMBOAFNECIIFEDDIH| fitness: 0.2 Fittest Neighbor: FMFDKJOPLJHNOKOJMKHFEBIPAFPJFLJINCLEHJID| fitness: Testing child: MMFDKJOPLJHNOKOJMKHFBLDPMBOAFNECIIFEDDIH Ran -0.4m during 6522ms and crashed RunInfo#1|MMFDKJOPLJHNOKOJMKHFBLDPMBOAFNECIIFEDDIH|150|-0.4|6522|C Testing child: FHFAMAGMGFDAIDHFBOFAEBIPAFPJFLJINCLEHJID Ran 0.5m during 3000ms and crashed RunInfo#1|FHFAMAGMGFDAIDHFBOFAEBIPAFPJFLJINCLEHJID|150|0.5|3000|C Crossover: Finding the fittest neighbor Runner: JBBBKGEJCHNGBAEGLFFCOBDJIKAMJIMMPODINHCI| fitness: 1.3 Fittest Neighbor: NCMDJBBOMPECHCDNFNFMENJKILOAECDFCPPHKHKI| fitness: Testing child: JBBBKGEJCHNGBAEGLFFMENJKILOAECDFCPPHNHCI Ran 1.1m during 3936ms and crashed RunInfo#1|JBBBKGEJCHNGBAEGLFFMENJKILOAECDFCPPHNHCI|150|1.1|3936|C Testing child: NCMDJBBOMPECHCDNFNFCOBDJIKAMJIMMPODIKHKI Ran 0.7m during 2718ms and crashed RunInfo#1|NCMDJBBOMPECHCDNFNFCOBDJIKAMJIMMPODIKHKI|150|0.7|2718|C Crossover: Finding the fittest neighbor Runner: PJCAMLJLIAJHNFHFIAMEJCOJHILDHPMGBILFDAGB| fitness: 0.3 Fittest Neighbor: FGBCHFGFEIEJPGHEIJMAKCKJKJCGCFLHDGJDAJLE| fitness: Testing child: PJCAMLJLIAJHNFHFIJMAKCKJKJCGCFLHDGLFDAGB Ran 0.6m during 3345ms and crashed RunInfo#1|PJCAMLJLIAJHNFHFIJMAKCKJKJCGCFLHDGLFDAGB|150|0.6|3345|C Testing child: FGBCHFGFEIEJPGHEIAMEJCOJHILDHPMGBIJDAJLE Ran 4.6m during 55042ms and crashed RunInfo#1|FGBCHFGFEIEJPGHEIAMEJCOJHILDHPMGBIJDAJLE|150|4.6|55042|C Crossover: Finding the fittest neighbor Runner: PDDGIFELMCOPCAJHICAPAJOGHMEFLPIPFGIFPKEE| fitness: -0.5 0.6 14.7 2.3 0.8 2.3 0.9 14.7 47 Fittest Neighbor: BOONAKFEKHAKFOEKNHADBNOPOLJFBCFDPLMHLIDG| fitness: Testing child: PDDGIFELMCOPFOEKICAPAJOGHMEFLPIPFGIFPKEE Ran 0.0m during 2280ms and crashed RunInfo#1|PDDGIFELMCOPFOEKICAPAJOGHMEFLPIPFGIFPKEE|150|0.0|2280|C Testing child: BOONAKFEKHAKCAJHNHADBNOPOLJFBCFDPLMHLIDG Ran 0.8m during 2851ms and crashed RunInfo#1|BOONAKFEKHAKCAJHNHADBNOPOLJFBCFDPLMHLIDG|150|0.8|2851|C Crossover: Finding the fittest neighbor Runner: GHOPIHFBMLEMJJEIMFMEIOLGHHDDLAFPHKMKPENG| fitness: 1.3 Fittest Neighbor: LNICNHMBBJKGGOLICDAIKCJFMALNEKGHIDKHPMBD| fitness: Testing child: GHOPIHFBMLEGGJEIMFMEIOLGHHDDLAFPHKMKPENG Ran 1.5m during 4352ms and crashed RunInfo#1|GHOPIHFBMLEGGJEIMFMEIOLGHHDDLAFPHKMKPENG|150|1.5|4352|C Testing child: LNICNHMBBJKMJOLICDAIKCJFMALNEKGHIDKHPMBD Ran 1.4m during 3469ms and crashed RunInfo#1|LNICNHMBBJKMJOLICDAIKCJFMALNEKGHIDKHPMBD|150|1.4|3469|C Crossover: Finding the fittest neighbor Runner: ODFPGMPHNHPGODGFLPNDIBFONDOBDPBPCHIOIALD| fitness: 0.2 Fittest Neighbor: FMFDKJOPLJHNOKOJMKHFEBIPAFPJFLJINCLEHJID| fitness: Testing child: ODFPGMPHNHPGODGFLPHFEBIPAFPJFLJPCHIOIALD Ran 0.2m during 2584ms and crashed RunInfo#1|ODFPGMPHNHPGODGFLPHFEBIPAFPJFLJPCHIOIALD|150|0.2|2584|C Testing child: FMFDKJOPLJHNOKOJMKNDIBFONDOBDPBINCLEHJID Ran 0.9m during 2546ms and crashed RunInfo#1|FMFDKJOPLJHNOKOJMKNDIBFONDOBDPBINCLEHJID|150|0.9|2546|C Crossover: Finding the fittest neighbor Runner: FOFBBIGGDAHKKKBHEEALBPIEEMCALBLMDLEIIOPA| fitness: 0.1 Fittest Neighbor: NCMDJBBOMPECHCDNFNFMENJKILOAECDFCPPHKHKI| fitness: Testing child: FOFBBIGGMPECHCBHEEALBPIEEMCALBLMDLEIIOPA Ran -0.4m during 5738ms and crashed RunInfo#1|FOFBBIGGMPECHCBHEEALBPIEEMCALBLMDLEIIOPA|150|-0.4|5738|C Testing child: NCMDJBBODAHKKKDNFNFMENJKILOAECDFCPPHKHKI Ran 0.9m during 3184ms and crashed RunInfo#1|NCMDJBBODAHKKKDNFNFMENJKILOAECDFCPPHKHKI|150|0.9|3184|C Crossover: Finding the fittest neighbor Runner: NCMDJBBOMPECHCDNFNFMENJKILOAECDFCPPHKHKI| fitness: 0.9 Fittest Neighbor: HMLIIKHLBNJEHNLNNKFFFDDMJBGPEOJBIKOKFHPG| fitness: Testing child: NCMDJBBLBNJEHNLNNKFFFDDMJBOAECDFCPPHKHKI Ran 0.4m during 2728ms and crashed RunInfo#1|NCMDJBBLBNJEHNLNNKFFFDDMJBOAECDFCPPHKHKI|150|0.4|2728|C Testing child: HMLIIKHOMPECHCDNFNFMENJKILGPEOJBIKOKFHPG Ran 0.6m during 7123ms and crashed RunInfo#1|HMLIIKHOMPECHCDNFNFMENJKILGPEOJBIKOKFHPG|150|0.6|7123|C Crossover: Finding the fittest neighbor Runner: MKIGDONODMMKLLPKPNLFEDLCONEOBNFKEDPHJODH| fitness: 0.8 Fittest Neighbor: MAIKPBHCMONBCLOIIBEGMFMJCBLHNEOGBIMADIKI| fitness: Testing child: MKIGDONODMMKLLPKPNEGMFMJCBLHBNFKEDPHJODH Ran -0.3m during 2584ms and crashed RunInfo#1|MKIGDONODMMKLLPKPNEGMFMJCBLHBNFKEDPHJODH|150|-0.3|2584|C Testing child: MAIKPBHCMONBCLOIIBLFEDLCONEONEOGBIMADIKI Ran 1.7m during 4669ms and crashed RunInfo#1|MAIKPBHCMONBCLOIIBLFEDLCONEONEOGBIMADIKI|150|1.7|4669|C Crossover: Finding the fittest neighbor Runner: GLEGEKMLIGAGMLLNAAAOPFOOKMKMKBNOIDHOAGMN| fitness: 0.6 Fittest Neighbor: GHOPIHFBMLEMJJEIMFMEIOLGHHDDLAFPHKMKPENG| fitness: Testing child: GHOPIKMLIGAGMLLNAAAOPFOOKMKMKBNOIDHOAGMN Ran 0.4m during 6188ms and crashed RunInfo#1|GHOPIKMLIGAGMLLNAAAOPFOOKMKMKBNOIDHOAGMN|150|0.4|6188|C Testing child: GLEGEHFBMLEMJJEIMFMEIOLGHHDDLAFPHKMKPENG Ran 0.5m during 1950ms and crashed RunInfo#1|GLEGEHFBMLEMJJEIMFMEIOLGHHDDLAFPHKMKPENG|150|0.5|1950|C Crossover: Finding the fittest neighbor Runner: LNICNHMBBJKGGOLICDAIKCJFMALNEKGHIDKHPMBD| fitness: 3.3 Fittest Neighbor: GHOPIHFBMLEMJJEIMFMEIOLGHHDDLAFPHKMKPENG| fitness: Testing child: LNICNHFBMLEGGOLICDAIKCJFMALNEKGHIDKHPMBD Ran 0.1m during 3184ms and crashed 0.8 3.3 2.3 0.9 1.6 1.2 1.3 1.3 48 RunInfo#1|LNICNHFBMLEGGOLICDAIKCJFMALNEKGHIDKHPMBD|150|0.1|3184|C Testing child: GHOPIHMBBJKMJJEIMFMEIOLGHHDDLAFPHKMKPENG Ran 1.6m during 3034ms and crashed RunInfo#1|GHOPIHMBBJKMJJEIMFMEIOLGHHDDLAFPHKMKPENG|150|1.6|3034|C Crossover: Finding the fittest neighbor Runner: AHNOGEBHEAOJNGIKBFMPANNFDBEGJADOBJOIBMEK| fitness: 0.1 Fittest Neighbor: LNICNHMBBJKGGOLICDAIKCJFMALNEKGHIDKHPMBD| fitness: Testing child: AHNOGEBHEAOJNGIKBFMIKCJFMALNEKGHIDOIBMEK Ran -0.1m during 5732ms and crashed RunInfo#1|AHNOGEBHEAOJNGIKBFMIKCJFMALNEKGHIDOIBMEK|150|-0.1|5732|C Testing child: LNICNHMBBJKGGOLICDAPANNFDBEGJADOBJKHPMBD Ran 2.1m during 8117ms and crashed RunInfo#1|LNICNHMBBJKGGOLICDAPANNFDBEGJADOBJKHPMBD|150|2.1|8117|C Crossover: Finding the fittest neighbor Runner: HLEPGHBFBPHIKHFOLFCILJHPJPKIMMACNLPHFFBN| fitness: 0.4 Fittest Neighbor: KDAAJGDBEBLICDECGJONFNMIJOBKPELBPPDIMALF| fitness: Testing child: HLEPGHBFBPHIKHFOGJONFNMIJPKIMMACNLPHFFBN Ran 1.4m during 3452ms and crashed RunInfo#1|HLEPGHBFBPHIKHFOGJONFNMIJPKIMMACNLPHFFBN|150|1.4|3452|C Testing child: KDAAJGDBEBLICDECLFCILJHPJOBKPELBPPDIMALF Ran 0.5m during 2417ms and crashed RunInfo#1|KDAAJGDBEBLICDECLFCILJHPJOBKPELBPPDIMALF|150|0.5|2417|C Crossover: Finding the fittest neighbor Runner: HMLIIKHLBNJEHNLNNKFFFDDMJBGPEOJBIKOKFHPG| fitness: 1.6 Fittest Neighbor: MAIKPBHCMONBCLOIIBEGMFMJCBLHNEOGBIMADIKI| fitness: Testing child: HMLIPBHCMNJEHNLNNKFFFDDMJBGPEOJBIKOKFHPG Ran 0.5m during 3945ms and crashed RunInfo#1|HMLIPBHCMNJEHNLNNKFFFDDMJBGPEOJBIKOKFHPG|150|0.5|3945|C Testing child: MAIKIKHLBONBCLOIIBEGMFMJCBLHNEOGBIMADIKI Ran 0.1m during 2545ms and crashed RunInfo#1|MAIKIKHLBONBCLOIIBEGMFMJCBLHNEOGBIMADIKI|150|0.1|2545|C Crossover: Finding the fittest neighbor Runner: MAIKPBHCMONBCLOIIBEGMFMJCBLHNEOGBIMADIKI| fitness: 1.2 Fittest Neighbor: HMLIIKHLBNJEHNLNNKFFFDDMJBGPEOJBIKOKFHPG| fitness: Testing child: MAIKPBHCMONBCLOIIKFFFDDMJBGPNEOGBIMADIKI Ran 1.3m during 3936ms and crashed RunInfo#1|MAIKPBHCMONBCLOIIKFFFDDMJBGPNEOGBIMADIKI|150|1.3|3936|C Testing child: HMLIIKHLBNJEHNLNNBEGMFMJCBLHEOJBIKOKFHPG Ran 0.3m during 4536ms and crashed RunInfo#1|HMLIIKHLBNJEHNLNNBEGMFMJCBLHEOJBIKOKFHPG|150|0.3|4536|C Crossover: Finding the fittest neighbor Runner: LDGCPHPNKBJIBGNHHBPBCJMPFNBAAMIMAFOLHNFK| fitness: -0.6 Fittest Neighbor: LNICNHMBBJKGGOLICDAIKCJFMALNEKGHIDKHPMBD| fitness: Testing child: LDGCPHPNKBJGGOLIHBPBCJMPFNBAAMIMAFOLHNFK Ran -0.5m during 2161ms and crashed RunInfo#1|LDGCPHPNKBJGGOLIHBPBCJMPFNBAAMIMAFOLHNFK|150|-0.5|2161|C Testing child: LNICNHMBBJKIBGNHCDAIKCJFMALNEKGHIDKHPMBD Ran 0.4m during 5288ms and crashed RunInfo#1|LNICNHMBBJKIBGNHCDAIKCJFMALNEKGHIDKHPMBD|150|0.4|5288|C Crossover: Finding the fittest neighbor Runner: PAJOHCDCALGEHJOAMLOGOANGLKPBCGDDINOEKNIO| fitness: 0.7 Fittest Neighbor: LNICNHMBBJKGGOLICDAIKCJFMALNEKGHIDKHPMBD| fitness: Testing child: PAJOHCDCALGEHOLICDAIKCJFMAPBCGDDINOEKNIO Ran 0.7m during 3182ms and crashed RunInfo#1|PAJOHCDCALGEHOLICDAIKCJFMAPBCGDDINOEKNIO|150|0.7|3182|C Testing child: LNICNHMBBJKGGJOAMLOGOANGLKLNEKGHIDKHPMBD Ran 0.3m during 6508ms and crashed RunInfo#1|LNICNHMBBJKGGJOAMLOGOANGLKLNEKGHIDKHPMBD|150|0.3|6508|C Crossover: Finding the fittest neighbor Runner: AFLDPICDAMCALMNAEHGBEBMHJFBCPFKAHKOLNGKB| fitness: 0.1 Fittest Neighbor: KDAAJGDBEBLICDECGJONFNMIJOBKPELBPPDIMALF| fitness: Testing child: AFLDPICDABLICDNAEHGBEBMHJFBCPFKAHKOLNGKB Ran 0.2m during 2584ms and crashed RunInfo#1|AFLDPICDABLICDNAEHGBEBMHJFBCPFKAHKOLNGKB|150|0.2|2584|C Testing child: KDAAJGDBEMCALMECGJONFNMIJOBKPELBPPDIMALF Ran 7.3m during 60197ms and was stopped 3.3 7.4 1.2 1.6 3.3 3.3 7.4 49 RunInfo#1|KDAAJGDBEMCALMECGJONFNMIJOBKPELBPPDIMALF|150|7.3|60197|S Child 2 fit enough to join next generation: KDAAJGDBEMCALMECGJONFNMIJOBKPELBPPDIMALF|7.3 Replacing: AFLDPICDAMCALMNAEHGBEBMHJFBCPFKAHKOLNGKB|0.1 Crossover: Finding the fittest neighbor Runner: KDAAJGDBEBLICDECGJONFNMIJOBKPELBPPDIMALF| fitness: 7.4 Fittest Neighbor: AIACHFMILCOFKPACJGNDGHBDJOALDPJCMJMMLPBP| fitness: 0.9 Testing child: KDAAJGDILCOFKPECGJONFNMIJOBKPELBPPDIMALF Ran 0.3m during 3485ms and crashed RunInfo#1|KDAAJGDILCOFKPECGJONFNMIJOBKPELBPPDIMALF|150|0.3|3485|C Testing child: AIACHFMBEBLICDACJGNDGHBDJOALDPJCMJMMLPBP Ran 0.7m during 2428ms and crashed RunInfo#1|AIACHFMBEBLICDACJGNDGHBDJOALDPJCMJMMLPBP|150|0.7|2428|C Crossover: Finding the fittest neighbor Runner: AIACHFMILCOFKPACJGNDGHBDJOALDPJCMJMMLPBP| fitness: 0.9 Fittest Neighbor: KDAAJGDBEBLICDECGJONFNMIJOBKPELBPPDIMALF| fitness: 7.4 Testing child: AIACJGDBEBLICDECJGNDGHBDJOALDPJCMJMMLPBP Ran 1.3m during 11008ms and crashed RunInfo#1|AIACJGDBEBLICDECJGNDGHBDJOALDPJCMJMMLPBP|150|1.3|11008|C Testing child: KDAAHFMILCOFKPACGJONFNMIJOBKPELBPPDIMALF Ran 0.5m during 2167ms and crashed RunInfo#1|KDAAHFMILCOFKPACGJONFNMIJOBKPELBPPDIMALF|150|0.5|2167|C Crossover: Finding the fittest neighbor Runner: EMEIBHBHOAPFNGJCLHEDBOBMHKMKKBLCKFIMDFNA| fitness: 0.5 Fittest Neighbor: FGBCHFGFEIEJPGHEIJMAKCKJKJCGCFLHDGJDAJLE| fitness: 14.7 Testing child: EMEIBHBHOAPFNGJCLHMAKCKJKJCGCFLHKFIMDFNA Ran 0.2m during 2718ms and crashed RunInfo#1|EMEIBHBHOAPFNGJCLHMAKCKJKJCGCFLHKFIMDFNA|150|0.2|2718|C Testing child: FGBCHFGFEIEJPGHEIJEDBOBMHKMKKBLCDGJDAJLE Ran 0.3m during 3653ms and crashed RunInfo#1|FGBCHFGFEIEJPGHEIJEDBOBMHKMKKBLCDGJDAJLE|150|0.3|3653|C Crossover: Finding the fittest neighbor Runner: NNGFPMOLIKMEMHNINGAEMCLMHNACKFOOFNJEKPBI| fitness: 1.2 Fittest Neighbor: PAJOHCDCALGEHJOAMLOGOANGLKPBCGDDINOEKNIO| fitness: 0.7 Testing child: NNGFPMOLIKMEHJOAMLOGOANGLKPBCGOOFNJEKPBI Ran 1.4m during 2733ms and crashed RunInfo#1|NNGFPMOLIKMEHJOAMLOGOANGLKPBCGOOFNJEKPBI|150|1.4|2733|C Testing child: PAJOHCDCALGEMHNINGAEMCLMHNACKFDDINOEKNIO Ran 0.5m during 3351ms and crashed RunInfo#1|PAJOHCDCALGEMHNINGAEMCLMHNACKFDDINOEKNIO|150|0.5|3351|C ########## Generation 2 ########## … // log continues through 79 generations 50 APPENDIX C. SAMPLE GENERATION SUMMARY LOG //This log contains the first 10 generations of the Configuration 3 run shown in Figure //12 and featured in Appendix B ########## 1 ) -0.9 2 ) 0.0 3 ) -0.2 4 ) -0.1 5 ) 14.7 6 ) 0.6 7 ) 0.8 8 ) 2.3 9 ) 0.2 10) 1.3 11) 0.3 12) -0.5 13) 1.3 14) 0.2 15) 0.1 16) 0.9 17) 0.8 18) 0.6 19) 3.3 20) 0.1 21) 0.4 22) 1.6 23) 1.2 24) -0.6 25) 0.7 26) 0.1 27) 7.4 28) 0.9 29) 0.5 30) 1.2 Generation ########## 1 ) 2 ) 3 ) 4 ) 5 ) 6 ) 7 ) 8 ) 9 ) 10) 11) 12) 13) 14) 15) 16) 17) 18) 19) 20) 21) 22) 23) 24) -0.9 0.0 -0.2 -0.1 14.7 0.6 0.8 2.3 0.2 1.3 0.3 -0.5 1.3 0.2 0.1 0.9 0.8 0.6 3.3 0.1 0.4 1.6 1.2 -0.6 Generation 1 ########## | DLMPDJINICNLKLMPHDMHIJNHFOPCOOJMPPODGJGH | LBBANMKDEKMFINFPDIEOOAFMGLHFIOILAFPHDPIC | GBKDPHFADPDJFNNMGINMJCJIEJDOLHLLFEBOAAMC | BBNBGGCKDFNIFFAOOJFNNIFFHNHDNIPOAMOFKIJH | FGBCHFGFEIEJPGHEIJMAKCKJKJCGCFLHDGJDAJLE | HCEJNGFOIIHMIBHEPGFJMLNIHKNIFPCDEHPGHEGF | BOONAKFEKHAKFOEKNHADBNOPOLJFBCFDPLMHLIDG | FMFDKJOPLJHNOKOJMKHFEBIPAFPJFLJINCLEHJID | MHFAMAGMGFDAIDHFBOFABLDPMBOAFNECIIFEDDIH | JBBBKGEJCHNGBAEGLFFCOBDJIKAMJIMMPODINHCI | PJCAMLJLIAJHNFHFIAMEJCOJHILDHPMGBILFDAGB | PDDGIFELMCOPCAJHICAPAJOGHMEFLPIPFGIFPKEE | GHOPIHFBMLEMJJEIMFMEIOLGHHDDLAFPHKMKPENG | ODFPGMPHNHPGODGFLPNDIBFONDOBDPBPCHIOIALD | FOFBBIGGDAHKKKBHEEALBPIEEMCALBLMDLEIIOPA | NCMDJBBOMPECHCDNFNFMENJKILOAECDFCPPHKHKI | MKIGDONODMMKLLPKPNLFEDLCONEOBNFKEDPHJODH | GLEGEKMLIGAGMLLNAAAOPFOOKMKMKBNOIDHOAGMN | LNICNHMBBJKGGOLICDAIKCJFMALNEKGHIDKHPMBD | AHNOGEBHEAOJNGIKBFMPANNFDBEGJADOBJOIBMEK | HLEPGHBFBPHIKHFOLFCILJHPJPKIMMACNLPHFFBN | HMLIIKHLBNJEHNLNNKFFFDDMJBGPEOJBIKOKFHPG | MAIKPBHCMONBCLOIIBEGMFMJCBLHNEOGBIMADIKI | LDGCPHPNKBJIBGNHHBPBCJMPFNBAAMIMAFOLHNFK | PAJOHCDCALGEHJOAMLOGOANGLKPBCGDDINOEKNIO | AFLDPICDAMCALMNAEHGBEBMHJFBCPFKAHKOLNGKB | KDAAJGDBEBLICDECGJONFNMIJOBKPELBPPDIMALF | AIACHFMILCOFKPACJGNDGHBDJOALDPJCMJMMLPBP | EMEIBHBHOAPFNGJCLHEDBOBMHKMKKBLCKFIMDFNA | NNGFPMOLIKMEMHNINGAEMCLMHNACKFOOFNJEKPBI 1 Average Fitness: 1.3066669 Generation 2 | | | | | | | | | | | | | | | | | | | | | | | | ########## DLMPDJINICNLKLMPHDMHIJNHFOPCOOJMPPODGJGH LBBANMKDEKMFINFPDIEOOAFMGLHFIOILAFPHDPIC GBKDPHFADPDJFNNMGINMJCJIEJDOLHLLFEBOAAMC BBNBGGCKDFNIFFAOOJFNNIFFHNHDNIPOAMOFKIJH FGBCHFGFEIEJPGHEIJMAKCKJKJCGCFLHDGJDAJLE HCEJNGFOIIHMIBHEPGFJMLNIHKNIFPCDEHPGHEGF BOONAKFEKHAKFOEKNHADBNOPOLJFBCFDPLMHLIDG FMFDKJOPLJHNOKOJMKHFEBIPAFPJFLJINCLEHJID MHFAMAGMGFDAIDHFBOFABLDPMBOAFNECIIFEDDIH JBBBKGEJCHNGBAEGLFFCOBDJIKAMJIMMPODINHCI PJCAMLJLIAJHNFHFIAMEJCOJHILDHPMGBILFDAGB PDDGIFELMCOPCAJHICAPAJOGHMEFLPIPFGIFPKEE GHOPIHFBMLEMJJEIMFMEIOLGHHDDLAFPHKMKPENG ODFPGMPHNHPGODGFLPNDIBFONDOBDPBPCHIOIALD FOFBBIGGDAHKKKBHEEALBPIEEMCALBLMDLEIIOPA NCMDJBBOMPECHCDNFNFMENJKILOAECDFCPPHKHKI MKIGDONODMMKLLPKPNLFEDLCONEOBNFKEDPHJODH GLEGEKMLIGAGMLLNAAAOPFOOKMKMKBNOIDHOAGMN LNICNHMBBJKGGOLICDAIKCJFMALNEKGHIDKHPMBD AHNOGEBHEAOJNGIKBFMPANNFDBEGJADOBJOIBMEK HLEPGHBFBPHIKHFOLFCILJHPJPKIMMACNLPHFFBN HMLIIKHLBNJEHNLNNKFFFDDMJBGPEOJBIKOKFHPG MAIKPBHCMONBCLOIIBEGMFMJCBLHNEOGBIMADIKI LDGCPHPNKBJIBGNHHBPBCJMPFNBAAMIMAFOLHNFK 51 25) 0.7 26) 7.3 27) 7.4 28) 0.9 29) 0.5 30) 1.2 Generation ########## 1 ) -0.9 2 ) 9.7 3 ) -0.2 4 ) -0.1 5 ) 14.7 6 ) 0.6 7 ) 0.8 8 ) 2.3 9 ) 1.1 10) 1.3 11) 0.3 12) -0.5 13) 1.3 14) 0.2 15) 0.1 16) 0.9 17) 0.8 18) 0.6 19) 3.3 20) 5.1 21) 0.4 22) 1.6 23) 1.2 24) -0.6 25) 12.8 26) 7.3 27) 7.4 28) 0.9 29) 12.6 30) 1.2 Generation ########## 1 ) 2 ) 3 ) 4 ) 5 ) 6 ) 7 ) 8 ) 9 ) 10) 11) 12) 13) 14) 15) 16) 17) 18) 19) 20) 21) 22) -0.9 10.6 -0.2 -0.1 14.7 0.6 0.8 2.3 1.1 1.3 9.6 3.9 1.3 0.2 2.3 0.9 7.3 5.5 8.1 10.9 0.4 1.6 | PAJOHCDCALGEHJOAMLOGOANGLKPBCGDDINOEKNIO | KDAAJGDBEMCALMECGJONFNMIJOBKPELBPPDIMALF | KDAAJGDBEBLICDECGJONFNMIJOBKPELBPPDIMALF | AIACHFMILCOFKPACJGNDGHBDJOALDPJCMJMMLPBP | EMEIBHBHOAPFNGJCLHEDBOBMHKMKKBLCKFIMDFNA | NNGFPMOLIKMEMHNINGAEMCLMHNACKFOOFNJEKPBI 2 Average Fitness: 1.5466669 Generation 3 ########## | DLMPDJINICNLKLMPHDMHIJNHFOPCOOJMPPODGJGH | KDAAJGDBEMCALMECGJEOOAFMGLHFIOILAFPHDPIC | GBKDPHFADPDJFNNMGINMJCJIEJDOLHLLFEBOAAMC | BBNBGGCKDFNIFFAOOJFNNIFFHNHDNIPOAMOFKIJH | FGBCHFGFEIEJPGHEIJMAKCKJKJCGCFLHDGJDAJLE | HCEJNGFOIIHMIBHEPGFJMLNIHKNIFPCDEHPGHEGF | BOONAKFEKHAKFOEKNHADBNOPOLJFBCFDPLMHLIDG | FMFDKJOPLJHNOKOJMKHFEBIPAFPJFLJINCLEHJID | FMFAMAGMGFDAIDHJMKHFEBIPAFPJFLJINCLEHJID | JBBBKGEJCHNGBAEGLFFCOBDJIKAMJIMMPODINHCI | PJCAMLJLIAJHNFHFIAMEJCOJHILDHPMGBILFDAGB | PDDGIFELMCOPCAJHICAPAJOGHMEFLPIPFGIFPKEE | GHOPIHFBMLEMJJEIMFMEIOLGHHDDLAFPHKMKPENG | ODFPGMPHNHPGODGFLPNDIBFONDOBDPBPCHIOIALD | FOFBBIGGDAHKKKBHEEALBPIEEMCALBLMDLEIIOPA | NCMDJBBOMPECHCDNFNFMENJKILOAECDFCPPHKHKI | MKIGDONODMMKLLPKPNLFEDLCONEOBNFKEDPHJODH | GLEGEKMLIGAGMLLNAAAOPFOOKMKMKBNOIDHOAGMN | LNICNHMBBJKGGOLICDAIKCJFMALNEKGHIDKHPMBD | ADAAJGDBEMCALMECGFMPANNFDBEGJADOBJOIBMEK | HLEPGHBFBPHIKHFOLFCILJHPJPKIMMACNLPHFFBN | HMLIIKHLBNJEHNLNNKFFFDDMJBGPEOJBIKOKFHPG | MAIKPBHCMONBCLOIIBEGMFMJCBLHNEOGBIMADIKI | LDGCPHPNKBJIBGNHHBPBCJMPFNBAAMIMAFOLHNFK | KDAAJGDBEMCALMOAMLOGOANGLKPBCELBPPDIMALF | KDAAJGDBEMCALMECGJONFNMIJOBKPELBPPDIMALF | KDAAJGDBEBLICDECGJONFNMIJOBKPELBPPDIMALF | AIACHFMILCOFKPACJGNDGHBDJOALDPJCMJMMLPBP | FGBCHFGFEIPFNGJCLHEDKCKJKJCGCFLHDGJDAJLE | NNGFPMOLIKMEMHNINGAEMCLMHNACKFOOFNJEKPBI 3 Average Fitness: 2.8733332 Generation 4 | | | | | | | | | | | | | | | | | | | | | | ########## DLMPDJINICNLKLMPHDMHIJNHFOPCOOJMPPODGJGH KDAAJGDBEMCALMECGJONFNMIJOBKPELBPPDIMALF GBKDPHFADPDJFNNMGINMJCJIEJDOLHLLFEBOAAMC BBNBGGCKDFNIFFAOOJFNNIFFHNHDNIPOAMOFKIJH FGBCHFGFEIEJPGHEIJMAKCKJKJCGCFLHDGJDAJLE HCEJNGFOIIHMIBHEPGFJMLNIHKNIFPCDEHPGHEGF BOONAKFEKHAKFOEKNHADBNOPOLJFBCFDPLMHLIDG FMFDKJOPLJHNOKOJMKHFEBIPAFPJFLJINCLEHJID FMFAMAGMGFDAIDHJMKHFEBIPAFPJFLJINCLEHJID JBBBKGEJCHNGBAEGLFFCOBDJIKAMJIMMPODINHCI FGBCHFGFEIEJPGHEIJMAKCKJKJCGCFLHDGJDAJLE BOONAKELMCOPCAJHICAPBNOPOLJFBCFDPLMHLIDG GHOPIHFBMLEMJJEIMFMEIOLGHHDDLAFPHKMKPENG ODFPGMPHNHPGODGFLPNDIBFONDOBDPBPCHIOIALD FOFBBIGGDAHKKKBJMKHFEBIPAFPALBLMDLEIIOPA NCMDJBBOMPECHCDNFNFMENJKILOAECDFCPPHKHKI MKIGDONODMMKLLPKPNLGMFMJCBEOBNFKEDPHJODH GHOPEKMLIGEMJJEIMFMEIOLGHHDDLAFPHKMKPENG KDAAJGDBEMCALMOAMLOIKCJFMALNEKGHIDKIMALF KDAAJGDBEMCALMECGFMNFNMIJOBKPELBPPDIMALF HLEPGHBFBPHIKHFOLFCILJHPJPKIMMACNLPHFFBN HMLIIKHLBNJEHNLNNKFFFDDMJBGPEOJBIKOKFHPG 52 23) 1.2 24) -0.6 25) 12.8 26) 10.7 27) 7.4 28) 0.9 29) 15.5 30) 1.2 Generation ########## 1 ) -0.9 2 ) 10.6 3 ) -0.2 4 ) -0.1 5 ) 16.1 6 ) 13.1 7 ) 0.8 8 ) 8.5 9 ) 5.3 10) 14.5 11) 15.8 12) 3.9 13) 1.3 14) 0.2 15) 2.3 16) 0.9 17) 7.3 18) 5.5 19) 8.1 20) 10.9 21) 0.4 22) 1.6 23) 1.2 24) 6.9 25) 12.8 26) 10.7 27) 11.6 28) 0.9 29) 15.5 30) 14.2 Generation ########## 1 ) 2 ) 3 ) 4 ) 5 ) 6 ) 7 ) 8 ) 9 ) 10) 11) 12) 13) 14) 15) 16) 17) 18) 19) 20) -0.9 10.6 -0.2 16.1 16.1 16.0 0.8 8.5 5.7 14.5 15.8 3.9 7.0 0.2 2.3 0.9 7.3 12.4 11.3 10.9 | MAIKPBHCMONBCLOIIBEGMFMJCBLHNEOGBIMADIKI | LDGCPHPNKBJIBGNHHBPBCJMPFNBAAMIMAFOLHNFK | KDAAJGDBEMCALMOAMLOGOANGLKPBCELBPPDIMALF | KDAAJGDBEMCALMECGJONFNMIJOBKPELBPPDIMALF | KDAAJGDBEBLICDECGJONFNMIJOBKPELBPPDIMALF | AIACHFMILCOFKPACJGNDGHBDJOALDPJCMJMMLPBP | FGBCHFGFEIEFNGJCLHEDKCKJKJCGCFLHDGJDAJLE | NNGFPMOLIKMEMHNINGAEMCLMHNACKFOOFNJEKPBI 4 Average Fitness: 4.3766665 Generation 5 ########## | DLMPDJINICNLKLMPHDMHIJNHFOPCOOJMPPODGJGH | KDAAJGDBEMCALMECGJONFNMIJOBKPELBPPDIMALF | GBKDPHFADPDJFNNMGINMJCJIEJDOLHLLFEBOAAMC | BBNBGGCKDFNIFFAOOJFNNIFFHNHDNIPOAMOFKIJH | FGBCHFGFEIEFNGJCLHMDKCKJKJCGCFLHDGJDAJLE | FGBCHFGFEIEJPGHEIJFJMLKJKJCGCFLHDGJDAJLE | BOONAKFEKHAKFOEKNHADBNOPOLJFBCFDPLMHLIDG | KDAAJGDBEMCALMECMKHNFNMIJOBKPELBPPDIMALF | FMFAMAGMGFDAIKBJMKHFEBIPAFPJFLJINCLEHJID | FGBCHFGFEIEJPGHEIFFCOBDJKJCGCFLHDGJDAJLE | FGBCHFGFEIEJPGHEIJMAKCKJKJCGCFLHDGJDAJLE | BOONAKELMCOPCAJHICAPBNOPOLJFBCFDPLMHLIDG | GHOPIHFBMLEMJJEIMFMEIOLGHHDDLAFPHKMKPENG | ODFPGMPHNHPGODGFLPNDIBFONDOBDPBPCHIOIALD | FOFBBIGGDAHKKKBJMKHFEBIPAFPALBLMDLEIIOPA | NCMDJBBOMPECHCDNFNFMENJKILOAECDFCPPHKHKI | MKIGDONODMMKLLPKPNLGMFMJCBEOBNFKEDPHJODH | GHOPEKMLIGEMJJEIMFMEIOLGHHDDLAFPHKMKPENG | KDAAJGDBEMCALMOAMLOIKCJFMALNEKGHIDKIMALF | KDAAJGDBEMCALMECGFMNFNMIJOBKPELBPPDIMALF | HLEPGHBFBPHIKHFOLFCILJHPJPKIMMACNLPHFFBN | HMLIIKHLBNJEHNLNNKFFFDDMJBGPEOJBIKOKFHPG | MAIKPBHCMONBCLOIIBEGMFMJCBLHNEOGBIMADIKI | KDAAJGDBEMCALMOAMBPBCJJFMALNEKGHIDKIMALF | KDAAJGDBEMCALMOAMLOGOANGLKPBCELBPPDIMALF | KDAAJGDBEMCALMECGJONFNMIJOBKPELBPPDIMALF | KDAAJGDBEMCALMECGJONFNMIJOBKPELBPPDIMALF | AIACHFMILCOFKPACJGNDGHBDJOALDPJCMJMMLPBP | FGBCHFGFEIEFNGJCLHEDKCKJKJCGCFLHDGJDAJLE | FGBCHFGFEIEFNGJINGAEMCLJKJCGCFLHDGJDAJLE 5 Average Fitness: 6.6566668 Generation 6 | | | | | | | | | | | | | | | | | | | | ########## DLMPDJINICNLKLMPHDMHIJNHFOPCOOJMPPODGJGH KDAAJGDBEMCALMECGJONFNMIJOBKPELBPPDIMALF GBKDPHFADPDJFNNMGINMJCJIEJDOLHLLFEBOAAMC FGBCHFGFEIEFNGJCLHMNKCKJKJCGCFLHDGJDAJLE FGBCHFGFEIEFNGJCLHMDKCKJKJCGCFLHDGJDAJLE FGBCHFGFEIEFNGJCLHMDKCKJKJCGCFLHDGJDAJLE BOONAKFEKHAKFOEKNHADBNOPOLJFBCFDPLMHLIDG KDAAJGDBEMCALMECMKHNFNMIJOBKPELBPPDIMALF FMFAMAGMGFEJPGHEIFFFEBIPAFPJFLJINCLEHJID FGBCHFGFEIEJPGHEIFFCOBDJKJCGCFLHDGJDAJLE FGBCHFGFEIEJPGHEIJMAKCKJKJCGCFLHDGJDAJLE BOONAKELMCOPCAJHICAPBNOPOLJFBCFDPLMHLIDG KDAAJGDBEMEMJJEIMFMEIOLFMALNEKGHIDKIMALF ODFPGMPHNHPGODGFLPNDIBFONDOBDPBPCHIOIALD FOFBBIGGDAHKKKBJMKHFEBIPAFPALBLMDLEIIOPA NCMDJBBOMPECHCDNFNFMENJKILOAECDFCPPHKHKI MKIGDONODMMKLLPKPNLGMFMJCBEOBNFKEDPHJODH GHOPEKMLIMMKLLPKPNLEIOLGHHDDLAFPHKMKPENG KDAAJGDBEMCALMOAMLOGKCJFMALNEKGHIDKIMALF KDAAJGDBEMCALMECGFMNFNMIJOBKPELBPPDIMALF 53 21) 0.4 22) 1.6 23) 7.9 24) 6.9 25) 12.8 26) 11.5 27) 11.6 28) 0.9 29) 15.5 30) 21.0 Generation ########## 1 ) -0.9 2 ) 10.6 3 ) -0.2 4 ) 16.1 5 ) 16.1 6 ) 20.2 7 ) 0.8 8 ) 8.5 9 ) 5.7 10) 17.9 11) 15.8 12) 3.9 13) 12.0 14) 0.2 15) 2.3 16) 0.9 17) 9.8 18) 13.5 19) 11.3 20) 10.9 21) 9.8 22) 1.6 23) 11.1 24) 6.9 25) 16.4 26) 11.5 27) 11.6 28) 0.9 29) 20.3 30) 21.0 Generation ########## 1 ) 2 ) 3 ) 4 ) 5 ) 6 ) 7 ) 8 ) 9 ) 10) 11) 12) 13) 14) 15) 16) 17) 18) 16.1 10.6 14.9 18.9 16.1 20.2 7.7 8.5 12.8 17.9 15.8 3.9 12.0 0.2 2.3 0.9 16.3 13.5 | HLEPGHBFBPHIKHFOLFCILJHPJPKIMMACNLPHFFBN | HMLIIKHLBNJEHNLNNKFFFDDMJBGPEOJBIKOKFHPG | FGBCHFGFEIEFNGJCIBEGMFMJCBLHNEOGBIMDAJLE | KDAAJGDBEMCALMOAMBPBCJJFMALNEKGHIDKIMALF | KDAAJGDBEMCALMOAMLOGOANGLKPBCELBPPDIMALF | KDAAJGDBEMCALMEAMLOGOANGLKPBPELBPPDIMALF | KDAAJGDBEMCALMECGJONFNMIJOBKPELBPPDIMALF | AIACHFMILCOFKPACJGNDGHBDJOALDPJCMJMMLPBP | FGBCHFGFEIEFNGJCLHEDKCKJKJCGCFLHDGJDAJLE | FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE 6 Average Fitness: 8.309999 Generation 7 ########## | DLMPDJINICNLKLMPHDMHIJNHFOPCOOJMPPODGJGH | KDAAJGDBEMCALMECGJONFNMIJOBKPELBPPDIMALF | GBKDPHFADPDJFNNMGINMJCJIEJDOLHLLFEBOAAMC | FGBCHFGFEIEFNGJCLHMNKCKJKJCGCFLHDGJDAJLE | FGBCHFGFEIEFNGJCLHMDKCKJKJCGCFLHDGJDAJLE | FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE | BOONAKFEKHAKFOEKNHADBNOPOLJFBCFDPLMHLIDG | KDAAJGDBEMCALMECMKHNFNMIJOBKPELBPPDIMALF | FMFAMAGMGFEJPGHEIFFFEBIPAFPJFLJINCLEHJID | FGBCHFGFEIEFNGJCLFFCOBDJKJCGCFLHDGJDAJLE | FGBCHFGFEIEJPGHEIJMAKCKJKJCGCFLHDGJDAJLE | BOONAKELMCOPCAJHICAPBNOPOLJFBCFDPLMHLIDG | GHOPEKMLEMEMJJEIMFMEIOLFMALNEKGHIDKIMALF | ODFPGMPHNHPGODGFLPNDIBFONDOBDPBPCHIOIALD | FOFBBIGGDAHKKKBJMKHFEBIPAFPALBLMDLEIIOPA | NCMDJBBOMPECHCDNFNFMENJKILOAECDFCPPHKHKI | FGBCHFGFEIEJPGHKPNLGMFMJCBEOBNFHDGJDAJLE | GHOPEKNODMMKLLPKPNLGMFMGHHDDLAFPHKMKPENG | KDAAJGDBEMCALMOAMLOGKCJFMALNEKGHIDKIMALF | KDAAJGDBEMCALMECGFMNFNMIJOBKPELBPPDIMALF | KDAAJGDBEMCALMECGJCILJHPJOBKPELBPPDIMALF | HMLIIKHLBNJEHNLNNKFFFDDMJBGPEOJBIKOKFHPG | FGBCHFGFEIEFNGJCIBEGMFMJCBLHNFLHDGJDAJLE | KDAAJGDBEMCALMOAMBPBCJJFMALNEKGHIDKIMALF | FGBCHFGBEMCALGJCLHEEMCLJKJCGCFLHDGJDAJLE | KDAAJGDBEMCALMEAMLOGOANGLKPBPELBPPDIMALF | KDAAJGDBEMCALMECGJONFNMIJOBKPELBPPDIMALF | AIACHFMILCOFKPACJGNDGHBDJOALDPJCMJMMLPBP | FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE | FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE 7 Average Fitness: 9.55 Generation 8 | | | | | | | | | | | | | | | | | | ########## FGBCHFGFEIEFNGMPHDEEMCLJKJCGCFLHDGJDAJLE KDAAJGDBEMCALMECGJONFNMIJOBKPELBPPDIMALF FGBCHFGFEIEFNNNMGHMNKCKJKJCGCFLHDGJDAJLE FGBCHFGFEIEFNGJCLFFCOBKJKJCGCFLHDGJDAJLE FGBCHFGFEIEFNGJCLHMDKCKJKJCGCFLHDGJDAJLE FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE GHOPEKMEKHAKFOEIMFMEIOLFMALNEKGHIDKIMALF KDAAJGDBEMCALMECMKHNFNMIJOBKPELBPPDIMALF FGBCHFGMGFEJPGHEIFFFEBIPKJCGCFLHDGJDAJLE FGBCHFGFEIEFNGJCLFFCOBDJKJCGCFLHDGJDAJLE FGBCHFGFEIEJPGHEIJMAKCKJKJCGCFLHDGJDAJLE BOONAKELMCOPCAJHICAPBNOPOLJFBCFDPLMHLIDG GHOPEKMLEMEMJJEIMFMEIOLFMALNEKGHIDKIMALF ODFPGMPHNHPGODGFLPNDIBFONDOBDPBPCHIOIALD FOFBBIGGDAHKKKBJMKHFEBIPAFPALBLMDLEIIOPA NCMDJBBOMPECHCDNFNFMENJKILOAECDFCPPHKHKI FGBCHFGFEIEJPGHKPNLAKCKJKJCGCFLHDGJDAJLE GHOPEKNODMMKLLPKPNLGMFMGHHDDLAFPHKMKPENG 54 19) 15.1 20) 10.9 21) 10.3 22) 1.6 23) 18.6 24) 6.9 25) 16.4 26) 11.5 27) 11.6 28) 17.1 29) 20.3 30) 21.0 Generation ########## 1 ) 19.2 2 ) 12.3 3 ) 18.0 4 ) 18.9 5 ) 22.4 6 ) 20.2 7 ) 10.1 8 ) 8.5 9 ) 12.8 10) 18.3 11) 15.8 12) 3.9 13) 12.0 14) 0.2 15) 2.3 16) 0.9 17) 20.2 18) 17.2 19) 16.0 20) 14.0 21) 10.3 22) 1.6 23) 21.0 24) 6.9 25) 19.1 26) 11.5 27) 11.9 28) 17.5 29) 20.6 30) 21.0 Generation ########## 1 ) 2 ) 3 ) 4 ) 5 ) 6 ) 7 ) 8 ) 9 ) 10) 11) 12) 13) 14) 15) 16) 21.9 13.3 19.6 22.1 22.4 20.2 20.4 14.8 12.8 18.4 18.4 3.9 12.2 0.2 2.3 0.9 | KDAAJGDBEMCALGJCLHEEMCLJKJCGCFLHDGJDAJLE | KDAAJGDBEMCALMECGFMNFNMIJOBKPELBPPDIMALF | KDAAJGDBEMCALMECGJONFNMIJOBKPELBPPDIMALF | HMLIIKHLBNJEHNLNNKFFFDDMJBGPEOJBIKOKFHPG | FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE | KDAAJGDBEMCALMOAMBPBCJJFMALNEKGHIDKIMALF | FGBCHFGBEMCALGJCLHEEMCLJKJCGCFLHDGJDAJLE | KDAAJGDBEMCALMEAMLOGOANGLKPBPELBPPDIMALF | KDAAJGDBEMCALMECGJONFNMIJOBKPELBPPDIMALF | FGBCHFGFEIOFKPACJHEEMCLJKJCGCFLHDGJDAJLE | FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE | FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE 8 Average Fitness: 12.33 Generation 9 ########## | FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE | FGBCHFGFEIEFNGMPHDEEMCLJKJCGCFLHDGJDAJLE | FGBCHFGFEIEFNGJCLFFCOBKJKJCGCFLHDGJDAJLE | FGBCHFGFEIEFNGJCLFFCOBKJKJCGCFLHDGJDAJLE | FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE | FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE | GHOPEKMEKHEKFOEIMFMEIOLFMALNEKGHIDKIMALF | KDAAJGDBEMCALMECMKHNFNMIJOBKPELBPPDIMALF | FGBCHFGMGFEJPGHEIFFFEBIPKJCGCFLHDGJDAJLE | FGBCHFGFEIEFNGJCLFFCOBKJKJCGCFLHDGJDAJLE | FGBCHFGFEIEJPGHEIJMAKCKJKJCGCFLHDGJDAJLE | BOONAKELMCOPCAJHICAPBNOPOLJFBCFDPLMHLIDG | GHOPEKMLEMEMJJEIMFMEIOLFMALNEKGHIDKIMALF | ODFPGMPHNHPGODGFLPNDIBFONDOBDPBPCHIOIALD | FOFBBIGGDAHKKKBJMKHFEBIPAFPALBLMDLEIIOPA | NCMDJBBOMPECHCDNFNFMENJKILOAECDFCPPHKHKI | FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE | FGBCHFGFEIEJPGHKPNLAKCKJKJCGCFLHDGJDAJLE | FGBCHFGBEMCALGJCLHEEMCLJKJCGCFLHDGJDAJLE | KDAAJGDBEMCALGJCLFMNFNMIKJCGCFLHDGJDAJLE | KDAAJGDBEMCALMECGJONFNMIJOBKPELBPPDIMALF | HMLIIKHLBNJEHNLNNKFFFDDMJBGPEOJBIKOKFHPG | FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE | KDAAJGDBEMCALMOAMBPBCJJFMALNEKGHIDKIMALF | FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE | KDAAJGDBEMCALMEAMLOGOANGLKPBPELBPPDIMALF | FGBCHFGFEMCALMECJHEEMCLJKJCGCFLHDGJDAJLE | FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE | FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE | FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE 9 Average Fitness: 13.486667 Generation 10 | | | | | | | | | | | | | | | | ########## FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE FGBCHFGFEIEFNGJPHDEEMCLJKJCGCFLHDGJDAJLE FGBCHFGFEIEFNGJCLFFCOBKJKJCGCFLHDGJDAJLE FGBCHFGFEIEFNGJCLFFCOBKJKJCGCFLHDGJDAJLE FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE FGBCHFGFEIEFNGJCLHEEIOLFMALNEKGHDGJDAJLE KDAAJFGMGFEJPGECMKHNFNMIJOBKPELBPPDIMALF FGBCHFGMGFEJPGHEIFFFEBIPKJCGCFLHDGJDAJLE FGBCHFGFEIEFNGJCLFFCOBKJKJCGCFLHDGJDAJLE FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE BOONAKELMCOPCAJHICAPBNOPOLJFBCFDPLMHLIDG GHOPHFGFEIEMJJEIMFMEIOLFMALNEKGHIDKIMALF ODFPGMPHNHPGODGFLPNDIBFONDOBDPBPCHIOIALD FOFBBIGGDAHKKKBJMKHFEBIPAFPALBLMDLEIIOPA NCMDJBBOMPECHCDNFNFMENJKILOAECDFCPPHKHKI 55 17) 20.2 18) 17.2 19) 19.3 20) 14.0 21) 10.3 22) 1.6 23) 21.0 24) 6.9 25) 19.1 26) 14.9 27) 11.9 28) 20.4 29) 20.6 30) 21.0 Generation | | | | | | | | | | | | | | 10 FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE FGBCHFGFEIEJPGHKPNLAKCKJKJCGCFLHDGJDAJLE FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE KDAAJGDBEMCALGJCLFMNFNMIKJCGCFLHDGJDAJLE KDAAJGDBEMCALMECGJONFNMIJOBKPELBPPDIMALF HMLIIKHLBNJEHNLNNKFFFDDMJBGPEOJBIKOKFHPG FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE KDAAJGDBEMCALMOAMBPBCJJFMALNEKGHIDKIMALF FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE KGBCHFGFEIEALMEAMLOGOANGLKPBPELBPPDIMALF FGBCHFGFEMCALMECJHEEMCLJKJCGCFLHDGJDAJLE FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE FGBCHFGFEIEFNGJCLHEEMCLJKJCGCFLHDGJDAJLE Average Fitness: 14.739999 56 BIBLIOGRAPHY [1] B. Foddy. QWOP [Flash Game] (2008). Retrieved 6 November 2013 from http://www.foddy.net/Athletics.html [2] C. Wheeler. QWOP and Simulation Design, Pt. 2. The Rules on the Field blog. Posted on 10 July 2012. Retrieved 6 November 2013 from http://therulesonthefield.com/2012/07/10/qwop-and-simulation-design-pt-2/ [3] T. Jakobsen. Advanced Character Physics. IO Interactive, Denmark. Posted on 10 April 2008. Retrieved 6 November 2013 from http://web.archive.org/web/20080410171619/http://www.teknikus.dk/tj/gdc2001.htm [4] G. Brodman and R. Volstad. QWOP Learning. Department of Computer Science, Stanford University, 2012. Retrieved 6 November 2013 from http://cs229.stanford.edu/proj2012/BrodmanVoldstad-QWOPLearning.pdf [5] L. Vaucher. Genetically Engineered QWOP (Part 1). Slow Frog blog. Posted on 31 March 2011. Retrieved 6 November 2013 from http://slowfrog.blogspot.com/2011/03/genetically-engineered-qwop-part-1.html [6] D. Whitley and J. Kauth. GENITOR: A Different Genetic Algorithm. Department of Computer Science, Colorado State University, 1988. [7] H. Muhlenbein and T. Mahnig. A comparison of stochastic local search and population based search. Proceedings of the Congress on Evolutionary Computation (CEC ‘02), 1:255-260, May 2002. [8] G. Parker, D. Braun, and I. Cyliax. Evolving Hexapod Gaits Using a Cyclic Genetic Algorithm. In Proceedings of the IASTED International Conference on Artificial Intelligence and Soft Computing, 1997. 57 [9] J. Clune, B. Beckmann, C. Ofria, and R. Pennock. Evolving Coordinated Quadruped Gaits with the HyperNEAT Generative Encoding. In Proceedings of the 2009 IEEE Congress on Evolutionary Computation (CEC ‘09). May 2009. [10] K. Wolff and P. Nordin. Evolution of Efficient Gait With An Autonomous Biped Robot Using Visual Feedback. 2001 Genetic and Evolutionary Computation Conference Late Breaking Papers, page 482—489. San Francisco, California, September 2001 [11] R. Ramachandra. Fastest 100m run, QWOP (flash game). Guinnes World Records. Retrieved 6 November 2013 from http://challengers.guinnessworldrecords.com/challenges/160-fastest-100m-run-qwopflash-game