From b6d0ac9d3a9aa6c30b586da9434087ba438ad1e5 Mon Sep 17 00:00:00 2001 From: Aaron Veden Date: Mon, 1 Jan 2018 22:05:21 -0800 Subject: [PATCH] swiched tile, cliff, resource scans to events. Added artillery turret to pheromones. Lowered pass score cutoff. added surface checks --- control.lua | 92 +++++++++++++++++--- libs/ChunkProcessor.lua | 41 ++++++++- libs/ChunkUtils.lua | 187 +++++++++++++++++++++++++++++----------- libs/Constants.lua | 5 +- libs/MapProcessor.lua | 1 - libs/SquadDefense.lua | 2 +- libs/UnitGroupUtils.lua | 12 +-- 7 files changed, 260 insertions(+), 80 deletions(-) diff --git a/control.lua b/control.lua index 1136fbf..3e52943 100755 --- a/control.lua +++ b/control.lua @@ -45,6 +45,7 @@ local roundToNearest = mathUtils.roundToNearest local getChunkByPosition = mapUtils.getChunkByPosition local processPendingChunks = chunkProcessor.processPendingChunks +local processScanChunks = chunkProcessor.processScanChunks local processMap = mapProcessor.processMap local processPlayers = mapProcessor.processPlayers @@ -144,6 +145,7 @@ local function rebuildRegionMap() regionMap.chunkToRallys = {} regionMap.chunkToPlayerBase = {} regionMap.chunkToResource = {} + regionMap.chunkToPassScan = {} -- preallocating memory to be used in code, making it fast by reducing garbage generated. regionMap.neighbors = { SENTINEL_IMPASSABLE_CHUNK, @@ -273,13 +275,15 @@ local function onTick(event) processPendingChunks(natives, regionMap, surface, pendingChunks, tick) scanMap(regionMap, surface, natives) + + regionMap.chunkToPassScan = processScanChunks(regionMap, surface) end if (tick == regionMap.logicTick) then regionMap.logicTick = regionMap.logicTick + INTERVAL_LOGIC local gameRef = game local surface = gameRef.surfaces[1] - + planning(natives, gameRef.forces.enemy.evolution_factor, tick, @@ -304,16 +308,22 @@ end local function onBuild(event) local entity = event.created_entity - addRemovePlayerEntity(regionMap, entity, natives, true, false) - if natives.safeBuildings then - if natives.safeEntities[entity.type] or natives.safeEntityName[entity.name] then - entity.destructible = false + if (entity.surface.index == 1) then + addRemovePlayerEntity(regionMap, entity, natives, true, false) + if natives.safeBuildings then + if natives.safeEntities[entity.type] or natives.safeEntityName[entity.name] then + entity.destructible = false + end end end end local function onMine(event) - addRemovePlayerEntity(regionMap, event.entity, natives, false, false) + local entity = event.entity + local surface = entity.surface + if (surface.index == 1) then + addRemovePlayerEntity(regionMap, entity, natives, false, false) + end end local function onDeath(event) @@ -351,10 +361,10 @@ local function onDeath(event) end end - elseif (entity.type == "unit-spawner") or (entity.type == "turret") then + elseif event.force and (event.force.name == "player") and (entity.type == "unit-spawner") or (entity.type == "turret") then local tick = event.tick - if (chunk ~= SENTINEL_IMPASSABLE_CHUNK) then + if (chunk ~= SENTINEL_IMPASSABLE_CHUNK) then unregisterEnemyBaseStructure(regionMap, entity) rallyUnits(chunk, regionMap, surface, natives, tick) @@ -388,14 +398,64 @@ local function onDeath(event) end local function onEnemyBaseBuild(event) - registerEnemyBaseStructure(regionMap, event.entity, nil) + local entity = event.entity + local surface = entity.surface + if (surface.index == 1) then + registerEnemyBaseStructure(regionMap, entity, nil) + end end local function onSurfaceTileChange(event) - -- local player = game.players[event.player_index] - -- if (player.surface.index == 1) then - -- aiAttackWave.fillTunnel(regionMap, player.surface, natives, event.positions) - -- end + local surfaceIndex = event.surface_index or event.robot.surface.index + if (event.item.name == "landfill") and (surfaceIndex == 1) then + local chunks = {} + local positions = event.positions + for i=1,#positions do + local position = positions[i] + local chunk = mapUtils.getChunkByPosition(regionMap, position, true) + + -- weird bug with table pointer equality using name instead pointer comparison + if not chunk.name then + regionMap.chunkToPassScan[chunk] = true + else + local x,y = mapUtils.positionToChunkXY(position) + local addMe = true + for ci=1,#chunks do + local c = chunks[ci] + if (c.x == x) and (c.y == y) then + addMe = false + break + end + end + if addMe then + chunks[#chunks+1] = {x=x,y=y} + end + end + end + for i=1,#chunks do + onChunkGenerated({area = { left_top = chunks[i] }, + surface = game.surfaces[surfaceIndex]}) + end + end +end + +local function onResourceDepleted(event) + local entity = event.entity + if (entity.surface.index == 1) then + chunkUtils.unregisterResource(entity, regionMap) + end +end + +local function onUsedCapsule(event) + local surface = game.players[event.player_index].surface + if (event.item.name == "cliff-explosives") and (surface.index == 1) then + local cliffs = surface.find_entities_filtered({area={{event.position.x-0.75,event.position.y-0.75}, + {event.position.x+0.75,event.position.y+0.75}}, + type="cliff"}) + for i=1,#cliffs do + chunkUtils.queueChunkForPassScan(regionMap, cliffs[i]) + end + end end local function onInit() @@ -418,7 +478,11 @@ script.on_load(onLoad) script.on_event(defines.events.on_runtime_mod_setting_changed, onModSettingsChange) script.on_configuration_changed(onConfigChanged) -script.on_event(defines.events.on_player_built_tile, onSurfaceTileChange) +script.on_event(defines.events.on_resource_depleted, onResourceDepleted) +script.on_event({defines.events.on_player_built_tile, + defines.events.on_robot_built_tile}, onSurfaceTileChange) + +script.on_event(defines.events.on_player_used_capsule, onUsedCapsule) script.on_event(defines.events.on_biter_base_built, onEnemyBaseBuild) script.on_event({defines.events.on_player_mined_entity, diff --git a/libs/ChunkProcessor.lua b/libs/ChunkProcessor.lua index b0d6341..33c2324 100755 --- a/libs/ChunkProcessor.lua +++ b/libs/ChunkProcessor.lua @@ -4,7 +4,6 @@ local chunkProcessor = {} local chunkUtils = require("ChunkUtils") local constants = require("Constants") -local squadDefense = require("SquadDefense") -- constants @@ -15,7 +14,8 @@ local SENTINEL_IMPASSABLE_CHUNK = constants.SENTINEL_IMPASSABLE_CHUNK -- imported functions local createChunk = chunkUtils.createChunk -local analyzeChunk = chunkUtils.analyzeChunk +local initialScan = chunkUtils.initialScan +local chunkPassScan = chunkUtils.chunkPassScan -- module code @@ -41,9 +41,9 @@ function chunkProcessor.processPendingChunks(natives, regionMap, surface, pendin bottomOffset[1] = x + CHUNK_SIZE bottomOffset[2] = y + CHUNK_SIZE - chunk = analyzeChunk(chunk, natives, surface, regionMap) + chunk = initialScan(chunk, natives, surface, regionMap) - if (chunk ~= SENTINEL_IMPASSABLE_CHUNK) then + if (chunk ~= SENTINEL_IMPASSABLE_CHUNK) then local chunkX = chunk.x if regionMap[chunkX] == nil then @@ -56,4 +56,37 @@ function chunkProcessor.processPendingChunks(natives, regionMap, surface, pendin end end +function chunkProcessor.processScanChunks(regionMap, surface) + local area = regionMap.area + + local topOffset = area[1] + local bottomOffset = area[2] + + local removals = {} + + for chunk,_ in pairs(regionMap.chunkToPassScan) do + local x = chunk.x + local y = chunk.y + + topOffset[1] = x + topOffset[2] = y + bottomOffset[1] = x + CHUNK_SIZE + bottomOffset[2] = y + CHUNK_SIZE + + chunk = chunkPassScan(chunk, surface, regionMap) + + if (chunk == SENTINEL_IMPASSABLE_CHUNK) then + regionMap[x][y] = nil + + removals[#removals+1] = chunk + end + end + + for i=#removals,1,-1 do + table.remove(regionMap.processQueue, i) + end + + return {} +end + return chunkProcessor diff --git a/libs/ChunkUtils.lua b/libs/ChunkUtils.lua index 6029c40..07070a0 100755 --- a/libs/ChunkUtils.lua +++ b/libs/ChunkUtils.lua @@ -33,6 +33,8 @@ local PATH_RATING = constants.PATH_RATING local PASSABLE = constants.PASSABLE +local RESOURCE_GENERATOR_INCREMENT = constants.RESOURCE_GENERATOR_INCREMENT + -- imported functions local getChunkByUnalignedXY = mapUtils.getChunkByUnalignedXY @@ -193,7 +195,7 @@ end -- external functions -function chunkUtils.analyzeChunk(chunk, natives, surface, regionMap) +function chunkUtils.calculatePassScore(surface, regionMap) local count_tiles_filtered = surface.count_tiles_filtered local filteredTilesQuery = regionMap.filteredTilesQuery @@ -203,72 +205,120 @@ function chunkUtils.analyzeChunk(chunk, natives, surface, regionMap) passScore = passScore + count_tiles_filtered(filteredTilesQuery) end - passScore = 1 - (passScore * 0.0009765625) - + return 1 - (passScore * 0.0009765625) +end + +function chunkUtils.scanChunkPaths(chunk, surface, regionMap) local pass = CHUNK_IMPASSABLE - if (passScore >= 0.60) then - local passableNorthSouth, passableEastWest = fullScan(chunk, - surface.can_place_entity, - regionMap.canPlaceQuery) + local passableNorthSouth, passableEastWest = fullScan(chunk, + surface.can_place_entity, + regionMap.canPlaceQuery) + + if passableEastWest and passableNorthSouth then + pass = CHUNK_ALL_DIRECTIONS + elseif passableEastWest then + pass = CHUNK_EAST_WEST + elseif passableNorthSouth then + pass = CHUNK_NORTH_SOUTH + end + return pass +end - if passableEastWest and passableNorthSouth then - pass = CHUNK_ALL_DIRECTIONS - elseif passableEastWest then - pass = CHUNK_EAST_WEST - elseif passableNorthSouth then - pass = CHUNK_NORTH_SOUTH - end - - local entities = surface.find_entities_filtered(regionMap.filteredEntitiesPlayerQuery) +function chunkUtils.scorePlayerBuildings(surface, regionMap, natives) + local entities = surface.find_entities_filtered(regionMap.filteredEntitiesPlayerQuery) - local playerObjects = 0 - local safeBuildings = natives.safeBuildings - local safeEntities = natives.safeEntities - local safeEntityName = natives.safeEntityName - if safeBuildings then - for i=1, #entities do - local entity = entities[i] - local entityType = entity.type + local playerObjects = 0 + local safeBuildings = natives.safeBuildings + local safeEntities = natives.safeEntities + local safeEntityName = natives.safeEntityName + if safeBuildings then + for i=1, #entities do + local entity = entities[i] + local entityType = entity.type - if safeEntities[entityType] or safeEntityName[entity.name] then - entity.destructible = false - end - - playerObjects = playerObjects + (BUILDING_PHEROMONES[entityType] or 0) + if safeEntities[entityType] or safeEntityName[entity.name] then + entity.destructible = false end - else - for i=1, #entities do - playerObjects = playerObjects + (BUILDING_PHEROMONES[entities[i].type] or 0) - end - end - local query = regionMap.filteredEntitiesEnemyTypeQuery + playerObjects = playerObjects + (BUILDING_PHEROMONES[entityType] or 0) + end + else + for i=1, #entities do + playerObjects = playerObjects + (BUILDING_PHEROMONES[entities[i].type] or 0) + end + end + + return playerObjects +end + +function chunkUtils.scoreEnemyBuildings(surface, regionMap) + local query = regionMap.filteredEntitiesEnemyTypeQuery + + query.type = "unit-spawner" + local nests = surface.count_entities_filtered(regionMap.filteredEntitiesEnemyTypeQuery) + + query.type = "turret" + local worms = surface.count_entities_filtered(regionMap.filteredEntitiesEnemyTypeQuery) + + return nests, worms +end + +function chunkUtils.initialScan(chunk, natives, surface, regionMap) + local passScore = chunkUtils.calculatePassScore(surface, regionMap) + + if (passScore >= 0.40) then + local pass = chunkUtils.scanChunkPaths(chunk, surface, regionMap) - query.type = "unit-spawner" - local nests = surface.count_entities_filtered(regionMap.filteredEntitiesEnemyTypeQuery) - chunkUtils.setNestCount(regionMap, chunk, nests) - - query.type = "turret" - local worms = surface.count_entities_filtered(regionMap.filteredEntitiesEnemyTypeQuery) - chunkUtils.setWormCount(regionMap, chunk, worms) + local playerObjects = chunkUtils.scorePlayerBuildings(surface, regionMap, natives) + + local nests, worms = chunkUtils.scoreEnemyBuildings(surface, regionMap) local resources = surface.count_entities_filtered(regionMap.countResourcesQuery) * 0.001 if ((playerObjects > 0) or (nests > 0)) and (pass == CHUNK_IMPASSABLE) then pass = CHUNK_ALL_DIRECTIONS end - + + chunkUtils.setNestCount(regionMap, chunk, nests) chunkUtils.setPlayerBaseGenerator(regionMap, chunk, playerObjects) chunkUtils.setResourceGenerator(regionMap, chunk, resources) - end + chunkUtils.setWormCount(regionMap, chunk, worms) - if (pass == CHUNK_IMPASSABLE) then - return SENTINEL_IMPASSABLE_CHUNK - else chunk[PASSABLE] = pass chunk[PATH_RATING] = passScore + return chunk end + + return SENTINEL_IMPASSABLE_CHUNK +end + +function chunkUtils.chunkPassScan(chunk, surface, regionMap) + local passScore = chunkUtils.calculatePassScore(surface, regionMap) + + if (passScore >= 0.40) then + local pass = chunkUtils.scanChunkPaths(chunk, surface, regionMap) + + local playerObjects = chunkUtils.getPlayerBaseGenerator(regionMap, chunk) + + local nests = chunkUtils.getNestCount(regionMap, chunk) + + if ((playerObjects > 0) or (nests > 0)) and (pass == CHUNK_IMPASSABLE) then + pass = CHUNK_ALL_DIRECTIONS + end + + chunk[PASSABLE] = pass + chunk[PATH_RATING] = passScore + + return chunk + end + + return SENTINEL_IMPASSABLE_CHUNK +end + +function chunkUtils.analyzeChunk(chunk, natives, surface, regionMap) + local playerObjects = chunkUtils.scorePlayerBuildings(surface, regionMap, natives) + chunkUtils.setPlayerBaseGenerator(regionMap, chunk, playerObjects) end -- function chunkUtils.remakeChunk(regionMap, chunk, surface, natives, tick, tempQuery) @@ -360,6 +410,10 @@ function chunkUtils.getResourceGenerator(regionMap, chunk) return regionMap.chunkToResource[chunk] or 0 end +function chunkUtils.addResourceGenerator(regionMap, chunk, delta) + regionMap.chunkToResource[chunk] = (regionMap.chunkToResource[chunk] or 0) + delta +end + function chunkUtils.getPlayerBaseGenerator(regionMap, chunk) return regionMap.chunkToPlayerBase[chunk] or 0 end @@ -373,7 +427,7 @@ function chunkUtils.setPlayerBaseGenerator(regionMap, chunk, playerGenerator) end function chunkUtils.addPlayerBaseGenerator(regionMap, chunk, playerGenerator) - regionMap.chunkToPlayerBase[chunk] = regionMap.chunkToPlayerBase[chunk] + playerGenerator + regionMap.chunkToPlayerBase[chunk] = (regionMap.chunkToPlayerBase[chunk] or 0) + playerGenerator end function chunkUtils.createChunk(topX, topY) @@ -402,6 +456,23 @@ function chunkUtils.colorChunk(x, y, tileType, surface) surface.set_tiles(tiles, false) end +function chunkUtils.entityForPassScan(regionMap, entity) + local leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(regionMap, entity) + + if (leftTop ~= SENTINEL_IMPASSABLE_CHUNK) then + regionMap.chunkToPassScan[leftTop] = true + end + if (rightTop ~= SENTINEL_IMPASSABLE_CHUNK) then + regionMap.chunkToPassScan[rightTop] = true + end + if (leftBottom ~= SENTINEL_IMPASSABLE_CHUNK) then + regionMap.chunkToPassScan[leftBottom] = true + end + if (rightBottom ~= SENTINEL_IMPASSABLE_CHUNK) then + regionMap.chunkToPassScan[rightBottom] = true + end +end + function chunkUtils.registerEnemyBaseStructure(regionMap, entity, base) local entityType = entity.type if ((entityType == "unit-spawner") or (entityType == "turret")) and (entity.force.name == "enemy") then @@ -471,6 +542,26 @@ function chunkUtils.addRemovePlayerEntity(regionMap, entity, natives, addObject, return entity end +function chunkUtils.unregisterResource(entity, regionMap) + if entity.prototype.infinite_resource then + return + end + local leftTop, rightTop, leftBottom, rightBottom = getEntityOverlapChunks(regionMap, entity) + + if (leftTop ~= SENTINEL_IMPASSABLE_CHUNK) then + chunkUtils.addResourceGenerator(regionMap, leftTop, -RESOURCE_GENERATOR_INCREMENT) + end + if (rightTop ~= SENTINEL_IMPASSABLE_CHUNK) then + chunkUtils.addResourceGenerator(regionMap, rightTop, -RESOURCE_GENERATOR_INCREMENT) + end + if (leftBottom ~= SENTINEL_IMPASSABLE_CHUNK) then + chunkUtils.addResourceGenerator(regionMap, leftBottom, -RESOURCE_GENERATOR_INCREMENT) + end + if (rightBottom ~= SENTINEL_IMPASSABLE_CHUNK) then + chunkUtils.addResourceGenerator(regionMap, rightBottom, -RESOURCE_GENERATOR_INCREMENT) + end +end + function chunkUtils.makeImmortalEntity(surface, entity) local repairPosition = entity.position local repairName = entity.name diff --git a/libs/Constants.lua b/libs/Constants.lua index 6221525..a224566 100755 --- a/libs/Constants.lua +++ b/libs/Constants.lua @@ -42,6 +42,7 @@ constants.INTERVAL_CHUNK = 17 constants.INTERVAL_LOGIC = 61 constants.INTERVAL_SQUAD = 41 +constants.RESOURCE_GENERATOR_INCREMENT = 0.001 constants.PLAYER_PHEROMONE_MULTIPLER = 100 @@ -174,7 +175,7 @@ constants.BUILDING_PHEROMONES["beacon"] = 10 constants.BUILDING_PHEROMONES["furnace"] = 12 constants.BUILDING_PHEROMONES["programmable-speaker"] = 8 constants.BUILDING_PHEROMONES["mining-drill"] = 35 -constants.BUILDING_PHEROMONES["rocket-silo"] = 18 +constants.BUILDING_PHEROMONES["rocket-silo"] = 120 constants.BUILDING_PHEROMONES["lamp"] = 4 constants.BUILDING_PHEROMONES["radar"] = 20 constants.BUILDING_PHEROMONES["lab"] = 15 @@ -186,6 +187,7 @@ constants.BUILDING_PHEROMONES["wall"] = 0.5 constants.BUILDING_PHEROMONES["electric-turret"] = 20 constants.BUILDING_PHEROMONES["fluid-turret"] = 28 constants.BUILDING_PHEROMONES["turret"] = 10 +constants.BUILDING_PHEROMONES["artillery-turret"] = 100 constants.retreatFilter = {} constants.retreatFilter[constants.SQUAD_RETREATING] = true @@ -211,6 +213,7 @@ constants.UNIT_GROUP_SLOWDOWN_FACTOR = 0.9 constants.SENTINEL_IMPASSABLE_CHUNK = {} +constants.SENTINEL_IMPASSABLE_CHUNK.name = "ImpassableChunk" constants.SENTINEL_IMPASSABLE_CHUNK[constants.MOVEMENT_PHEROMONE] = constants.IMPASSABLE_TERRAIN_GENERATOR_AMOUNT constants.SENTINEL_IMPASSABLE_CHUNK[constants.BASE_PHEROMONE] = constants.IMPASSABLE_TERRAIN_GENERATOR_AMOUNT constants.SENTINEL_IMPASSABLE_CHUNK[constants.PLAYER_PHEROMONE] = constants.IMPASSABLE_TERRAIN_GENERATOR_AMOUNT diff --git a/libs/MapProcessor.lua b/libs/MapProcessor.lua index e85dac4..91554bd 100755 --- a/libs/MapProcessor.lua +++ b/libs/MapProcessor.lua @@ -31,7 +31,6 @@ local AI_SQUAD_COST = constants.AI_SQUAD_COST local AI_VENGENCE_SQUAD_COST = constants.AI_VENGENCE_SQUAD_COST local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE -local BUILDING_PHEROMONES = constants.BUILDING_PHEROMONES -- imported functions diff --git a/libs/SquadDefense.lua b/libs/SquadDefense.lua index 3782bdb..c18596c 100755 --- a/libs/SquadDefense.lua +++ b/libs/SquadDefense.lua @@ -87,7 +87,7 @@ function aiDefense.retreatUnits(chunk, position, squad, regionMap, surface, nati if newSquad then if enemiesToSquad then - membersToSquad(newSquad, enemiesToSquad, (radius == RETREAT_SPAWNER_GRAB_RADIUS)) + membersToSquad(newSquad, enemiesToSquad, force) else membersToSquad(newSquad, squad.group.members, true) newSquad.penalties = squad.penalties diff --git a/libs/UnitGroupUtils.lua b/libs/UnitGroupUtils.lua index 6232ac2..9c4fca2 100755 --- a/libs/UnitGroupUtils.lua +++ b/libs/UnitGroupUtils.lua @@ -105,17 +105,7 @@ function unitGroupUtils.convertUnitGroupToSquad(natives, unitGroup) return squad end end - local returnSquad = { group = unitGroup, - status = SQUAD_GUARDING, - penalties = {}, - rabid = false, - frenzy = false, - kamikaze = false, - frenzyPosition = {x = 0, - y = 0}, - cycles = 0 } - squads[#squads+1] = returnSquad - return returnSquad + return nil end function unitGroupUtils.calculateKamikazeThreshold(squad, natives)