Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
* develop:
  Pheromones inspired "NEW" algorithm #4.  Implemented and working. SPEED improved! 6,000ants at 30fps (from 20 fps alg #3)
  updateOnGrid cleaning
  smaller cargo food circle draw
  refactored all ant comm algorithms, separated function calls, clean the mix
  localizing a function, passing it as parameter has bad performance
  not mine, delete.

# Conflicts:
#	main.lua
  • Loading branch information
piXelicidio committed Mar 8, 2018
2 parents 2f88ae8 + 55cad1c commit a68bc40
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 110 deletions.
2 changes: 1 addition & 1 deletion code/actor.lua
Expand Up @@ -25,7 +25,7 @@ function TActor.create()
obj.position = {0, 0}
obj.radius = 1
obj.nodeRefs = {} --keys=values, store nodes of TQuickLists where the actor may be referenced. To make a clean "destruction" of the actor.
obj.gridInfo = {} --store stuff useful for Grid
obj.gridInfo = {posi = {0,0}} --store stuff useful for Grid

--PUBLIC functions
obj.classType = TActor
Expand Down
6 changes: 3 additions & 3 deletions code/ant.lua
Expand Up @@ -42,10 +42,10 @@ function TAnt.create()
obj.acceleration = 0.04 + math.random()*0.05
obj.erratic = cfg.antErratic --crazyness
obj.maxSpeed = cfg.antMaxSpeed
obj.tasks = {'food','cave'}
obj.tasks = {'food','cave'} --TODO: no need for Array of task, they can only have to targets, use two variables and swap
obj.lookingForTask = 1
obj.comingFromTask = 0
obj.lookingFor = 'food'
--obj.lookingFor = 'food'
obj.comingFrom = ''
obj.lastTimeSeenFood = -1
obj.lastTimeSeenCave = -1
Expand Down Expand Up @@ -194,7 +194,7 @@ function TAnt.create()
apiG.line(obj.position[1] - obj.direction[1]*2, obj.position[2] - obj.direction[2]*2, obj.position[1] + obj.direction[1]*2, obj.position[2] + obj.direction[2]*2 )
if obj.cargo.count~=0 then
apiG.setColor(cfg.colorFood)
if not cfg.debugGrid then apiG.circle("line", obj.position[1] + obj.direction[1]*2, obj.position[2] + obj.direction[2]*2, 1) end
if not cfg.debugGrid then apiG.circle("line", obj.position[1] + obj.direction[1]*2, obj.position[2] + obj.direction[2]*2, 0.5) end
end
-- debug
end
Expand Down
56 changes: 33 additions & 23 deletions code/map.lua
Expand Up @@ -27,24 +27,37 @@ map.limitsColor = cfg.colorBkLimits

local limitsRect = {}
local gridBorder = 2

-- calculating grid map dimensions, -- extra border (fGridBorder) to get rid of validations
map.minXg = math.floor(map.minX / map.gridSize) - gridBorder
map.maxXg = math.floor(map.maxX / map.gridSize) + gridBorder
map.minYg = math.floor(map.minY / map.gridSize) - gridBorder
map.maxYg = math.floor(map.maxY / map.gridSize) + gridBorder

function map.init()
-- calculating grid map dimensions
-- extra border (fGridBorder) to get rid of validations

-- initializing all Grid data structure, avoiding future validations and mem allocation

for i = map.minXg, map.maxXg do
map.grid[i]={}
for j = map.minYg, map.maxYg do
map.grid[i][j] = {
qlist = TQuickList.create(),
dcolor = {math.random(160), math.random(160), math.random(250)}
dcolor = {math.random(160), math.random(160), math.random(250)},
pheromInfo = {
seen = {}
}
}
for k = 1, #cfg.antInterests do
map.grid[i][j].pheromInfo.seen[ cfg.antInterests[k] ] = {
time = -1,
where = {1,0} --last position remembered on the direction coming from
}
end

end
end


end

--TODO: discard AddActor OR (AddAnt and addSurface) ... think...
Expand All @@ -58,25 +71,24 @@ function map.updateOnGrid_firstTime(grid, actor )
--vector position inside grid, integer values x,y
local idxX, idxY = math.floor(actor.position[1]/map.gridSize), math.floor(actor.position[2]/map.gridSize)
actor.gridInfo = {
posi = { idxX, idxY },
lastPosi = { idxX, idxY }
posi = { idxX, idxY }
}
-- insert my node on the bidimentional array grid
grid[ actor.gridInfo.posi[1] ][ actor.gridInfo.posi[2] ].qlist.add( actor.nodeRefs.gridNode )
end

