How to implement double chances into this chance system

I am looking for a way to implement 2x chances into this random chance system

local pets = {
  ['Pet 1'] = {0, 35},
  ['Pet 2'] = {35, 70},
  ['Pet 3'] = {70, 85},
  ['Pet 4'] = {85, 95},
  ['Pet 5'] = {95, 99.9},
  ['Pet 6'] = {99.9, 99.99999},
  ['Pet 7'] = {99.99999, 100},
}

local function getPet()
  local random = Random.new()
  local randomNumber = random:NextNumber(0,100)

  for petName, petData in pairs(pets) do
    if not ((randomNumber > petData[1]) and (randomNumber < petData[2])) then continue end
        —got pet
  end
  if not (#selectedPets > 0) then return warn('There was an error while finding the hatched pet!') end
end```
1 Like

Here is a function that picks from weighted probability values rather than ranges, so it should be less confusing. To double the chance of something being drawn you could then just increase the weight value directly.

local pets = {
	['Pet 1'] = 35,
	['Pet 2'] = 35,
	['Pet 3'] = 15,
	['Pet 4'] = 10,
	['Pet 5'] = 4.9,
	['Pet 6'] = 0.09999,
	['Pet 7'] = 0.00001
}

local function getPet()
	local random = Random.new()
	local total = 0
	for _, chanceValue in pairs(pets) do
		total = total + chanceValue
	end
	local randomNumber = Random:NextNumber() * total
	total = 0
	for petName, chanceValue in pairs(pets) do
		total = total + chanceValue
		if (total >= randomNumber) then
			return petName
		end
	end
end

I will be using this weighted system, but i’m still confused on how I would make certain chances double without ruining the total of the chances being equal to 100 (so it’s accurate)

let’s say i would like to make epics+ x2 chance, that would make the total weight above 100, how would i fix that

local pets = {
	['common'] = 40,
	['uncommon'] = 35,
	['rare'] = 15,
	['epic'] = 9.8489,
	['legend'] = 0.1,
	['legend'] = 0.05,
	['legend'] = 0.001,
	['secret'] = 0.0001,
}
1 Like

This can be solved algebraically. Let’s say that the current weight of epics in that list is e1, and the total sum of all weights in that list is t1. The chance of picking an epic level pet right now would be e1/t1, but we want to double that. So let’s add some number x to e1 such that the probability would be:

e2/t2 = (e1+x) / (t1+x) = 2 * e1 / t1

(Increasing e1 by x also increases t1 by x.)
Then solve for x:

t * (e1 + x) = 2 * e1 * (t1 + x)
t*e1 + t*x = 2*e1*t1 + 2*e1*x
t*e1 - 2*e1*t1 = 2*e1*x - t1*x = x * (2*e1 - t1)
x = (t1*e1 - 2*e1*t1) / (2*e1 - t1)
x = (-t1*e1) / (2*e1 - t1)

The generic equation, where N is the factor by which you want to multiply the chance, would be:

x = t1*e1(1 - N) / (N*e1 - t1)

My test code:

-- pick a random key from a weighted list
local function pick(list)
	local total = 0
	for _, v in pairs(list) do
		total = total + v
	end
	local r = Random.new():NextNumber() * total
	total = 0
	for k, v in pairs(list) do
		total = total + v
		if (total >= r) then
			return k
		end
	end
end

-- calculates the number you would need to add to a specific weight in order to change it's probability
local function increment(list, key, factor)
	local t = 0
	for _, v in pairs(list) do
		t = t + v
	end
	local p = list[key]
	local a = t * p * (1 - factor)
	local b = factor * p - t
	-- if it makes the chance go over 100%
	if (b == 0 or (factor > 1 and math.sign(a) ~= math.sign(b))) then
		return math.huge
	else
		return a / b
	end
end

local probs = {
	a = 30,
	b = 20, -- note that 'b' is 20/100
	c = 20,
	d = 25,
	e = 5
}
local results = {
	a = 0, b = 0, c = 0, d = 0, e = 0
}

local n = 10000
for i = 1, n do
	local k = pick(probs)
	results[k] = results[k] + 1
end
print("test 1")
for k, v in pairs(results) do
	print(k, ":", v / n) -- 'b' should be around .2
end

local results = {
	a = 0, b = 0, c = 0, d = 0, e = 0
}
print("-")
probs['b'] = probs['b'] + increment(probs, 'b', 2)
for i = 1, n do
	local k = pick(probs)
	results[k] = results[k] + 1
end
print("test 2")
for k, v in pairs(results) do
	print(k, ":", v / n) -- 'b' should be around .4
end

In the two outputs, the percentage of ‘b’ outputs in the second test should be about double what it was in the first test. Also be aware that the probabilities of everything else get smaller when one weight value is increased.

1 Like

You don’t need the chances to equal to 100 when you go based on the total (which the script above does). If you have two pets each with weights of 1, the two pets would be 1/2 which would be 50%

1 Like

i’m not sure if this is what i need or not because i don’t really understand the code and the whole process but just in case i’ll explain what i’m looking for more exact:

i want epics, legendaries (1-3) and secrets to have double the chance of being chosen but if i simply multiply them all by 2 i get a total weight higher than 100 and then in that case the percentages wouldn’t be accurate. i’m looking for a way to multiply those by 2 and still keep the total 100. a way i see many games do this is by bringing the chances of commons-rares down but don’t know how they do it.

local pets = {
	['common'] = 40,
	['uncommon'] = 35,
	['rare'] = 15,
	['epic'] = 9.8489,
	['legend'] = 0.1,
	['legend'] = 0.05,
	['legend'] = 0.001,
	['secret'] = 0.0001,
}

If a weight value is adjusted you could just iterate through all of them afterwards and scale them so that the total equals 100 again. Their ratios to each other wouldn’t change.

local newTotal = 0
for _, weight in pairs(pets) do
	newTotal = newTotal + weight
end
for petName, weight in pairs(pets) do
	pets[petName] = 100 * weight / newTotal
end
2 Likes