1
0
mirror of https://github.com/veden/Rampant.git synced 2024-12-26 20:54:12 +02:00

see changelog 0.14.4

This commit is contained in:
Aaron Veden 2016-10-14 17:00:18 -07:00
parent 04c184078d
commit d989322407
20 changed files with 578 additions and 452 deletions

1
.gitignore vendored
View File

@ -47,3 +47,4 @@ luac.out
*.dumpjump
/.emacs.desktop
/.emacs.desktop.lock
/README.html

View File

@ -7,6 +7,7 @@ https://forums.factorio.com/viewtopic.php?f=94&t=31445
# Notes
0.14.14 factorio version fixed save corruption
0.14.10 factorio version fixed more pathing issues
0.14.4 factorio version fixed some issues with unit groups commands
@ -33,36 +34,56 @@ Base Expansion
# Version History
0.14.3 - slightly lowered Rampant attack wave frequency
Altered attack wave size to ramp up slower
Added configuration options for:
attack wave generation area
attack wave threshold
attack wave size
turn off rampant attack waves
0.14.4 -
- Fixed a bug in the processing queue when upgrading mod
- Greatly decreased Player pheromone radius, now sits at roughly 4 chunks around the player
- Reworked pheromone pathfinding
- Removed base and defense attack wave trigger, in favor of using player pheromone and pollution
- Added periods of time where the enemy is not sending Rampant attack waves
- Adjusted retreat percentage to suit the reduced attack wave size
- Improved responsiveness on larger maps
- Reduced AI max build points
- Fixed player iteration bug
0.14.2 - adjusted unit retreat group size threshold
adjusted squad attack pattern (https://forums.factorio.com/viewtopic.php?f=94&t=31445&start=20#p203861)
Fixed migration issue
0.14.3 -
- Slightly lowered Rampant attack wave frequency
- Altered attack wave size to ramp up slower
- Added configuration options for:
- - Attack wave generation area
- - Attack wave threshold
- - Attack wave size
- - Turn off rampant attack waves
0.14.1 - fixed ai created bases not being counted in logic
Optimization to offset ai created bases scanning
0.14.2 -
- Adjusted unit retreat group size threshold
- Adjusted squad attack pattern (https://forums.factorio.com/viewtopic.php?f=94&t=31445&start=20#p203861)
- Fixed migration issue
0.14.1 -
- Fixed ai created bases not being counted in logic
- Optimization to offset ai created bases scanning
0.13.4 = 0.14.4
0.13.3 = 0.14.3
0.13.2 = 0.14.2
0.13.1 - backported 0.14 factorio version to 0.13 factorio version
0.13.1 - Backported 0.14 factorio version to 0.13 factorio version
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)
increased squad size max from 125 to 150, (larger waves)
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)
- Increased squad size max from 125 to 150, (larger waves)
0.0.6 - some speed improvements
MP is working (https://github.com/veden/Rampant/issues/1)
0.0.6 -
- Some speed improvements
- MP is working (https://github.com/veden/Rampant/issues/1)
0.0.5 - fix for nil chunk in ai attack (https://mods.factorio.com/mods/Veden/Rampant/discussion/2512)
checks for main surface (https://forums.factorio.com/viewtopic.php?f=94&t=31445&p=198228#p198563)
updated info with forum homepage
0.0.5 -
- Fix for nil chunk in ai attack (https://mods.factorio.com/mods/Veden/Rampant/discussion/2512)
- Xhecks for main surface (https://forums.factorio.com/viewtopic.php?f=94&t=31445&p=198228#p198563)
- Updated info with forum homepage
0.0.4 - initial release

View File

@ -4,29 +4,37 @@ local config = {}
the attackWaveGenerationUse* options are used to score chunks with biter nests that will generate a Rampant attack wave.
Pollution, the vanilla pollution mechanic (shown on the minimap).
Player Proximity, if a player moves near a biter nest there is a chance for the nest to spawn attack waves (not shown on the minimap).
Player Base Proximity, if the player builds near biters nest there is a chance for the nest to spawn attack waves (not shown on the minimap).
Player Defense Proximity, if the player builds defense near biters nest there is a chance for the nest to spawn attack waves (not shown on the minimap).
switching all to false will turn off Rampant biter waves, does not turn off vanilla biter waves.
switching all to false will turn off Rampant biter waves
DOES NOT affect vanilla biters waves
--]]
config.attackWaveGenerationUsePollution = true
config.attackWaveGenerationUsePlayerProximity = true
config.attackWaveGenerationUsePlayerBaseProximity = true
config.attackWaveGenerationUsePlayerDefenseProximity = true
--[[
attackWaveGenerationThreshold is the score that the attackWaveGenerationUse* has to reach in order for an attack wave to spawn.
increasing this will reduce the radius of attack wave generation.
default number is 70
DOES NOT affect vanilla biters waves
scaling linearly with evolution factor
starts 20 @ 0.0 evolution
ends 0 @ 100.0 evolution
default max is 20
default min is 00
--]]
config.attackWaveGenerationThreshold = 70
config.attackWaveGenerationThresholdMax = 20
config.attackWaveGenerationThresholdMin = 0
--[[
attackWaveMaxSize is the largest size that can be initially spawned by Rampant
--]]
config.attackWaveMaxSize = 150
--[[
attackWaveScaling is used to calculate the attack wave size from the evolutionFactor
default is 150 * (evolutionFactor ^ 1.666667)
150 is the max group size
default is config.attackWaveMaxSize * (evolutionFactor ^ 1.666667)
DOES NOT affect vanilla biters waves
--]]
config.attackWaveScaling = function (evolutionFactor)
return math.ceil(150 * (evolutionFactor ^ 1.666667))
return math.ceil(config.attackWaveMaxSize * (evolutionFactor ^ 1.666667))
end
return config

View File

@ -9,20 +9,27 @@ local pheromoneUtils = require("libs/PheromoneUtils")
local aiDefense = require("libs/AIDefense")
local aiAttack = require("libs/AIAttack")
local aiBuilding = require("libs/AIBuilding")
local aiPlanning = require("libs/AIPlanning")
local mathUtils = require("libs/MathUtils")
local tests = require("tests")
-- constants
local INTERVAL_LOGIC = constants.INTERVAL_LOGIC
local INTERVAL_PROCESS = constants.INTERVAL_PROCESS
-- imported functions
local processPendingChunks = chunkProcessor.processPendingChunks
local processMap = mapProcessor.processMap
local processPlayers = mapProcessor.processPlayers
local scanMap = mapProcessor.scanMap
local accumulatePoints = aiBuilding.accumulatePoints
local planning = aiPlanning.planning
local removeScout = aiBuilding.removeScout
-- local scouting = aiBuilding.scouting
local playerScent = pheromoneUtils.playerScent
local deathScent = pheromoneUtils.deathScent
local regroupSquads = unitGroupUtils.regroupSquads
@ -35,6 +42,8 @@ local retreatUnits = aiDefense.retreatUnits
local addRemoveEntity = entityUtils.addRemoveEntity
local roundToNearest = mathUtils.roundToNearest
-- local references to global
local regionMap
@ -44,11 +53,9 @@ local pendingChunks
-- hook functions
local function onLoad()
-- print("load")
regionMap = global.regionMap
natives = global.natives
pendingChunks = global.pendingChunks
-- pheromoneTotals = global.pheromoneTotals
end
local function onChunkGenerated(event)
@ -60,7 +67,6 @@ local function onChunkGenerated(event)
end
local function onConfigChanged()
-- print("reprocess")
if (global.version == nil) then
-- removed in version 9
@ -84,11 +90,6 @@ local function onConfigChanged()
regionMap.pP = nil
regionMap.pR = nil
regionMap.processQueue = {}
regionMap.processPointer = 1
regionMap.scanPointer = 1
regionMap.processRoll = -1
global.version = constants.VERSION_9
end
if (global.version < constants.VERSION_10) then
@ -98,40 +99,64 @@ local function onConfigChanged()
squad.rabid = false
end
-- queue all current chunks that wont be generated during play
local surface = game.surfaces[1]
for chunk in surface.get_chunks() do
onChunkGenerated({ surface = surface,
area = { left_top = { x = chunk.x * 32,
y = chunk.y * 32 }}})
end
global.version = constants.VERSION_10
end
if (global.version < constants.VERSION_11) then
for _,squad in pairs(natives.squads) do
squad.status = constants.SQUAD_GUARDING
end
natives.state = constants.AI_STATE_AGGRESSIVE
natives.temperament = 0
-- needs to be on inner logic tick loop interval
natives.stateTick = roundToNearest(game.tick + INTERVAL_LOGIC, INTERVAL_LOGIC)
natives.temperamentTick = roundToNearest(game.tick + INTERVAL_LOGIC, INTERVAL_LOGIC)
-- clear old regionMap processing Queue
-- prevents queue adding duplicate chunks
-- chunks are by key, so should overwrite old
regionMap.processQueue = {}
regionMap.processPointer = 1
regionMap.scanPointer = 1
-- clear pending chunks, will be added when loop runs below
pendingChunks = {}
-- queue all current chunks that wont be generated during play
local surface = game.surfaces[1]
for chunk in surface.get_chunks() do
onChunkGenerated({ surface = surface,
area = { left_top = { x = chunk.x * 32,
y = chunk.y * 32 }}})
end
global.version = constants.VERSION_11
end
end
local function onTick(event)
if (event.tick % 20 == 0) then
if (event.tick % INTERVAL_PROCESS == 0) then
local surface = game.surfaces[1]
local evolutionFactor = game.evolution_factor
local players = game.players
processPendingChunks(regionMap, surface, pendingChunks)
scanMap(regionMap, surface)
processPendingChunks(regionMap, surface, pendingChunks)
scanMap(regionMap, surface)
if (event.tick % 40 == 0) then
if (event.tick % INTERVAL_LOGIC == 0) then
local tick = game.tick
planning(natives, evolutionFactor, tick)
accumulatePoints(natives)
regroupSquads(natives)
-- put down player pheromone for player hunters
playerScent(regionMap, game.players)
processPlayers(players, regionMap, surface, natives, evolutionFactor, tick)
regroupSquads(natives)
-- scouting(regionMap, natives)
-- scouting(regionMap, natives)
squadBeginAttack(natives, players, evolutionFactor)
squadAttack(regionMap, surface, natives)
end
squadBeginAttack(natives, game.players, game.evolution_factor)
squadAttack(regionMap, surface, natives)
end
processMap(regionMap, surface, natives, game.evolution_factor)
processMap(regionMap, surface, natives, evolutionFactor)
end
end
@ -181,16 +206,13 @@ local function onSurfaceTileChange(event)
end
local function onInit()
-- print("init")
global.regionMap = {}
global.pendingChunks = {}
global.natives = {}
-- global.pheromoneTotals = {}
regionMap = global.regionMap
natives = global.natives
pendingChunks = global.pendingChunks
-- pheromoneTotals = global.pheromoneTotals
onConfigChanged()
end
@ -216,15 +238,15 @@ script.on_event(defines.events.on_tick, onTick)
script.on_event(defines.events.on_chunk_generated, onChunkGenerated)
remote.add_interface("rampant", {
test1 = tests.test1,
test2 = tests.test2,
test3 = tests.test3,
test4 = tests.test4,
test5 = tests.test5,
test6 = tests.test6,
test7 = tests.test7,
test8 = tests.test8,
test9 = tests.test9,
test10 = tests.test10,
test11 = tests.test11
})
test1 = tests.test1,
test2 = tests.test2,
test3 = tests.test3,
test4 = tests.test4,
test5 = tests.test5,
test6 = tests.test6,
test7 = tests.test7,
test8 = tests.test8,
test9 = tests.test9,
test10 = tests.test10,
test11 = tests.test11
})

View File

@ -1,7 +1,7 @@
{
"name" : "Rampant",
"factorio_version" : "0.14",
"version" : "0.14.3",
"version" : "0.14.4",
"title" : "Rampant AI",
"author" : "Veden",
"homepage" : "https://forums.factorio.com/viewtopic.php?f=94&t=31445",

View File

@ -7,29 +7,26 @@ 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
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
local DEATH_PHEROMONE = constants.DEATH_PHEROMONE
local ENEMY_BASE_PHEROMONE = constants.ENEMY_BASE_PHEROMONE
local PLAYER_BASE_PHEROMONE = constants.PLAYER_BASE_PHEROMONE
--local PLAYER_DEFENSE_PHEROMONE = constants.PLAYER_DEFENSE_PHEROMONE
local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
local BASE_PHEROMONE = constants.BASE_PHEROMONE
local SQUAD_RAIDING = constants.SQUAD_RAIDING
local SQUAD_SUICIDE_RAID = constants.SQUAD_SUICIDE_RAID
local SQUAD_HUNTING = constants.SQUAD_HUNTING
local SQUAD_GUARDING = constants.SQUAD_GUARDING
local SQUAD_SUICIDE_HUNT = constants.SQUAD_SUICIDE_HUNT
--local ENEMY_BASE_GENERATOR = constants.ENEMY_BASE_GENERATOR
--local MAGIC_MAXIMUM_NUMBER = constants.MAGIC_MAXIMUM_NUMBER
--local HALF_CHUNK_SIZE = constants.HALF_CHUNK_SIZE
--local CHUNK_SIZE = constants.CHUNK_SIZE
local PLAYER_BASE_GENERATOR = constants.PLAYER_BASE_GENERATOR
local PLAYER_DEFENSE_GENERATOR = constants.PLAYER_DEFENSE_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
@ -46,7 +43,7 @@ local playersWithinProximityToPosition = playerUtils.playersWithinProximityToPos
local scoreNeighborsWithDirection = neighborUtils.scoreNeighborsWithDirection
local mLog = math.log10
-- module code
@ -56,16 +53,8 @@ end
local function scoreAttackLocation(position, squad, neighborChunk, surface)
local squadMovementPenalty = lookupSquadMovementPenalty(squad, neighborChunk.cX, neighborChunk.cY)
local damageScore = surface.get_pollution(position) + neighborChunk[PLAYER_BASE_PHEROMONE] + neighborChunk[PLAYER_PHEROMONE] + neighborChunk[PLAYER_DEFENSE_GENERATOR]
local avoidScore = neighborChunk[DEATH_PHEROMONE] + neighborChunk[ENEMY_BASE_PHEROMONE]
return damageScore - avoidScore - squadMovementPenalty
end
local function scoreHuntPlayerLocation(position, squad, neighborChunk, surface)
local squadMovementPenalty = lookupSquadMovementPenalty(squad, neighborChunk.cX, neighborChunk.cY)
local damageScore = neighborChunk[PLAYER_PHEROMONE]
local avoidScore = neighborChunk[DEATH_PHEROMONE] + neighborChunk[ENEMY_BASE_PHEROMONE] + neighborChunk[PLAYER_DEFENSE_GENERATOR]
return damageScore - avoidScore - squadMovementPenalty
local r = surface.get_pollution(position) + neighborChunk[MOVEMENT_PHEROMONE] + neighborChunk[BASE_PHEROMONE] + neighborChunk[PLAYER_PHEROMONE]
return r - squadMovementPenalty
end
function aiAttack.squadAttack(regionMap, surface, natives)
@ -82,17 +71,7 @@ function aiAttack.squadAttack(regionMap, surface, natives)
for i=1,#squads do
local squad = squads[i]
local group = squad.group
local raiding = false
local hunting = false
local scoreLocation
if (squad.status == SQUAD_RAIDING) or (squad.status == SQUAD_SUICIDE_RAID) then
raiding = true
scoreLocation = scoreAttackLocation
elseif (squad.status == SQUAD_HUNTING) or (squad.status == SQUAD_SUICIDE_HUNT) then
hunting = true
scoreLocation = scoreHuntPlayerLocation
end
if group.valid and (raiding or hunting) then
if group.valid and ((squad.status == SQUAD_RAIDING) or (squad.status == SQUAD_SUICIDE_RAID)) then
if (group.state == defines.group_state.finished) or (group.state == defines.group_state.gathering) or ((group.state == defines.group_state.moving) and (squad.cycles == 0)) then
local chunk = getChunkByPosition(regionMap, group.position.x, group.position.y)
if (chunk ~= nil) then
@ -100,15 +79,15 @@ function aiAttack.squadAttack(regionMap, surface, natives)
local attackChunk, attackDirection = scoreNeighborsWithDirection(chunk,
getCardinalChunksWithDirection(regionMap, chunk.cX, chunk.cY),
validLocation,
scoreLocation,
scoreAttackLocation,
squad,
surface,
attackPosition)
if (attackChunk ~= nil) then
if ((attackChunk[PLAYER_BASE_GENERATOR] == 0) and (attackChunk[PLAYER_DEFENSE_GENERATOR] == 0)) or
((group.state == defines.group_state.finished) or (group.state == defines.group_state.gathering)) then
if (attackChunk[PLAYER_BASE_GENERATOR] == 0) or
((group.state == defines.group_state.finished) or (group.state == defines.group_state.gathering)) then
positionFromDirectionAndChunkCardinal(attackDirection, squad.group.position, attackPosition)
positionFromDirectionAndChunkCardinal(attackDirection, squad.group.position, attackPosition)
squad.cycles = 3
@ -122,14 +101,14 @@ function aiAttack.squadAttack(regionMap, surface, natives)
attackCmd.distraction = defines.distraction.by_enemy
end
group.set_command(attackCmd)
group.start_moving()
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
(attackChunk[PLAYER_BASE_GENERATOR] ~= 0) or (attackChunk[PLAYER_DEFENSE_GENERATOR] ~= 0)) then
squad.frenzy = true
squad.frenzyPosition.x = squad.group.position.x
squad.frenzyPosition.y = squad.group.position.y
((group.state == defines.group_state.attacking_distraction) or (group.state == defines.group_state.attacking_distraction) or
(attackChunk[PLAYER_BASE_GENERATOR] ~= 0)) then
squad.frenzy = true
squad.frenzyPosition.x = squad.group.position.x
squad.frenzyPosition.y = squad.group.position.y
end
end
end
@ -143,7 +122,10 @@ 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 threshold = 0.05 + (evolution_factor * 0.20) + (#squad.group.members * 0.0033)
local threshold = NO_RETREAT_BASE_PERCENT + (evolution_factor * NO_RETREAT_EVOLUTION_BONUS_MAX)
local a = (#squad.group.members / CONFIG_ATTACK_WAVE_MAX_SIZE) ^ 1.4
local squadSizeBonus = mLog(a + 0.1) + 1
threshold = threshold + (NO_RETREAT_SQUAD_SIZE_BONUS_MAX * squadSizeBonus)
local playerNearby = playersWithinProximityToPosition(players, squad.group.position, 100)
if playerNearby then
@ -152,24 +134,15 @@ function aiAttack.squadBeginAttack(natives, players, evolution_factor)
squad.frenzyPosition.y = squad.group.position.y
end
-- check to hunt player
if (math.random() < 0.30) and playerNearby then
if (math.random() < threshold) then
squad.status = SQUAD_SUICIDE_HUNT
else
squad.status = SQUAD_HUNTING
end
end
-- check to raid base
if (squad.status == SQUAD_GUARDING) and (math.random() < 0.70) then
-- check to raid base
if (math.random() < 0.70) then
if (math.random() < threshold) then
squad.status = SQUAD_SUICIDE_RAID
else
squad.status = SQUAD_RAIDING
end
end
end
squad.status = SQUAD_SUICIDE_RAID
else
squad.status = SQUAD_RAIDING
end
end
end
end
end

View File

@ -11,42 +11,26 @@ local config = require("config")
-- constants
-- local SQUAD_GUARDING = constants.SQUAD_GUARDING
-- local SQUAD_BURROWING = constants.SQUAD_BURROWING
local PLAYER_BASE_PHEROMONE = constants.PLAYER_BASE_PHEROMONE
local BASE_PHEROMONE = constants.BASE_PHEROMONE
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
local PLAYER_DEFENSE_PHEROMONE = constants.PLAYER_DEFENSE_PHEROMONE
-- local ENEMY_BASE_PHEROMONE = constants.ENEMY_BASE_PHEROMONE
local DEATH_PHEROMONE = constants.DEATH_PHEROMONE
local AI_POINT_GENERATOR_AMOUNT = constants.AI_POINT_GENERATOR_AMOUNT
local AI_MAX_POINTS = constants.AI_MAX_POINTS
local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
local ENEMY_BASE_GENERATOR = constants.ENEMY_BASE_GENERATOR
-- local AI_SCOUT_COST = constants.AI_SCOUT_COST
local AI_SQUAD_COST = constants.AI_SQUAD_COST
-- local AI_TUNNEL_COST = constants.AI_TUNNEL_COST
local AI_MAX_SQUAD_COUNT = constants.AI_MAX_SQUAD_COUNT
local AI_SQUAD_COST = constants.AI_SQUAD_COST
local HALF_CHUNK_SIZE = constants.HALF_CHUNK_SIZE
local CHUNK_SIZE = constants.CHUNK_SIZE
-- local MAGIC_MAXIMUM_NUMBER = constants.MAGIC_MAXIMUM_NUMBER
local NORTH_SOUTH_PASSABLE = constants.NORTH_SOUTH_PASSABLE
local EAST_WEST_PASSABLE = constants.EAST_WEST_PASSABLE
-- local COMMAND_GROUP = defines.command.group
-- local DISTRACTION_BY_DAMAGE = defines.distraction.by_damage
local CONFIG_USE_PLAYER_PROXIMITY = config.attackWaveGenerationUsePlayerProximity
local CONFIG_USE_PLAYER_BASE_PROXIMITY = config.attackWaveGenerationUsePlayerBaseProximity
local CONFIG_USE_PLAYER_DEFENSE_PROXIMITY = config.attackWaveGenerationUsePlayerDefenseProximity
local CONFIG_USE_POLLUTION_PROXIMITY = config.attackWaveGenerationUsePollution
local CONFIG_USE_THRESHOLD = config.attackWaveGenerationThreshold
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
-- imported functions
@ -55,25 +39,23 @@ local scoreNeighbors = neighborUtils.scoreNeighbors
local createSquad = unitGroupUtils.createSquad
local attackWaveScaling = config.attackWaveScaling
local mMax = math.max
-- module code
local function attackWaveValidCandidate(chunk, surface)
local function attackWaveValidCandidate(chunk, surface, evolutionFactor)
local total = 0;
if CONFIG_USE_PLAYER_PROXIMITY then
total = total + chunk[PLAYER_PHEROMONE]
end
if CONFIG_USE_PLAYER_BASE_PROXIMITY then
total = total + chunk[PLAYER_BASE_PHEROMONE]
end
if CONFIG_USE_PLAYER_DEFENSE_PROXIMITY then
total = total + chunk[PLAYER_DEFENSE_PHEROMONE]
end
if CONFIG_USE_POLLUTION_PROXIMITY then
total = total + surface.get_pollution({chunk.pX, chunk.pY})
end
if (total >= CONFIG_USE_THRESHOLD) then
local delta = CONFIG_USE_THRESHOLD_RANGE * evolutionFactor
if (total > (CONFIG_USE_THRESHOLD_MAX - delta)) then
return true
else
return false
@ -81,21 +63,13 @@ local function attackWaveValidCandidate(chunk, surface)
end
local function scoreUnitGroupLocation(position, squad, neighborChunk, surface)
local attackScore = surface.get_pollution(position) + neighborChunk[PLAYER_PHEROMONE] + neighborChunk[PLAYER_DEFENSE_PHEROMONE]
local avoidScore = neighborChunk[DEATH_PHEROMONE]
return attackScore - avoidScore
return surface.get_pollution(position) + neighborChunk[PLAYER_PHEROMONE] + neighborChunk[MOVEMENT_PHEROMONE] + neighborChunk[BASE_PHEROMONE]
end
local function validUnitGroupLocation(x, chunk, neighborChunk)
return neighborChunk[NORTH_SOUTH_PASSABLE] and neighborChunk[EAST_WEST_PASSABLE]
end
function aiBuilding.accumulatePoints(natives)
if (natives.points < AI_MAX_POINTS) then
natives.points = natives.points + math.floor(AI_POINT_GENERATOR_AMOUNT * math.random())
end
end
function aiBuilding.removeScout(entity, natives)
--[[
local scouts = natives.scouts
@ -142,12 +116,10 @@ function aiBuilding.scouting(regionMap, natives)
--]]
end
function aiBuilding.formSquads(regionMap, surface, natives, chunk, evolution_factor)
if (natives.points > AI_SQUAD_COST) and (chunk[ENEMY_BASE_GENERATOR] ~= 0) and (#natives.squads < (AI_MAX_SQUAD_COUNT * evolution_factor)) then
local valid = attackWaveValidCandidate(chunk, surface)
if valid and (math.random() < 0.03) then
local valid = attackWaveValidCandidate(chunk, surface, evolution_factor)
if valid and (math.random() < mMax((0.25 * evolution_factor), 0.10)) then
local squadPosition = {x=0, y=0}
local squadPath, squadScore = scoreNeighbors(chunk,
getNeighborChunks(regionMap, chunk.cX, chunk.cY),

View File

@ -9,12 +9,11 @@ local neighborUtils = require("NeighborUtils")
-- constants
local DEATH_PHEROMONE = constants.DEATH_PHEROMONE
local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
local ENEMY_BASE_PHEROMONE = constants.ENEMY_BASE_PHEROMONE
local PLAYER_DEFENSE_PHEROMONE = constants.PLAYER_DEFENSE_PHEROMONE
local BASE_PHEROMONE = constants.BASE_PHEROMONE
local RETREAT_DEATH_PHEROMONE_LEVEL = constants.RETREAT_DEATH_PHEROMONE_LEVEL
local RETREAT_MOVEMENT_PHEROMONE_LEVEL = constants.RETREAT_MOVEMENT_PHEROMONE_LEVEL
local ENEMY_BASE_GENERATOR = constants.ENEMY_BASE_GENERATOR
@ -46,14 +45,14 @@ local function validRetreatLocation(x, chunk, neighborChunk)
end
local function scoreRetreatLocation(position, squad, neighborChunk, surface)
local safeScore = neighborChunk[ENEMY_BASE_PHEROMONE]
local dangerScore = neighborChunk[DEATH_PHEROMONE] + surface.get_pollution(position) + neighborChunk[PLAYER_PHEROMONE] + neighborChunk[PLAYER_DEFENSE_PHEROMONE] + (neighborChunk[ENEMY_BASE_GENERATOR] * 6)
local safeScore = -neighborChunk[BASE_PHEROMONE] + neighborChunk[MOVEMENT_PHEROMONE]
local dangerScore = surface.get_pollution(position) + neighborChunk[PLAYER_PHEROMONE] + (neighborChunk[ENEMY_BASE_GENERATOR] * 6)
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[DEATH_PHEROMONE] > (game.evolution_factor * RETREAT_DEATH_PHEROMONE_LEVEL)) then
if (chunk ~= nil) and (chunk[MOVEMENT_PHEROMONE] > -(game.evolution_factor * RETREAT_MOVEMENT_PHEROMONE_LEVEL)) then
local performRetreat = false
local enemiesToSquad

51
libs/AIPlanning.lua Normal file
View File

@ -0,0 +1,51 @@
local aiPlanning = {}
-- imports
local constants = require("Constants")
local mathUtils = require("MathUtils")
-- constants
local AI_STATE_PEACEFUL = constants.AI_STATE_PEACEFUL
local AI_STATE_AGGRESSIVE = constants.AI_STATE_AGGRESSIVE
local AI_MAX_POINTS = constants.AI_MAX_POINTS
local AI_POINT_GENERATOR_AMOUNT = constants.AI_POINT_GENERATOR_AMOUNT
local AI_MIN_STATE_DURATION = constants.AI_MIN_STATE_DURATION
local AI_MIN_TEMPERAMENT_DURATION = constants.AI_MIN_TEMPERAMENT_DURATION
local AI_MAX_STATE_DURATION = constants.AI_MAX_STATE_DURATION
local AI_MAX_TEMPERAMENT_DURATION = constants.AI_MAX_TEMPERAMENT_DURATION
-- imported functions
local randomTickEvent = mathUtils.randomTickEvent
local mMax = math.max
-- module code
function aiPlanning.planning(natives, evolution_factor, tick)
local maxPoints = AI_MAX_POINTS * evolution_factor
if (natives.points < maxPoints) then
natives.points = natives.points + math.floor(AI_POINT_GENERATOR_AMOUNT * math.random())
end
if (natives.temperamentTick == tick) then
natives.temperament = math.random()
natives.temperamentTick = randomTickEvent(tick, AI_MIN_TEMPERAMENT_DURATION, AI_MAX_TEMPERAMENT_DURATION)
end
if (natives.stateTick == tick) then
local roll = math.random() * mMax(1 - evolution_factor, 0.15)
if (roll > natives.temperament) then
natives.state = AI_STATE_PEACEFUL
else
natives.state = AI_STATE_AGGRESSIVE
end
natives.stateTick = randomTickEvent(tick, AI_MIN_STATE_DURATION, AI_MAX_STATE_DURATION)
end
end
return aiPlanning

View File

@ -13,6 +13,7 @@ local scoreChunk = chunkUtils.scoreChunk
-- module code
function chunkProcessor.processPendingChunks(regionMap, surface, pendingStack)
local processQueue = regionMap.processQueue
for _=#pendingStack, 1, -1 do
local event = pendingStack[#pendingStack]
@ -29,8 +30,6 @@ function chunkProcessor.processPendingChunks(regionMap, surface, pendingStack)
checkChunkPassability(chunk, surface)
scoreChunk(chunk, surface)
local processQueue = regionMap.processQueue
processQueue[#processQueue+1] = chunk
end
end

View File

@ -2,22 +2,15 @@ local chunkUtils = {}
-- imports
-- local mapUtils = require("MapUtils")
local constants = require("Constants")
-- constants
local DEATH_PHEROMONE = constants.DEATH_PHEROMONE
local ENEMY_BASE_PHEROMONE = constants.ENEMY_BASE_PHEROMONE
local BASE_PHEROMONE = constants.BASE_PHEROMONE
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
local PLAYER_BASE_PHEROMONE = constants.PLAYER_BASE_PHEROMONE
local PLAYER_DEFENSE_PHEROMONE = constants.PLAYER_DEFENSE_PHEROMONE
local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
local DEFENSE_PHEROMONES = constants.DEFENSE_PHEROMONES
local BUILDING_PHEROMONES = constants.BUILDING_PHEROMONES
local PLAYER_DEFENSE_GENERATOR = constants.PLAYER_DEFENSE_GENERATOR
local PLAYER_BASE_GENERATOR = constants.PLAYER_BASE_GENERATOR
local ENEMY_BASE_GENERATOR = constants.ENEMY_BASE_GENERATOR
@ -29,6 +22,8 @@ local NORTH_SOUTH_PASSABLE = constants.NORTH_SOUTH_PASSABLE
local ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT = constants.ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT
local CHUNK_TICK = constants.CHUNK_TICK
-- module code
function chunkUtils.checkForDeadendTiles(constantCoordinate, iteratingCoordinate, direction, surface)
@ -85,7 +80,6 @@ function chunkUtils.scoreChunk(chunk, surface)
local entities = surface.count_entities_filtered(enemyChunkQuery)
local playerObjects = 0
local playerDefenses = 0
chunk[ENEMY_BASE_GENERATOR] = entities * ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT
@ -94,44 +88,41 @@ function chunkUtils.scoreChunk(chunk, surface)
for i=1, #entities do
local entityType = entities[i].type
local entityScore = DEFENSE_PHEROMONES[entityType]
if (entityScore ~= nil) then
playerDefenses = playerDefenses + entityScore
end
entityScore = BUILDING_PHEROMONES[entityType]
local entityScore = BUILDING_PHEROMONES[entityType]
if (entityScore ~= nil) then
playerObjects = playerObjects + entityScore
end
end
chunk[PLAYER_BASE_GENERATOR] = playerObjects
chunk[PLAYER_DEFENSE_GENERATOR] = playerDefenses
end
function chunkUtils.createChunk(topX, topY)
local chunk = {
pX = topX,
pY = topY,
cX = topX * 0.03125,
cY = topY * 0.03125
}
chunk[DEATH_PHEROMONE] = 0
chunk[ENEMY_BASE_PHEROMONE] = 0
chunk[PLAYER_PHEROMONE] = 0
chunk[PLAYER_BASE_PHEROMONE] = 0
chunk[PLAYER_DEFENSE_PHEROMONE] = 0
pX = topX,
pY = topY,
cX = topX * 0.03125,
cY = topY * 0.03125
}
chunk[MOVEMENT_PHEROMONE] = 0
chunk[BASE_PHEROMONE] = 0
chunk[PLAYER_PHEROMONE] = 0
chunk[ENEMY_BASE_GENERATOR] = 0
chunk[PLAYER_BASE_GENERATOR] = 0
chunk[NORTH_SOUTH_PASSABLE] = false
chunk[EAST_WEST_PASSABLE] = false
chunk[CHUNK_TICK] = 0
return chunk
end
-- function chunkUtils.colorChunk(x, y, tileType, surface)
-- local tiles = {}
-- for xi=x+5, x + 27 do
-- for yi=y+5, y + 27 do
-- tiles[#tiles+1] = {name=tileType, position={xi, yi}}
-- end
-- end
-- surface.set_tiles(tiles, false)
-- local tiles = {}
-- for xi=x+5, x + 27 do
-- for yi=y+5, y + 27 do
-- tiles[#tiles+1] = {name=tileType, position={xi, yi}}
-- end
-- end
-- surface.set_tiles(tiles, false)
-- end
return chunkUtils

View File

@ -5,44 +5,49 @@ local constants = {}
constants.VERSION_5 = 5
constants.VERSION_9 = 9
constants.VERSION_10 = 10
constants.VERSION_11 = 11
-- misc
constants.MAGIC_MAXIMUM_NUMBER = 1e99 -- used in loops trying to find the lowest/highest score
constants.RETREAT_DEATH_PHEROMONE_LEVEL = 10000
constants.RETREAT_MOVEMENT_PHEROMONE_LEVEL = 10000
constants.PROCESS_QUEUE_SIZE = 350
constants.PROCESS_QUEUE_SIZE = 500
constants.SCAN_QUEUE_SIZE = 10
constants.PROCESS_PLAYER_BOUND = 3
-- temps
constants.TICKS_A_SECOND = 60
constants.TICKS_A_MINUTE = constants.TICKS_A_SECOND * 60
-- constants.ATTACK_POSITION = 1
-- constants.ATTACK_COMMAND = 2
-- constants.ATTACK_DIRECTION = 3
-- constants.GROUP_COMMAND = 4
-- constants.SQUAD_POSITION = 5
-- constants.RETREAT_POSITION = 6
-- constants.RETREAT_NEIGHBORS_WITH_DIRECTION = 7
-- -- constants.RETREAT_COMMAND = 8
-- constants.MULTI_GROUP_COMMAND = 8
constants.INTERVAL_PROCESS = 20
constants.INTERVAL_LOGIC = 40
-- ai
constants.AI_POINT_GENERATOR_AMOUNT = 6
constants.AI_SCOUT_COST = 45
constants.AI_SQUAD_COST = 150
constants.AI_SQUAD_COST = 175
constants.AI_SETTLER_COST = 75
constants.AI_BASE_BUILDING_COST = 500
constants.AI_TUNNEL_COST = 100
constants.AI_MAX_POINTS = 30000
constants.AI_MAX_POINTS = 10000
--constants.AI_MAX_SQUAD_SIZE = 150
constants.AI_MAX_SQUAD_COUNT = 30
constants.AI_STATE_PEACEFUL = 1
constants.AI_STATE_AGGRESSIVE = 2
constants.AI_MIN_STATE_DURATION = 1
constants.AI_MAX_STATE_DURATION = 4
constants.AI_MIN_TEMPERAMENT_DURATION = 5
constants.AI_MAX_TEMPERAMENT_DURATION = 15
-- ai retreat
constants.NO_RETREAT_BASE_PERCENT = 0.10
constants.NO_RETREAT_EVOLUTION_BONUS_MAX = 0.25
constants.NO_RETREAT_SQUAD_SIZE_BONUS_MAX = 0.40
-- chunk properties
constants.CHUNK_SIZE = 32
@ -53,46 +58,41 @@ constants.EAST_WEST = 2
-- pheromone amounts
constants.MOVEMENT_PENALTY_PHEROMONE_GENERATOR_AMOUNT = 500
constants.MOVEMENT_PHEROMONE_GENERATOR_AMOUNT = 500
constants.ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT = 175
constants.ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT = 15
constants.DEATH_PHEROMONE_GENERATOR_AMOUNT = 100
constants.PLAYER_PHEROMONE_GENERATOR_AMOUNT = 300
constants.PLAYER_PHEROMONE_GENERATOR_AMOUNT = 100
-- pheromone diffusion amounts
constants.STANDARD_PHERONOME_DIFFUSION_AMOUNT = 0.10
constants.DEATH_PHEROMONE_DIFFUSION_AMOUNT = 0.02
constants.STANDARD_PHERONOME_DIFFUSION_AMOUNT = 0.05
constants.MOVEMENT_PHEROMONE_DIFFUSION_AMOUNT = 0.02
constants.DEATH_PHEROMONE_PERSISTANCE = 0.99
constants.MOVEMENT_PHEROMONE_PERSISTANCE = 0.98
constants.STANDARD_PHEROMONE_PERSISTANCE = 0.98
-- chunk attributes
constants.DEATH_PHEROMONE = 1
constants.ENEMY_BASE_PHEROMONE = 2
constants.MOVEMENT_PHEROMONE = 1
constants.BASE_PHEROMONE = 2
constants.PLAYER_PHEROMONE = 3
constants.PLAYER_BASE_PHEROMONE = 4
constants.PLAYER_DEFENSE_PHEROMONE = 5
constants.MOVEMENT_PHEROMONE = 6
constants.ENEMY_BASE_GENERATOR = 7
constants.PLAYER_BASE_GENERATOR = 8
constants.PLAYER_DEFENSE_GENERATOR = 9
constants.ENEMY_BASE_GENERATOR = 4
constants.PLAYER_BASE_GENERATOR = 5
constants.NORTH_SOUTH_PASSABLE = 10
constants.EAST_WEST_PASSABLE = 11
constants.NORTH_SOUTH_PASSABLE = 6
constants.EAST_WEST_PASSABLE = 7
constants.CHUNK_TICK = 8
-- Squad status
constants.SQUAD_RETREATING = 1 -- used during squad retreat
constants.SQUAD_GUARDING = 2 -- used when squad is idle
constants.SQUAD_ATTACKING = 3 -- used as an attack state to be transitioned into hunt, raid, siege, burrow
constants.SQUAD_HUNTING = 4 -- used when player is close to unit group
constants.SQUAD_SUICIDE_HUNT = 5 -- used when player is close with no retreat
constants.SQUAD_BURROWING = 6
constants.SQUAD_RAIDING = 8 -- used when player stuff is close
constants.SQUAD_SUICIDE_RAID = 9 -- when player stuff is close with no retreat
constants.SQUAD_BURROWING = 4
constants.SQUAD_RAIDING = 5 -- used when player stuff is close
constants.SQUAD_SUICIDE_RAID = 6 -- when player stuff is close with no retreat
-- constants.SQUAD_SCOUTING = 7
-- constants.SQUAD_SIEGE = 3
@ -101,36 +101,30 @@ constants.SQUAD_SUICIDE_RAID = 9 -- when player stuff is close with no retreat
constants.BUILDING_PHEROMONES = {}
-- constants.buildingPheromones["container"] = 1
-- constants.buildingPheromones["storage-tank"] = 1
constants.BUILDING_PHEROMONES["generator"] = 60
constants.BUILDING_PHEROMONES["pump"] = 8
constants.BUILDING_PHEROMONES["offshore-pump"] = 8
constants.BUILDING_PHEROMONES["generator"] = 8
constants.BUILDING_PHEROMONES["pump"] = 2
constants.BUILDING_PHEROMONES["offshore-pump"] = 2
-- constants.buildingPheromones["constant-combinator"] = 1
-- constants.buildingPheromones["train-stop"] = 2
-- constants.buildingPheromones["rail-signal"] = 1
-- constants.BUILDING_PHEROMONES["electric-pole"] = 4
constants.BUILDING_PHEROMONES["transport-belt"] = 4
constants.BUILDING_PHEROMONES["accumulator"] = 40
constants.BUILDING_PHEROMONES["solar-panel"] = 32
constants.BUILDING_PHEROMONES["boiler"] = 60
constants.BUILDING_PHEROMONES["assembling-machine"] = 48
constants.BUILDING_PHEROMONES["roboport"] = 40
constants.BUILDING_PHEROMONES["beacon"] = 40
constants.BUILDING_PHEROMONES["furnace"] = 60
constants.BUILDING_PHEROMONES["mining-drill"] = 80
constants.BUILDING_PHEROMONES["transport-belt"] = 1
constants.BUILDING_PHEROMONES["accumulator"] = 10
constants.BUILDING_PHEROMONES["solar-panel"] = 8
constants.BUILDING_PHEROMONES["boiler"] = 12
constants.BUILDING_PHEROMONES["assembling-machine"] = 12
constants.BUILDING_PHEROMONES["roboport"] = 10
constants.BUILDING_PHEROMONES["beacon"] = 10
constants.BUILDING_PHEROMONES["furnace"] = 12
constants.BUILDING_PHEROMONES["mining-drill"] = 15
-- player defense pheromones
constants.DEFENSE_PHEROMONES = {}
constants.DEFENSE_PHEROMONES["ammo-turret"] = 5
constants.DEFENSE_PHEROMONES["wall"] = 0.5
constants.DEFENSE_PHEROMONES["electric-turret"] = 7.5
constants.DEFENSE_PHEROMONES["fluid-turret"] = 10
constants.DEFENSE_PHEROMONES["turret"] = 3
-- enemy units
-- constants.deathPheromones = {}
-- constants.deathPheromones[""]
constants.BUILDING_PHEROMONES["ammo-turret"] = 2.5
constants.BUILDING_PHEROMONES["wall"] = 0.25
constants.BUILDING_PHEROMONES["electric-turret"] = 4.25
constants.BUILDING_PHEROMONES["fluid-turret"] = 5
constants.BUILDING_PHEROMONES["turret"] = 3.5
constants.retreatFilter = {}
constants.retreatFilter[constants.SQUAD_RETREATING] = true

View File

@ -8,10 +8,8 @@ local constants = require("Constants")
-- constants
local BUILDING_PHEROMONES = constants.BUILDING_PHEROMONES
local DEFENSE_PHEROMONES = constants.DEFENSE_PHEROMONES
local ENEMY_BASE_GENERATOR = constants.ENEMY_BASE_GENERATOR
local PLAYER_DEFENSE_GENERATOR = constants.PLAYER_DEFENSE_GENERATOR
local PLAYER_BASE_GENERATOR = constants.PLAYER_BASE_GENERATOR
local ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT = constants.ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT
@ -87,9 +85,6 @@ function entityUtils.addRemoveEntity(regionMap, entity, natives, addObject)
if (BUILDING_PHEROMONES[entity.type] ~= nil) then
entityValue = BUILDING_PHEROMONES[entity.type]
pheromoneType = PLAYER_BASE_GENERATOR
elseif (DEFENSE_PHEROMONES[entity.type] ~= nil) then
entityValue = DEFENSE_PHEROMONES[entity.type]
pheromoneType = PLAYER_DEFENSE_GENERATOR
elseif (entity.type == "unit-spawner") and (entity.force.name == "enemy") then
entityValue = ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT
pheromoneType = ENEMY_BASE_GENERATOR

View File

@ -2,10 +2,10 @@ local mapProcessor = {}
-- imports
local mapUtils = require("MapUtils")
local pheromoneUtils = require("PheromoneUtils")
local aiBuilding = require("AIBuilding")
local constants = require("Constants")
local mapUtils = require("MapUtils")
-- constants
@ -15,6 +15,11 @@ local SCAN_QUEUE_SIZE = constants.SCAN_QUEUE_SIZE
local CHUNK_SIZE = constants.CHUNK_SIZE
local ENEMY_BASE_GENERATOR = constants.ENEMY_BASE_GENERATOR
local AI_STATE_AGGRESSIVE = constants.AI_STATE_AGGRESSIVE
local PROCESS_PLAYER_BOUND = constants.PROCESS_PLAYER_BOUND
local CHUNK_TICK = constants.CHUNK_TICK
-- imported functions
@ -24,17 +29,38 @@ local processPheromone = pheromoneUtils.processPheromone
local makeScouts = aiBuilding.makeScouts
local formSquads = aiBuilding.formSquads
local getCardinalChunks = mapUtils.getCardinalChunks
local getChunkByIndex = mapUtils.getChunkByIndex
local getChunkByPosition = mapUtils.getChunkByPosition
local playerScent = pheromoneUtils.playerScent
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, temps)
local function nonRepeatingRandom(players)
local ordering = {}
for _,player in pairs(players) do
ordering[#ordering+1] = player.index
end
for i=#ordering,1,-1 do
local s = math.random(i)
local t = ordering[i]
ordering[i] = ordering[s]
ordering[s] = t
end
return ordering
end
--[[
processing is not consistant as it depends on the number of chunks that have been generated
so if we process 400 chunks an iteration and 200 chunks have been generated than these are
processed 3 times a second and 1200 generated chunks would be 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.processRoll
local index = regionMap.processPointer
local scouts = false
@ -49,8 +75,8 @@ function mapProcessor.processMap(regionMap, surface, natives, evolution_factor,
scouts = true
end
if (0.11 <= roll) and (roll <= 0.35) then
squads = true
if (natives.state == AI_STATE_AGGRESSIVE) and (0.11 <= roll) and (roll <= 0.35) then
squads = true
end
local processQueue = regionMap.processQueue
@ -64,10 +90,10 @@ function mapProcessor.processMap(regionMap, surface, natives, evolution_factor,
makeScouts(surface, natives, chunk, evolution_factor)
end
if squads then
formSquads(regionMap, surface, natives, chunk, evolution_factor, temps)
formSquads(regionMap, surface, natives, chunk, evolution_factor)
end
processPheromone(chunk, getCardinalChunks(regionMap, chunk.cX, chunk.cY))
processPheromone(regionMap, chunk)
end
if (endIndex == #processQueue) then
@ -77,25 +103,90 @@ function mapProcessor.processMap(regionMap, surface, natives, evolution_factor,
end
end
--[[
Localized player radius were processing takes place in realtime, doesn't store state
between calls.
vs
the slower passive version processing the entire map in multiple passes.
--]]
function mapProcessor.processPlayers(players, regionMap, surface, natives, evolution_factor, tick)
-- put down player pheromone for player hunters
-- randomize player order to ensure a single player isn't singled out
local playerOrdering = nonRepeatingRandom(players)
local scouts = false
local squads = false
local roll = math.random()
if (0.05 <= roll) and (roll <= 0.7) then
scouts = true
end
if (natives.state == AI_STATE_AGGRESSIVE) and (0.11 <= roll) and (roll <= 0.20) then
squads = true
end
for i=1,#playerOrdering do
local player = players[playerOrdering[i]]
if (player ~= nil) and player.connected and (player.character ~= nil) and player.character.valid and (player.character.surface.index == 1) then
local playerPosition = player.character.position
local playerChunk = getChunkByPosition(regionMap, playerPosition.x, playerPosition.y)
if (playerChunk ~= nil) then
playerScent(playerChunk)
end
end
end
for i=1,#playerOrdering do
local player = players[playerOrdering[i]]
if (player ~= nil) and player.connected and (player.character ~= nil) and player.character.valid and (player.character.surface.index == 1) then
local playerPosition = player.character.position
local playerChunk = getChunkByPosition(regionMap, playerPosition.x, playerPosition.y)
if (playerChunk ~= nil) then
for x=playerChunk.cX - PROCESS_PLAYER_BOUND, playerChunk.cX + PROCESS_PLAYER_BOUND do
for y=playerChunk.cY - PROCESS_PLAYER_BOUND, playerChunk.cY + PROCESS_PLAYER_BOUND do
local chunk = getChunkByIndex(regionMap, x, y)
if (chunk ~= nil) and (chunk[CHUNK_TICK] ~= tick) then
chunk[CHUNK_TICK] = tick
scents(chunk)
if scouts then
makeScouts(surface, natives, chunk, evolution_factor)
end
if squads then
formSquads(regionMap, surface, natives, chunk, evolution_factor)
end
processPheromone(regionMap, chunk)
end
end
end
end
end
end
end
function mapProcessor.scanMap(regionMap, surface)
local index = regionMap.scanPointer
local processQueue = regionMap.processQueue
local endIndex = mMin(index + SCAN_QUEUE_SIZE, #processQueue)
for x=index,endIndex do
local chunk = processQueue[x]
local chunk = processQueue[x]
local spawners = surface.count_entities_filtered({area = {{chunk.pX, chunk.pY},
{chunk.pX + CHUNK_SIZE, chunk.pY + CHUNK_SIZE}},
type = "unit-spawner",
force = "enemy"})
chunk[ENEMY_BASE_GENERATOR] = spawners * ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT
local spawners = surface.count_entities_filtered({area = {{chunk.pX, chunk.pY},
{chunk.pX + CHUNK_SIZE, chunk.pY + CHUNK_SIZE}},
type = "unit-spawner",
force = "enemy"})
chunk[ENEMY_BASE_GENERATOR] = spawners * ENEMY_BASE_PHEROMONE_GENERATOR_AMOUNT
end
if (endIndex == #processQueue) then
regionMap.scanPointer = 1
regionMap.scanPointer = 1
else
regionMap.scanPointer = endIndex + 1
regionMap.scanPointer = endIndex + 1
end
end

28
libs/MathUtils.lua Normal file
View File

@ -0,0 +1,28 @@
local mathUtils = {}
-- imports
local constants = require("Constants")
-- constants
local TICKS_A_MINUTE = constants.TICKS_A_MINUTE
local INTERVAL_LOGIC = constants.INTERVAL_LOGIC
-- imported functions
local mMax = math.max
function mathUtils.roundToNearest(number, multiple)
local num = number + (multiple * 0.5)
return num - (num % multiple)
end
function mathUtils.randomTickEvent(tick, low, high)
local minutesToTick = mMax(high * math.random(), low)
local nextTick = mathUtils.roundToNearest(TICKS_A_MINUTE * minutesToTick, INTERVAL_LOGIC)
return tick + nextTick
end
return mathUtils

View File

@ -7,96 +7,76 @@ local constants = require("Constants")
-- constants
local DEATH_PHEROMONE = constants.DEATH_PHEROMONE
local PLAYER_DEFENSE_PHEROMONE = constants.PLAYER_DEFENSE_PHEROMONE
local PLAYER_BASE_PHEROMONE = constants.PLAYER_BASE_PHEROMONE
local ENEMY_BASE_PHEROMONE = constants.ENEMY_BASE_PHEROMONE
local MOVEMENT_PHEROMONE = constants.MOVEMENT_PHEROMONE
local BASE_PHEROMONE = constants.BASE_PHEROMONE
local PLAYER_PHEROMONE = constants.PLAYER_PHEROMONE
local PLAYER_DEFENSE_GENERATOR = constants.PLAYER_DEFENSE_GENERATOR
local PLAYER_BASE_GENERATOR = constants.PLAYER_BASE_GENERATOR
local ENEMY_BASE_GENERATOR = constants.ENEMY_BASE_GENERATOR
local PLAYER_PHEROMONE_GENERATOR_AMOUNT = constants.PLAYER_PHEROMONE_GENERATOR_AMOUNT
local DEATH_PHEROMONE_GENERATOR_AMOUNT = constants.DEATH_PHEROMONE_GENERATOR_AMOUNT
local DEATH_PHEROMONE_GENERATOR_AMOUNT = constants.MOVEMENT_PHEROMONE_GENERATOR_AMOUNT
local STANDARD_PHERONOME_DIFFUSION_AMOUNT = constants.STANDARD_PHERONOME_DIFFUSION_AMOUNT
local DEATH_PHEROMONE_DIFFUSION_AMOUNT = constants.DEATH_PHEROMONE_DIFFUSION_AMOUNT
local MOVEMENT_PHEROMONE_DIFFUSION_AMOUNT = constants.MOVEMENT_PHEROMONE_DIFFUSION_AMOUNT
local DEATH_PHEROMONE_PERSISTANCE = constants.DEATH_PHEROMONE_PERSISTANCE
local MOVEMENT_PHEROMONE_PERSISTANCE = constants.MOVEMENT_PHEROMONE_PERSISTANCE
local STANDARD_PHEROMONE_PERSISTANCE = constants.STANDARD_PHEROMONE_PERSISTANCE
-- imported functions
local getChunkByPosition = mapUtils.getChunkByPosition
local getCardinalChunks = mapUtils.getCardinalChunks
-- module code
function pheromoneUtils.scents(chunk)
local amount = chunk[PLAYER_DEFENSE_GENERATOR]
if (amount > 0) then
chunk[PLAYER_DEFENSE_PHEROMONE] = chunk[PLAYER_DEFENSE_PHEROMONE] + amount
end
amount = chunk[PLAYER_BASE_GENERATOR]
if (amount > 0) then
chunk[PLAYER_BASE_PHEROMONE] = chunk[PLAYER_BASE_PHEROMONE] + amount
end
amount = chunk[ENEMY_BASE_GENERATOR]
if (amount > 0) then
chunk[ENEMY_BASE_PHEROMONE] = chunk[ENEMY_BASE_PHEROMONE] + amount
end
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
chunk[DEATH_PHEROMONE] = chunk[DEATH_PHEROMONE] + DEATH_PHEROMONE_GENERATOR_AMOUNT
-- pheromoneTotals[DEATH_PHEROMONE] = pheromoneTotals[DEATH_PHEROMONE] + DEATH_PHEROMONE_GENERATOR_AMOUNT
chunk[MOVEMENT_PHEROMONE] = chunk[MOVEMENT_PHEROMONE] - DEATH_PHEROMONE_GENERATOR_AMOUNT
end
end
function pheromoneUtils.playerScent(regionMap, players)
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
local playerPosition = player.character.position
local playerChunk = getChunkByPosition(regionMap, playerPosition.x, playerPosition.y)
if (playerChunk ~= nil) then
playerChunk[PLAYER_PHEROMONE] = playerChunk[PLAYER_PHEROMONE] + PLAYER_PHEROMONE_GENERATOR_AMOUNT
end
end
end
function pheromoneUtils.playerScent(playerChunk)
playerChunk[PLAYER_PHEROMONE] = playerChunk[PLAYER_PHEROMONE] + PLAYER_PHEROMONE_GENERATOR_AMOUNT
end
function pheromoneUtils.processPheromone(chunk, neighbors)
function pheromoneUtils.processPheromone(regionMap, chunk)
local neighbors
-- pheromone level indexes on chunks are 1 - 6
-- pheromone level indexes on chunks are 1 - 3
-- unrolled loop one level
local diffusionAmount = DEATH_PHEROMONE_DIFFUSION_AMOUNT
local persistence = DEATH_PHEROMONE_PERSISTANCE
local diffusionAmount = MOVEMENT_PHEROMONE_DIFFUSION_AMOUNT
local persistence = MOVEMENT_PHEROMONE_PERSISTANCE
local totalDiffused = 0
local chunkValue = chunk[DEATH_PHEROMONE] * persistence
local chunkValue = chunk[MOVEMENT_PHEROMONE] * persistence
local diffusedAmount = chunkValue * diffusionAmount
if (chunkValue > 2) then
if (diffusedAmount > 1.5) or (diffusedAmount < -1.5) then
neighbors = getCardinalChunks(regionMap, chunk.cX, chunk.cY)
for i=1,#neighbors do
local neighborChunk = neighbors[i]
if (neighborChunk ~= nil) then
totalDiffused = totalDiffused + diffusedAmount
neighborChunk[DEATH_PHEROMONE] = neighborChunk[DEATH_PHEROMONE] + diffusedAmount
neighborChunk[MOVEMENT_PHEROMONE] = neighborChunk[MOVEMENT_PHEROMONE] + diffusedAmount
end
end
end
chunk[DEATH_PHEROMONE] = (chunkValue - totalDiffused)
chunk[MOVEMENT_PHEROMONE] = (chunkValue - totalDiffused)
diffusionAmount = STANDARD_PHERONOME_DIFFUSION_AMOUNT
persistence = STANDARD_PHEROMONE_PERSISTANCE
for x=2,6 do
totalDiffused = 0
chunkValue = chunk[x] * persistence
diffusedAmount = chunkValue * diffusionAmount
if (chunkValue > 2) then
for x=2,3 do
totalDiffused = 0
chunkValue = chunk[x] * persistence
diffusedAmount = chunkValue * diffusionAmount
if (diffusedAmount > 1.5) then
if (neighbors == nil) then
neighbors = getCardinalChunks(regionMap, chunk.cX, chunk.cY)
end
for i=1,#neighbors do
local neighborChunk = neighbors[i]
if (neighborChunk ~= nil) then
@ -105,7 +85,7 @@ function pheromoneUtils.processPheromone(chunk, neighbors)
end
end
end
chunk[x] = (chunkValue - totalDiffused)
chunk[x] = (chunkValue - totalDiffused)
end
end

View File

@ -11,13 +11,12 @@ local euclideanDistanceNamed = mapUtils.euclideanDistanceNamed
-- module code
function playerUtils.playersWithinProximityToPosition(players, position, distance)
for x=1,#players do
local player = players[x]
if (player ~= nil) and player.connected and (player.character ~= nil) and player.character.valid and (player.character.surface.index == 1) then
if (euclideanDistanceNamed(player.character.position, position) < distance) then
return true
end
end
for _,player in pairs(players) do
if (player ~= nil) and player.connected and (player.character ~= nil) and player.character.valid and (player.character.surface.index == 1) then
if (euclideanDistanceNamed(player.character.position, position) < distance) then
return true
end
end
end
return false
end

View File

@ -7,7 +7,7 @@ local constants = require("Constants")
-- constants
local MOVEMENT_PENALTY_PHEROMONE_GENERATOR_AMOUNT = constants.MOVEMENT_PENALTY_PHEROMONE_GENERATOR_AMOUNT
local MOVEMENT_PHEROMONE_GENERATOR_AMOUNT = constants.MOVEMENT_PHEROMONE_GENERATOR_AMOUNT
local GROUP_STATE_FINISHED = defines.group_state.finished
@ -93,14 +93,14 @@ function unitGroupUtils.addSquadMovementPenalty(squad, chunkX, chunkY)
for i=1,#penalties do
local penalty = penalties[i]
if (penalty.x == chunkX) and (penalty.y == chunkY) then
penalty.v = penalty.v + MOVEMENT_PENALTY_PHEROMONE_GENERATOR_AMOUNT
penalty.v = penalty.v + MOVEMENT_PHEROMONE_GENERATOR_AMOUNT
return
end
end
if (#penalties == 10) then
tableRemove(penalties, 10)
end
tableInsert(penalties, 1, { v = MOVEMENT_PENALTY_PHEROMONE_GENERATOR_AMOUNT,
tableInsert(penalties, 1, { v = MOVEMENT_PHEROMONE_GENERATOR_AMOUNT,
x = chunkX,
y = chunkY })
end

113
make.rkt
View File

@ -6,74 +6,75 @@
;(define modFolder "C:/Users/veden/AppData/Roaming/Factorio/mods/")
;(define zipModFolder "C:/Program Files/Factorio_0.14.1/mods/")
(define modFolder "/home/veden/.factorio/mods/")
(define zipModFolder "/data/games/factorio14.13/mods/")
(define zipModFolder "/data/games/factorio14.14/mods/")
(define configuration (call-with-input-file "info.json"
(lambda (port)
(string->jsexpr (port->string port)))))
(define packageName (string-append (string-replace (hash-ref configuration 'name) " " "_")
"_"
(hash-ref configuration 'version)))
"_"
(hash-ref configuration 'version)))
(define (makeZip)
(let ((packagePath (string->path (string-append modFolder
packageName
".zip"))))
(when (file-exists? packagePath)
(delete-file packagePath)))
(zip (string-append modFolder
packageName
".zip")
#:path-prefix packageName
(string->path "info.json")
(string->path "control.lua")
(string->path "config.lua")
(string->path "data.lua")
(string->path "LICENSE.md")
(string->path "tests.lua")
; (string->path "setupUtils.lua")
(string->path "README.md")
; (string->path "setup.lua")
(string->path "NOTICE")
(string->path "libs")
(string->path "locale")
(string->path "graphics")
(string->path "prototypes")))
(define (makeZip folder)
(let ((packagePath (string->path (string-append folder
packageName
".zip"))))
(when (file-exists? packagePath)
(delete-file packagePath)))
(zip (string-append folder
packageName
".zip")
#:path-prefix packageName
(string->path "info.json")
(string->path "control.lua")
(string->path "config.lua")
(string->path "data.lua")
(string->path "LICENSE.md")
(string->path "tests.lua")
; (string->path "setupUtils.lua")
(string->path "README.md")
; (string->path "setup.lua")
(string->path "NOTICE")
(string->path "libs")
(string->path "locale")
(string->path "graphics")
(string->path "prototypes")))
;(current-directory "..")
(define (copyFile fileName modFolder)
(copy-file (string->path fileName)
(string->path (string-append modFolder
packageName
"/"
fileName))))
(copy-file (string->path fileName)
(string->path (string-append modFolder
packageName
"/"
fileName))))
(define (copyDirectory directoryName modFolder)
(copy-directory/files (string->path directoryName)
(string->path (string-append modFolder
packageName
"/"
directoryName))))
(copy-directory/files (string->path directoryName)
(string->path (string-append modFolder
packageName
"/"
directoryName))))
(define (copyFiles modFolder)
(let ((packagePath (string->path (string-append modFolder
packageName))))
(when (directory-exists? packagePath)
(delete-directory/files packagePath))
(sleep 0.1)
(make-directory packagePath)
(copyFile "control.lua" modFolder)
(copyFile "config.lua" modFolder)
(copyFile "info.json" modFolder)
; (copyFile "setupUtils.lua" modFolder)
(copyFile "data.lua" modFolder)
(copyFile "tests.lua" modFolder)
(copyDirectory "libs" modFolder)
(copyDirectory "locale" modFolder)
(copyDirectory "graphics" modFolder)
(copyDirectory "prototypes" modFolder)))
(let ((packagePath (string->path (string-append modFolder
packageName))))
(when (directory-exists? packagePath)
(delete-directory/files packagePath))
(sleep 0.1)
(make-directory packagePath)
(copyFile "control.lua" modFolder)
(copyFile "config.lua" modFolder)
(copyFile "info.json" modFolder)
; (copyFile "setupUtils.lua" modFolder)
(copyFile "data.lua" modFolder)
(copyFile "tests.lua" modFolder)
(copyDirectory "libs" modFolder)
(copyDirectory "locale" modFolder)
(copyDirectory "graphics" modFolder)
(copyDirectory "prototypes" modFolder)))
;; (copyFiles modFolder)
;; (copyFiles zipModFolder)
(makeZip)
;; (copyFiles modFolder)
;; (copyFiles zipModFolder)
(makeZip modFolder)
(makeZip zipModFolder)
)

View File

@ -10,8 +10,8 @@ function tests.test1()
print(#global.regionMap.processQueue)
print(playerChunkX .. ", " .. playerChunkY)
print("--")
for x=playerChunkX-3, playerChunkX+3 do
for y=playerChunkY-3, playerChunkY+3 do
for x=playerChunkX-9, playerChunkX+9 do
for y=playerChunkY-9, playerChunkY+9 do
if (global.regionMap[x] ~= nil) then
local chunk = global.regionMap[x][y]
if (chunk ~= nil) then
@ -19,13 +19,14 @@ function tests.test1()
for i=1,#chunk do
str = str .. " " .. tostring(i) .. "/" .. tostring(chunk[i])
end
if (chunk.cX == playerChunkX) and (chunk.cY == playerChunkY) then
print("*", chunk.cX, chunk.cY, str)
else
print(chunk.cX, chunk.cY, str)
end
-- print(str)
print("-")
str = str .. " " .. "p/" .. game.surfaces[1].get_pollution({x=chunk.pX, y=chunk.pY})
if (chunk.cX == playerChunkX) and (chunk.cY == playerChunkY) then
print("*", chunk.cX, chunk.cY, str)
else
print(chunk.cX, chunk.cY, str)
end
-- print(str)
print("-")
end
end
end