Okay, I was bored over the last couple of days and decided to try and write something like the bug program I mentioned. I did it in AU3 though because I wanted to use arrays

.
Basically, the script creates a starting population of "bugs" with random "genes" such as speed and gender (the others aren't implemented yet) and they attempt to gather food and reproduce. The offspring have a chance to mutate a little, and they randomly receive "genes" from their parents. After 50 days they reach maturity, and the bugs die of old age after 500 days. They will also starve to death if they don't find enough food (average of about 1 food/33 days I think).
Anyways, this script seems to demonstrate at least a small amount of learning capability in that the bugs are getting more efficient at surviving. On my test run (which I have going right now) the bugs started out with a population that averaged 35-36, and now after almost 2000 days the population is sitting at 40-41. I know this isn't really anything that amazing, but it's a work in progress and I thought someone might find it interesting.
Here's the script:
Code:
; Bug Structure
; 0 => Genome
; 0 => Gender (0 = male, 1 = female)
; 1 => Speed (faster = faster health drain, but can get food better)
; 2 => Size (larger = faster health drain, but can fight better) [NYI]
; 3 => Recklessness (higher = less regard for self in fights, lower = more regard for self in fights) [NYI]
; 1 => Stats
; 0 => Age ( >= 50 = mature, Die at 500 )
; 1 => Health ( 0 = Dead, 100 = Start/Max )
; 2 => Move Mode ( 1 = To Food, 2 = To Mate )
; 3 => PosX
; 4 => PosY
; 5 => Recent Mating (Can only mate once/10 ages - Females only)
; 6 => Graphic Control ID
; Food Structure
; 0 => PosX
; 1 => PosY
; 2 => Graphic Control ID
#Include <Array.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
Global $AgeCD = 10, $AgeTotal = 0, $MaxBugs = 0, $BoundX = 100, $BoundY = 100, $FoodRegen = 1, $FoodRegenAmt = 5, $FoodRegenCD = 1, $DyingMessage_Gend = 0
GuiCreate("Bugs!",$BoundX*4,$BoundY*4)
$Graphic = GUICtrlCreateGraphic(0,0,$BoundX*4+1,$BoundY*4+1)
GUICtrlSetGraphic($Graphic,$GUI_GR_COLOR,0x00BB00,0x00BB00)
GuiCtrlSetGraphic($Graphic,$GUI_GR_RECT,0,0,$BoundX*4+1,$BoundY*4+1)
Func BuildStartingPopulation($Num)
Global $Bugs[$Num][2][7]
For $i=0 to $Num - 1 Step 1
$Bugs[$i][0][0] = Random(0,1,1)
$Bugs[$i][0][1] = Random(1,5,1)
$Bugs[$i][0][2] = Random(1,5,1)
$Bugs[$i][0][3] = Random(1,5,1)
$Bugs[$i][1][0] = 100
$Bugs[$i][1][1] = 100
$Bugs[$i][1][2] = Random(1,2,1)
$Bugs[$i][1][3] = Random(0,$BoundX,1)
$Bugs[$i][1][4] = Random(0,$BoundY,1)
$Bugs[$i][1][5] = 0
If $Bugs[$i][0][0] Then
$Bugs[$i][1][6] = CreateNewGraphic(0xEE0000,$Bugs[$i][1][3],$Bugs[$i][1][4])
Else
$Bugs[$i][1][6] = CreateNewGraphic(0x990000,$Bugs[$i][1][3],$Bugs[$i][1][4])
EndIf
Next
EndFunc
Func BuildStartingFood($Num)
Global $Food[$Num][3]
For $i=0 to $Num - 1 Step 1
Local $X = Random(0,$BoundX,1), $Y = Random(0,$BoundY,1)
If FoodAt($X,$Y) Then
$i -= 1
ContinueLoop
EndIf
$Food[$i][0] = $X
$Food[$i][1] = $Y
$Food[$i][2] = CreateNewGraphic(0x000099,$X,$Y)
Next
EndFunc
Func CreateNewGraphic($Color,$PosX,$PosY)
Local $NGraphic = GUICtrlCreateGraphic($PosX*4,$PosY*4,2,2)
GUICtrlSetGraphic($NGraphic,$GUI_GR_COLOR,$Color,$Color)
GuiCtrlSetGraphic($NGraphic,$GUI_GR_RECT,1,1,2,2)
; Fixes redraw issues
GuiCtrlSetPos($NGraphic,($PosX+1)*4,($PosY+1)*4,4,4)
GuiCtrlSetPos($NGraphic,$PosX*4,($PosY+1)*4,4,4)
GuiCtrlSetPos($NGraphic,($PosX+1)*4,$PosY*4,4,4)
GuiCtrlSetPos($NGraphic,$PosX*4,$PosY*4,4,4)
Return $NGraphic
EndFunc
Func MoveGraphic($ID,$PosX,$PosY)
GuiCtrlSetPos($ID,$PosX*4,$PosY*4,4,4)
EndFunc
Func DelGraphic($ID)
;GuiCtrlSetPos($ID,-1,-1)
GuiCtrlDelete($ID)
EndFunc
Func FoodAt($X,$Y)
For $i=0 to UBound($Food) - 1 Step 1
If $Food[$i][0] = $X AND $Food[$i][1] = $Y Then Return 1
Next
Return 0
EndFunc
Func Propagate($P1_ID,$P2_ID)
If $Bugs[$P2_ID][1][5] OR $Bugs[$P1_ID][0][0] = $Bugs[$P2_ID][0][0] OR $Bugs[$P1_ID][0][0] OR NOT $Bugs[$P2_ID][0][0] OR NOT CanMate($P1_ID) OR NOT CanMate($P2_ID) Then Return 0
ReDim $Bugs[UBound($Bugs)+1][UBound($Bugs,2)][UBound($Bugs,3)]
$NewBug = UBound($Bugs) - 1
$Parts = Round(UBound($Bugs,2) / 2)
$P1_P = 0
$P2_P = 0
Dim $Child[2][UBound($Bugs,2)]
For $i=0 to UBound($Bugs,2)-1 Step 1
$P = Random(1,2,1)
If (Execute("$P"&$P&"_P") < $Parts) Then
Execute("$P"&$P&"_P += 1")
Else
$P = Not($P - 1) + 1
EndIf
$Bugs[$NewBug][0][$i] = Execute("$Bugs[$P"&$P&"_ID][0]["&$i&"]")
If ($i > 0 And Random(0,5) = 5) Then $Bugs[$NewBug][0][$i] = Mutate($Bugs[$NewBug][0][$i])
Next
$Bugs[$NewBug][1][0] = 0
$Bugs[$NewBug][1][1] = 100
$Bugs[$NewBug][1][3] = $Bugs[$P1_ID][1][3]
$Bugs[$NewBug][1][4] = $Bugs[$P1_ID][1][4]
If $Bugs[$NewBug][0][0] Then
$Bugs[$NewBug][1][6] = CreateNewGraphic(0xEE0000,$Bugs[$NewBug][1][3],$Bugs[$NewBug][1][4])
Else
$Bugs[$NewBug][1][6] = CreateNewGraphic(0x990000,$Bugs[$NewBug][1][3],$Bugs[$NewBug][1][4])
EndIf
$Bugs[$P1_ID][1][1] -= 50
$Bugs[$P2_ID][1][1] -= 50
$Bugs[$P2_ID][1][5] = 10
Return 1
EndFunc
Func Mutate($Val)
Return Round($Val * Random(Random(.5,1),Random(1,1.5)))
EndFunc
Func CanMate($id)
If ($Bugs[$id][1][0] < 50 OR $Bugs[$id][1][1] < 50 OR ($Bugs[$id][0][0] AND $Bugs[$id][1][5])) Then Return 0
Return 1
EndFunc
Func AgeBugs()
$BugDies = 1
While $BugDies
$i = 0
While $i <= UBound($Bugs) - 1
$BugDies = 0
$Bugs[$i][1][1] -= $Bugs[$i][0][1]
If $Bugs[$i][1][5] > 0 Then $Bugs[$i][1][5] -= 1
If ($Bugs[$i][1][0] >= 500 OR $Bugs[$i][1][1] <= 0) Then
DelGraphic($Bugs[$i][1][6])
$BugDies = 1
If UBound($Bugs) > 1 Then
For $x=$i To UBound($Bugs)-2 Step 1
For $n=0 To UBound($Bugs,2) - 1 Step 1
For $o=0 To UBound($Bugs,3) - 1 Step 1
$Bugs[$x][$n][$o] = $Bugs[$x+1][$n][$o]
Next
Next
Next
ReDim $Bugs[UBound($Bugs) - 1][UBound($Bugs,2)][UBound($Bugs,3)]
Else
MsgBox(0,"Extinct","Your bugs have become extinct!"&@LF&@LF&"Time Passed: "&@TAB&$AgeTotal&@LF&"Max Pop: "&@TAB&$MaxBugs)
Exit
EndIf
Else
$Bugs[$i][1][0] += 1
EndIf
$i += 1
WEnd
WEnd
EndFunc
Func Eat($Bug_ID,$Food_ID)
$Bugs[$Bug_ID][1][1] += 10
If $Bugs[$Bug_ID][1][1] > 100 Then $Bugs[$Bug_ID][1][1] = 100
DelGraphic($Food[$Food_ID][2])
_ArrayDelete($Food,$Food_ID)
EndFunc
Func UpdateFood()
If $AgeCD Then Return 0
$FoodRegenCD -= 1
If $FoodRegenCD <= 0 Then
For $i=0 to $FoodRegenAmt - 1 Step 1
Local $X = Random(0,$BoundX,1), $Y = Random(0,$BoundY,1)
If FoodAt($X,$Y) Then
$i -= 1
ContinueLoop
EndIf
If Not UBound($Food) Then
Global $Food[1][3]
Else
ReDim $Food[UBound($Food)+1][3]
EndIf
$Food[UBound($Food) - 1][0] = $X
$Food[UBound($Food) - 1][1] = $Y
$Food[UBound($Food) - 1][2] = CreateNewGraphic(0x000099,$X,$Y)
Next
$FoodRegenCD = $FoodRegen
EndIf
EndFunc
Func UpdateBugs()
If UBound($Bugs) > $MaxBugs Then $MaxBugs = UBound($Bugs)
If $AgeCD <= 0 Then
AgeBugs()
$AgeCD = 10
$AgeTotal += 1
TrayTip("","",0)
TrayTip("Bug Info","Time Passed:"&@TAB&$AgeTotal&@LF&"Population:"&@TAB&UBound($Bugs)&@LF&"Food:"&@TAB&@TAB&UBound($Food),10)
EndIf
$AgeCD -= 1
For $i=0 to UBound($Bugs)-1 Step 1
If (CanMate($i)) Then
$Bugs[$i][1][2] = 2
Else
$Bugs[$i][1][2] = 1
EndIf
Next
EndFunc
Func MoveBugs()
For $i=0 to UBound($Bugs)-1 Step 1
If ($Bugs[$i][1][2] = 1) Then
$CFood = ClosestFood($i)
If $CFood >= 0 Then
For $x = 1 to 3 * $Bugs[$i][0][1] Step 1
$DistX = Abs($Bugs[$i][1][3] - $Food[$CFood][0])
$DistY = Abs($Bugs[$i][1][4] - $Food[$CFood][1])
If $DistX AND $DistY Then
If $DistX > $DistY Then
$Coord = 3
Else
$Coord = 4
EndIf
If $Bugs[$i][1][$Coord] > $Food[$CFood][$Coord - 3] Then
$Bugs[$i][1][$Coord] -= 1
Else
$Bugs[$i][1][$Coord] += 1
EndIf
MoveGraphic($Bugs[$i][1][6],$Bugs[$i][1][3],$Bugs[$i][1][4])
Else
Eat($i,$CFood)
ExitLoop
EndIf
Next
EndIf
Else
$Mate = ClosestMate($i)
If $Mate >= 0 Then
For $x = 1 to 5 * $Bugs[$i][0][1] Step 1
$DistX = Abs($Bugs[$i][1][3] - $Bugs[$Mate][1][3])
$DistY = Abs($Bugs[$i][1][4] - $Bugs[$Mate][1][4])
If $DistX AND $DistY Then
If $DistX > $DistY Then
$Coord = 3
Else
$Coord = 4
EndIf
If $Bugs[$i][1][$Coord] > $Bugs[$Mate][1][$Coord] Then
$Bugs[$i][1][$Coord] -= 1
Else
$Bugs[$i][1][$Coord] += 1
EndIf
MoveGraphic($Bugs[$i][1][6],$Bugs[$i][1][3],$Bugs[$i][1][4])
Else
If $Bugs[$i][0][1] Then
Propagate($Mate,$i)
Else
Propagate($i,$Mate)
EndIf
ExitLoop
EndIf
Next
EndIf
EndIf
Next
EndFunc
Func ClosestMate($id)
Local $Closest[2]
$Closest[0] = -1
$Closest[1] = -1
For $i=0 to UBound($Bugs)-1 Step 1
If $i = $id OR $Bugs[$id][0][0] = $Bugs[$i][0][0] Then ContinueLoop
If Sqrt(($Bugs[$i][1][3] - $Bugs[$id][1][3])^2 + ($Bugs[$i][1][4] - $Bugs[$id][1][4])^2) < $Closest[1] Or $Closest[1] = -1 Then
$Closest[0] = $i
$Closest[1] = Sqrt(($Bugs[$i][1][3] - $Bugs[$id][1][3])^2 + ($Bugs[$i][1][4] - $Bugs[$id][1][4])^2)
EndIf
Next
If $Closest[0] = -1 Then
If Not $Bugs[$id][0][0] Then
$Gend = "Females"
Else
$Gend = "Males"
EndIf
If Not $DyingMessage_Gend Then
MsgBox(0,"Dying!","All of the "&$Gend&" of your bug population have died! It's only a matter of time before the others die too.")
$DyingMessage_Gend = 1
EndIf
Return -1
EndIf
Return $Closest[0]
EndFunc
Func ClosestFood($id)
Local $Closest[2]
$Closest[0] = -1
$Closest[1] = -1
For $i=0 to UBound($Food)-1 Step 1
If Sqrt(($Food[$i][0] - $Bugs[$id][1][3])^2 + ($Food[$i][1] - $Bugs[$id][1][4])^2) < $Closest[1] Or $Closest[1] = -1 Then
$Closest[0] = $i
$Closest[1] = Sqrt(($Food[$i][0] - $Bugs[$id][1][3])^2 + ($Food[$i][1] - $Bugs[$id][1][4])^2)
EndIf
Next
Return $Closest[0]
EndFunc
BuildStartingPopulation(10)
BuildStartingFood(25)
GuiSetState()
While 1
UpdateFood()
UpdateBugs()
MoveBugs()
WEnd
You can change the starting population/amount of food with the lines "BuildStartingPopulation(10)" and "BuildStartingPopulation(25)". You can also change any of these globals found near the beginning of the script (the other ones are for the script to function):
$BoundX - Highest X coordinate for food creation
$BoundY - Highest Y coordinate for food creation
$FoodRegen - Number of days for new food to be made
$FoodRegenAmt - Amount of new food made each time
What the script does (atm) is create a traytip with the time passed, current population and current amount of food. Planning on making a gui with moving pixels for bugs/food eventually.
*Note - The script is slow, I still need to optimize it. Also it might take a few tries to get a population that doesn't go extinct within the first 500 days (usually due to no males or females being in the starting population)