Loot roller (beginner)
This is not a tutorial. It is a half-finished project, and you write the missing piece. You are handed a loot table where each item has a name and a weight, and your job is to finish a function that picks one item at random so that common items show up far more often than rare ones. Everything runs in your browser on real Lua. Read the brief, fill in the one part marked -- TODO:, and only reveal the solution once you have tried.
The goal
You are building a function called rollLoot. You hand it a list of items, and it hands you back the name of one item it picked.
The catch is that the pick is not a flat coin flip. Each item carries a weight: a number that says how likely it is. An item with weight 70 should win far more often than an item with weight 5. This is exactly how a real game decides whether you open a crate and get a common skin or a legendary one.
Here is the table you will roll against:
{ name = "common", weight = 70 },
{ name = "rare", weight = 25 },
{ name = "legendary", weight = 5 },Add those weights up and you get 100. So rollLoot should return "common" about 70 times out of 100, "rare" about 25 times out of 100, and "legendary" about 5 times out of 100. The numbers will not land exactly on 70, 25, and 5 every run because it is random, but over many rolls "common" should clearly be the most frequent and "legendary" the rarest. That spread is the whole point.
What you start with
Press RUN. The code already works, but rollLoot is unfinished: right now it always returns the very first item, so every roll comes back "common". Your job in the next section is to replace the -- TODO: line with a real weighted pick.
The test loop at the bottom rolls 1000 times and counts how often each name comes up, so you can see the spread change the moment your logic is correct.
Press RUN to execute.
Your job
Replace the -- TODO: line inside rollLoot with logic that picks one item by weight. Work through it in these four steps.
1. Sum the weights
Loop over the list and add up every item's weight into a total. For the table above, total ends at 100. This is the size of the range you will roll inside.
2. Roll a random number
Use math.random to pick one integer from 1 to total. Picture all the weights laid out in a line from 1 to 100: positions 1 to 70 belong to common, 71 to 95 belong to rare, and 96 to 100 belong to legendary. Your random number lands somewhere on that line.
3. Walk the items and subtract
Loop over the list again. For each item, subtract its weight from your roll. The moment the roll drops to zero or below, you have landed inside that item's slice of the line. That is your winner.
4. Return the name
Return that item's name. Because common owns the biggest slice of the line, your roll lands in it most often, which is exactly the behaviour you want.
Hints
Three nudges if you are stuck
math.random(1, total)gives you an integer from 1 up to and includingtotal. No decimals to worry about.- Keep a running variable for the roll. As you walk the list, do
roll = roll - item.weight. Whenroll <= 0, that item is the winner, so return its name right there inside the loop. - Use
ipairsto walk the list in order:for _, item in ipairs(list) do ... end. Eachitemis one of the small tables, soitem.weightanditem.nameare what you need.
Reveal the solution
Stuck or done? Reveal a working solution
Here is a complete, correct rollLoot. Drop this in place of the unfinished version and run it.
local function rollLoot(list)
-- Step 1: add up every weight.
local total = 0
for _, item in ipairs(list) do
total = total + item.weight
end
-- Step 2: roll an integer somewhere inside that range.
local roll = math.random(1, total)
-- Step 3: walk the list, subtracting each weight from the roll.
-- When the roll crosses zero, we have landed in that item's slice.
for _, item in ipairs(list) do
roll = roll - item.weight
if roll <= 0 then
-- Step 4: return the winner's name.
return item.name
end
end
endHow the weighted pick works: imagine the three weights laid end to end on a number line from 1 to 100. Common takes the first 70 spots, rare the next 25, legendary the last 5. math.random(1, total) drops a dart somewhere on that line. The loop then walks left to right, peeling off one slice at a time by subtracting each weight. The instant the leftover roll hits zero or goes negative, the dart is sitting inside the slice you just subtracted, so that item wins. Bigger slices catch the dart more often, which is why common comes back far more than legendary. The final return always fires before the loop ends, because the slices add up to exactly total and the roll is never larger than total.
What you practiced
- Stored structured data as a table of small tables, each with a name and a weight.
- Looped over a table twice with ipairs: once to sum, once to pick.
- Used math.random to draw an integer inside a range you computed at runtime.
- Turned a list of weights into a fair weighted choice by subtracting until you cross zero.
- Wrote a reusable function that returns a value instead of just printing.
You just built the exact pattern real servers use for crate drops, random spawns, and reward rolls. Once this clicks, try the next one up: the Teleport command (intermediate) template, where you wire logic like this into a real command instead of a test loop.