Once upon a night of insomnia last week, I went a little bit too far on the weird part of YouTube, got inspired and decided to create "artificial life" in B4J.
(See attached jar file)
After a few hours of coding, a set of artificial creatures had been created.
They hunt, mate and sleep, usually in this order.
They're kinda on a cannibalistic diet, because I didn't bother to add any other species.
I'm happy to provide the source code, but bear in mind that I didn't bother to comment much, so the logic I used isn't explained at all. Besides, there's a bunch of hard-coded random numbers thrown in. This keeps the population stable, at least for a few generations, until they all eat each-other.
Here's the code, provided as is, written by a madman who couldn't sleep:
Again, please forgive me the shameless copy-pasting, the hard-coding and the lack of logic and structure. This is not a serious project.
(See attached jar file)
After a few hours of coding, a set of artificial creatures had been created.
They hunt, mate and sleep, usually in this order.
They're kinda on a cannibalistic diet, because I didn't bother to add any other species.
I'm happy to provide the source code, but bear in mind that I didn't bother to comment much, so the logic I used isn't explained at all. Besides, there's a bunch of hard-coded random numbers thrown in. This keeps the population stable, at least for a few generations, until they all eat each-other.
Here's the code, provided as is, written by a madman who couldn't sleep:
B4X:
#Region Project Attributes
#MainFormWidth: 800
#MainFormHeight: 600
#End Region
Sub Process_Globals
Private fx As JFX
Private MainForm As Form
'Custom Types
Type Position(x As Double, y As Double)
Type Velocity(x As Double, y As Double, angle As Double, magnitude As Double)
Type Physical(radius As Double, mass As Double, color As Paint, mouth As Double, mouthDir As Double)
Type Biology(energy As Double, food As Double, sleep As Double, health As Double, male As Int, children As Int)
Type Psychology(food As Double, exploration As Double)
Type Entity(Position As Position, Target As Position, Velocity As Velocity, Biology As Biology, Physical As Physical, Psychology As Psychology, Status As String, ID As Int)
'Constants and not so constants
Dim MAX_NUMBER_OF_CREATURES = 1024 As Int
Dim MAX_NUMBER_OF_CHILDREN = Rnd(16, 32) As Int
Dim STARTING_POPULATION = Rnd(32, 128) As Int
'Canvas
Dim cvs As Canvas
'Biological Entities
Dim Creatures(MAX_NUMBER_OF_CREATURES) As Entity
'Timers
Dim MainCycle As Timer
End Sub
Sub AppStart (Form1 As Form, Args() As String)
MainForm = Form1
MainForm.SetFormStyle("UNIFIED")
MainForm.BackColor = fx.Colors.RGB(0, 64, 128)
MainForm.Show
cvs.Initialize("cvs")
MainForm.RootPane.AddNode(cvs, 0, 0, MainForm.Width, MainForm.Height)
GenerateCreatures
MainCycle.Initialize("MainCycle", 16)
MainCycle.Enabled = True
End Sub
Sub GenerateCreatures
Dim id = 0 As Int
For Each Creature As Entity In Creatures
Dim biology As Biology
Dim position As Position
Dim target As Position
Dim velocity As Velocity
Dim physical As Physical
Dim psychology As Psychology
Dim color As Paint
color = fx.Colors.RGB(255, 255, 255)
biology.health = 1
biology.energy = 1
biology.food = Rnd(50, 80) / 100
biology.sleep = 0
biology.male = Rnd(0, 2)
biology.children = 0
physical.mouth = 0
physical.mouthDir = 0.04
physical.radius = 5
physical.mass = Rnd(10, 10 + physical.radius)
physical.color = color
position.x = Rnd(0 + physical.radius, MainForm.Width - physical.radius)
position.y = Rnd(0 + physical.radius, MainForm.Height - physical.radius)
target.x = position. x
target.y = position. y
velocity.x = 0
velocity.y = 0
velocity.angle = Rnd(0, 360) - 180
velocity.magnitude = 0
'let's roll with this for now
psychology.exploration = biology.food
'actually, let's not even using it...
Creature.Biology = biology
Creature.Position = position
Creature.Target = target
Creature.Velocity = velocity
Creature.Physical = physical
Creature.Status = "NORMAL"
Creature.ID = id
If id >= STARTING_POPULATION Then biology.health = 0
id = id + 1
Next
End Sub
Sub GenerateCreature(newCreature As Entity, x As Double, y As Double) As Entity
Dim biology As Biology
Dim position As Position
Dim target As Position
Dim velocity As Velocity
Dim physical As Physical
Dim psychology As Psychology
Dim color As Paint
color = fx.Colors.RGB(255, 255, 255)
biology.health = 1
biology.energy = 1
biology.food = 0.10
biology.sleep = 0
biology.male = Rnd(0, 2)
biology.children = 0
physical.mouth = 0
physical.mouthDir = 0.04
physical.radius = 3
physical.mass = Rnd(10, 10 + physical.radius)
physical.color = color
position.x = x
position.y = y
target.x = position. x
target.y = position. y
velocity.x = 0
velocity.y = 0
velocity.angle = Rnd(0, 360) - 180
velocity.magnitude = 0
'let's roll with this for now
psychology.exploration = biology.food
newCreature.Biology = biology
newCreature.Position = position
newCreature.Target = target
newCreature.Velocity = velocity
newCreature.Physical = physical
newCreature.Status = "NORMAL"
Return newCreature
End Sub
Sub MainCycle_Tick
cvs.ClearRect(0, 0, cvs.Width, cvs.Height)
For Each Creature As Entity In Creatures
If Creature.Biology.health > 0 Then
'Initialization
'-------------------------------------------------------------------------------------------
Dim biology = Creature.Biology As Biology
Dim position = Creature.Position As Position
Dim target = Creature.Target As Position
Dim velocity = Creature.Velocity As Velocity
Dim physical = Creature.Physical As Physical
Dim healthIndicator As Paint
Dim beingAlive = Rnd(1, 100) / 100000 As Double
Dim awereness = 50 As Double
'-------------------------------------------------------------------------------------------
'Biological Calculations
'-------------------------------------------------------------------------------------------
'Sleep
Dim sleepFactor As Double
If (biology.energy < 0.25 Or biology.sleep > 0.9) And _
biology.food > 0.50 Then Creature.Status = "SLEEP"
If (biology.sleep = 0 Or biology.food < 0.25) And _
biology.energy > 0.75 Then Creature.Status = "NORMAL"
If Creature.Status = "SLEEP" Then
biology.sleep = Max(0, biology.sleep - beingAlive * 2)
biology.energy = Min(1, biology.energy + beingAlive * (biology.food * 0.5))
sleepFactor = 0.33
Else
biology.sleep = Min(1, biology.sleep + beingAlive * 0.5)
sleepFactor = 1
End If
'Digestion
Dim foodFactor = 0.15 As Double
biology.food = Max(0, biology.food - beingAlive * foodFactor * sleepFactor)
'Energy
Dim energyFactor = 1 As Double
If biology.food = 0 Then biology.energy = Max(0, biology.energy - beingAlive * energyFactor * sleepFactor)
'Health
biology.health = Min(1, biology.health + beingAlive * biology.food)
Dim healthFactor = 1.5 As Double
Dim sleepFactor = 2 As Double
If biology.energy = 0 Then biology.health = Max(0, biology.health - beingAlive * healthFactor * biology.sleep * sleepFactor)
healthIndicator = fx.Colors.ARGB((1 - biology.health) * 255, 255, 0, 0)
'-------------------------------------------------------------------------------------------
'Decision Making
'-------------------------------------------------------------------------------------------
If Creature.Status <> "SLEEP" Then
'Search for food
If Creature.Biology.food < 0.75 Then
physical.mouth = physical.mouth + physical.mouthDir
If physical.mouth >= 0.5 Or physical.mouth <= 0 Then physical.mouthDir = -physical.mouthDir
For Each OtherCreature As Entity In Creatures
If Creature.ID = OtherCreature.ID Or OtherCreature.Biology.health = 0 Then Continue
Dim otherBiology = OtherCreature.Biology As Biology
If InRadius(Creature.Position.x, Creature.Position.y, _
OtherCreature.Position.x, OtherCreature.Position.y, _
awereness, OtherCreature.Physical.radius) _
Then
target.x = OtherCreature.Position.x
target.y = OtherCreature.Position.y
velocity.magnitude = Max(0.66, Creature.Biology.energy) * (Rnd(85, 175) / 100)
If InRadius(Creature.Position.x, Creature.Position.y, _
OtherCreature.Position.x, OtherCreature.Position.y, _
Creature.Physical.radius, 0) _
Then
physical.radius = physical.radius + physical.radius * 0.25
physical.mass = physical.mass + physical.mass * 0.10
biology.food = 1
otherBiology.health = 0
Exit
End If
Exit
End If
Next
Else
physical.mouth = 0.2
For Each OtherCreature As Entity In Creatures
If Creature.ID = OtherCreature.ID Or OtherCreature.Biology.health = 0 Then Continue
Dim otherBiology = OtherCreature.Biology As Biology
If biology.male <> otherBiology.male _
And Creature.Physical.radius > 4.5 _
And OtherCreature.Physical.radius > 4.5 _
And biology.children < MAX_NUMBER_OF_CHILDREN _
And otherBiology.children < MAX_NUMBER_OF_CHILDREN _
And InRadius(Creature.Position.x, Creature.Position.y, _
OtherCreature.Position.x, OtherCreature.Position.y, _
awereness, OtherCreature.Physical.radius) _
Then
target.x = OtherCreature.Position.x
target.y = OtherCreature.Position.y
velocity.magnitude = Max(0.66, Creature.Biology.energy) * (Rnd(85, 175) / 100)
If InRadius(Creature.Position.x, Creature.Position.y, _
OtherCreature.Position.x, OtherCreature.Position.y, _
Creature.Physical.radius, 0) _
Then
For Each unbornCreature As Entity In Creatures
If unbornCreature.biology.health = 0 Then
If Rnd(0, 2) = 1 Then
unbornCreature = GenerateCreature(unbornCreature, Creature.Position.x, Creature.Position.y)
biology.children = biology.children + 1
otherBiology.children = otherBiology.children + 1
End If
Exit
End If
Next
RndSeed(Rnd(0, 2147483647))
target.x = ((Rnd(MainForm.Width * 0.5 + Creature.Physical.radius, MainForm.Width * 1.5 - Creature.Physical.radius) - MainForm.Width) + (Rnd(Creature.Physical.radius, MainForm.Width - Creature.Physical.radius))) / 2
RndSeed(Rnd(0, 2147483647))
target.y = ((Rnd(MainForm.Height * 0.5 + Creature.Physical.radius, MainForm.Height * 1.5 - Creature.Physical.radius) - MainForm.Height) + (Rnd(Creature.Physical.radius, MainForm.Width - Creature.Physical.radius))) / 2
velocity.magnitude = Max(0.66, Creature.Biology.energy) * (Rnd(60, 100) / 100)
Exit
End If
Exit
End If
Next
End If
If InRadius(position.x, position.y, target.x, target.y, Creature.Physical.radius, 0) Then
RndSeed(Rnd(0, 2147483647))
target.x = ((Rnd(MainForm.Width * 0.5 + Creature.Physical.radius, MainForm.Width * 1.5 - Creature.Physical.radius) - MainForm.Width) + (Rnd(Creature.Physical.radius, MainForm.Width - Creature.Physical.radius))) / 2
RndSeed(Rnd(0, 2147483647))
target.y = ((Rnd(MainForm.Height * 0.5 + Creature.Physical.radius, MainForm.Height * 1.5 - Creature.Physical.radius) - MainForm.Height) + (Rnd(Creature.Physical.radius, MainForm.Width - Creature.Physical.radius))) / 2
velocity.magnitude = Max(0.66, Creature.Biology.energy) * (Rnd(60, 100) / 100)
End If
Dim energyFactor = Creature.Physical.mass * 0.02 As Double
biology.energy = Max(0, biology.energy - beingAlive * energyFactor)
Else
velocity.magnitude = velocity.magnitude * 0.9
End If
'Move
Dim targetAngle = ATan2D(target.y - position.y, target.x - position.x) As Double
Dim deltaAngle = Abs(velocity.angle - targetAngle) As Double
If deltaAngle < 180 Then
Dim turningDegrees = 4 * deltaAngle / 180 As Double
If targetAngle = velocity.angle Then
velocity.angle = velocity.angle
else if targetAngle < velocity.angle Then
velocity.angle = velocity.angle - turningDegrees
else if targetAngle > velocity.angle Then
velocity.angle = velocity.angle + turningDegrees
End If
Else
Dim turningDegrees = 4 * (deltaAngle - 180) / 180 As Double
If targetAngle = velocity.angle Then
velocity.angle = velocity.angle
else if targetAngle < velocity.angle Then
velocity.angle = velocity.angle + turningDegrees
else if targetAngle > velocity.angle Then
velocity.angle = velocity.angle - turningDegrees
End If
End If
If velocity.angle >= 360 Then velocity.angle = velocity.angle - 360
If velocity.angle <= -360 Then velocity.angle = velocity.angle + 360
velocity.x = CosD(velocity.angle) * velocity.magnitude
velocity.y = SinD(velocity.angle) * velocity.magnitude
position.x = position.x + velocity.x
position.y = position.y + velocity.y
If position.x < 0 Then position.x = position.x + cvs.Width
If position.x > cvs.Width Then position.x = position.x - cvs.Width
If position.y < 0 Then position.y = position.y + cvs.Height
If position.y > cvs.Height Then position.y = position.y - cvs.Height
'-------------------------------------------------------------------------------------------
'Tail B
cvs.DrawCircle(Creature.Position.x - CosD(Creature.Velocity.angle) * Creature.Physical.radius * 1.5, _
Creature.Position.y - SinD(Creature.Velocity.angle) * Creature.Physical.radius * 1.5, _
Creature.Physical.radius * 0.75, Creature.Physical.color, True, 0)
cvs.DrawCircle(Creature.Position.x - CosD(Creature.Velocity.angle) * Creature.Physical.radius * 1.5, _
Creature.Position.y - SinD(Creature.Velocity.angle) * Creature.Physical.radius * 1.5, _
Creature.Physical.radius * 0.75, healthIndicator, True, 0)
'Tail A
cvs.DrawCircle(Creature.Position.x - CosD(Creature.Velocity.angle) * Creature.Physical.radius * 2.5, _
Creature.Position.y - SinD(Creature.Velocity.angle) * Creature.Physical.radius * 2.5, _
Creature.Physical.radius * 0.5, Creature.Physical.color, True, 0)
cvs.DrawCircle(Creature.Position.x - CosD(Creature.Velocity.angle) * Creature.Physical.radius * 2.5, _
Creature.Position.y - SinD(Creature.Velocity.angle) * Creature.Physical.radius * 2.5, _
Creature.Physical.radius * 0.5, healthIndicator, True, 0)
'Head
cvs.DrawCircle(Creature.Position.x, Creature.Position.y, Creature.Physical.radius, Creature.Physical.color, True, 0)
cvs.DrawCircle(Creature.Position.x, Creature.Position.y, Creature.Physical.radius, healthIndicator, True, 0)
'Eyes
If Creature.Status = "SLEEP" Then
cvs.DrawLine(Creature.Position.x + Creature.Physical.radius * 0.25, _
Creature.Position.y - Creature.Physical.radius * 0.45, _
Creature.Position.x + Creature.Physical.radius * 0.50, _
Creature.Position.y - Creature.Physical.radius * 0.45, _
fx.Colors.Black, 1)
cvs.DrawLine(Creature.Position.x - Creature.Physical.radius * 0.25, _
Creature.Position.y - Creature.Physical.radius * 0.45, _
Creature.Position.x - Creature.Physical.radius * 0.50, _
Creature.Position.y - Creature.Physical.radius * 0.45, _
fx.Colors.Black, 1)
Else
If Creature.Biology.male = 1 Then
cvs.DrawCircle(Creature.Position.x + Creature.Physical.radius * 0.35, Creature.Position.y - Creature.Physical.radius * 0.45, Creature.Physical.radius * 0.25, fx.Colors.Blue, True, 0)
cvs.DrawCircle(Creature.Position.x - Creature.Physical.radius * 0.35, Creature.Position.y - Creature.Physical.radius * 0.45, Creature.Physical.radius * 0.25, fx.Colors.Blue, True, 0)
Else
cvs.DrawCircle(Creature.Position.x + Creature.Physical.radius * 0.35, Creature.Position.y - Creature.Physical.radius * 0.45, Creature.Physical.radius * 0.25, fx.Colors.Magenta, True, 0)
cvs.DrawCircle(Creature.Position.x - Creature.Physical.radius * 0.35, Creature.Position.y - Creature.Physical.radius * 0.45, Creature.Physical.radius * 0.25, fx.Colors.Magenta, True, 0)
End If
End If
'Mouth
cvs.DrawCircle(Creature.Position.x, Creature.Position.y + Creature.Physical.radius * 0.55, Creature.Physical.radius * Creature.Physical.mouth, fx.Colors.Black, True, 0)
End If
Next
End Sub
Sub InRadius(x1 As Double, y1 As Double, x2 As Double, y2 As Double, r1 As Double, r2 As Double) As Boolean
Dim dX = x1 - x2 As Double
Dim dY = y1 - y2 As Double
Dim sqRadialSum = (r1 + r2) * (r1 + r2) As Double
Return ((dX * dX) + (dY * dY) <= sqRadialSum)
End Sub
Again, please forgive me the shameless copy-pasting, the hard-coding and the lack of logic and structure. This is not a serious project.