From LiveCode to LiveMorphs

by Hanson Schmidt-Cornelius on May 7, 2015 No comments

Evolution and natural selection are fundamental principals on which science bases the existence and origin of all natural living beings. The fundamental rules of this process are very simple but provide a vastly powerful means by which adaptation to a particular environment can take place. Evolution is covered to varying depths in biology text books, explaining how these principals influence life on this planet.

This blog looks at some of the principals behind evolution and selection and how this paradigm started to find its way into a relatively new field of computer science, artificial intelligence.

In his book “The Blind Watchmaker”, Richard Dawkins explores the mechanism of pure random development and contrasts this with guided random development that uses incremental selection. He then proceeds to turn these observations into a computer program that uses randomness and selection to evolve graphical shapes called biomorphs.

bunny

What I find nice about his approach is that it demonstrates evolution in an interactive fashion, allowing the user to take an active role in forming the shape of the biomorphs. Many advanced evolutionary programming techniques used in software development do not provide this kind of visual feedback.

The LiveCode application demonstrated in this blog shows how to create visual shapes similar to the biomorphs Richard Dawkins created. It also uses similar principles of data representation and data visualisation, but I call the figures LiveMorphs. You can construct the application by copying the functions and commands into the card script of a LiveCode stack and invoking the openCard handler, for example, by saving your stack and then reopening it or by calling the openStack handler from the message box.

The idea behind the application is that each LiveMorph has an underlying representation, similar to a gene that stores the unique representation of a living being. This representation or genotype is then converted into a physical representation that draws the LiveMorph. The physical representation is also referred to as phenotype. Separating the encoding of the representation from the actual representation is very important. It allows us to choose a genotype that can easily be exposed to modifications that are based on evolutionary rules. Purists may argue that the genotype should, as closely as possible, represent the genetic encoding of base pairs, and there clearly are valid reasons for doing so. I prefer to choose the genetic representation and the type of modification depending on what feels best for the application. The genotype for the LiveMorphs is a list of 13 integers that is set in the openCard handler and is represented as follows:

"10,10,10,10,10,10,10,10,10,10,10,10,10"

Using mutation, this is the default genotype from which all future generations are evolved:

function mutateGene pGene

local tGene, tItem

// Cycle through each inter in the genotype

repeat with tItem = 1 to the number of items in pGene

if tItem is a number then

// Mutate each integer and ensure the value is within a set range

put min (max (item tItem of pGene + random (21) - 11, -50), 50) & comma after tGene

end if

end repeat

return tGene

end mutateGene

We use min and max to restrict the values in the mutation. Using a more purist binary representation would remove the need for this limit at the mutation stage, allowing us to focus more on true evolutionary rules. You can also see that we only use mutation to change information between subsequent LiveMorphs. Evolution clearly provides other options that can be applied, for example, when genes from more than one organism are combined, but that is beyond the scope of this blog post.

stickperson

We now have the functional mechanism to represent and change the genotype of our LiveMorphs. The next consideration should be to decide on how this genetic representation is transformed into the physical representation. We have a lot of freedom to consider how to generate the phenotype. In this example, I use the graphic of buttons to represent the phenotype of LiveMorphs:

on genotypeToPhenotype pBtn

local tPhenotype, tGenotype, tX, tY

// Get the gene that is to be translated into the graphical shape

put the cGene of button pBtn into tGenotype

// Get the location where the shape is to be placed

put item 1 of the location of button pBtn into tX

put item 2 of the location of button pBtn into tY

put tX & comma & tY & return into tPhenotype

// Create the first side of the phenotype

repeat with tItem = 1 to the number of items in tGenotype step 2

put tX + item tItem of tGenotype & comma & tY + item tItem + 1 of tGenotype & return after tPhenotype

end repeat

put line 1 of tPhenotype & return after tPhenotype

// Create the second side of the phenotype

repeat with tItem = 1 to the number of items in tGenotype step 2

put tX - item tItem of tGenotype & comma & tY + item tItem + 1 of tGenotype & return after tPhenotype

end repeat

// Ensure the shape is closed

put line 1 of tPhenotype after tPhenotype

// Display the shape

set the points of graphic pBtn to tPhenotype

end genotypeToPhenotype

The main operation lies in two repeat loops that cycle through the genotype. The first repeat loop generates one side of the visual representation and the second loop generates the other side of the visual representation. This gives us a symmetric phenotype, similar to most living beings we are used to seeing in real life.

We now have the main building blocks for evolving LiveMorphs. Let us now add the remaining LiveCode sections that complete this application.

The openCard handler initialises the environment and ensures that all operational components are available for when we use the application:

on openCard

local tItem

// Ensure that any past operation of this code are removed

repeat with tItem = 1 to the number of controls

delete control 1

end repeat

// Set the parameters of the templateButton for our use

set the showName of templateButton to false

set the height of templateButton to 200

set the width of templateButton to 200

set the width of this stack to 400

set the height of this stack to 400

set the style of templateGraphic to "polygon"

set the opaque of templateGraphic to true

// Create four buttons that are to display the LiveMorphs

repeat with tItem = 1 to 4

new button tItem

new graphic tItem

// Add random colour to each LiveMorph being displayed

set the backgroundColor of graphic tItem to random (50)

end repeat

// Place the buttons

set the location of button "1" to 100,100

set the location of button "2" to 300,100

set the location of button "3" to 100,300

set the location of button "4" to 300,300

// Set up the default gene of each LiveMorph

set the cGene of this card to "10,10,10,10,10,10,10,10,10,10,10,10,10"

mouseUp

end openCard

First, possible previous execution content of the application is removed. Default button information is then set up that allows the buttons to appear in the appropriate size to display graphical objects. Next, four buttons are created and placed in a grid, presenting us with the user interface for the application. At this point we also add a random colour that is part of the LiveMorph on the particular button. Finally, we add the default genetic representation of the LiveMorphs and tell the application to mutate this information, using the code we discussed earlier in this blog.

The remaining section of code implements the selection of the parent whose gene is to move forward into the next generation to create new LiveMorphs. This is possibly the main aspect of the code as it provides part of the fitness function in which all LiveMorphs compete for selection. The user selects which LiveMorph is fit for selection by pressing the button of the LiveMorph that is visually most appealing. After selection, the gene of the chosen LiveMorph is used to create the next generation of four LiveMorphs from which yet again a new parent can be selected for mutation. This feature is implemented in a mouseUp handler as follows:

on mouseUp pButton

local tBtn, tParentGene

if target begins with "graphic" then exit mouseUp

put the cGene of target into tParentGene

repeat with tBtn = 1 to 4

set the cGene of button tBtn to mutateGene (tParentGene)

genotypeToPhenotype tBtn

end repeat

end mouseUp

This blog introduces an interesting aspect of Artificial Intelligence, using keywords that allow you to explore this field further in an appropriate Internet search engine. I also hope that the code demonstrated here is sufficiently short to allow you to make your own modifications and generate customized LiveMorphs with much altered properties.

Hanson Schmidt-CorneliusFrom LiveCode to LiveMorphs

Join the conversation

*