The Genetic Lander: an experiment in Genetic Programming

The Genetic Lander program was developed as an experiment to view the practicality of using an artificial intelligence technique known as Genetic Programming to develop a hypothetical lunar lander controller. By itself, this lunar lander simulator is to simple to be of any use since the physics have been so pruned, but the fact that it is a simple environment makes it a great test platform for learning algorithms. This applet serves more as a research tool than as a utility for developing a device to be used in the real world.

The applet tries to evolve a controller that will land the lunar lander on the blue landing platform. The applet starts automatically as soon as it is loaded. Genetic Lander consists of two main parts. In the background, Genetic Lander is constantly evolving new populations from an initial set of random programs. The display portion of the applet shows the best performing expression from the current generation in action.

The lunar lander is represented by a red square. This hypothetical lander is capable of thrusting from any direction underneath itself. A black line coming from the center of the craft represents the thrust.

The simulation can end in one of four ways before automatically restarting with the most current expression. These outcomes are dictated by the fact that the craft can't safely land if its vertical velocity exceeds 2.0 units/second or its horizontal velocity exceeds 0.8 units/second:
 
Crash the craft hit the ground exceeding the maximum vertical or horizontal velocity
Safe Landing the craft hit the ground at safe speeds, but it missed the mark
Successful Landing the craft landed on the landing pad at safe speeds
Out of Fuel the craft ran out of fuel before touching the ground

What is Genetic Programming?

Genetic Programming is a way to get the computer to simulate learning by evolving a solution to the given problem, in this case a lunar lander. Genetic programming was inspired by natural selection. The main idea behind it is that the computer starts with an initial population of randomly generated mini programs, 100 in this case, and by recombining and saving the good programs and removing the bad ones, the overall population of mini programs will gradually improve. Theoretically, good programs are composed of good building blocks and these building blocks can be rearranged into better programs. The mini programs, or expressions, are written in a language very similar to the programming language LISP. A math function such as:

3x + 2y
would be written as:
( add ( mul 3 x ) ( mul 2 y ))
in LISP. The main advantage is that programs written this way can be easily stored and visualized as a tree. The advantage of having a set of program trees is that it easier for the swapping of portions of the program. To combine two programs from a population is as simple as grabbing a sub-tree of one and inserting into a spot in another program tree. In this way, the expressions will always remain syntactically correct. Whether they will always perform well is another matter.

Deciding which expressions to keep, recombine, or removed is the responsibility of the fitness function. The fitness function scores each expression on the basis of performance. The fitness function in Genetic Lander runs in the background giving each expression in the population 5 chances to land correctly. Some factors that determine score are:

On average, a fitness value greater than 7000 means a successful landing. A fitness between 1000 and 2000 usually means a safe landing, and a negative fitness usually means the lander crashed or ran out of fuel.

Each expression has available to is a few basic math operations, a conditional, and some environmental variables. The number of available elements that expressions could be composed of was kept small to reduce the solution space and also to prevent a single operator that does too much. Add, subtract, multiply, divide, cosine, sine, inverse cosine, and inverse sine are include, as well as a branching conditional "if less than zero branch one way else branch the other way." The following environmental variables are available to expressions:
 
xVelocity the current horizontal velocity of the craft - positive is to the right
yVelocity the current vertical velocity of the craft - positive is down
directDistance the distance from the center of the craft to the center of the landing pad
directAngle the angle, in radians, from the craft to the landing pad - straight down is zero

The output of the expression determines at which angle the lunar lander will shoot out the thrust. Anything returned that is less than negative 5 or greater than positive 5 means no action will be performed by the lander. The lander will simply accelerate downwards in responce to gravity. Anything between -5 and 5 will be mapped into a thrust between 90 degrees to the left and 90 degrees to the right.

Using Genetic Lander

The applet is composed of two parts: the "working area" on the left and the statistics on the right. The working area is composed of two pages: the Lander Display page and the Controller Update page. The tabs at the bottom of this area is used to switch between the two.

Lander Display Page

The main purpose of the Lander Display is to show the current best expression in action. The current best expression is updated only when the lander lands or crashes not while the craft is still in the air. Clicking anywhere on the Lander Display page will start the lander at that location with full fuel.

Controller Update Page

The Controller Update page is where the user can interact with the evolution process. The expression in the text area shows the controller for the current lander being run. The user can modify this expression, or a whole new expression can be entered. WARNING: there is no real error checking. An invalid expression will just not be used.
 
