1
0
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:
veden 2016-08-28 17:05:28 -07:00
parent 58f831e1e9
commit a88f48bba0
9 changed files with 108 additions and 44 deletions

View File

@ -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)

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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