mirror of
https://github.com/veden/Rampant.git
synced 2024-12-26 20:54:12 +02:00
fixed ai made bases being missed
This commit is contained in:
parent
58f831e1e9
commit
a88f48bba0
@ -25,11 +25,12 @@ Pathfinding - unit groups will use potential fields to perform only single step
|
||||
Tunneling Biters
|
||||
Fire Biters
|
||||
Suicide Biters
|
||||
infesting Biters
|
||||
Base Expansion
|
||||
|
||||
# Version History
|
||||
|
||||
0.0.9 -
|
||||
0.0.9 - fixed ai created bases not being counted in logic
|
||||
|
||||
0.0.8 - fixed retreat oscillations (https://forums.factorio.com/viewtopic.php?f=94&t=31445&start=10#p198750)
|
||||
added scaling for kamikaze attack (https://forums.factorio.com/viewtopic.php?f=94&t=31445&start=10#p199401)
|
||||
|
45
control.lua
45
control.lua
@ -16,6 +16,7 @@ local tests = require("tests")
|
||||
local processPendingChunks = chunkProcessor.processPendingChunks
|
||||
|
||||
local processMap = mapProcessor.processMap
|
||||
local scanMap = mapProcessor.scanMap
|
||||
|
||||
local accumulatePoints = aiBuilding.accumulatePoints
|
||||
local removeScout = aiBuilding.removeScout
|
||||
@ -38,6 +39,7 @@ local addRemoveEntity = entityUtils.addRemoveEntity
|
||||
|
||||
local regionMap
|
||||
local natives
|
||||
local pheromoneTotals
|
||||
local pendingChunks
|
||||
|
||||
-- hook functions
|
||||
@ -47,11 +49,13 @@ function onInit()
|
||||
global.regionMap = {}
|
||||
global.pendingChunks = {}
|
||||
global.natives = {}
|
||||
global.pheromoneTotals = {}
|
||||
|
||||
regionMap = global.regionMap
|
||||
natives = global.natives
|
||||
pendingChunks = global.pendingChunks
|
||||
|
||||
pheromoneTotals = global.pheromoneTotals
|
||||
|
||||
onConfigChanged()
|
||||
end
|
||||
|
||||
@ -60,15 +64,18 @@ function onLoad()
|
||||
regionMap = global.regionMap
|
||||
natives = global.natives
|
||||
pendingChunks = global.pendingChunks
|
||||
pheromoneTotals = global.pheromoneTotals
|
||||
end
|
||||
|
||||
function onConfigChanged()
|
||||
-- print("reprocess")
|
||||
if (global.version == nil) then
|
||||
regionMap.pQ = {{}} -- processing queue
|
||||
regionMap.pI = 1 -- insertion location for chunk processing
|
||||
regionMap.pP = 1 -- index for the chunk set to process
|
||||
regionMap.pR = -1 -- current processing roll
|
||||
|
||||
-- removed in version 9
|
||||
-- regionMap.pQ = {{}} -- processing queue
|
||||
-- regionMap.pI = 1 -- insertion location for chunk processing
|
||||
-- regionMap.pP = 1 -- index for the chunk set to process
|
||||
-- regionMap.pR = -1 -- current processing roll
|
||||
natives.squads = {}
|
||||
natives.scouts = {}
|
||||
natives.tunnels = {}
|
||||
@ -78,7 +85,25 @@ function onConfigChanged()
|
||||
global.version = constants.VERSION_5
|
||||
end
|
||||
if (global.version < constants.VERSION_9) then
|
||||
|
||||
|
||||
-- remove version 5 references
|
||||
regionMap.pQ = nil
|
||||
regionMap.pI = nil
|
||||
regionMap.pP = nil
|
||||
regionMap.pR = nil
|
||||
|
||||
regionMap.processQueue = {}
|
||||
regionMap.processPointer = 1
|
||||
regionMap.scanPointer = 1
|
||||
regionMap.processRoll = -1
|
||||
|
||||
pheromoneTotals[constants.DEATH_PHEROMONE] = 0
|
||||
pheromoneTotals[constants.ENEMY_BASE_PHEROMONE] = 0
|
||||
pheromoneTotals[constants.PLAYER_PHEROMONE] = 0
|
||||
pheromoneTotals[constants.PLAYER_BASE_PHEROMONE] = 0
|
||||
pheromoneTotals[constants.PLAYER_DEFENSE_PHEROMONE] = 0
|
||||
pheromoneTotals[constants.MOVEMENT_PHEROMONE] = 0
|
||||
|
||||
-- queue all current chunks that wont be generated during play
|
||||
local surface = game.surfaces[1]
|
||||
for chunk in surface.get_chunks() do
|
||||
@ -107,7 +132,7 @@ function onTick(event)
|
||||
accumulatePoints(natives)
|
||||
|
||||
-- put down player pheromone for player hunters
|
||||
playerScent(regionMap, game.players)
|
||||
playerScent(regionMap, game.players, pheromoneTotals)
|
||||
|
||||
regroupSquads(natives)
|
||||
|
||||
@ -119,7 +144,9 @@ function onTick(event)
|
||||
|
||||
processPendingChunks(regionMap, surface, natives, pendingChunks)
|
||||
|
||||
processMap(regionMap, surface, natives, game.evolution_factor)
|
||||
scanMap(regionMap, surface)
|
||||
|
||||
processMap(regionMap, surface, natives, pheromoneTotals, game.evolution_factor)
|
||||
end
|
||||
end
|
||||
|
||||
@ -140,7 +167,7 @@ function onDeath(event)
|
||||
local entityPosition = entity.position
|
||||
|
||||
-- drop death pheromone where unit died
|
||||
deathScent(regionMap, entityPosition)
|
||||
deathScent(regionMap, entityPosition, pheromoneTotals)
|
||||
|
||||
if (event.force ~= nil) and (event.force.name == "player") then
|
||||
retreatUnits(entityPosition,
|
||||
|
@ -65,6 +65,7 @@ function aiBuilding.accumulatePoints(natives)
|
||||
end
|
||||
|
||||
function aiBuilding.removeScout(entity, natives)
|
||||
--[[
|
||||
local scouts = natives.scouts
|
||||
for i=1, #scouts do
|
||||
local scout = scouts[i]
|
||||
@ -73,9 +74,11 @@ function aiBuilding.removeScout(entity, natives)
|
||||
return
|
||||
end
|
||||
end
|
||||
--]]
|
||||
end
|
||||
|
||||
function aiBuilding.makeScouts(surface, natives, chunk, evolution_factor)
|
||||
--[[
|
||||
if (natives.points > AI_SCOUT_COST) then
|
||||
if (#global.natives.scouts < 5) and (math.random() < 0.05) then -- TODO scaled with evolution factor
|
||||
local enemy = surface.find_nearest_enemy({ position = { x = chunk.pX + HALF_CHUNK_SIZE,
|
||||
@ -89,10 +92,11 @@ function aiBuilding.makeScouts(surface, natives, chunk, evolution_factor)
|
||||
end
|
||||
end
|
||||
end
|
||||
--]]
|
||||
end
|
||||
|
||||
function aiBuilding.scouting(regionMap, natives)
|
||||
-- print("scouting")
|
||||
--[[
|
||||
local scouts = natives.scouts
|
||||
for i=1,#scouts do
|
||||
local scout = scouts[i]
|
||||
@ -103,12 +107,13 @@ function aiBuilding.scouting(regionMap, natives)
|
||||
distraction=defines.distraction.none})
|
||||
end
|
||||
end
|
||||
--]]
|
||||
end
|
||||
|
||||
function aiBuilding.formSquads(regionMap, surface, natives, chunk, evolution_factor)
|
||||
if (natives.points > AI_SQUAD_COST) then
|
||||
local score = chunk[PLAYER_BASE_PHEROMONE] + chunk[PLAYER_PHEROMONE] + chunk[PLAYER_DEFENSE_PHEROMONE] + surface.get_pollution({chunk.pX, chunk.pY})
|
||||
if (score > 20) and (chunk[ENEMY_BASE_GENERATOR] ~= 0) and (#natives.squads < AI_MAX_SQUAD_COUNT * evolution_factor) and (math.random() < 0.03) then
|
||||
if (score > 20) and (chunk[ENEMY_BASE_GENERATOR] ~= 0) and (#natives.squads < (AI_MAX_SQUAD_COUNT * evolution_factor)) and (math.random() < 0.03) then
|
||||
local squadPath, squadScore = scoreNeighbors(chunk,
|
||||
getNeighborChunks(regionMap, chunk.cX, chunk.cY),
|
||||
validUnitGroupLocation,
|
||||
|
@ -46,8 +46,6 @@ local createSquad = unitGroupUtils.createSquad
|
||||
local membersToSquad = unitGroupUtils.membersToSquad
|
||||
local scoreNeighborsWithDirection = neighborUtils.scoreNeighborsWithDirection
|
||||
|
||||
local mfloor = math.floor
|
||||
|
||||
-- module code
|
||||
|
||||
local function validRetreatLocation(x, chunk, neighborChunk)
|
||||
@ -62,7 +60,7 @@ end
|
||||
|
||||
function aiDefense.retreatUnits(position, squad, regionMap, surface, natives)
|
||||
local chunk = getChunkByPosition(regionMap, position.x, position.y)
|
||||
if (chunk ~= nil) and (chunk[DEATH_PHEROMONE] > mfloor(game.evolution_factor * RETREAT_DEATH_PHEROMONE_LEVEL)) then
|
||||
if (chunk ~= nil) and (chunk[DEATH_PHEROMONE] > (game.evolution_factor * RETREAT_DEATH_PHEROMONE_LEVEL)) then
|
||||
local performRetreat = false
|
||||
local enemiesToSquad
|
||||
|
||||
|
@ -35,13 +35,7 @@ function chunkProcessor.processPendingChunks(regionMap, surface, natives, pendin
|
||||
checkChunkPassability(chunk, surface, natives)
|
||||
scoreChunk(chunk, surface, natives)
|
||||
|
||||
local processQueue = regionMap.pQ[regionMap.pI]
|
||||
if (#processQueue == CHUNK_MAX_QUEUE_SIZE) then
|
||||
regionMap.pI = regionMap.pI + 1
|
||||
regionMap.pQ[regionMap.pI] = {}
|
||||
processQueue = regionMap.pQ[regionMap.pI]
|
||||
end
|
||||
|
||||
local processQueue = regionMap.processQueue
|
||||
processQueue[#processQueue+1] = chunk
|
||||
end
|
||||
end
|
||||
|
@ -10,7 +10,8 @@ constants.VERSION_9 = 9
|
||||
constants.MAGIC_MAXIMUM_NUMBER = 1e99 -- used in loops trying to find the lowest/highest score
|
||||
constants.RETREAT_DEATH_PHEROMONE_LEVEL = 10000
|
||||
|
||||
constants.CHUNK_MAX_QUEUE_SIZE = 350
|
||||
constants.PROCESS_QUEUE_SIZE = 350
|
||||
constants.SCAN_QUEUE_SIZE = 10
|
||||
|
||||
-- ai
|
||||
|
||||
@ -42,11 +43,11 @@ constants.PLAYER_PHEROMONE_GENERATOR_AMOUNT = 300
|
||||
|
||||
-- pheromone diffusion amounts
|
||||
|
||||
constants.STANDARD_PHERONOME_DIFFUSION_AMOUNT = 0.05
|
||||
constants.STANDARD_PHERONOME_DIFFUSION_AMOUNT = 0.10
|
||||
constants.DEATH_PHEROMONE_DIFFUSION_AMOUNT = 0.02
|
||||
|
||||
constants.DEATH_PHEROMONE_PERSISTANCE = 0.99
|
||||
constants.STANDARD_PHEROMONE_PERSISTANCE = 0.95
|
||||
constants.STANDARD_PHEROMONE_PERSISTANCE = 0.98
|
||||
|
||||
-- chunk attributes
|
||||
|
||||
|
@ -5,6 +5,7 @@ local mapProcessor = {}
|
||||
local mapUtils = require("MapUtils")
|
||||
local pheromoneUtils = require("PheromoneUtils")
|
||||
local aiBuilding = require("AIBuilding")
|
||||
local constants = require("Constants")
|
||||
|
||||
-- imported functions
|
||||
|
||||
@ -16,21 +17,23 @@ local formSquads = aiBuilding.formSquads
|
||||
|
||||
local getCardinalChunks = mapUtils.getCardinalChunks
|
||||
|
||||
local mMin = math.min
|
||||
|
||||
-- module code
|
||||
|
||||
-- processing is not consistant as it depends on the number of chunks that have been generated
|
||||
-- so 200 chunks is processed 3 times a second and 1200 chunks is processed once a second
|
||||
-- In theory, this might be fine as smaller bases have less surface to attack and need to have
|
||||
-- pheromone dissipate at a faster rate.
|
||||
function mapProcessor.processMap(regionMap, surface, natives, evolution_factor)
|
||||
local roll = regionMap.pR
|
||||
local index = regionMap.pP
|
||||
function mapProcessor.processMap(regionMap, surface, natives, pheromoneTotals, evolution_factor)
|
||||
local roll = regionMap.processRoll
|
||||
local index = regionMap.processPointer
|
||||
local scouts = false
|
||||
local squads = false
|
||||
|
||||
if (regionMap.pP == 1) then
|
||||
if (index == 1) then
|
||||
roll = math.random()
|
||||
regionMap.pR = roll
|
||||
regionMap.processRoll = roll
|
||||
end
|
||||
|
||||
if (0.05 <= roll) and (roll <= 0.10) then
|
||||
@ -41,11 +44,12 @@ function mapProcessor.processMap(regionMap, surface, natives, evolution_factor)
|
||||
squads = true
|
||||
end
|
||||
|
||||
local chunkQueue = regionMap.pQ[index]
|
||||
for x=1,#chunkQueue do
|
||||
local chunk = chunkQueue[x]
|
||||
local processQueue = regionMap.processQueue
|
||||
local endIndex = mMin(index + constants.PROCESS_QUEUE_SIZE, #processQueue)
|
||||
for x=index,endIndex do
|
||||
local chunk = processQueue[x]
|
||||
|
||||
scents(chunk)
|
||||
scents(chunk, pheromoneTotals)
|
||||
|
||||
if scouts then
|
||||
makeScouts(surface, natives, chunk, evolution_factor)
|
||||
@ -54,12 +58,35 @@ function mapProcessor.processMap(regionMap, surface, natives, evolution_factor)
|
||||
formSquads(regionMap, surface, natives, chunk, evolution_factor)
|
||||
end
|
||||
|
||||
processPheromone(chunk, getCardinalChunks(regionMap, chunk.cX, chunk.cY))
|
||||
processPheromone(chunk, getCardinalChunks(regionMap, chunk.cX, chunk.cY), pheromoneTotals)
|
||||
end
|
||||
|
||||
regionMap.pP = regionMap.pP + 1
|
||||
if (regionMap.pP > regionMap.pI) then
|
||||
regionMap.pP = 1
|
||||
if (endIndex == #processQueue) then
|
||||
regionMap.processPointer = 1
|
||||
else
|
||||
regionMap.processPointer = endIndex + 1
|
||||
end
|
||||
end
|
||||
|
||||
function mapProcessor.scanMap(regionMap, surface)
|
||||
local index = regionMap.scanPointer
|
||||
|
||||
local processQueue = regionMap.processQueue
|
||||
local endIndex = mMin(index + constants.SCAN_QUEUE_SIZE, #processQueue)
|
||||
for x=index,endIndex do
|
||||
local chunk = processQueue[x]
|
||||
|
||||
local spawners = surface.count_entities_filtered({area = {{chunk.pX, chunk.pY},
|
||||
{chunk.pX + constants.CHUNK_SIZE, chunk.pY + constants.CHUNK_SIZE}},
|
||||
type = "unit-spawner",
|
||||
force = "enemy"})
|
||||
chunk[constants.ENEMY_BASE_GENERATOR] = spawners * constants.ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT
|
||||
end
|
||||
|
||||
if (endIndex == #processQueue) then
|
||||
regionMap.scanPointer = 1
|
||||
else
|
||||
regionMap.scanPointer = endIndex + 1
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -32,31 +32,35 @@ local getChunkByPosition = mapUtils.getChunkByPosition
|
||||
|
||||
-- module code
|
||||
|
||||
function pheromoneUtils.scents(chunk)
|
||||
function pheromoneUtils.scents(chunk, pheromoneTotals)
|
||||
local amount = chunk[PLAYER_DEFENSE_GENERATOR]
|
||||
if (amount > 0) then
|
||||
chunk[PLAYER_DEFENSE_PHEROMONE] = chunk[PLAYER_DEFENSE_PHEROMONE] + amount
|
||||
-- pheromoneTotals[PLAYER_DEFENSE_PHEROMONE] = pheromoneTotals[PLAYER_DEFENSE_PHEROMONE] + amount
|
||||
end
|
||||
|
||||
amount = chunk[PLAYER_BASE_GENERATOR]
|
||||
if (amount > 0) then
|
||||
chunk[PLAYER_BASE_PHEROMONE] = chunk[PLAYER_BASE_PHEROMONE] + amount
|
||||
-- pheromoneTotals[PLAYER_BASE_PHEROMONE] = pheromoneTotals[PLAYER_BASE_PHEROMONE] + amount
|
||||
end
|
||||
|
||||
amount = chunk[ENEMY_BASE_GENERATOR]
|
||||
if (amount > 0) then
|
||||
chunk[ENEMY_BASE_PHEROMONE] = chunk[ENEMY_BASE_PHEROMONE] + amount
|
||||
-- pheromoneTotals[ENEMY_BASE_PHEROMONE] = pheromoneTotals[ENEMY_BASE_PHEROMONE] + amount
|
||||
end
|
||||
end
|
||||
|
||||
function pheromoneUtils.deathScent(regionMap, position)
|
||||
function pheromoneUtils.deathScent(regionMap, position, pheromoneTotals)
|
||||
local chunk = getChunkByPosition(regionMap, position.x, position.y)
|
||||
if (chunk ~= nil) then
|
||||
chunk[DEATH_PHEROMONE] = chunk[DEATH_PHEROMONE] + DEATH_PHEROMONE_GENERATOR_AMOUNT
|
||||
-- pheromoneTotals[DEATH_PHEROMONE] = pheromoneTotals[DEATH_PHEROMONE] + DEATH_PHEROMONE_GENERATOR_AMOUNT
|
||||
end
|
||||
end
|
||||
|
||||
function pheromoneUtils.playerScent(regionMap, players)
|
||||
function pheromoneUtils.playerScent(regionMap, players, pheromoneTotals)
|
||||
for i=1,#players do
|
||||
local player = players[i]
|
||||
if (player ~= nil) and player.connected and (player.character ~= nil) and player.character.valid and (player.character.surface.index == 1) then
|
||||
@ -64,12 +68,13 @@ function pheromoneUtils.playerScent(regionMap, players)
|
||||
local playerChunk = getChunkByPosition(regionMap, playerPosition.x, playerPosition.y)
|
||||
if (playerChunk ~= nil) then
|
||||
playerChunk[PLAYER_PHEROMONE] = playerChunk[PLAYER_PHEROMONE] + PLAYER_PHEROMONE_GENERATOR_AMOUNT
|
||||
-- pheromoneTotals[PLAYER_PHEROMONE] = pheromoneTotals[PLAYER_PHEROMONE] + PLAYER_PHEROMONE_GENERATOR_AMOUNT
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pheromoneUtils.processPheromone(chunk, neighbors)
|
||||
function pheromoneUtils.processPheromone(chunk, neighbors, pheromoneTotals)
|
||||
local diffusionAmount
|
||||
local persistence
|
||||
for x=1,6 do -- pheromone level indexes on chunks are 1 - 6
|
||||
@ -81,7 +86,7 @@ function pheromoneUtils.processPheromone(chunk, neighbors)
|
||||
persistence = STANDARD_PHEROMONE_PERSISTANCE
|
||||
end
|
||||
local totalDiffused = 0
|
||||
local chunkValue = chunk[x]
|
||||
local chunkValue = chunk[x] * persistence
|
||||
for i=1,#neighbors do
|
||||
local neighborChunk = neighbors[i]
|
||||
if (neighborChunk ~= nil) then
|
||||
@ -90,7 +95,8 @@ function pheromoneUtils.processPheromone(chunk, neighbors)
|
||||
neighborChunk[x] = neighborChunk[x] + diffusedAmount
|
||||
end
|
||||
end
|
||||
chunk[x] = (chunkValue - totalDiffused) * persistence
|
||||
-- pheromoneTotals[x] = pheromoneTotals[x] + (chunkValue - chunk[x])
|
||||
chunk[x] = (chunkValue - totalDiffused)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -7,6 +7,7 @@ function tests.test1()
|
||||
local playerChunkX = math.floor(player.position.x / 32)
|
||||
local playerChunkY = math.floor(player.position.y / 32)
|
||||
print("------")
|
||||
print(#global.regionMap.processQueue)
|
||||
print(playerChunkX .. ", " .. playerChunkY)
|
||||
print("--")
|
||||
for x=playerChunkX-3, playerChunkX+3 do
|
||||
@ -113,4 +114,8 @@ function tests.test10()
|
||||
game.forces.player.research_all_technologies()
|
||||
end
|
||||
|
||||
function tests.test11()
|
||||
print(serpent.dump(global.pheromoneTotals))
|
||||
end
|
||||
|
||||
return tests
|
Loading…
Reference in New Issue
Block a user