1
0
mirror of https://github.com/veden/Rampant.git synced 2025-02-05 13:14:51 +02:00

rng should now be deterministic

This commit is contained in:
Aaron Veden 2021-12-05 15:33:24 -08:00
parent 2a534af8df
commit 24d52de8a1
No known key found for this signature in database
GPG Key ID: FF5990B1C6DD3F84
7 changed files with 75 additions and 118 deletions

View File

@ -26,6 +26,7 @@ Date: 23. 11. 2021
- Decreased wander time when a settle command fails to 20 seconds
- Reduced player, enemy, resource scanned chunks to 2 every 7 ticks
- Reduced pheromone map processed chunks to 105 every 7 ticks
- All random numbers now are based of the mod settings seed added with the map seed. This means that the AI should always perform the same actions when loading a save.
Bugfixes:
- Fixed chunks not processed due to chunk not being actually generated by game engine. You may notice a small delay before the spawners and worms convert to Rampant new enemy versions.
- Fixed vengence squads only processing half the expected chunks

View File

@ -41,6 +41,7 @@ local ENERGY_THIEF_LOOKUP = constants.ENERGY_THIEF_LOOKUP
-- imported functions
local distortPosition = mathUtils.distortPosition
local prepMap = upgrade.prepMap
local registerEnemyBaseStructure = chunkUtils.registerEnemyBaseStructure
@ -115,8 +116,6 @@ local cleanSquads = squadAttack.cleanSquads
local upgradeEntity = baseUtils.upgradeEntity
local rebuildNativeTables = baseUtils.rebuildNativeTables
local mRandom = math.random
local tRemove = table.remove
local sFind = string.find
@ -286,7 +285,7 @@ local function onConfigChanged()
settings.startup["rampant--newEnemies"].value)
if universe.NEW_ENEMIES then
rebuildNativeTables(universe, game.create_random_generator(universe.ENEMY_SEED))
rebuildNativeTables(universe, universe.random)
else
universe.buildingHiveTypeLookup = {}
universe.buildingHiveTypeLookup["biter-spawner"] = "biter-spawner"
@ -394,7 +393,7 @@ local function onDeath(event)
map.lostEnemyUnits = map.lostEnemyUnits + 1
if (mRandom() < universe.rallyThreshold) and not surface.peaceful_mode then
if (universe.random() < universe.rallyThreshold) and not surface.peaceful_mode then
rallyUnits(chunk, map, tick)
end
end
@ -735,7 +734,7 @@ local function onEntitySpawned(event)
return
end
if universe.buildingHiveTypeLookup[entity.name] then
local disPos = mathUtils.distortPosition(entity.position, 8)
local disPos = distortPosition(universe.random, entity.position, 8)
local chunk = getChunkByPosition(map, disPos)
if (chunk ~= -1) then
@ -783,7 +782,7 @@ local function onUnitGroupCreated(event)
return
end
if not universe.aiNocturnalMode then
local settler = mRandom() < 0.25 and
local settler = universe.random() < 0.25 and
canMigrate(map) and
(universe.builderCount < universe.AI_MAX_BUILDER_COUNT)
@ -817,7 +816,7 @@ local function onUnitGroupCreated(event)
return
end
local settler = mRandom() < 0.25 and
local settler = universe.random() < 0.25 and
canMigrate(map) and
(universe.builderCount < universe.AI_MAX_BUILDER_COUNT)
@ -881,7 +880,7 @@ local function onGroupFinishedGathering(event)
end
end
else
local settler = mRandom() < 0.25 and
local settler = universe.random() < 0.25 and
canMigrate(map) and
(universe.builderCount < universe.AI_MAX_BUILDER_COUNT)
@ -1012,7 +1011,7 @@ script.on_event(defines.events.on_tick,
processPendingUpgrades(map, tick)
cleanSquads(map, tick)
-- game.print({"", "--dispatch4 ", profiler, ", ", pick, ", ", game.tick, " ", mRandom()})
-- game.print({"", "--dispatch4 ", profiler, ", ", pick, ", ", game.tick, " ", universe.random()})
end)
script.on_event(defines.events.on_surface_deleted, onSurfaceDeleted)