Test Entered Expression sets the expression entered in the text box to the current expression on display
Add to Population adds the expression entered into the actual evolving population and also displays it
Get Current Best sets the best from the evolution process to the current expression on display
Auto Update if this flag is checked, then when the lander lands or crashes the current expression will be updated with a newer expression

 

Lander Statistics

The lander statistics shows the performance of the expression currently being shown on the Lander Display page. The coordinate (0,0) is the upper left corner of the screen.
 
Position the current position of the lander
Velocity the current velocity of the lander
Direct Angle the angle between the lander and the landing pad
Direct Distance the distance between the lander and the landing pad
Fuel the remaining fuel
Time the time elapsed since the current expression started running
Now Showing tells from what generation the current lander controller came from

Evolution Statistics

The evolution statistics shows the performance of the population being evolved in the background.
 
Generation the current generation number
Max Fitness the average fitness of the best performing expression
Average Fitness the average fitness of all the trials of all the expressions in this generation

Miscellaneous
 
New Population restarts the population of randomly created expressions - may take a couple seconds to process
Speed sets the Lander Display page speed - 0 is the fastest. by using large numbers, more processor time is given to the expressions being evolved in the background

 

Example Expressions:

Since the process of evolving a controller is subject to randomness, there are times when nothing will evolve that correctly lands on the landing pad. The evolution of the population may have become stagnant because of a few semi-good performers saturated the whole population. In this case it is best just to start with a new population. Here are some controllers that do reasonable well:
 
 

(sub (div (iflz (sin (cos (yVelocity))) (mul (div (directAngle) (mul (cos (yVelocity)) (add -2.305528473404248 -6.22291397636536))) 7.0404892298177195) (directDistance)) (yVelocity)) (xVelocity))
 
 

(sub (div (iflz (sin (cos (yVelocity))) (mul (div (directAngle) (mul (sub (asin 9.982073944360085) (yVelocity)) (add -2.305528473404248 (sub (div (iflz (sin (iflz (sin (cos (mul (sub (iflz (sin (yVelocity)) (directAngle) 7.0404892298177195) (yVelocity)) (add -2.305528473404248 (iflz (directAngle) (iflz -6.587768719548546 -8.440287062002804 (directAngle)) (directDistance)))))) (mul (div (directAngle) (mul (sub -8.440287062002804 (yVelocity)) (add (yVelocity) (iflz -6.587768719548546 (iflz -6.587768719548546 -8.440287062002804 (directAngle)) (add -2.305528473404248 (sub (div -6.587768719548546 (acos -8.075929770342773)) (xVelocity))))))) 7.0404892298177195) (directDistance))) (directAngle) (directDistance)) (acos -8.075929770342773)) (xVelocity))))) 7.0404892298177195) (directDistance)) (acos -6.615442979253265)) (xVelocity))
 
 

(iflz (iflz (sub (cos (directAngle)) (sin (yVelocity))) (sub (sub -2.4684089325679404 (asin -4.1692158746125525)) (sin (add -7.69110794441678 6.751196509037012))) (sub (mul (mul 4.877219410183999 6.468809843282834) (sin -9.498963974382432)) (cos (acos 8.187091167795458)))) (sub (directAngle) (iflz (add (sin (iflz (cos 2.8207755195175075) (xVelocity) 6.680786356368312)) (sub -3.792151134673853 (cos (directAngle)))) (xVelocity) (asin (div 8.187091167795458 (asin (directAngle)))))) (sub (div -8.075151056600305 (asin (directAngle))) (xVelocity)))
 
 

(iflz (iflz (sub (cos (sin -5.1497036256975655)) (sin (yVelocity))) (sub (sub -2.4684089325679404 (asin -4.1692158746125525)) (sin (add -7.69110794441678 6.751196509037012))) (sub (mul (mul 4.877219410183999 6.468809843282834) (sin -9.498963974382432)) (cos (acos 8.187091167795458)))) (sub (directAngle) (iflz (add (sin -1.018408489051728) (sub -3.792151134673853 (cos (directAngle)))) (xVelocity) (asin 8.682452651236034))) (sub (div -8.075151056600305 (asin (directAngle))) (xVelocity)))
 
 

(div (div (acos 8.509996845824482) (directAngle)) (add (mul (div (div (mul (sub (sub -8.934308268248476 8.279085031748117) -6.997432626974267) 9.77190654881824) -6.997432626974267) 9.77190654881824) (asin (cos 0.9432053390287809))) (yVelocity)))