function map.updateOnGrid(grid, actor)
actor.gridInfo.posi[1] = math.floor(actor.position[1]/map.gridSize)
actor.gridInfo.posi[2] = math.floor(actor.position[2]/map.gridSize)
local posiX = math.floor(actor.position[1]/map.gridSize)
local posiY = math.floor(actor.position[2]/map.gridSize)
--comparing to know if actor is now in a new grid X,Y
if (actor.gridInfo.posi[1] ~= actor.gridInfo.lastPosi[1] ) or (actor.gridInfo.posi[2] ~= actor.gridInfo.lastPosi[2] ) then
if (posiX ~= actor.gridInfo.posi[1] ) or (posiY ~= actor.gridInfo.posi[2] ) then
--move from old list to new list
actor.nodeRefs.gridNode.selfRemove()
grid[ actor.gridInfo.posi[1] ][ actor.gridInfo.posi[2] ].qlist.add( actor.nodeRefs.gridNode )
actor.gridInfo.lastPosi[1] = actor.gridInfo.posi[1]
actor.gridInfo.lastPosi[2] = actor.gridInfo.posi[2]
if cfg.debugGrid then actor.color = grid[ actor.gridInfo.posi[1] ][ actor.gridInfo.posi[2] ].dcolor end
end
grid[ posiX ][ posiY ].qlist.add( actor.nodeRefs.gridNode )
actor.gridInfo.posi[1] = posiX
actor.gridInfo.posi[2] = posiY
--if cfg.debugGrid then actor.color = grid[ posiX ][ posiY ].dcolor end
end
end

function map.addAnt( ant )
Expand Down Expand Up @@ -116,16 +128,14 @@ function map.draw()
apiG.setColor( map.limitsColor )
apiG.rectangle("line", map.minX, map.minY, map.maxX-map.minX, map.maxY-map.minY )
--debuging grid
--TODO: function as parameter is pretty slow, but this is debug.. fix?
if cfg.debugGrid then
map.gridForEachCell(
function(cell, i, j)
if cell.qlist.count>0 then
apiG.setColor( cell.dcolor )
apiG.print(cell.qlist.count, i * map.gridSize, j * map.gridSize)
end
local cellcount = function(cell, i, j)
if cell.qlist.count>0 then
apiG.setColor( cell.dcolor )
apiG.print(cell.qlist.count, i * map.gridSize, j * map.gridSize)
end
)
end
if cfg.debugGrid then
map.gridForEachCell( cellcount )
end
end

Expand Down
6 changes: 3 additions & 3 deletions code/simconfig.lua
@@ -1,15 +1,15 @@
---"Constants", defaults, globals........
local simconfig = {

numAnts = 4000,
numAnts = 6000,
antMaxSpeed = 1.2,
antComAlgorithm = 2, -- 0 = Comm disabled; 1 = No optimizations; 2= Old 2003 gridmap way; 3 = New improved 2018 gridmap group-to-group comm.
antComAlgorithm = 4, -- 0 = Comm disabled; 1 = No optimizations; 2= Old 2003 gridmap way; 3 = New improved 2018 gridmap group-to-group comm.
antComRadius = 40, -- Ants communications radious, ignored on algorithm >1 ... gridSize*3/2 is the equivalent
antComEveryFrame = false, -- comunicate every frame? or use values of antComNeedFrameStep below
antComNeedFrameStep = {5,15}, -- {a,b} ant would need for comunication with other ants every amount of frames form a to b. Greater values more speed less path quality.
antComMaxBetterPaths = 3, -- During communicaitons, in a single frame each ant gets many better advices of new direction, how many are enough?
antSightDistance = 40, -- Only bellow this distance the ant can identify and locate things, bettr if > than antComRadius
antPositionMemorySize = 25, -- How many past position they can remember
antPositionMemorySize = 15, -- How many past position they can remember
antErratic = 0.1,
antInterests = {'food','cave'},

Expand Down
106 changes: 79 additions & 27 deletions code/simulation.lua
Expand Up @@ -9,11 +9,19 @@ local vec = require('libs.vec2d_arr')

local sim = {}

sim.interactionAlgorithm = {}

function sim.init()
math.randomseed(os.time())
map.init()


sim.interactionAlgorithm[0] = sim.algorithm0_doNothing
sim.interactionAlgorithm[1] = sim.algorithm1_ZeroOptimization
sim.interactionAlgorithm[2] = sim.algorithm2_oldChat
sim.interactionAlgorithm[3] = sim.algorithm3_groupCells
sim.interactionAlgorithm[4] = sim.algorithm4_pheromones

map.init()

local newSur
for i=1,1 do
newSur = TSurface.createCave(-250+200*(math.random()-0.5), 300*(math.random()-0.5), 20)
Expand Down Expand Up @@ -52,8 +60,11 @@ function sim.init()
if node.obj.classType == TAnt then numAnts = numAnts + 1 end
if node.obj.classType == TSurface then numSurs = numSurs + 1 end
end

print('numAnts: ',numAnts,' numSurs', numSurs)
print('Initial memory: '..math.floor( collectgarbage ('count'))..'kb')
print('Mem used: '..math.floor( collectgarbage ('count'))..'kb')


end

function sim.collisionAntWithLimits(ant)
Expand Down Expand Up @@ -89,45 +100,52 @@ function sim.collisionAntWithSurfaces(ant)
end
end

function sim.collisionDetection()
local ant
if cfg.antComAlgorithm ~= 3 then

function sim.algorithm0_doNothing()
for _,node in pairs(map.ants.array) do
--ant bounces with limits
ant = node.obj
local ant = node.obj
sim.collisionAntWithLimits(ant)
end --for ant node
end

-- **1) No optimizaiton, just test N*N all with all ants** no brain.
--if you are porting the code to other language or api start implementing this for simplicity and safety
function sim.algorithm1_ZeroOptimization()
for _,node in pairs(map.ants.array) do
--ant bounces with limits
local ant = node.obj
if not sim.collisionAntWithLimits(ant) then
--ants with surfaces
if not sim.collisionAntWithSurfaces(ant) then