View File

@ -42,8 +42,6 @@ local findNearbyBase = baseUtils.findNearbyBase
local calculateKamikazeThreshold = unitGroupUtils.calculateKamikazeThreshold
local mRandom = math.random
local positionFromDirectionAndChunk = mapUtils.positionFromDirectionAndChunk
local getPassable = chunkPropertyUtils.getPassable
@ -53,7 +51,7 @@ local getNestActiveness = chunkPropertyUtils.getNestActiveness
local getRallyTick = chunkPropertyUtils.getRallyTick
local setRallyTick = chunkPropertyUtils.setRallyTick
local gaussianRandomRange = mathUtils.gaussianRandomRange
local gaussianRandomRangeRG = mathUtils.gaussianRandomRangeRG
local getNeighborChunks = mapUtils.getNeighborChunks
local getChunkByXY = mapUtils.getChunkByXY
@ -67,17 +65,19 @@ local mCeil = math.ceil
-- module code
local function settlerWaveScaling(universe)
return mCeil(gaussianRandomRange(universe.settlerWaveSize,
universe.settlerWaveDeviation,
universe.expansionMinSize,
universe.expansionMaxSize))
return mCeil(gaussianRandomRangeRG(universe.settlerWaveSize,
universe.settlerWaveDeviation,
universe.expansionMinSize,
universe.expansionMaxSize,
universe.random))
end
local function attackWaveScaling(universe)
return mCeil(gaussianRandomRange(universe.attackWaveSize,
universe.attackWaveDeviation,
1,
universe.attackWaveUpperBound))
return mCeil(gaussianRandomRangeRG(universe.attackWaveSize,
universe.attackWaveDeviation,
1,
universe.attackWaveUpperBound,
universe.random))
end
local function attackWaveValidCandidate(chunk, map)
@ -193,7 +193,7 @@ end
function aiAttackWave.formSettlers(map, chunk)
local universe = map.universe
if (universe.builderCount < universe.AI_MAX_BUILDER_COUNT) and
(mRandom() < universe.formSquadThreshold) and
(map.random() < universe.formSquadThreshold) and
((map.points - AI_SETTLER_COST) > 0)
then
local surface = map.surface
@ -223,10 +223,11 @@ function aiAttackWave.formSettlers(map, chunk)
if squadPosition then
local squad = createSquad(squadPosition, surface, nil, true)
squad.maxDistance = gaussianRandomRange(universe.expansionMaxDistance * 0.5,
universe.expansionMaxDistanceDerivation,
10,
universe.expansionMaxDistance)
squad.maxDistance = gaussianRandomRangeRG(universe.expansionMaxDistance * 0.5,
universe.expansionMaxDistanceDerivation,
10,
universe.expansionMaxDistance,
universe.random)
local scaledWaveSize = settlerWaveScaling(universe)
universe.formGroupCommand.group = squad.group
@ -236,7 +237,7 @@ function aiAttackWave.formSettlers(map, chunk)
if universe.NEW_ENEMIES then
squad.base = findNearbyBase(map, chunk)
end
squad.kamikaze = mRandom() < calculateKamikazeThreshold(foundUnits, universe)
squad.kamikaze = map.random() < calculateKamikazeThreshold(foundUnits, universe)
universe.builderCount = universe.builderCount + 1
map.points = map.points - AI_SETTLER_COST
if universe.aiPointsPrintSpendingToChat then
@ -256,7 +257,7 @@ end
function aiAttackWave.formVengenceSquad(map, chunk)
local universe = map.universe
if (universe.squadCount < universe.AI_MAX_SQUAD_COUNT) and
(mRandom() < universe.formSquadThreshold) and
(map.random() < universe.formSquadThreshold) and
((map.points - AI_VENGENCE_SQUAD_COST) > 0)
then
local surface = map.surface
@ -276,7 +277,7 @@ function aiAttackWave.formVengenceSquad(map, chunk)
if squadPosition then
local squad = createSquad(squadPosition, surface)
squad.rabid = mRandom() < 0.03
squad.rabid = map.random() < 0.03
local scaledWaveSize = attackWaveScaling(universe)
universe.formGroupCommand.group = squad.group
@ -286,7 +287,7 @@ function aiAttackWave.formVengenceSquad(map, chunk)
if universe.NEW_ENEMIES then
squad.base = findNearbyBase(map, chunk)
end
squad.kamikaze = mRandom() < calculateKamikazeThreshold(foundUnits, universe)
squad.kamikaze = map.random() < calculateKamikazeThreshold(foundUnits, universe)
map.groupNumberToSquad[squad.groupNumber] = squad
universe.squadCount = universe.squadCount + 1
map.points = map.points - AI_VENGENCE_SQUAD_COST
@ -307,7 +308,7 @@ function aiAttackWave.formSquads(map, chunk)
local universe = map.universe
if (universe.squadCount < universe.AI_MAX_SQUAD_COUNT) and
attackWaveValidCandidate(chunk, map) and
(mRandom() < universe.formSquadThreshold) and
(map.random() < universe.formSquadThreshold) and
((map.points - AI_SQUAD_COST) > 0)
then
local surface = map.surface
@ -327,7 +328,7 @@ function aiAttackWave.formSquads(map, chunk)
if squadPosition then
local squad = createSquad(squadPosition, surface)
squad.rabid = mRandom() < 0.03
squad.rabid = map.random() < 0.03
local scaledWaveSize = attackWaveScaling(universe)
universe.formGroupCommand.group = squad.group
@ -337,7 +338,7 @@ function aiAttackWave.formSquads(map, chunk)
if universe.NEW_ENEMIES then
squad.base = findNearbyBase(map, chunk)
end
squad.kamikaze = mRandom() < calculateKamikazeThreshold(foundUnits, universe)
squad.kamikaze = map.random() < calculateKamikazeThreshold(foundUnits, universe)
map.points = map.points - AI_SQUAD_COST
universe.squadCount = universe.squadCount + 1
map.groupNumberToSquad[squad.groupNumber] = squad

