mirror of
https://github.com/veden/Rampant.git
synced 2025-01-14 02:23:01 +02:00
added basic logic for unit group backoff if getting slaughtered
This commit is contained in:
parent
8caad56849
commit
40a471cbf8
110
control.lua
110
control.lua
@ -1,36 +1,45 @@
|
||||
local chunkUtils = require("libs/ChunkUtils")
|
||||
local regionUtils = require("libs/RegionUtils")
|
||||
local mapUtils = require("libs/MapUtils")
|
||||
local chunkProcessor = require("libs/ChunkProcessor")
|
||||
local mapProcessor = require("libs/MapProcessor")
|
||||
local constants = require("libs/Constants")
|
||||
local pheromoneUtils = require("libs/PheromoneUtils")
|
||||
local ai = require("libs/AI")
|
||||
local tests = require("Tests")
|
||||
|
||||
local pheromoneRoutine --coroutine holding state of in progress processing
|
||||
local mapRoutine --coroutine holding state of in progress processing
|
||||
local chunkRoutine
|
||||
|
||||
local regionMaps -- chunk based map
|
||||
local chunkProcessingQueue -- pending chunks to be processed
|
||||
local units -- units that are being commanded
|
||||
-- local players
|
||||
local regionMap -- chunk based map
|
||||
local pendingChunks -- pending chunks to be processed
|
||||
local natives -- units that are being commanded
|
||||
local surface -- main game surface
|
||||
|
||||
|
||||
-- hook functions
|
||||
|
||||
function onInit()
|
||||
print("init")
|
||||
global.regionMaps = {}
|
||||
global.chunkProcessingQueue = {}
|
||||
global.units = {}
|
||||
global.regionMap = {}
|
||||
global.pendingChunks = {}
|
||||
global.natives = {}
|
||||
|
||||
regionMaps = global.regionMaps
|
||||
chunkProcessingQueue = global.chunkProcessingQueue
|
||||
units = global.units
|
||||
regionMap = global.regionMap
|
||||
pendingChunks = global.pendingChunks
|
||||
natives = global.natives
|
||||
natives.squads = {}
|
||||
natives.bases = {}
|
||||
|
||||
-- game.map_settings.enemy_expansion.enabled = false
|
||||
|
||||
-- turn off enemy ai
|
||||
game.surfaces[1].peaceful_mode = true
|
||||
-- game.surfaces[1].peaceful_mode = true
|
||||
-- remove enemies that aren't off
|
||||
game.forces.enemy.kill_all_units()
|
||||
-- game.forces.enemy.kill_all_units()
|
||||
|
||||
-- queue all current chunks that wont be generated during play
|
||||
local surface = game.surfaces[1]
|
||||
surface = game.surfaces[1]
|
||||
for chunk in surface.get_chunks() do
|
||||
onChunkGenerated({surface=surface,
|
||||
area={left_top={x=chunk.x * 32,
|
||||
@ -40,45 +49,76 @@ end
|
||||
|
||||
function onLoad()
|
||||
print("load")
|
||||
regionMaps = global.regionMaps
|
||||
chunkProcessingQueue = global.chunkProcessingQueue
|
||||
units = global.units
|
||||
regionMap = global.regionMap
|
||||
pendingChunks = global.pendingChunks
|
||||
natives = global.natives
|
||||
end
|
||||
|
||||
function onChunkGenerated(event)
|
||||
-- queue generated chunk for delayed processing, queuing is required because some mods (RSO) mess with chunk as they
|
||||
-- are generated, which messes up the scoring.
|
||||
chunkProcessingQueue[#chunkProcessingQueue+1] = event
|
||||
if (event.surface.index == 1) then
|
||||
pendingChunks[#pendingChunks+1] = event
|
||||
end
|
||||
end
|
||||
|
||||
function onTick(event)
|
||||
if (event.tick % 45 == 0) then
|
||||
if (event.tick % 40 == 0) then
|
||||
-- using coroutines to keep the cpu load time managable will still being able to work large maps
|
||||
|
||||
if (#chunkProcessingQueue > 0) and ((chunkRoutine == nil) or (coroutine.status(chunkRoutine)=="dead")) then
|
||||
-- coroutines start suspended, so you have to resume them after creation
|
||||
chunkRoutine = coroutine.create(chunkUtils.chunkProcess)
|
||||
end
|
||||
if (chunkRoutine ~= nil) then
|
||||
if (chunkRoutine ~= nil) and (coroutine.status(chunkRoutine) ~= "dead") then
|
||||
coroutine.resume(chunkRoutine)
|
||||
elseif (#pendingChunks > 0) then
|
||||
-- coroutines start suspended, so you have to resume them after creation
|
||||
chunkRoutine = coroutine.create(chunkProcessor.processPendingChunks)
|
||||
coroutine.resume(chunkRoutine, regionMap, surface, natives, pendingChunks)
|
||||
end
|
||||
|
||||
if (pheromoneRoutine == nil) or (coroutine.status(pheromoneRoutine)=="dead") then
|
||||
pheromoneRoutine = coroutine.create(regionUtils.pheromoneProcess)
|
||||
end
|
||||
if (pheromoneRoutine ~= nil) then
|
||||
coroutine.resume(pheromoneRoutine)
|
||||
-- put down player pheromone for player hunters
|
||||
-- pheromoneUtils.playerScent(regionMap, game.players)
|
||||
|
||||
-- ai.attackPlayerNearNest(regionMap, surface, natives, game.players)
|
||||
|
||||
if (mapRoutine ~= nil) and (coroutine.status(mapRoutine) ~= "dead") then
|
||||
coroutine.resume(mapRoutine)
|
||||
elseif (mapRoutine == nil) or (coroutine.status(mapRoutine) == "dead") then
|
||||
mapRoutine = coroutine.create(mapProcessor.processMap)
|
||||
coroutine.resume(mapRoutine, regionMap, surface, natives)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function onDeath(event)
|
||||
local entity = event.entity
|
||||
if (entity.force.name == "enemy") and (entity.type == "unit") then
|
||||
local entityPosition = entity.position
|
||||
-- drop death pheromone where unit died
|
||||
pheromoneUtils.deathScent(regionMap,
|
||||
entityPosition.x,
|
||||
entityPosition.y,
|
||||
50)
|
||||
|
||||
ai.addAutonomousUnitGroup(entity.unit_group, natives)
|
||||
|
||||
--ai.purgeUnitGroups(natives)
|
||||
ai.retreatUnitGroup(entityPosition, entity.unit_group, regionMap, surface, natives)
|
||||
end
|
||||
end
|
||||
|
||||
-- setup variables in the various modules
|
||||
function onInitialTick(event)
|
||||
ai.init(regionMaps, units)
|
||||
regionUtils.init(regionMaps)
|
||||
chunkUtils.init(regionMaps, chunkProcessingQueue)
|
||||
-- players = game.players
|
||||
if (surface == nil) then
|
||||
surface = game.surfaces[1]
|
||||
end
|
||||
-- add processing handler into generated chunk event loop
|
||||
-- chunkProcessor.install(chunkUtils.checkChunkPassability)
|
||||
-- chunkProcessor.install(chunkUtils.scoreChunk)
|
||||
|
||||
-- add processing handler into chunk map processing
|
||||
-- mapProcessor.install(pheromoneUtils.enemyBaseScent)
|
||||
-- mapProcessor.install(ai.sendScouts)
|
||||
mapProcessor.install(pheromoneUtils.processPheromone)
|
||||
|
||||
-- used for debugging
|
||||
tests.initTester()
|
||||
@ -92,9 +132,11 @@ end
|
||||
script.on_init(onInit)
|
||||
script.on_load(onLoad)
|
||||
|
||||
script.on_event(defines.events.on_entity_died, onDeath)
|
||||
script.on_event(defines.events.on_tick, onInitialTick)
|
||||
script.on_event(defines.events.on_chunk_generated, onChunkGenerated)
|
||||
|
||||
remote.add_interface("rampant", {
|
||||
test1 = tests.test1
|
||||
test1 = tests.test1,
|
||||
test2 = tests.test2
|
||||
})
|
||||
|
147
libs/AI.lua
147
libs/AI.lua
@ -1,19 +1,140 @@
|
||||
local ai = {}
|
||||
|
||||
local regionMaps
|
||||
local units
|
||||
local retreatNeighbors = {1,2,3,4,5,6,7,8}
|
||||
local constants = require("Constants")
|
||||
local mapUtils = require("MapUtils")
|
||||
local unitGroupUtils = require("UnitGroupUtils")
|
||||
|
||||
function ai.attackPlayerNearNest(regionMap, player)
|
||||
--[[
|
||||
function ai.attackPlayerNearNest(regionMap, surface, natives, players)
|
||||
for i=1,#players do
|
||||
local player = players[i]
|
||||
local chunk = mapUtils.getChunkByPosition(regionMap, player.position.x, player.position.y)
|
||||
if (chunk ~= nil) and (chunk[constants.BASE_PHEROMONE] > 125) and (chunk[constants.DEATH_PHEROMONE] < 800) then
|
||||
local enemies = surface.find_enemy_units(player.position,
|
||||
30)
|
||||
if (#enemies > 0) then
|
||||
local unitGroup
|
||||
local enemyIndex = 1
|
||||
while (enemyIndex <= #enemies) and (unitGroup == nil) do
|
||||
local enemy = enemies[enemyIndex]
|
||||
if (enemy.unit_group ~= nil) then
|
||||
unitGroup = enemy.unit_group
|
||||
end
|
||||
enemyIndex = enemyIndex + 1
|
||||
end
|
||||
if (unitGroup == nil) then
|
||||
unitGroup = surface.create_unit_group({position=enemies[1].position})
|
||||
-- natives.squads[#natives.squads+1] = unitGroup
|
||||
end
|
||||
-- print("before " .. tostring(unitGroup.state))
|
||||
for x=1,#enemies do
|
||||
local enemy = enemies[x]
|
||||
if (enemy.unit_group == nil) then
|
||||
-- unitGroup.add_member(enemy)
|
||||
enemy.set_command({type=defines.command.group,
|
||||
group=unitGroup,
|
||||
distraction=defines.distraction.none})
|
||||
end
|
||||
end
|
||||
-- print("after " .. tostring(unitGroup.state))
|
||||
if (unitGroup.state == defines.group_state.gathering) then
|
||||
unitGroup.set_command({type=defines.command.attack,
|
||||
target=player.character})
|
||||
unitGroup.start_moving()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
]]--
|
||||
|
||||
function ai.retreatUnitGroup(position, unitGroup, regionMap, surface, natives)
|
||||
if (unitGroup ~= nil) then
|
||||
local memberCount = #unitGroup.members
|
||||
if (memberCount > 0) then
|
||||
local chunk = mapUtils.getChunkByPosition(regionMap, position.x, position.y)
|
||||
if (chunk ~= nil) then
|
||||
if (chunk[constants.DEATH_PHEROMONE] > 1500) and (memberCount > 5) then
|
||||
mapUtils.getNeighborChunks(regionMap,
|
||||
chunk.cX,
|
||||
chunk.cY,
|
||||
retreatNeighbors)
|
||||
local exitPath
|
||||
local exitScore = constants.MAX_PHEROMONE + 1
|
||||
local exitDirection
|
||||
for i=1, 8 do
|
||||
local neighborChunk = retreatNeighbors[i]
|
||||
if (neighborChunk ~= nil) then
|
||||
if (neighborChunk[constants.DEATH_PHEROMONE] < exitScore) then
|
||||
exitScore = neighborChunk[constants.DEATH_PHEROMONE]
|
||||
exitPath = neighborChunk
|
||||
exitDirection = i
|
||||
end
|
||||
end
|
||||
end
|
||||
if (exitPath ~= nil) then
|
||||
local exitPosition = {x=exitPath.pX,
|
||||
y=exitPath.pY}
|
||||
local nearGroup = unitGroupUtils.findNearByUnitGroup(natives, exitPosition, 50)
|
||||
|
||||
if (nearGroup == nil) then
|
||||
nearGroup = surface.create_unit_group({position=exitPosition})
|
||||
natives.squads[#natives.squads+1] = nearGroup
|
||||
end
|
||||
for i=1, memberCount do
|
||||
local member = unitGroup.members[i]
|
||||
if (member ~= nil) and member.valid then
|
||||
member.set_command({type=defines.command.group,
|
||||
group=nearGroup,
|
||||
distraction=defines.distraction.none})
|
||||
end
|
||||
end
|
||||
-- unitGroup.destroy()
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif (memberCount == 0) then
|
||||
unitGroup.destroy()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ai.addAutonomousUnitGroup(unitGroup, natives)
|
||||
if (unitGroup ~= nil) then
|
||||
local squads = natives.squads
|
||||
local addUnitGroup = true
|
||||
local i = 1
|
||||
while (i <= #squads) and addUnitGroup do
|
||||
local squad = squads[i]
|
||||
if (squad == unitGroup) then
|
||||
addUnitGroup = false
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
if addUnitGroup then
|
||||
squads[#squads+1] = unitGroup
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- function ai.purgeUnitGroups(natives)
|
||||
-- for i=1, #natives.squads do
|
||||
-- local squad = natives.squads[i]
|
||||
-- if (squad == nil) then
|
||||
-- table.remove(natives.squads, i)
|
||||
-- elseif (not squad.valid) then
|
||||
-- table.remove(natives.squads, i)
|
||||
-- elseif (#squad.members == 0) then
|
||||
-- squad.destroy()
|
||||
-- table.remove(natives.squads, i)
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- function ai.sendScouts(regionMap, surface, natives, chunk, neighbors, validNeighbors)
|
||||
|
||||
end
|
||||
|
||||
function ai.initializeScouts(regionMap)
|
||||
|
||||
end
|
||||
|
||||
function ai.init(maps, troops)
|
||||
units = troops
|
||||
regionMaps = maps
|
||||
end
|
||||
-- return validNeighbors
|
||||
-- end
|
||||
|
||||
return ai
|
40
libs/ChunkProcessor.lua
Executable file
40
libs/ChunkProcessor.lua
Executable file
@ -0,0 +1,40 @@
|
||||
local chunkProcessor = {}
|
||||
|
||||
local chunkUtils = require("ChunkUtils")
|
||||
|
||||
local processors = {}
|
||||
|
||||
function chunkProcessor.processPendingChunks(regionMap, surface, natives, pendingStack)
|
||||
local createChunk = chunkUtils.createChunk
|
||||
|
||||
local count = 0
|
||||
while (#pendingStack > 0) do
|
||||
local event = pendingStack[#pendingStack]
|
||||
pendingStack[#pendingStack] = nil
|
||||
|
||||
local chunk = createChunk(event.area.left_top.x,
|
||||
event.area.left_top.y)
|
||||
|
||||
local chunkX = chunk.cX
|
||||
|
||||
if regionMap[chunkX] == nil then
|
||||
regionMap[chunkX] = {}
|
||||
end
|
||||
regionMap[chunkX][chunk.cY] = chunk
|
||||
|
||||
for i=1, #processors do
|
||||
processors[i](chunk, surface, natives)
|
||||
end
|
||||
|
||||
count = count + 1
|
||||
if (count % 5 == 0) and (#pendingStack < 20) then
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function chunkProcessor.install(processor)
|
||||
processors[#processors+1] = processor
|
||||
end
|
||||
|
||||
return chunkProcessor
|
34
libs/MapProcessor.lua
Executable file
34
libs/MapProcessor.lua
Executable file
@ -0,0 +1,34 @@
|
||||
local mapProcessor = {}
|
||||
|
||||
local neighborsArray = {1,2,3,4,5,6,7,8}
|
||||
local processors = {}
|
||||
|
||||
function mapProcessor.processMap(regionMap, surface, natives)
|
||||
local neighbors = neighborsArray
|
||||
local validNeighbors = false
|
||||
|
||||
local count = 0
|
||||
|
||||
for _,ys in pairs(regionMap) do
|
||||
for _,chunk in pairs(ys) do
|
||||
|
||||
validNeighbors = false
|
||||
-- validNeighbors flag if the processor retrieved the neighbors of the chunk then true
|
||||
for i=1, #processors do
|
||||
validNeighbors = processors[i](regionMap, surface, natives, chunk, neighbors, validNeighbors)
|
||||
end
|
||||
|
||||
count = count + 1
|
||||
if (count % 1100 == 0) then
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function mapProcessor.install(processor)
|
||||
processors[#processors+1] = processor
|
||||
end
|
||||
|
||||
return mapProcessor
|
46
libs/MapUtils.lua
Executable file
46
libs/MapUtils.lua
Executable file
@ -0,0 +1,46 @@
|
||||
local mapUtils = {}
|
||||
|
||||
local constants = require("Constants")
|
||||
|
||||
local mFloor = math.floor
|
||||
local mMin = math.min
|
||||
local mMax = math.max
|
||||
|
||||
function mapUtils.getChunkByPosition(regionMap, x, y)
|
||||
local cX = mFloor(x * 0.03125)
|
||||
if (regionMap[cX] ~= nil) then
|
||||
return regionMap[cX][mFloor(y * 0.03125)]
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
--[[
|
||||
1 2 3
|
||||
\|/
|
||||
4- -5
|
||||
/|\
|
||||
6 7 8
|
||||
]]--
|
||||
function mapUtils.getNeighborChunks(regionMap, chunkX, chunkY, neighbors)
|
||||
local xChunks = regionMap[chunkX-1]
|
||||
if (xChunks ~= nil) then
|
||||
neighbors[1] = xChunks[chunkY-1]
|
||||
neighbors[4] = xChunks[chunkY]
|
||||
neighbors[6] = xChunks[chunkY+1]
|
||||
end
|
||||
|
||||
xChunks = regionMap[chunkX+1]
|
||||
if (xChunks ~= nil) then
|
||||
neighbors[3] = xChunks[chunkY-1]
|
||||
neighbors[5] = xChunks[chunkY]
|
||||
neighbors[8] = xChunks[chunkY+1]
|
||||
end
|
||||
|
||||
xChunks = regionMap[chunkX]
|
||||
neighbors[2] = xChunks[chunkY-1]
|
||||
neighbors[7] = xChunks[chunkY+1]
|
||||
end
|
||||
|
||||
return mapUtils
|
90
libs/PheromoneUtils.lua
Executable file
90
libs/PheromoneUtils.lua
Executable file
@ -0,0 +1,90 @@
|
||||
local pheromoneUtils = {}
|
||||
|
||||
local mapUtils = require("MapUtils")
|
||||
local constants = require("Constants")
|
||||
local mMin = math.min
|
||||
local mFloor = math.floor
|
||||
|
||||
function pheromoneUtils.deathScent(regionMap, x, y, amount)
|
||||
pheromoneUtils.placePheromoneByPosition(regionMap,
|
||||
x,
|
||||
y,
|
||||
constants.DEATH_PHEROMONE,
|
||||
200)
|
||||
end
|
||||
|
||||
function pheromoneUtils.enemyBaseScent(regionMap, surface, natives, chunk, neighbors, validNeighbors)
|
||||
local BASE_PHEROMONE = constants.BASE_PHEROMONE
|
||||
local x = chunk.cX
|
||||
local y = chunk.cY
|
||||
|
||||
local nativeBase = natives.bases[x]
|
||||
if (nativeBase ~= nil) then
|
||||
nativeBase = nativeBase[y]
|
||||
if (nativeBase ~= nil) then
|
||||
local spawners = nativeBase.bG
|
||||
if (spawners > 0) then
|
||||
local nativeChunk = regionMap[x][y]
|
||||
nativeChunk[BASE_PHEROMONE] = nativeChunk[BASE_PHEROMONE] + (spawners * 35)
|
||||
end
|
||||
end
|
||||
end
|
||||
return validNeighbors
|
||||
end
|
||||
|
||||
function pheromoneUtils.playerScent(regionMap, players)
|
||||
local placePheromoneByPosition = pheromoneUtils.placePheromoneByPosition
|
||||
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
|
||||
local mathFloor = mFloor
|
||||
|
||||
for i=1, #players do
|
||||
local playerPosition = players[i].position
|
||||
local playerChunk = regionMap[mathFloor(playerPosition.x * 0.03125)][mathFloor(playerPosition.y * 0.03125)]
|
||||
playerChunk[PLAYER_PHEROMONE] = playerChunk[PLAYER_PHEROMONE] + 100
|
||||
end
|
||||
end
|
||||
|
||||
function pheromoneUtils.placePheromoneByPosition(regionMap, x, y, pType, amount)
|
||||
local chunk = mapUtils.getChunkByPosition(regionMap, x, y)
|
||||
if (chunk~=nil) then
|
||||
chunk[pType] = mMin(constants.MAX_PHEROMONE, chunk[pType] + amount)
|
||||
end
|
||||
end
|
||||
|
||||
function pheromoneUtils.placePheromoneByChunk(regionMap, x, y, pType, amount)
|
||||
local chunk = regionMap[x][y]
|
||||
chunk[pType] = mMin(constants.MAX_PHEROMONE, chunk[pType] + amount)
|
||||
end
|
||||
|
||||
function pheromoneUtils.processPheromone(regionMap, surface, natives, chunk, neighbors, validNeighbors)
|
||||
local mathMin = mMin
|
||||
local getNeighborChunks = mapUtils.getNeighborChunks
|
||||
local DIFFUSION_AMOUNT = constants.DIFFUSION_AMOUNT
|
||||
local MAX_PHEROMONE = constants.MAX_PHEROMONE
|
||||
|
||||
for x=1,3 do
|
||||
if ((x ~= DEATH_PHEROMONE) and (chunk[x] > 75)) or ((x == DEATH_PHEROMONE) and (chunk[x] > 125)) then
|
||||
if not validNeighbors then
|
||||
getNeighborChunks(regionMap, chunk.cX, chunk.cY, neighbors)
|
||||
validNeighbors = true
|
||||
end
|
||||
local totalDiffused = 0
|
||||
for i=1,8 do
|
||||
local neighborChunk = neighbors[i]
|
||||
if (neighborChunk ~= nil) then
|
||||
local diffusedAmount = (chunk[x] * DIFFUSION_AMOUNT)
|
||||
totalDiffused = totalDiffused + diffusedAmount
|
||||
neighborChunk[x] = mathMin(MAX_PHEROMONE, neighborChunk[x] + diffusedAmount)
|
||||
end
|
||||
end
|
||||
chunk[x] = chunk[x] - totalDiffused
|
||||
end
|
||||
chunk[x] = chunk[x] * 0.995
|
||||
if (chunk[x] < 2) then
|
||||
chunk[x] = 0
|
||||
end
|
||||
end
|
||||
return validNeighbors
|
||||
end
|
||||
|
||||
return pheromoneUtils
|
@ -1,129 +0,0 @@
|
||||
local regionUtils = {}
|
||||
|
||||
local constants = require("Constants")
|
||||
|
||||
local regionMaps
|
||||
local neighborArray = {1,2,3,4,5,6,7,8}
|
||||
local mExp = math.exp
|
||||
local mFloor = math.floor
|
||||
local mMin = math.min
|
||||
local mMax = math.max
|
||||
|
||||
function regionUtils.addChunkToRegionMap(regionMap, chunk)
|
||||
|
||||
local chunkX = mFloor(chunk.x * 0.03125)
|
||||
local chunkY = mFloor(chunk.y * 0.03125)
|
||||
|
||||
chunk.x = chunkX
|
||||
chunk.y = chunkY
|
||||
|
||||
if (regionMap == nil) then
|
||||
regionMap = {}
|
||||
end
|
||||
if regionMap[chunkX] == nil then
|
||||
regionMap[chunkX] = {}
|
||||
end
|
||||
regionMap[chunkX][chunkY] = chunk
|
||||
end
|
||||
|
||||
function regionUtils.getChunkByPosition(regionMap, x, y)
|
||||
local chunkX = mFloor(x * 0.03125)
|
||||
local chunkY = mFloor(y * 0.03125)
|
||||
|
||||
return regionMap[chunkX][chunkY]
|
||||
end
|
||||
|
||||
function regionUtils.getChunkByIndex(regionMap, chunkX, chunkY)
|
||||
return regionMap[chunkX][chunkY]
|
||||
end
|
||||
|
||||
function regionUtils.processPheromone(regionMap, surface)
|
||||
local mathMin = mMin
|
||||
local neighbors = neighborArray
|
||||
local getNeighborChunks = regionUtils.getNeighborChunks
|
||||
local DIFFUSION_AMOUNT = constants.DIFFUSION_AMOUNT
|
||||
local MAX_PHEROMONE = constants.MAX_PHEROMONE
|
||||
local BASE_PHEROMONE = constants.BASE_PHEROMONE
|
||||
|
||||
local count = 0
|
||||
|
||||
for _,ys in pairs(regionMap) do
|
||||
for _,chunk in pairs(ys) do
|
||||
if (chunk.bG > 0) then
|
||||
chunk[BASE_PHEROMONE] = chunk[BASE_PHEROMONE] + (chunk.bG * 10)
|
||||
end
|
||||
local chunks
|
||||
for x=1,3 do
|
||||
if (chunk[x] > 75) then
|
||||
if (chunks == nil) then
|
||||
chunks = getNeighborChunks(regionMap, chunk.x, chunk.y, neighbors)
|
||||
end
|
||||
local totalDiffused = 0
|
||||
for i=1,8 do
|
||||
local neighborChunk = chunks[i]
|
||||
if (neighborChunk ~= nil) then
|
||||
local diffusedAmount = (chunk[x] * DIFFUSION_AMOUNT)
|
||||
totalDiffused = totalDiffused + diffusedAmount
|
||||
neighborChunk[x] = mathMin(MAX_PHEROMONE, neighborChunk[x] + diffusedAmount)
|
||||
end
|
||||
end
|
||||
chunk[x] = chunk[x] - totalDiffused
|
||||
end
|
||||
chunk[x] = chunk[x] * 0.95
|
||||
if (chunk[x] < 2) then
|
||||
chunk[x] = 0
|
||||
end
|
||||
end
|
||||
count = count + 1
|
||||
if (count % 1250 == 0) then
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function regionUtils.placePheromone(regionMap, x, y, pType, amount)
|
||||
local chunk = regionUtils.getChunkByPosition(regionMap, x, y)
|
||||
chunk[pType] = mMin(constants.MAX_PHEROMONE, chunk[pType] + amount)
|
||||
end
|
||||
|
||||
--[[
|
||||
1 2 3
|
||||
\|/
|
||||
4- -5
|
||||
/|\
|
||||
6 7 8
|
||||
]]--
|
||||
function regionUtils.getNeighborChunks(regionMap, chunkX, chunkY, neighbors)
|
||||
local xChunks = regionMap[chunkX-1]
|
||||
if (xChunks ~= nil) then
|
||||
neighbors[1] = xChunks[chunkY-1]
|
||||
neighbors[4] = xChunks[chunkY]
|
||||
neighbors[6] = xChunks[chunkY+1]
|
||||
end
|
||||
|
||||
xChunks = regionMap[chunkX+1]
|
||||
if (xChunks ~= nil) then
|
||||
neighbors[3] = xChunks[chunkY-1]
|
||||
neighbors[5] = xChunks[chunkY]
|
||||
neighbors[8] = xChunks[chunkY+1]
|
||||
end
|
||||
|
||||
xChunks = regionMap[chunkX]
|
||||
neighbors[2] = xChunks[chunkY-1]
|
||||
neighbors[7] = xChunks[chunkY+1]
|
||||
|
||||
return neighbors
|
||||
end
|
||||
|
||||
function regionUtils.init(maps)
|
||||
regionMaps = maps
|
||||
end
|
||||
|
||||
function regionUtils.pheromoneProcess()
|
||||
local player = game.players[1]
|
||||
regionUtils.placePheromone(regionMaps[player.surface.index], player.position.x, player.position.y, constants.PLAYER_PHEROMONE, 100)
|
||||
regionUtils.processPheromone(regionMaps[player.surface.index], player.surface, 2)
|
||||
end
|
||||
|
||||
return regionUtils
|
23
libs/UnitGroupUtils.lua
Executable file
23
libs/UnitGroupUtils.lua
Executable file
@ -0,0 +1,23 @@
|
||||
local unitGroupUtils = {}
|
||||
|
||||
local utils = require("Utils")
|
||||
|
||||
function unitGroupUtils.findNearByUnitGroup(natives, position, distance)
|
||||
local squads = natives.squads
|
||||
local i = 1
|
||||
while (i <= #squads) do
|
||||
local squad = squads[i]
|
||||
if (squad ~= nil) and squad.valid then
|
||||
if (utils.euclideanDistanceNamed(squad.position, position) <= distance) then
|
||||
return squad
|
||||
end
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
function unitGroupUtils.mergeNearByUnitGroup(natives, unitGroup)
|
||||
|
||||
end
|
||||
|
||||
return unitGroupUtils
|
@ -1,12 +1,12 @@
|
||||
local utils = {}
|
||||
|
||||
function euclideanDistanceNamed(p1, p2)
|
||||
function utils.euclideanDistanceNamed(p1, p2)
|
||||
local xs = p1.x - p2.x
|
||||
local ys = p1.y - p2.y
|
||||
return ((xs * xs) + (ys * ys)) ^ 0.5
|
||||
end
|
||||
|
||||
function euclideanDistanceArray(p1, p2)
|
||||
function utils.euclideanDistanceArray(p1, p2)
|
||||
local xs = p1[1] - p2[1]
|
||||
local ys = p1[2] - p2[2]
|
||||
return ((xs * xs) + (ys * ys)) ^ 0.5
|
||||
|
@ -1,7 +1,9 @@
|
||||
local chunkUtils = {}
|
||||
|
||||
local regionUtils = require("RegionUtils")
|
||||
local mapUtils = require("MapUtils")
|
||||
local constants = require("Constants")
|
||||
|
||||
local natives
|
||||
local regionMaps
|
||||
local chunkProcessingQueue
|
||||
|
||||
@ -28,12 +30,15 @@ function chunkUtils.checkForDeadendTiles(constantCoordinate, iteratingCoordinate
|
||||
return deadEnd
|
||||
end
|
||||
|
||||
function chunkUtils.checkChunkPassability(x, y, surface)
|
||||
function chunkUtils.checkChunkPassability(chunk, surface, natives)
|
||||
local checkForDeadendTiles = chunkUtils.checkForDeadendTiles
|
||||
local NORTH_SOUTH = constants.NORTH_SOUTH
|
||||
local EAST_WEST = constants.EAST_WEST
|
||||
local CHUNK_SIZE = constants.CHUNK_SIZE
|
||||
|
||||
local x = chunk.pX
|
||||
local y = chunk.pY
|
||||
|
||||
local passableNorthSouth = false
|
||||
local passableEastWest = false
|
||||
local xi = x
|
||||
@ -59,70 +64,43 @@ function chunkUtils.checkChunkPassability(x, y, surface)
|
||||
else
|
||||
chunkUtils.colorChunk(x, y, "concrete", surface)
|
||||
end
|
||||
return {eastWest = passableEastWest, northSouth = passableNorthSouth}
|
||||
|
||||
chunk.eW = passableEastWest
|
||||
chunk.nS = passableNorthSouth
|
||||
end
|
||||
|
||||
function chunkUtils.checkChunkValues(x, y, surface)
|
||||
function chunkUtils.scoreChunk(chunk, surface, natives)
|
||||
local x = chunk.pX
|
||||
local y = chunk.pY
|
||||
local cX = chunk.cX
|
||||
local cY = chunk.cY
|
||||
local CHUNK_SIZE = constants.CHUNK_SIZE
|
||||
|
||||
local spawnerCount = surface.count_entities_filtered({area={{x, y},
|
||||
{x+CHUNK_SIZE, y+CHUNK_SIZE}},
|
||||
type="unit-spawner",
|
||||
force="enemy"})
|
||||
return {
|
||||
base = spawnerCount
|
||||
}
|
||||
end
|
||||
|
||||
function chunkUtils.processChunks(regionMaps, stack)
|
||||
local createChunk = chunkUtils.createChunk
|
||||
local addChunkToRegionMap = regionUtils.addChunkToRegionMap
|
||||
local checkChunkPassability = chunkUtils.checkChunkPassability
|
||||
local checkChunkValues = chunkUtils.checkChunkValues
|
||||
|
||||
local count = 0
|
||||
while (#stack > 0) do
|
||||
local event = stack[#stack]
|
||||
stack[#stack] = nil
|
||||
local surface = event.surface
|
||||
local surfaceIndex = surface.index
|
||||
|
||||
if (surfaceIndex == 1) then
|
||||
if (regionMaps[surfaceIndex] == nil) then
|
||||
regionMaps[surfaceIndex] = {}
|
||||
end
|
||||
|
||||
local topX = event.area.left_top.x
|
||||
local topY = event.area.left_top.y
|
||||
|
||||
local directions = checkChunkPassability(topX, topY, surface)
|
||||
local scores = checkChunkValues(topX, topY, surface)
|
||||
local chunk = createChunk(topX,
|
||||
topY,
|
||||
directions,
|
||||
scores)
|
||||
|
||||
addChunkToRegionMap(regionMaps[surfaceIndex],
|
||||
chunk)
|
||||
end
|
||||
count = count + 1
|
||||
if (count % 7 == 0) and (#stack < 70) then
|
||||
coroutine.yield()
|
||||
end
|
||||
local spawners = surface.count_entities_filtered({area={{x, y},
|
||||
{x+CHUNK_SIZE, y+CHUNK_SIZE}},
|
||||
type="unit-spawner",
|
||||
force="enemy"})
|
||||
if (natives.bases[cX] == nil) then
|
||||
natives.bases[cX] = {}
|
||||
end
|
||||
local nativeX = natives.bases[cX]
|
||||
if (nativeX[cY] == nil) then
|
||||
nativeX[cY] = {}
|
||||
end
|
||||
nativeX[cY].bG = spawners
|
||||
end
|
||||
|
||||
function chunkUtils.createChunk(topX, topY, directions, scores)
|
||||
return {
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
x = topX,
|
||||
y = topY,
|
||||
nS = directions.northSouth, -- passable north_south
|
||||
eW = directions.eastWest, -- passable east_west
|
||||
bG = scores.base, -- value of pheromone base generator
|
||||
}
|
||||
function chunkUtils.createChunk(topX, topY)
|
||||
local chunk = {
|
||||
pX = topX,
|
||||
pY = topY,
|
||||
cX = topX * 0.03125,
|
||||
cY = topY * 0.03125
|
||||
}
|
||||
chunk[constants.BASE_PHEROMONE] = 0
|
||||
chunk[constants.PLAYER_PHEROMONE] = 0
|
||||
chunk[constants.DEATH_PHEROMONE] = 0
|
||||
return chunk
|
||||
end
|
||||
|
||||
function chunkUtils.colorChunk(x, y, tileType, surface)
|
||||
@ -137,13 +115,4 @@ function chunkUtils.colorChunk(x, y, tileType, surface)
|
||||
surface.set_tiles(tiles, false)
|
||||
end
|
||||
|
||||
function chunkUtils.init(maps, chunkQueue)
|
||||
regionMaps = maps
|
||||
chunkProcessingQueue = chunkQueue
|
||||
end
|
||||
|
||||
function chunkUtils.chunkProcess()
|
||||
chunkUtils.processChunks(regionMaps, chunkProcessingQueue)
|
||||
end
|
||||
|
||||
return chunkUtils
|
13
tests.lua
13
tests.lua
@ -1,14 +1,13 @@
|
||||
local tests = {}
|
||||
|
||||
local regionMaps
|
||||
local regionMap
|
||||
|
||||
function tests.initTester()
|
||||
regionMaps = global.regionMaps
|
||||
regionMap = global.regionMap
|
||||
end
|
||||
|
||||
function tests.test1()
|
||||
local player = game.players[1]
|
||||
local regionMap = regionMaps[player.surface.index]
|
||||
local playerChunkX = math.floor(player.position.x / 32)
|
||||
local playerChunkY = math.floor(player.position.y / 32)
|
||||
print("------")
|
||||
@ -26,10 +25,12 @@ function tests.test1()
|
||||
end
|
||||
end
|
||||
|
||||
-- function test2()
|
||||
function test2()
|
||||
-- local playerPosition = game.players[1].position
|
||||
-- spreadPheromone(regionMaps[1], playerPosition.x, playerPosition.y)
|
||||
-- end
|
||||
-- playerPosition.x = playerPosition.x + 10
|
||||
-- local turret = game.surfaces[1].create_entity({name="small-worm-turret", position=playerPosition})
|
||||
-- turret
|
||||
end
|
||||
|
||||
-- function test3()
|
||||
-- local playerPosition = game.players[1].position
|
||||
|
Loading…
Reference in New Issue
Block a user