see readme

This commit is contained in:
Aaron Veden
2017-01-19 21:58:36 -08:00
parent 88076f189d
commit 071866d1fe
10 changed files with 189 additions and 77 deletions
+12
View File
@@ -36,6 +36,18 @@ Base Expansion
# Version History
0.14.8 -
- Feature: Rallying death cry, when a native dies on a chunk past the death threshold it will attempt to summon reinforcements from nearby nests
- Tweak: Increased unit group merge distance from 16 to 28 tiles
- Tweak: Increased retreat grab radius from 15 tiles to 24 tiles
- Tweak: Decreased vengence squad cost from 50 to 45
- Improvement: On group merge recalculate the kamikaze threshold, so groups that become large have a chance to kamikaze before attacking
- Improvement: Disallow group merges when units are taking and receiving damage
- Fix: Corrected unit group frenzy trigger based on engaging a target
- Optimization: Switched to increment tick counter for processing and logic event
- Optimization: Rate limited rally cry to 3/0.75 sec
- Optimization: Rate limited retreat to 8/0.75 sec
0.14.7 -
- Feature: Counterattack waves trigger when the player is standing in a chunk with the death pheromone past the retreat threshold
- Feature: Reinforcement waves trigger when the player is standind in a chunk that contains a nest
+59 -8
View File
@@ -1,6 +1,7 @@
-- imports
local entityUtils = require("libs/EntityUtils")
local mapUtils = require("libs/MapUtils")
local unitGroupUtils = require("libs/UnitGroupUtils")
local chunkProcessor = require("libs/ChunkProcessor")
local mapProcessor = require("libs/MapProcessor")
@@ -18,8 +19,20 @@ local tests = require("tests")
local INTERVAL_LOGIC = constants.INTERVAL_LOGIC
local INTERVAL_PROCESS = constants.INTERVAL_PROCESS
local MAX_RALLY_CRIES = constants.MAX_RALLY_CRIES
local MAX_RETREATS = constants.MAX_RETREATS
local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
local BASE_RALLY_CHANCE = constants.BASE_RALLY_CHANCE
local BONUS_RALLY_CHANCE = constants.BONUS_RALLY_CHANCE
local RETREAT_MOVEMENT_PHEROMONE_LEVEL = constants.RETREAT_MOVEMENT_PHEROMONE_LEVEL
-- imported functions
local getChunkByPosition = mapUtils.getChunkByPosition
local processPendingChunks = chunkProcessor.processPendingChunks
local processMap = mapProcessor.processMap
@@ -27,9 +40,11 @@ local processPlayers = mapProcessor.processPlayers
local scanMap = mapProcessor.scanMap
local planning = aiPlanning.planning
local removeScout = aiBuilding.removeScout
-- local removeScout = aiBuilding.removeScout
-- local scouting = aiBuilding.scouting
local rallyUnits = aiBuilding.rallyUnits
local deathScent = pheromoneUtils.deathScent
local regroupSquads = unitGroupUtils.regroupSquads
@@ -138,11 +153,23 @@ local function onConfigChanged()
global.version = constants.VERSION_12
end
if (global.version <= constants.VERSION_13) then
-- switched over to tick event
regionMap.logicTick = roundToNearest(game.tick + INTERVAL_LOGIC, INTERVAL_LOGIC)
regionMap.processTick = roundToNearest(game.tick + INTERVAL_PROCESS, INTERVAL_PROCESS)
-- used to rate limit the number of rally cries during a period of time
natives.rallyCries = MAX_RALLY_CRIES
natives.retreats = MAX_RETREATS
global.version = constants.VERSION_13
end
end
local function onTick(event)
local tick = event.tick
if (tick % INTERVAL_PROCESS == 0) then
if (tick == regionMap.processTick) then
regionMap.processTick = regionMap.processTick + INTERVAL_PROCESS
local surface = game.surfaces[1]
local evolutionFactor = game.evolution_factor
local players = game.players
@@ -150,10 +177,16 @@ local function onTick(event)
processPendingChunks(regionMap, surface, pendingChunks)
scanMap(regionMap, surface)
if (tick % INTERVAL_LOGIC == 0) then
if (tick == regionMap.logicTick) then
regionMap.logicTick = regionMap.logicTick + INTERVAL_LOGIC
natives.rallyCries = MAX_RALLY_CRIES
natives.retreats = MAX_RETREATS
planning(natives, evolutionFactor, tick)
regroupSquads(natives)
regroupSquads(natives, evolutionFactor)
processPlayers(players, regionMap, surface, natives, evolutionFactor, tick)
@@ -182,20 +215,38 @@ local function onDeath(event)
if (entity.force.name == "enemy") then
if (entity.type == "unit") then
local entityPosition = entity.position
local deathChunk = getChunkByPosition(regionMap, entityPosition.x, entityPosition.y)
if (deathChunk ~= nil) then
-- drop death pheromone where unit died
deathScent(regionMap, entityPosition)
deathScent(deathChunk)
if (event.force ~= nil) and (event.force.name == "player") then
retreatUnits(entityPosition,
if ((event.force ~= nil) and (event.force.name == "player")) then
local evolutionFactor = game.evolution_factor
if (deathChunk[MOVEMENT_PHEROMONE] < -(evolutionFactor * RETREAT_MOVEMENT_PHEROMONE_LEVEL)) then
if (natives.retreats >= 0) then
retreatUnits(deathChunk,
convertUnitGroupToSquad(natives,
entity.unit_group),
regionMap,
surface,
natives)
end
local rallyThreshold = BASE_RALLY_CHANCE + (evolutionFactor * BONUS_RALLY_CHANCE)
if (natives.rallyCries >= 0) and (math.random() < rallyThreshold) then
natives.rallyCries = natives.rallyCries - 1
rallyUnits(deathChunk,
regionMap,
surface,
natives,
evolutionFactor)
end
end
end
end
removeScout(entity, global.natives)
-- removeScout(entity, natives)
elseif (entity.type == "unit-spawner") then
addRemoveEntity(regionMap, entity, natives, false, false)
end
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name" : "Rampant",
"factorio_version" : "0.14",
"version" : "0.14.7",
"version" : "0.14.8",
"title" : "Rampant AI",
"author" : "Veden",
"homepage" : "https://forums.factorio.com/viewtopic.php?f=94&t=31445",
+3 -14
View File
@@ -7,8 +7,6 @@ local mapUtils = require("MapUtils")
local unitGroupUtils = require("UnitGroupUtils")
local playerUtils = require("PlayerUtils")
local neighborUtils = require("NeighborUtils")
package.path = "../?.lua;" .. package.path
local config = require("config")
-- constants
@@ -21,12 +19,6 @@ local SQUAD_GUARDING = constants.SQUAD_GUARDING
local PLAYER_BASE_GENERATOR = constants.PLAYER_BASE_GENERATOR
local NO_RETREAT_BASE_PERCENT = constants.NO_RETREAT_BASE_PERCENT
local NO_RETREAT_EVOLUTION_BONUS_MAX = constants.NO_RETREAT_EVOLUTION_BONUS_MAX
local NO_RETREAT_SQUAD_SIZE_BONUS_MAX = constants.NO_RETREAT_SQUAD_SIZE_BONUS_MAX
local CONFIG_ATTACK_WAVE_MAX_SIZE = config.attackWaveMaxSize
-- imported functions
local getNeighborChunksWithDirection = mapUtils.getNeighborChunksWithDirection
@@ -34,6 +26,7 @@ local getChunkByPosition = mapUtils.getChunkByPosition
local canMoveChunkDirection = mapUtils.canMoveChunkDirection
local addSquadMovementPenalty = unitGroupUtils.addSquadMovementPenalty
local lookupSquadMovementPenalty = unitGroupUtils.lookupSquadMovementPenalty
local calculateKamikazeThreshold = unitGroupUtils.calculateKamikazeThreshold
local positionFromDirectionAndChunk = mapUtils.positionFromDirectionAndChunk
local euclideanDistanceNamed = mapUtils.euclideanDistanceNamed
@@ -42,8 +35,6 @@ local playersWithinProximityToPosition = playerUtils.playersWithinProximityToPos
local scoreNeighborsWithDirection = neighborUtils.scoreNeighborsWithDirection
local mLog = math.log10
-- module code
local function validLocation(x, chunk, neighborChunk)
@@ -104,7 +95,7 @@ function aiAttack.squadAttack(regionMap, surface, natives)
group.set_command(attackCmd)
group.start_moving()
elseif not squad.frenzy and not squad.rabid and
((group.state == defines.group_state.attacking_distraction) or (group.state == defines.group_state.attacking_distraction) or
((group.state == defines.group_state.attacking_distraction) or (group.state == defines.group_state.attacking_target) or
(attackChunk[PLAYER_BASE_GENERATOR] ~= 0)) then
squad.frenzy = true
squad.frenzyPosition.x = squad.group.position.x
@@ -122,9 +113,7 @@ function aiAttack.squadBeginAttack(natives, players, evolution_factor)
for i=1,#squads do
local squad = squads[i]
if (squad.status == SQUAD_GUARDING) and squad.group.valid then
local kamikazeThreshold = NO_RETREAT_BASE_PERCENT + (evolution_factor * NO_RETREAT_EVOLUTION_BONUS_MAX)
local squadSizeBonus = mLog((#squad.group.members / CONFIG_ATTACK_WAVE_MAX_SIZE) + 0.1) + 1
kamikazeThreshold = kamikazeThreshold + (NO_RETREAT_SQUAD_SIZE_BONUS_MAX * squadSizeBonus)
local kamikazeThreshold = calculateKamikazeThreshold(squad, evolution_factor)
local playerNearby = playersWithinProximityToPosition(players, squad.group.position, 100)
if playerNearby then
+19
View File
@@ -33,9 +33,15 @@ local CONFIG_USE_THRESHOLD_MIN = config.attackWaveGenerationThresholdMin
local CONFIG_USE_THRESHOLD_MAX = config.attackWaveGenerationThresholdMax
local CONFIG_USE_THRESHOLD_RANGE = CONFIG_USE_THRESHOLD_MAX - CONFIG_USE_THRESHOLD_MIN
local RETREAT_MOVEMENT_PHEROMONE_LEVEL = constants.RETREAT_MOVEMENT_PHEROMONE_LEVEL
local RALLY_CRY_DISTANCE = 3
-- imported functions
local getNeighborChunks = mapUtils.getNeighborChunks
local getChunkByPosition = mapUtils.getChunkByPosition
local getChunkByIndex = mapUtils.getChunkByIndex
local scoreNeighbors = neighborUtils.scoreNeighbors
local createSquad = unitGroupUtils.createSquad
local attackWaveScaling = config.attackWaveScaling
@@ -117,6 +123,19 @@ function aiBuilding.scouting(regionMap, natives)
--]]
end
function aiBuilding.rallyUnits(chunk, regionMap, surface, natives, evolutionFactor)
local cX = chunk.cX
local cY = chunk.cY
for x=cX - RALLY_CRY_DISTANCE, cX + RALLY_CRY_DISTANCE do
for y=cY - RALLY_CRY_DISTANCE, cY + RALLY_CRY_DISTANCE do
local rallyChunk = getChunkByIndex(regionMap, x, y)
if (x ~= cX) and (y ~= cY) and (rallyChunk[ENEMY_BASE_GENERATOR] ~= 0) then
aiBuilding.formSquads(regionMap, surface, natives, rallyChunk, evolutionFactor, AI_VENGENCE_SQUAD_COST)
end
end
end
end
function aiBuilding.formSquads(regionMap, surface, natives, chunk, evolution_factor, cost)
if (natives.points > cost) and (chunk[ENEMY_BASE_GENERATOR] ~= 0) and (#natives.squads < (AI_MAX_SQUAD_COUNT * evolution_factor)) then
local valid = false
+6 -7
View File
@@ -9,12 +9,12 @@ local neighborUtils = require("NeighborUtils")
-- constants
local RETREAT_GRAB_RADIUS = constants.RETREAT_GRAB_RADIUS
local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
local BASE_PHEROMONE = constants.BASE_PHEROMONE
local RETREAT_MOVEMENT_PHEROMONE_LEVEL = constants.RETREAT_MOVEMENT_PHEROMONE_LEVEL
local ENEMY_BASE_GENERATOR = constants.ENEMY_BASE_GENERATOR
local HALF_CHUNK_SIZE = constants.HALF_CHUNK_SIZE
@@ -25,7 +25,6 @@ local RETREAT_FILTER = constants.RETREAT_FILTER
-- imported functions
local getChunkByPosition = mapUtils.getChunkByPosition
local getNeighborChunksWithDirection = mapUtils.getNeighborChunksWithDirection
local findNearBySquad = unitGroupUtils.findNearBySquad
local addSquadMovementPenalty = unitGroupUtils.addSquadMovementPenalty
@@ -46,14 +45,14 @@ local function scoreRetreatLocation(position, squad, neighborChunk, surface)
return safeScore - dangerScore
end
function aiDefense.retreatUnits(position, squad, regionMap, surface, natives)
local chunk = getChunkByPosition(regionMap, position.x, position.y)
if (chunk ~= nil) and (chunk[MOVEMENT_PHEROMONE] < -(game.evolution_factor * RETREAT_MOVEMENT_PHEROMONE_LEVEL)) and (chunk[ENEMY_BASE_GENERATOR] == 0) then
function aiDefense.retreatUnits(chunk, squad, regionMap, surface, natives)
if (chunk[ENEMY_BASE_GENERATOR] == 0) then
local performRetreat = false
local enemiesToSquad
if (squad == nil) then
enemiesToSquad = surface.find_enemy_units(position, 15)
enemiesToSquad = surface.find_enemy_units({x=chunk.pX,
y=chunk.pY}, RETREAT_GRAB_RADIUS)
if (#enemiesToSquad > 0) then
performRetreat = true
end
+16 -1
View File
@@ -7,6 +7,7 @@ constants.VERSION_9 = 9
constants.VERSION_10 = 10
constants.VERSION_11 = 11
constants.VERSION_12 = 12
constants.VERSION_13 = 13
-- misc
@@ -28,7 +29,7 @@ constants.INTERVAL_LOGIC = 40
constants.AI_POINT_GENERATOR_AMOUNT = 6
constants.AI_SCOUT_COST = 45
constants.AI_SQUAD_COST = 175
constants.AI_VENGENCE_SQUAD_COST = 50
constants.AI_VENGENCE_SQUAD_COST = 45
constants.AI_SETTLER_COST = 75
constants.AI_BASE_BUILDING_COST = 500
constants.AI_TUNNEL_COST = 100
@@ -95,6 +96,20 @@ constants.SQUAD_GUARDING = 2 -- used when squad is idle
constants.SQUAD_BURROWING = 3
constants.SQUAD_RAIDING = 4 -- used when player stuff is close
-- Squad Related
constants.RETREAT_GRAB_RADIUS = 24
constants.BASE_RALLY_CHANCE = 0.01
constants.BONUS_RALLY_CHANCE = 0.01
constants.MAX_RETREATS = 7
constants.MAX_RALLY_CRIES = 2
constants.RALLY_CRY_DISTANCE = 3
constants.GROUP_MERGE_DISTANCE = 28
-- player building pheromones
constants.BUILDING_PHEROMONES = {}
+1 -4
View File
@@ -35,11 +35,8 @@ function pheromoneUtils.scents(chunk)
chunk[BASE_PHEROMONE] = chunk[BASE_PHEROMONE] + chunk[PLAYER_BASE_GENERATOR] - chunk[ENEMY_BASE_GENERATOR]
end
function pheromoneUtils.deathScent(regionMap, position)
local chunk = getChunkByPosition(regionMap, position.x, position.y)
if (chunk ~= nil) then
function pheromoneUtils.deathScent(chunk)
chunk[MOVEMENT_PHEROMONE] = chunk[MOVEMENT_PHEROMONE] - DEATH_PHEROMONE_GENERATOR_AMOUNT
end
end
function pheromoneUtils.playerScent(playerChunk)
+34 -4
View File
@@ -4,18 +4,31 @@ local unitGroupUtils = {}
local mapUtils = require("MapUtils")
local constants = require("Constants")
package.path = "../?.lua;" .. package.path
local config = require("config")
-- constants
local MOVEMENT_PHEROMONE_GENERATOR_AMOUNT = constants.MOVEMENT_PHEROMONE_GENERATOR_AMOUNT
local GROUP_STATE_FINISHED = defines.group_state.finished
local GROUP_STATE_ATTACKING_TARGET = defines.group_state.attacking_target
local GROUP_STATE_ATTACKING_DISTRACTION = defines.group_state.attacking_distraction
local SQUAD_RETREATING = constants.SQUAD_RETREATING
local SQUAD_GUARDING = constants.SQUAD_GUARDING
local GROUP_MERGE_DISTANCE = constants.GROUP_MERGE_DISTANCE
local NO_RETREAT_BASE_PERCENT = constants.NO_RETREAT_BASE_PERCENT
local NO_RETREAT_EVOLUTION_BONUS_MAX = constants.NO_RETREAT_EVOLUTION_BONUS_MAX
local NO_RETREAT_SQUAD_SIZE_BONUS_MAX = constants.NO_RETREAT_SQUAD_SIZE_BONUS_MAX
local CONFIG_ATTACK_WAVE_MAX_SIZE = config.attackWaveMaxSize
-- imported functions
local mLog = math.log10
local tableRemove = table.remove
local tableInsert = table.insert
local euclideanDistanceNamed = mapUtils.euclideanDistanceNamed
@@ -118,23 +131,41 @@ function unitGroupUtils.lookupSquadMovementPenalty(squad, chunkX, chunkY)
return 0
end
function unitGroupUtils.regroupSquads(natives)
function unitGroupUtils.calculateKamikazeThreshold(squad, evolution_factor)
local kamikazeThreshold = NO_RETREAT_BASE_PERCENT + (evolution_factor * NO_RETREAT_EVOLUTION_BONUS_MAX)
local squadSizeBonus = mLog((#squad.group.members / CONFIG_ATTACK_WAVE_MAX_SIZE) + 0.1) + 1
return kamikazeThreshold + (NO_RETREAT_SQUAD_SIZE_BONUS_MAX * squadSizeBonus)
end
local function isAttacking(squad)
return (squad.state == GROUP_STATE_ATTACKING_TARGET) or (squad.state == GROUP_STATE_ATTACKING_DISTRACTION)
end
function unitGroupUtils.regroupSquads(natives, evolution_factor)
local squads = natives.squads
for i=1,#squads do
local squad = squads[i]
if squad.group.valid then
if squad.group.valid and not isAttacking(squad) then
local squadPosition = squad.group.position
local mergedSquads = false
for x=i+1, #squads do
local mergeSquad = squads[x]
local mergeGroup = mergeSquad.group
if mergeGroup.valid and (mergeSquad.status == squad.status) and (euclideanDistanceNamed(squadPosition, mergeGroup.position) < 16) then
if mergeGroup.valid and (mergeSquad.status == squad.status) and not isAttacking(mergeSquad) and (euclideanDistanceNamed(squadPosition, mergeGroup.position) < GROUP_MERGE_DISTANCE) then
unitGroupUtils.membersToSquad(squad, mergeGroup.members, true)
if mergeSquad.kamikaze then
squad.kamikaze = true
end
mergedSquads = true
mergeGroup.destroy()
end
end
if mergedSquads and not squad.kamikaze then
local kamikazeThreshold = unitGroupUtils.calculateKamikazeThreshold(squad, evolution_factor)
if (math.random() < kamikazeThreshold) then
squad.kamikaze = true
end
end
end
end
@@ -158,7 +189,6 @@ function unitGroupUtils.regroupSquads(natives)
elseif (squad.cycles > 0) then
squad.cycles = squad.cycles - 1
end
end
end
end
+1 -1
View File
@@ -73,7 +73,7 @@
(copyDirectory "graphics" modFolder)
(copyDirectory "prototypes" modFolder)))
;; (copyFiles modFolder)
;;(copyFiles modFolder)
;; (copyFiles zipModFolder)
(makeZip modFolder)
;;(makeZip zipModFolder)