porting CEV-Eris pickweight procs.

This commit is contained in:
Ghommie
2020-04-23 21:47:54 +02:00
parent debbd3edab
commit de531bd047
6 changed files with 64 additions and 20 deletions
+59 -15
View File
@@ -231,40 +231,77 @@
//Picks a random element from a list based on a weighting system:
//1. Adds up the total of weights for each element
//2. Gets a number between 1 and that total
//2. Gets the total from 0% to 100% of previous total value.
//3. For each element in the list, subtracts its weighting from that number
//4. If that makes the number 0 or less, return that element.
/proc/pickweight(list/L)
/proc/pickweight(list/L, base_weight = 1)
var/total = 0
var/item
for (item in L)
if (!L[item])
L[item] = 1
L[item] = base_weight
total += L[item]
total = rand(1, total)
total = rand() * total
for (item in L)
total -=L [item]
total -= L[item]
if (total <= 0)
return item
return null
/proc/pickweightAllowZero(list/L) //The original pickweight proc will sometimes pick entries with zero weight. I'm not sure if changing the original will break anything, so I left it be.
//Picks a number of elements from a list based on weight.
//This is highly optimised and good for things like grabbing 200 items from a list of 40,000
//Much more efficient than many pickweight calls
/proc/pickweight_mult(list/L, quantity, base_weight = 1)
//First we total the list as normal
var/total = 0
var/item
for (item in L)
if (!L[item])
L[item] = 0
L[item] = base_weight
total += L[item]
total = rand(0, total)
for (item in L)
total -=L [item]
if (total <= 0 && L[item])
return item
//Next we will make a list of randomly generated numbers, called Requests
//It is critical that this list be sorted in ascending order, so we will build it in that order
//First one is free, so we start counting at 2
var/list/requests = list(rand(1, total))
for (var/i in 2 to quantity)
//Each time we generate the next request
var/newreq = rand()* total
//We will loop through all existing requests
for (var/j in 1 to requests.len)
//We keep going through the list until we find an element which is bigger than the one we want to add
if (requests[j] > newreq)
//And then we insert the newqreq at that point, pushing everything else forward
requests.Insert(j, newreq)
break
return null
//Now when we get here, we have a list of random numbers sorted in ascending order.
//The length of that list is equal to Quantity passed into this function
//Next we make a list to store results
var/list/results = list()
//Zero the total, we'll reuse it
total = 0
//Now we will iterate forward through the items list, adding each weight to the total
for (item in L)
total += L[item]
//After each item we do a while loop
while (requests.len && total >= requests[1])
//If the total is higher than the value of the first request
results += item //We add this item to the results list
requests.Cut(1,2) //And we cut off the top of the requests list
//This while loop will repeat until the next request is higher than the total.
//The current item might be added to the results list many times, in this process
//By the time we get here:
//Requests will be empty
//Results will have a length of quality
return results
//Pick a random element from the list and remove it from the list.
/proc/pick_n_take(list/L)
@@ -274,6 +311,13 @@
. = L[picked]
L.Cut(picked,picked+1) //Cut is far more efficient that Remove()
//Pick a random element from the list by weight and remove it from the list.
//Result is returned as a list in the format list(key, value)
/proc/pickweight_n_take(list/L, base_weight = 1)
if (L.len)
. = pickweight(L, base_weight)
L.Remove(.)
//Returns the top(last) element from the list and removes it from the list (typical stack function)
/proc/pop(list/L)
if(L.len)
+1 -1
View File
@@ -361,7 +361,7 @@ SUBSYSTEM_DEF(vote)
picked = S
runnable_storytellers[S] *= round(stored_gamemode_votes[initial(S.name)]*100000,1)
if(!picked)
picked = pickweightAllowZero(runnable_storytellers)
picked = pickweight(runnable_storytellers, 0)
GLOB.dynamic_storyteller_type = picked
if("map")
var/datum/map_config/VM = config.maplist[.]
+1 -1
View File
@@ -253,7 +253,7 @@
var/datum/ai_laws/lawtype
var/list/law_weights = CONFIG_GET(keyed_list/law_weight)
while(!lawtype && law_weights.len)
var/possible_id = pickweightAllowZero(law_weights)
var/possible_id = pickweight(law_weights, 0)
lawtype = lawid_to_type(possible_id)
if(!lawtype)
law_weights -= possible_id
+1 -1
View File
@@ -272,7 +272,7 @@
reports += config.mode_reports[config_tag]
Count++
for(var/i in Count to rand(3,5)) //Between three and five wrong entries on the list.
var/false_report_type = pickweightAllowZero(report_weights)
var/false_report_type = pickweight(report_weights, 0)
report_weights[false_report_type] = 0 //Make it so the same false report won't be selected twice
reports += config.mode_reports[false_report_type]
+1 -1
View File
@@ -483,7 +483,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/start/new_player)
if(!SSmapping.station_room_templates[t])
log_world("Station room spawner placed at ([T.x], [T.y], [T.z]) has invalid ruin name of \"[t]\" in its list")
templates -= t
template_name = pickweightAllowZero(templates)
template_name = pickweight(templates, 0)
if(!template_name)
GLOB.stationroom_landmarks -= src
qdel(src)
@@ -48,7 +48,7 @@
var/datum/traitor_class/class = GLOB.traitor_classes[C]
var/weight = LOGISTIC_FUNCTION(1.5*class.weight,chaos_weight,class.chaos,0)
weights[C] = weight * 1000
var/choice = pickweightAllowZero(weights)
var/choice = pickweight(weights, 0)
if(!choice)
choice = TRAITOR_HUMAN // it's an "easter egg"
var/datum/traitor_class/actual_class = GLOB.traitor_classes[choice]