-- **1) No optimizaiton, just test N*N all with all ants**.
--if you are porting the code to other language or api start implementing this for simplicity and safety
if cfg.antComAlgorithm == 1 then
local otherAnt
local betterPathCount = 0
if not sim.collisionAntWithSurfaces(ant) then

if (cfg.antComEveryFrame or ant.isComNeeded()) then
ant.communicateWithAnts(map.ants.array)
end

-- **2) Old 2003 way, chat with neighbors**
elseif cfg.antComAlgorithm == 2 then
end
end
end
end --for ant node
end

-- **2) Old 2003 way, chat with neighbors**
function sim.algorithm2_oldChat()
for _,node in pairs(map.ants.array) do
--ant bounces with limits
local ant = node.obj
if not sim.collisionAntWithLimits(ant) then
--ants with surfaces
if not sim.collisionAntWithSurfaces(ant) then
if (cfg.antComEveryFrame or ant.isComNeeded()) then
local antLists = map.antsNearMe( ant )
ant.communicateWithAnts_grid( antLists )
end
end
end
end
end --for ant node
else
-- **3) New 2018, go by cell and process a cell group at once.**
if cfg.antComAlgorithm == 3 then
sim.antCommunication3()
end
end --if cfg.antCom... ~= 3
end

-- 3) **New algorithm 2018 group info matters to all**, share it
function sim.antCommunication3()
function sim.algorithm3_groupCells()
local centerCell --TQuickList
local neiborCell --TQuickList

Expand Down Expand Up @@ -186,8 +204,42 @@ function sim.antCommunication3()
end --fori
end

-- **4) Old algorithm 2 plus Pheromones inspiration**, store bestSeen info on the cells.
-- this time they communicate indirectly using the Grid cells, equivalent to pheromones nature
function sim.algorithm4_pheromones()
for _,node in pairs(map.ants.array) do
--ant bounces with limits
local ant = node.obj
if not sim.collisionAntWithLimits(ant) then
--ants with surfaces
if not sim.collisionAntWithSurfaces(ant) then
if (cfg.antComEveryFrame or ant.isComNeeded()) then
--get info on ant cell position, of time and position stored from other ants.
local pheromInfoSeen = map.grid[ ant.gridInfo.posi[1] ][ ant.gridInfo.posi[2] ].pheromInfo.seen
local myInterest = pheromInfoSeen[ ant.tasks[ant.lookingForTask] ]

if myInterest.time > ant.maxTimeSeen then
ant.maxTimeSeen = myInterest.time
ant.headTo( myInterest.where )
end
-- share what i Know in the map...
for name,time in pairs(ant.lastTimeSeen) do
local interest = pheromInfoSeen[ name ]
if time > interest.time then
interest.time = time
interest.where[1] = ant.oldestPositionRemembered[1]
interest.where[2] = ant.oldestPositionRemembered[2]
end
end --for
end
end --ifnot
end
end --for ant node
end

function sim.update()
sim.collisionDetection()

sim.interactionAlgorithm[cfg.antComAlgorithm]()

for _,node in pairs(map.ants.array) do
node.obj.update()
Expand Down

0 comments on commit a68bc40

Please sign in to comment.