View File

@ -47,8 +47,6 @@ local linearInterpolation = mathUtils.linearInterpolation
local mFloor = math.floor
local mCeil = math.ceil
local mRandom = math.random
local mMax = math.max
local mMin = math.min
@ -96,7 +94,8 @@ function aiPlanning.planning(map, evolution_factor, tick)
if (map.canAttackTick < tick) then
map.maxAggressiveGroups = mCeil(map.activeNests / ACTIVE_NESTS_PER_AGGRESSIVE_GROUPS)
map.sentAggressiveGroups = 0
map.canAttackTick = randomTickEvent(tick,
map.canAttackTick = randomTickEvent(map.random,
tick,
AGGRESSIVE_CAN_ATTACK_WAIT_MIN_DURATION,
AGGRESSIVE_CAN_ATTACK_WAIT_MAX_DURATION)
end
@ -119,7 +118,7 @@ function aiPlanning.planning(map, evolution_factor, tick)
universe.unitRefundAmount = AI_UNIT_REFUND * evolution_factor
universe.kamikazeThreshold = NO_RETREAT_BASE_PERCENT + (evolution_factor * NO_RETREAT_EVOLUTION_BONUS_MAX)
local points = ((AI_POINT_GENERATOR_AMOUNT * mRandom()) + (map.activeNests * 0.003) +
local points = ((AI_POINT_GENERATOR_AMOUNT * universe.random()) + (map.activeNests * 0.003) +
(AI_POINT_GENERATOR_AMOUNT * mMax(evolution_factor ^ 2.5, 0.1)))
if (map.temperament < 0.05) or (map.temperament > 0.95) then
@ -149,7 +148,7 @@ function aiPlanning.planning(map, evolution_factor, tick)
end
if (map.stateTick <= tick) then
local roll = mRandom()
local roll = universe.random()
if (map.temperament < 0.05) then -- 0 - 0.05
if universe.enabledMigration then
if (roll < 0.7) and universe.siegeAIToggle then
@ -298,7 +297,7 @@ function aiPlanning.planning(map, evolution_factor, tick)
map.ionCannonBlasts = 0
map.artilleryBlasts = 0
map.stateTick = randomTickEvent(tick, AI_MIN_STATE_DURATION, AI_MAX_STATE_DURATION)
map.stateTick = randomTickEvent(map.random, tick, AI_MIN_STATE_DURATION, AI_MAX_STATE_DURATION)
if universe.printAIStateChanges then
game.print(map.surface.name .. ": AI is now: " .. constants.stateEnglish[map.state] .. ", Next state change is in " .. string.format("%.2f", (map.stateTick - tick) / (60*60)) .. " minutes @ " .. getTimeStringFromTick(map.stateTick) .. " playtime")

View File

@ -42,7 +42,7 @@ local euclideanDistancePoints = mathUtils.euclideanDistancePoints
local getChunkByPosition = mapUtils.getChunkByPosition
local gaussianRandomRange = mathUtils.gaussianRandomRange
local gaussianRandomRangeRG = mathUtils.gaussianRandomRangeRG
local linearInterpolation = mathUtils.linearInterpolation
@ -58,8 +58,6 @@ local getResourceGenerator = chunkPropertyUtils.getResourceGenerator
local next = next
local mRandom = math.random
-- module code
local function evoToTier(universe, evolutionFactor, maxSkips)
@ -68,7 +66,7 @@ local function evoToTier(universe, evolutionFactor, maxSkips)
for i=10,1,-1 do
if universe.evoToTierMapping[i] <= evolutionFactor then
v = i
if (skipsRemaining == 0) or (mRandom() <= 0.75) then
if (skipsRemaining == 0) or (universe.random() <= 0.75) then
break
end
skipsRemaining = skipsRemaining - 1
@ -104,7 +102,7 @@ local function findBaseMutation(map, targetEvolution)
local tier = evoToTier(universe, targetEvolution or map.evolutionLevel, 2)
local alignments = universe.evolutionTableAlignment[tier]
local roll = mRandom()
local roll = map.random()
for i=1,#alignments do
local alignment = alignments[i]
@ -123,7 +121,7 @@ local function initialEntityUpgrade(baseAlignment, tier, maxTier, map, useHiveTy
local useTier
local tierRoll = mRandom()
local tierRoll = map.random()
if (tierRoll < 0.4) then
useTier = maxTier
elseif (tierRoll < 0.7) then
@ -141,13 +139,13 @@ local function initialEntityUpgrade(baseAlignment, tier, maxTier, map, useHiveTy
for ui=1,#upgrades do
local upgrade = upgrades[ui]
if upgrade[3] == useHiveType then
entity = upgrade[2][mRandom(#upgrade[2])]
entity = upgrade[2][map.random(#upgrade[2])]
break
end
end
end
if not entity then
local roll = mRandom()
local roll = map.random()
for ui=1,#upgrades do
local upgrade = upgrades[ui]
@ -155,7 +153,7 @@ local function initialEntityUpgrade(baseAlignment, tier, maxTier, map, useHiveTy
roll = roll - upgrade[1]
if (roll <= 0) then
entity = upgrade[2][mRandom(#upgrade[2])]
entity = upgrade[2][map.random(#upgrade[2])]
break
end
end
@ -181,15 +179,15 @@ local function entityUpgrade(baseAlignment, tier, maxTier, originalEntity, map)
for i=1, #mapTypes do
local upgrade = factionLookup[mapTypes[i]]
if upgrade and (#upgrade > 0) then
entity = upgrade[mRandom(#upgrade)]
if mRandom() < 0.55 then
entity = upgrade[map.random(#upgrade)]
if map.random() < 0.55 then
return entity
end
end
end
elseif (#upgrades > 0) then
entity = upgrades[mRandom(#upgrades)]
if mRandom() < 0.55 then
entity = upgrades[map.random(#upgrades)]
if map.random() < 0.55 then
return entity
end
end
@ -213,8 +211,8 @@ local function findEntityUpgrade(baseAlignment, currentEvo, evoIndex, originalEn
if evolve then
local chunk = getChunkByPosition(map, originalEntity.position)
local makeHive = (chunk ~= -1) and (getResourceGenerator(map, chunk) > 0) and (mRandom() < 0.2)
makeHive = makeHive or (not makeHive and (mRandom() < 0.005))
local makeHive = (chunk ~= -1) and (getResourceGenerator(map, chunk) > 0) and (map.random() < 0.2)
makeHive = makeHive or (not makeHive and (map.random() < 0.005))
return initialEntityUpgrade(baseAlignment, tier, maxTier, map, (makeHive and "hive"))
else
return entityUpgrade(baseAlignment, tier, maxTier, originalEntity, map)
@ -223,10 +221,10 @@ end
local function findBaseInitialAlignment(map, evoIndex)
local dev = evoIndex * 0.15
local evoTop = gaussianRandomRange(evoIndex - (evoIndex * 0.075), dev, 0, evoIndex)
local evoTop = gaussianRandomRangeRG(evoIndex - (evoIndex * 0.075), dev, 0, evoIndex, map.random)
local result
if mRandom() < 0.05 then
if map.random() < 0.05 then
result = {findBaseMutation(map, evoTop), findBaseMutation(map, evoTop)}
else
result = {findBaseMutation(map, evoTop)}
@ -264,7 +262,7 @@ function baseUtils.upgradeEntity(entity, base, map, disPos, evolve, register)
local pickedBaseAlignment
if (#baseAlignment == 2) then
if mRandom() < 0.75 then
if map.random() < 0.75 then
pickedBaseAlignment = baseAlignment[2]
else
pickedBaseAlignment = baseAlignment[1]
@ -300,7 +298,7 @@ local function pickMutationFromDamageType(map, damageType, roll, base)
local mutation
if (damageFactions and (#damageFactions > 0)) then
mutation = damageFactions[mRandom(#damageFactions)]
mutation = damageFactions[map.random(#damageFactions)]
if baseAlignment[2] then
if (roll < 0.05) then
baseAlignment[2] = nil
@ -369,7 +367,7 @@ local function upgradeBaseBasedOnDamage(map, base)
base.damagedBy["RandomMutation"] = mutationAmount
total = total + mutationAmount
local pickedDamage
local roll = mRandom()
local roll = map.random()
for damageTypeName,amount in pairs(base.damagedBy) do
base.damagedBy[damageTypeName] = amount / total
end
@ -392,10 +390,10 @@ function baseUtils.processBase(chunk, map, tick, base)
local universe = map.universe
local point = universe.position
point.x = chunk.x + (CHUNK_SIZE * mRandom())
point.y = chunk.y + (CHUNK_SIZE * mRandom())
point.x = chunk.x + (CHUNK_SIZE * map.random())
point.y = chunk.y + (CHUNK_SIZE * map.random())
local upgradeRoll = mRandom()
local upgradeRoll = map.random()
if (base.state == BASE_AI_STATE_ACTIVE) and (base.points >= MINIMUM_BUILDING_COST) and (upgradeRoll < 0.30) then
local entities = surface.find_entities_filtered(universe.filteredEntitiesPointQueryLimited)
if #entities ~= 0 then
@ -433,7 +431,7 @@ function baseUtils.processBase(chunk, map, tick, base)
upgradeBaseBasedOnDamage(map, base)
base.mutations = base.mutations + 1
elseif (base.mutations == universe.MAX_BASE_MUTATIONS) then
local roll = mRandom()
local roll = map.random()
if (roll < 0.001) then
base.mutations = 0
elseif (roll > 0.999) then
@ -451,13 +449,14 @@ function baseUtils.processBase(chunk, map, tick, base)
end
if (base.stateTick <= tick) then
local roll = mRandom()
local roll = map.random()
if (roll < 0.85) then
base.state = BASE_AI_STATE_ACTIVE
else
base.state = BASE_AI_STATE_DORMANT
end
base.stateTick = randomTickEvent(tick,
base.stateTick = randomTickEvent(map.random,
tick,
BASE_AI_MIN_STATE_DURATION,
BASE_AI_MAX_STATE_DURATION)
end
@ -479,11 +478,12 @@ function baseUtils.createBase(map, chunk, tick)
local alignment = findBaseInitialAlignment(map, evoIndex) or {"neutral"}
local baseLevel = gaussianRandomRange(meanLevel, meanLevel * 0.3, meanLevel * 0.50, meanLevel * 1.50)
local baseDistanceThreshold = gaussianRandomRange(BASE_DISTANCE_THRESHOLD,
BASE_DISTANCE_THRESHOLD * 0.2,
BASE_DISTANCE_THRESHOLD * 0.75,
BASE_DISTANCE_THRESHOLD * 1.50)
local baseLevel = gaussianRandomRangeRG(meanLevel, meanLevel * 0.3, meanLevel * 0.50, meanLevel * 1.50, map.random)
local baseDistanceThreshold = gaussianRandomRangeRG(BASE_DISTANCE_THRESHOLD,
BASE_DISTANCE_THRESHOLD * 0.2,
BASE_DISTANCE_THRESHOLD * 0.75,
BASE_DISTANCE_THRESHOLD * 1.50,
map.random)
local distanceThreshold = (baseLevel * BASE_DISTANCE_LEVEL_BONUS) + baseDistanceThreshold
local base = {

View File

@ -90,7 +90,6 @@ local mMin = math.min
local mMax = math.max
local next = next
local mRandom = math.random
-- module code
@ -228,7 +227,7 @@ function mapProcessor.processPlayers(players, map, tick)
-- randomize player order to ensure a single player isn't singled out
-- not looping everyone because the cost is high enough already in multiplayer
if (#players > 0) then
local player = players[mRandom(#players)]
local player = players[map.random(#players)]
if validPlayer(player) then
if player.surface.index ~= map.surface.index then
return

View File

@ -31,9 +31,9 @@ function mathUtils.roundToNearest(number, multiple)
return num - (num % multiple)
end
function mathUtils.randomTickEvent(tick, low, high)
function mathUtils.randomTickEvent(rg, tick, low, high)
local range = high - low
local minutesToTick = (range * mRandom()) + low
local minutesToTick = (range * rg()) + low
return tick + mathUtils.roundToNearest(TICKS_A_MINUTE * minutesToTick, 1)
end
@ -69,51 +69,9 @@ function mathUtils.xorRandom(state)
end
end
function mathUtils.linearInterpolation(percent, min, max)
return ((max - min) * percent) + min
end
--[[
Used for gaussian random numbers
--]]
function mathUtils.gaussianRandom(mean, std_dev)
-- marsagliaPolarMethod
local iid1
local iid2
local q
repeat
iid1 = 2 * mRandom() + -1
iid2 = 2 * mRandom() + -1
q = (iid1 * iid1) + (iid2 * iid2)
until (q ~= 0) and (q < 1)
local s = mSqrt((-2 * mLog10(q)) / q)
local v = iid1 * s
return mean + (v * std_dev)
end
function mathUtils.gaussianRandomRange(mean, std_dev, min, max)
if (min >= max) then
return min
end
local r
repeat
local iid1
local iid2
local q
repeat
iid1 = 2 * mRandom() + -1
iid2 = 2 * mRandom() + -1
q = (iid1 * iid1) + (iid2 * iid2)
until (q ~= 0) and (q < 1)
local s = mSqrt((-2 * mLog10(q)) / q)
local v = iid1 * s
r = mean + (v * std_dev)
until (r >= min) and (r <= max)
return r
end
function mathUtils.gaussianRandomRG(mean, std_dev, rg)
-- marsagliaPolarMethod
local iid1
@ -175,9 +133,9 @@ function mathUtils.euclideanDistanceArray(p1, p2)
return ((xs * xs) + (ys * ys)) ^ 0.5
end
function mathUtils.distortPosition(position, size)
local xDistort = mathUtils.gaussianRandomRange(1, 0.5, 0, 2) - 1
local yDistort = mathUtils.gaussianRandomRange(1, 0.5, 0, 2) - 1
function mathUtils.distortPosition(rg, position, size)
local xDistort = mathUtils.gaussianRandomRangeRG(1, 0.5, 0, 2, rg) - 1
local yDistort = mathUtils.gaussianRandomRangeRG(1, 0.5, 0, 2, rg) - 1
position.x = position.x + (xDistort * size)
position.y = position.y + (yDistort * size)
return position