Skip to content
Permalink
Browse files

Various fixes for more stable lua iteration order

I ended up auditing every instance of `pairs`; these are the ones that
potentially interact with dungeon generation / randomness. Basically,
the use of `pairs` is now dispreferred for anything like the code being
touched here. I added a few utility functions for dealing with weight
tables in various ways (would have made my life easier to add these
before...)
  • Loading branch information...
rawlins committed Feb 1, 2019
1 parent 0b81bb8 commit 05a21ba7acd5a87080843e79e5e4596306f18505
@@ -331,31 +331,24 @@ function sroom_demon_pit(e)
local weight_d = 5 + runes

local tier_twos = {
hell_beast = weight_a,
green_death = weight_a,
reaper = weight_b,
lorocyproca = weight_b,
balrug = weight_c,
blizzard_demon = weight_c,
hellion = weight_d,
tormentor = weight_d,
cacodemon = 5,
{"hell_beast", weight_a},
{"green_death", weight_a},
{"reaper", weight_b},
{"lorocyproca", weight_b},
{"balrug", weight_c},
{"blizzard_demon", weight_c},
{"hellion", weight_d},
{"tormentor", weight_d},
{"cacodemon", 5},
}

-- Choose three demons as weighted above,
-- and don't choose the same one twice.
local mons = {}
for mon_num=1,3 do
local mon = nil
local cweight = 0
for k,wt in pairs(tier_twos) do
cweight = cweight + wt
if crawl.random2(cweight) < wt then
mon = k
end
end
mons[mon_num] = mon
tier_twos[mon] = 0
local chosen = util.random_choose_weighted_i(tier_twos)
mons[mon_num] = tier_twos[chosen][1]
tier_twos[chosen][2] = 0
end

lord_mon = "brimstone fiend / ice fiend / tzitzimitl / " ..
@@ -158,6 +158,7 @@ KMONS: t = draconian knight / draconian scorcher / draconian annihilator
: end
: end
: end
: table.sort(kmonsters)
: kmons("s = " .. table.concat(kmonsters, " / "))
KITEM: ? = any ring randart w:390 / ring_of_the_octopus_king
KITEM: ! = ring of slaying / ring of wizardry / any good_item ring no_uniq w:75
@@ -250,7 +250,7 @@ function dgn_run_map(...)
error("No current map?")
end
local env = dgn_map_meta_wrap(g_dgn_curr_map, dgn)
for _, map_chunk_function in pairs(map_chunk_functions) do
for _, map_chunk_function in ipairs(map_chunk_functions) do
if map_chunk_function then
ret = setfenv(map_chunk_function, env)()
end
@@ -320,7 +320,7 @@ end

function dgn.fnum_map(map)
local fnmap = { }
for k, v in pairs(map) do
for k, v in pairs(map) do -- arbitrary iter order should be ok here
fnmap[dgn.fnum(k)] = dgn.fnum(v)
end
return fnmap
@@ -51,13 +51,19 @@ function random_item_def(items, egos, args, separator)
args = args ~= nil and " " .. args or ""
separator = separator ~= nil and separator or '/'
local item_def
items_list = util.sorted_weight_table(items)

for iname, iweight in pairs(items) do
for i, item_pair in ipairs(items_list) do
iname = item_pair[1]
iweight = item_pair[2]
-- If we have egos, define an item spec with all item+ego
-- combinations, each with weight scaled by item rarity and ego
-- rarity.
if egos ~= nil then
for ename, eweight in pairs(egos) do
egos_list = util.sorted_weight_table(egos)
for j, ego_pair in ipairs(egos_list) do
ename = ego_pair[1]
eweight = ego_pair[2]
if (not iname:find("demon") or ename ~= "holy_wrath")
and (not iname:find("quick blade") or ename ~= "speed") then
def = iname .. args .. " ego:" .. ename .. " w:" ..
@@ -82,7 +82,11 @@ function zonify.fill_smallest_zones(zonemap, num_to_keep, fgroup, ffill, min_zon
largest[n].size = -999999
end

for name,group in pairs(zonemap) do
local sorted_zones = {}
for name,group in pairs(zonemap) do table.insert(sorted_zones, name) end
table.sort(sorted_zones)
for i, name in ipairs(sorted_zones) do
group = zonemap[name]
if type(fgroup) == "function" and fgroup(group) or name==fgroup then
for i,zone in ipairs(group) do
zsize = #(zone.cells)
@@ -115,7 +119,8 @@ function zonify.fill_smallest_zones(zonemap, num_to_keep, fgroup, ffill, min_zon
return false
end

for name,group in pairs(zonemap) do
for i,name in ipairs(sorted_zones) do
group = zonemap[name]
if type(fgroup) == "function" and fgroup(group) or name==fgroup then
for i,zone in ipairs(group) do
local replace = true
@@ -328,6 +328,23 @@ function util.random_weighted_keys(weightfn, list, order)
return chosen
end

-- convert a table of key-to-weight mappings into a table of key-weight pairs,
-- as long as the keys are sortable.
function util.sorted_weight_table(t, sort)
local keys = { }
for k, v in pairs(t) do table.insert(keys, k) end
if sort == nil then
table.sort(keys)
else
table.sort(keys, sort)
end
local res = { }
for i, k in ipairs(keys) do
table.insert(res, { k, t[k] })
end
return res
end

-- Given a list of pairs, where the second element of each pair is an integer
-- weight, randomly choose from the list. This *cannot* be reimplemented using
-- just a table, because iteration order through a table is indeterminate and
@@ -337,7 +354,7 @@ end
--
-- the implementation here is very similar to random_choose_weighted in
-- random.h. Someone clever could probably call that directly instead.
function util.random_choose_weighted(list)
function util.random_choose_weighted_i(list)
local total = 0
for i,k in ipairs(list) do
total = total + k[2]
@@ -347,12 +364,16 @@ function util.random_choose_weighted(list)
for i,k in ipairs(list) do
sum = sum + k[2]
if sum > r then
return k[1]
return i
end
end
-- not reachable
end

function util.random_choose_weighted(list)
return list[util.random_choose_weighted_i(list)][1]
end

--- Given a table of elements, choose a subset of size n without replacement.
function util.random_subset(set, n)
local result = {}
@@ -89,7 +89,7 @@ end
function hell_branches_remaining()
local hell_branches = { "Geh", "Coc", "Dis", "Tar" }
local ret = #hell_branches
for _, branch in pairs(hell_branches) do
for _, branch in ipairs(hell_branches) do
if travel.find_deepest_explored(branch) == 7 then
ret = ret - 1
end

0 comments on commit 05a21ba

Please sign in to comment.
You can’t perform that action at this time.