This paper presents an empirical estimation of Mudae's kakeraloot rates. We analyzed data
collected from a variety of sources (2.9 billion individual loots in 2 million validated loot
bundles) and evaluated seven candidate decay functions for each reward type, confirming which
types have rates that decline with count obtained (disable_limits, permanent_rolls, wlslots, swb, rtcd) and which are flat
(wishprotect, bku, rolls, kakera, mudapins). We find the best fit function parameters for each type, using estimates from two
independent methods - a per-loot binomial model over verified loot rewards and a Poisson model
over loot display snapshot deltas - and use their agreement for function form as the primary
correctness signal. We also investigated the mechanism behind the quality mechanic,
confirming that it is a conditional re-roll for loots and not a simple multiplier of reward
probabilities, and verified the rolls tiered amounts mechanic: the 0.7 tier unlocks at 500 loots
used and the 0.4 tier unlocks at 1000, with the tier probabilities shifting smoothly as loots
are used. The generalized hyperbolic function (1+x/k)−α emerges as the
unified decay form, with per-type (k, α) configuration for all decline types and α=0
for flat types.
→ If you're just looking for results, jump to pseudocode (§5.1)
The following terms are used throughout this paper:
$kl command for one or more
loots.$lk or /kakeraloots
display, showing a player's cumulative state at a point in time.kl_usage - the total number of loots a player has purchased,
as shown in their $lk display.count_before - the total amount obtained so far for
a given reward type, usually the number shown on the $lk display. This is the main
variable for the decay function.quantity_lvl / 100.:qualityup:, :quant2x:, and reward type emojis). Does not show up
for large bundles, which only have the summary lines.Data was collected from a variety of servers, including some batch downloads from the
MudaeScience server, and combined into a single unified dataset. The data was filtered to
bundles from validated sequences for analysis - once player state (number of loots and rewards
so far, quality/quantity levels, etc.) is confirmed, only the state variables and observed
outcomes matter for rate estimation. Sequences where deltas do not match (indicating missed
purchases, often due to export time gaps or difficulties with attributing reward messages to
users) were kept for secondary validation but excluded from the primary rate analysis.
$klreset and $klresetall commands reset a player's loot totals, and so
sequences were split at these reset boundaries.
The one exception for sequence validation was disable_limits, which did not
require the snapshot delta to match the sum of the bundles' rewards exactly. A mismatch for
disable_limits was allowed for two reasons: the $lk display's
disablelist total also includes gains from kakera towers ($kt), and the display
rounds to integers while the normalized per-reward hit counts can be fractional.
Since $lk also includes rewards from kakeraC-react loots,
which were not part of most of the data collected, some of the sequences have inflated reward
deltas. To account for that, sequence validation had a per-type tolerance: abs(expected
− observed) ≤ kakerac_delta × (1 + quantity_lvl / 100). In other words, we
accept the sequence if a difference between expected and observed rewards is explainable by the
missing kakeraC loots.
| Metric | Value |
|---|---|
| Total loots (all bundles) | 15,408,206,998 |
| Total loots (validated bundles) | 2,899,489,669 |
| Total bundles | 7,678,635 |
| Validated loot bundles | 2,070,057 |
| Total snapshots | 48,088 |
| Total sequences | 22,931 |
| Validated sequences | 15,995 |
| Total unique servers | 67 |
| Total unique players | 1,872 |
| Unique players in validated sequences | 862 |
The data was normalized to convert reward amounts into spawn events by dividing by two for
quantity level 100, with extra processing for per-type amounts (divide by 2 again for
bku, handling the different disable_limits amounts). Data at lower
quantity levels and individual snapshots where we don't know when quantity 100 was reached
can be ambiguous (a count of 4 could mean 4 separate spawn events or 2 with quantity 100), so we
restricted the analysis to data at quantity level 100 and sequences with the same start and
end levels. For rolls the count is not interesting since every loot spawns one
roll, so their amounts were normalized to the tier step count instead (more details in
§2.5).
Snapshots were not used for mudapins, kakera, and
rolls because their display totals don't track the count
(mudapins counts unique pins collected, kakera shows current balance,
rolls shows current stock).
We filtered the data even further for rtcd, kakera, and
mudapins for the actual parameter fit:
rtcd is capped at 15h the actual count is not recoverable for player
states past that, and so we had to restrict analysis to uncapped sequences where both endpoints
were under the 15 cap.kakera amounts are variable and we only see the sums, we restricted
analysis to 1-loot bundles, where we only need to see "did we get kakera or not".mudapins are also aggregated and the true spawn count isn't known
(the displayed 10 mudapins received in a 100 loot bundle could mean 10 loots spawned unique
mudapins, or 12 loots spawned mudapins with 2 duplicates), and so we also restricted to 1-loot
bundles.count_before obtained at quality=100. Rolls, kakera, and mudapins lack a count variable and so are graphed against kl_usage. Dot size is proportional to square root of the bundle count of the data point.It's important to note that the headline figure of billions of loots is misleading as a measure of statistical power. Statistical power comes from independent observations, and 98% of the validated loots come from just 4,474 sequences. The effective sample size, therefore, is only a few thousand independent observations, and not the billions from the individual loots.
The spawn mechanism was assumed to be "each loot in a $kl N purchase
independently rolls for each reward type", which means that multiple rewards
can be obtained by a single loot. We hypothesized the per-loot spawn probability for each reward
type to be:
where b is the reward type's starting base rate, q is the
multiplier based on the player's quality level, and f is a function on the
per-reward count obtained so far - count_before - that controls the decline
in rates.
Similar to quantity, we restricted data to quality level 100 so that quality and quantity are constant across the fitted data (for more information about quality, refer to §2.6). The overall filter for quantity level 100 and quality level 100 left us with 96% of the original data.
Seven candidate decay forms were tested for each decline reward type. Table 2 describes the functions for each form, and Figure 2 and Figure 3 graph some examples of those functions on a linear plot and log-log plots.
| Form | f(x) | Parameters | Notes |
|---|---|---|---|
flat | $1$ | (none) | No decay (null hypothesis) |
hyperbolic | $\displaystyle\frac{1}{1+x/k}$ | k | Rate halves at x = k, then 3k, 7k, 15k, etc. |
exponential | $\displaystyle e^{-x/k}$ | k | Multiplicative decay: each additional reward obtained cuts the rate by a constant fraction (like compound interest in reverse) |
power | $\displaystyle (x+1)^{-\alpha}$ | α | Scale-free decay: the rate is inversely proportional to a power of count - no characteristic scale, doubling count always cuts the rate by the same factor |
gen_hyperbolic | $\displaystyle (1+x/k)^{-\alpha}$ | k, α | Generalized form, can represent hyperbolic (α=1), power (k=1), flat (α→0) |
step | $\displaystyle\begin{cases}1 & x < t \\ r & x \geq t\end{cases}$ | t, r | Piecewise constant: rate drops abruptly from 1 to r at threshold t |
logistic | $\displaystyle\frac{1}{1+e^{(x-k)/s}}$ | k, s | Smooth step: a gradual transition from full rate to reduced rate around threshold k. Parameter s controls the transition width - small s approaches the abrupt step function, large s spreads the transition over a wide range |
gen_hyperbolic to slope −α, exponential curves downward (faster than any straight line), step is L-shaped, logistic is a smooth S-to-flat, flat is constant. Use as a visual key for interpreting Figure 1.There are a few options for what is actually meant by the count_before variable. One
option is the number of spawn events that happens, and another is the actual amount of rewards that
are obtained. At quantity levels 0 and 100 these two models are either exactly the same or exactly
different by a constant factor of 2, so our backsolve of the model from the data isn't able to tell
the difference since they would have the same shape. To differentiate the two we examined players
with mid range quantity levels or players that upgraded quantity within our data, and saw which
option fit those observations better. If the game tracks spawn events, then players who got the same
amounts with different quantity levels should have different spawn rates, whereas if the game tracks
amounts, it's only the displayed number that matters.
The functions were fit for each reward type via maximum likelihood estimation (MLE). Each
$kl purchase of N loots was modeled as N independent Bernoulli trials (N weighted
coin flips) where the reward probability is determined by the model and the player's state at
the time of purchase. MLE finds the parameter values that make the observed data most probable
through a directed, gradient-guided search, using the negative log-likelihood
to score different parameter sets. NLL is a way to quantify how well a prediction matches data,
with a lower value meaning the prediction lines up better with what was actually observed.
A second, independent estimation method used the differences between consecutive $lk
snapshots from the same player. The cumulative count of each reward type grows according to the
ordinary differential equation dc/dkl = b·q·f(c) (in other words, the
change in reward count from some number of loots bought is determined by the base rate, the quality
multiplier, and the function on count). This formula only has a closed form solution when b·q
is constant (same quality and quantity at both endpoints), which is true for our dataset restricted
to quantity level 100 and quality level 100. The solution gives us a Poisson model (counting events
in an interval) for predicting the end count based on the start count, number of loots, and function
parameters. Similar to the first method, MLE finds the parameter values that maximize the fit across
all observations.
These two methods operate on different views of the same underlying data: the per-bundle method
uses the individual bundles (exact account state if a bundle has been validated, but validation is
vulnerable to attribution errors), while the snapshot method uses cumulative totals between
$lk snapshots (authoritative state and no attribution risk, but far fewer data points
and there can be large changes in reward rates in between snapshots). The methods have different and
independent weaknesses, and so agreement between the two methods was considered stronger evidence
than either method's fit alone.
For large datasets, NLL values are correspondingly large, so practically equivalent models can differ in NLL by amounts that are only technically significant and likely can be ignored. A model only needs to fit the specific data marginally better to win on overall error score, so to avoid overfitting we selected the overall winner using three additional criteria:
gen_hyperbolic with α at the lower bound
(0) produces f(x) = (1+x/k)0 = 1 for all x, which is identical to the
flat form. In these degenerate cases the simpler special case was preferred.flat regardless of the fitted
parameters. This was for cases where a parameter is near but not exactly at a bound, and the
practical result of the function was flat in the relevant range.The same rules were applied to both methods' fits (the per-bundle Bernoulli model and the snapshot-delta Poisson model), so a reported winner always reflects the staged selection rather than the raw best error score.
The fitted values found through this process so far are noisy estimates of what are most likely
simple constants - that is, the actual code is more likely to use 1/600 as a
constant, instead of something like 0.00172743..., which is something that would come
out of trying to fit a function to finite data. After finding the best fit values, we checked a
number of nearby round-value candidates for each reward type and compared their predictive performance
against the best-fit values.
We compared performance by bucketing all the data by count_before for decline
rewards and kl_usage for flat rewards and scored each candidate set of round number
parameters by how much worse they were than the best-fit parameters. The gaps for each bucket (zero
if it was better) were squared and weighted by the bucket's size, and if the root of the weighted
mean was lower than 2% the candidate set was "good enough". The sets that passed were then ranked
by "roundness" (for example, whole numbers were better than multiples of 2.5).
Every loot spawns one roll, but the actual amount received changes depending on player state. Players only receive full rolls before 500 loots used, but a 0.7 roll reward can appear after 500 loots, and a 0.4 roll reward can appear after 1000 loots. Each loot independently draws from the three versions of the roll rewards, with the inclusion set and weights for drawing determined once per bundle (ie., all of the loots in a bundle operate as if they were the first loot). These mechanisms are easily verified by the data by checking when each reward tier shows up.
Figuring out how the draw weights change with kl_usage requires a bit more work. To
make the values a bit easier to work with, we can do some math. The three tiers (1.0, 0.7, or 0.4
rolls) are equally spaced by 0.3, so a given loot's result can be described as how many steps above
the base of 0.4 it is (0 if the reward was 0.4, 1 if 0.7, and 2 if 1.0). A bundle's total step count
X is the sum of steps from all its loots, and can be recovered from the displayed aggregate amount.
For a bundle with N loots that received $n_{\text{full}}$ full rewards, $n_{0.7}$ 0.7 rewards,
and $n_{0.4}$ 0.4 rewards ($N = n_{\text{full}} + n_{0.7} + n_{0.4}$):
After filtering the rolls data to post March 2021 (when the tiers nerf was introduced), we normalize all the rolls bundle amounts to be in terms of X. Given the probabilities of each tier, we can calculate an expected value of X, and therefore we can match different probability models to the observed X distribution.
The probability model we assume for the weight functions is of the form
where the probablity of getting a full roll is some function $w_a$ on kl_usage,
and the probability of getting a 0.7 roll multiplies the remaining probability with some other
function $w_b$ on kl_usage. This leaves the total remaining probability for getting
a 0.4 roll.
Fitting the weight functions was a three-step process similar to the process described in §2.3 and §2.4: 1. Fit the candidate tier weight functions to the data (using both the direct individual preamble line observations and the X normalized bundle data), 2. Search for potential clean constants around the fitted values, 3. Compare and evaluate candidates that are within a certain threshold.
We considered two options for how the quality mechanic works:
For reward types with a low rate the two options are nearly identical (since the $b^2$ term vanishes between $2b$ and $2b - b^2$), but they give different predictions for higher rate rewards, and they behave differently for the individual emoji lines of the reward message preambles. Four tests for differentiating between the two options are described below, and the outcomes are reported in §3.1.
The :qualityup: emoji shows up on a preamble line when quality was triggered for
at least one reward type, which means that the overall rate of the emoji is determined by a
combination of all the reward type rates. How that combination is calculated differs between the
two options:
Each factor starts with the probability that reward type $i$'s spawn was triggered because of
quality (the portion of probability contributed by quality). Multiplying all the complementary
probabilities gives the chance that no reward's spawn was quality-triggered, and
subtracting that from 1 gives the chance that at least one was. rolls is excluded
because it spawns on every loot regardless of quality, so the quality bonus never
“triggers” it.
For example, with two reward types with base rates 0.1 and 0.2 at full rate ($f = 1$):
Because each decline type's $f_i(c_i)$ falls toward 0 as its count grows, the aggregate prediction for real data is better described with a band rather than a single number: the ceiling assumes every type is at its full rate (count 0), while the floor assumes that decline types are effectively zero (high counts) and only the flat types contribute.
One flaw of the aggregate check is that it depends on the base rates that we are trying to estimate. The analysis of the base rates does not depend on which quality option is correct (as long as quality level is constant across the data being analyzed), but the test is still an indirect one. A more direct test looks at preamble lines where only one reward type was obtained. Since we know that only one reward roll was successful for that loot, the reward rate formulas collapse down into just the basic $b \cdot (1 + q/100)$ vs $b + (1-b) \cdot b \cdot q/100$, and the emoji rate on those lines is the fraction of the spawns that were quality-triggered.
Another direct comparison uses the actual rates of receiving rewards for a flat reward type.
Since the rate of a flat reward does not depend on count, the only difference between the rate of
rewards received at quality level 0 and the rate at quality level 100 should be the quality effect:
the multiplier option predicts a straightforward multiplier of exactly 2, while the conditional
re-roll option predicts a multiplier of $2 - b$. We ran this test on wishprotect only,
since for the other flat rewards:
rolls isn't affected by quality at all.mudapins can be disabled, affecting the data comparison.kakera requires dealing with the aggregate amounts of varying rewards sizes.bku had very little data at quality level 0.This test uses the fitted base rates, but does not depend on them, as any non-zero base rate would result in a difference.
The best-fit value of the base rate for each reward type also depends on which quality option we use. After finding the best fit of $f(\text{count})$ on quality level 100 data, the base has to be recovered from the fitted rate $r$ by inverting the quality factor: $r = 2b$ under the simple multiplier option ($b = r/2$), or $r = 2b - b^2$ under the conditional re-roll option ($b = 1 - \sqrt{1 - r}$). Assuming that the code's function parameters use simple constants, whichever option recovers values that are closer to recognizable simple fractions is more likely to be the correct one.
Quantity does not affect spawn probabilities at all, but instead doubles the amount
of rewards. The :quant2x: emoji shows up on a preamble line when quantity doubled
that loot's rewards, so the observed rate of the emoji directly reflects the doubling probability:
Figure 5 plots the observed emoji rate against the expected rate (quantity level / 100): the points sit on the y = x line, confirming the formula.
We first investigated the question of which quality option to use, as it affects how the best-fit parameters for the reward rates are interpreted. The two direct tests - the single-type emoji rate and the quality-0-to-100 ratio - both refute the simple multiplier option and agree with the conditional re-roll option, and using that interpretation results in consistent sanity checks from the other two tests as well. Table 3 summarizes the results:
| Test | Model M predicts | Model C predicts | Observed | Verdict |
|---|---|---|---|---|
| §2.6.2 Single-type emoji rate | exactly 0.500 for every type | 0.454 to 0.472 = (1−b)/(2−b), type-specific | 0.453 to 0.476 | §3.1.1 M refuted (12–33σ below 0.500), C matches (within 2.0σ each) |
| §2.6.3 Q=0 vs 100 rate ratio | exactly 2× | 2−b = 1.875 | 1.900±0.029 | §3.1.2 matches C (3.5σ below 2) |
§2.6.1 Aggregate :qualityup: rate | floor 0.327 to ceiling 0.358 | floor 0.305 to ceiling 0.337 | 0.305 | §3.1.3 M refuted (~106σ below M's floor), C barely matches |
| §2.6.4 Base rates roundness | 0.117 / 0.153 / 0.095 — no recognizable fractions | 0.1250 ≈ 1/8 / 0.1668 ≈ 1/6 / 0.0996 ≈ 1/10 — very close to simple fractions | Model C constants are cleaner | §3.1.4 C produces cleaner constants |
On single-type preamble lines at quality 100, the simple multiplier predicts exactly 0.5 for every type and the conditional re-roll predicts a type-specific (1−b)/(2−b) for the rate of the :qualityup: emoji. The observed rates for the three high-sample types:
| Type | Single-type lines | Observed | M predicts | C predicts | vs M | vs C |
|---|---|---|---|---|---|---|
kakera | 123,273 | 0.453 | 0.500 | 0.454 | 33σ below | +1.2σ |
wishprotect | 86,008 | 0.468 | 0.500 | 0.467 | 19σ below | -0.7σ |
mudapins | 65,910 | 0.476 | 0.500 | 0.472 | 12σ below | -2.0σ |
The simple multiplier predicts an exact 2× difference between the emoji rates between
quality level 0 and quality level 100 and the conditional re-roll predicts a multiplier of
$2-b$. The observed ratio is 1.900±0.029 for
wishprotect (3.5σ below 2), supporting the conditional re-roll.
The simple multiplier option predicts an aggregate emoji rate at quality level 100 between 0.327 and 0.358, while the conditional re-roll option predicts 0.305 to 0.337. The observed rate is 0.305 over 5,011,915 loots, which is some 106 standard errors below the floor for the simple multiplier and right at the conditional re-roll's predicted floor. Considering that the bulk of the loots will be from high loot count player states, where decline reward rates are low, the rate being near the floor makes sense.
Converting the model spawn rates of the flat types to the base rates under each reading:
| Type | Population rate | Conditional base | Multiplier base |
|---|---|---|---|
wishprotect | 0.234 | 0.1250 ≈ 1/8 | 0.1172 |
kakera | 0.306 | 0.1668 ≈ 1/6 | 0.1529 |
mudapins | 0.189 | 0.0996 ≈ 1/10 | 0.0946 |
It's far more likely that Mudae uses 1/8, 1/6, and 1/10 as its constants rather than the values given by the multiplier option, which makes this both evidence for the conditional option and the basis of the clean constants in §3.5.
We first settle the question of which value is used as the variable for the model, then figure out the best function form for each reward type, then find the best fit parameters for each function.
For all the buckets that we looked at, using the amounts as the variable for the model
gave a better fit to the data than using spawn events. This means that when a
player upgrades quantity does not matter in terms of their rates - for example, getting
100 permanent_rolls at quantity level 0 and getting 100
permanent_rolls from half the loots at quantity level 100 are the same for what
the resulting rates are (it's important to note that though they're identical in terms of
subsequent rates, the quantity level 100 path requires half the total loots).
Of the 7 types that have a cumulative-obtained counter, the form selection rules
(§2.3.1) identified two as flat: wishprotect (where the
gen_hyperbolic fit's α hit the lower bound, collapsing to flat)
and bku (where α was pinned at the lower bound, mapping to flat
via degenerate detection). The other 5 had realistic decay parameters. The remaining 3 types
(rolls, kakera, mudapins) have no cumulative count
variable to fit a decay against, but were pretty recognizably flat. Their rates are covered in
§3.3.
Every decline type's best-fitting function was one of the special cases of the
gen_hyperbolic form. The non-family forms (exponential, step, logistic) fit far
worse for most types (Appendix A). Since gen_hyperbolic
$(1+x/k)^{-\alpha}$ is a generalized form of all of the competitive candidates, we adopted it
as the overall model so that all 5 decline types share one decay form with
per-type (k, α) configuration.
Table 6 shows the fitted form, base rate, and function parameters for all
ten reward types. Several types converge to recognizable special cases:
permanent_rolls and wlslots have α≈1 (hyperbolic).
disable_limits and swb retain the general form with both parameters
active. rtcd has very high k and α values, which approximates exponential
decay, but it also had the least usable data, so confidence in that result is not high.
| Type | Form | Base rate | k | α | n sequences | Occurrences |
|---|---|---|---|---|---|---|
disable_limits | gen_hyperbolic | 0.0361 | 233.5 | 1.025 | 19,344 | 370,363 |
permanent_rolls | hyperbolic (gen_hyperbolic) | 0.0017 | 5.9 | 1 | 19,302 | 45,437 |
rtcd | gen_hyperbolic | 0.0036 | 9998.4 | 1600.660 | 1,171 | 263 |
swb | gen_hyperbolic | 0.0025 | 19.6 | 3.555 | 9,241 | 5,721 |
wlslots | hyperbolic (gen_hyperbolic) | 0.0029 | 7.0 | 1 | 19,332 | 59,984 |
bku | flat | 0.0004996 | — | — | 9,244 | 238,159 |
kakera | flat | 0.1674 | — | — | N/A | 794 |
mudapins | flat | 0.1043 | — | — | N/A | 287 |
rolls | flat | 1 | — | — | N/A | 1,988,324 |
wishprotect | flat | 0.125 | — | — | 19,383 | 1,949,974 |
gen_hyperbolic model overlaid as solid red curves on each subplot, assuming quality level 100. rolls, kakera, and mudapins are graphed against kl_usage. Each subplot auto-scales to its own data so the fit is clearly visible for all types.We estimated every rate two independent ways (described in §2.3):
Table 7 compares the two methods side by side for all ten reward types.
Three types (rolls, kakera, mudapins) are excluded from
the snapshot method for the reasons given in §2.1, and appear as N/A in the
snapshot columns.
| Type | Per-bundle method | Snapshot method | Form Agreement | ||||
|---|---|---|---|---|---|---|---|
| Form | Base | Params | Form | Base | Params | ||
disable_limits | gen_hyperbolic | 0.0361 | k=233.5, α=1.025 | gen_hyperbolic | 0.217 | k=40.1, α=1.008 | yes |
permanent_rolls | hyperbolic | 0.0017 | k=5.9 | gen_hyperbolic | 0.0061 | k=1.5, α=0.985 | same family |
rtcd | gen_hyperbolic | 0.0036 | k=9998.4, α=1600.660 | power | 0.0171 | α=1.233 | same family |
swb | gen_hyperbolic | 0.0025 | k=19.6, α=3.555 | gen_hyperbolic | 0.3158 | k=2.6, α=3.032 | yes |
wlslots | hyperbolic | 0.0029 | k=7.0 | gen_hyperbolic | 0.0309 | k=0.6, α=0.985 | same family |
bku | flat | 0.0004996 | — | flat | 0.0005259 | — | yes |
kakera | flat | 0.1674 | — | N/A | N/A | N/A | — |
mudapins | flat | 0.1043 | — | N/A | N/A | N/A | — |
rolls | flat | 1 | — | N/A | N/A | N/A | — |
wishprotect | flat | 0.125 | — | flat | 0.1318 | — | yes |
The two methods agree on the overall function form for most types. For rtcd both methods find a decay over the usable range below the cap: the per-bundle method's gen_hyperbolic with very high k and α is numerically close to exp(−x/6.2), and the snapshot method adopts a power law (1+x)−1.23 (same family and same shape).
They have pretty big disagreements about the exact parameter values, however, and this is likely because of some inherent limitations of our methods:
Given this disagreement on specific parameters, we chose to use the per-bundle method's results. The per-bundle method operates on exact account state at each purchase (not interval predictions), has more data points (every validated bundle vs every snapshot pair), and is not affected by the sparse-data problem that can distort snapshot fits on reward types with only a few clean intervals. The snapshot method serves as independent validation of the decay family - when both methods agree that a type declines (or both find it flat), that consensus is stronger evidence than either method alone. When they disagree on the family (e.g. snapshot hits degenerate bounds on sparse data), the per-bundle result is trusted.
Separately, the model validation in §3.4 subsequently confirms that the per-bundle fits produce well-calibrated predictions, which supports this decision since that is ultimately what we want.
| Type | Base rate |
|---|---|
wishprotect | 0.125 |
bku | 0.0004996 |
rolls | 1 |
kakera | 0.1674 |
mudapins | 0.1043 |
5 reward types show no decay with count_before:
wishprotect, bku, rolls, kakera, mudapins. Their spawn rates are constant, affected
only by quality: the actual spawn probability at quality level q is the conditional factor $p =
b + (1-b) \cdot b \cdot q/100$ from §2.6. The base rates found are listed
in Table 8.
The kakera reward has four size tiers (Small, Medium, Large, Largest) for the
amounts that a spawn can give, and Table 9 shows each tier's share of
kakera spawn events in the confirmed single-loot sample. The key question is how much a player
would get on average per loot. Svessinn's
formula predicts ~335 kakera per event at the default $klvalue of 1000.
| Size | Amount range | Observed share |
|---|---|---|
| Small | 100-220 | 80.0% |
| Medium | 600-1049 | 17.5% |
| Large | 2500-3000 | 2.0% |
| Largest | 42069 | 0.002% |
Our analysis confirms this, with two independent measurements:
klvalue), and divide by the number of kakera
spawn events counted in the preamble's per-loot emoji lines (doubling included via the
:quant2x: emojis). This gets one direct kakera average per event from each bundle,
and K is the mean across all bundles: 331.1809 (-1.21% compared
to Svessinn).The two estimators use different samples and different measurement paths, and they bracket Svessinn's value. The spawn-count estimator is preferred (much larger sample, tighter CI).
The rolls reward spawns at a constant 1.0 rate - every loot spawns a roll.
What varies is the amount via the tiers mechanic, and what we want to find are
the functions governing how the tier weights change with kl_usage (described
in §2.5).
Fitted by maximum likelihood over 724,136 preamble tier observations plus 1,341,438 big-bundle step-count (X) observations together, the winning function forms are gen-hyperbolic for $w_a$ and log-logistic for $w_b$:
Similar to the decline rates, the MLE fitted values do not land on round constants, and also similarly to the decline rate data, at this sample size NLL differences are more a result of overfitting rather than actual predictive quality, so likelihood alone is not a good differentiator between multiple similar parameter sets. The two fitted exponents ($\alpha_a$ and $s_b$, shown above) are both pretty close to 1, the value at which both the gen-hyperbolic and log-logistic functions reduce to a plain hyperbolic $1/(1+x/C)$, and so the three candidate function shapes are effectively interchangeable here. After running a similar clean-constant search to §2.4, we choose the hyperbolic (5000), hyperbolic (5000) pair as safe operating constants (constants that we can use for making predictions, and not necessarily a statement of exact truth).
Besides checking the fit against the measured tier probabilities, we can also check against
the observed amounts. Dividing the expected value of a bundle's step count X (described in
§2.5) by the number of loots in that bundle gives us what we'll call
$x_{\text{loot}} = p_{0.7} + 2\,p_{\text{full}}$ (the probability to get one step
because of 0.7 and two steps from a full). The relationship between that and a loot's overall
expected rolls amount is simply $0.4 + 0.3\,x_{\text{loot}}$ (if the probability of getting
a full loot is 1, then we'll always expect to get a roll amount of 1). The functions for the tier
weights can be used to give a formula for $x_{\text{loot}}$ - using the clean constants
and $k$ for kl_usage:
Of the two resulting terms, the first grows linearly with $k$ while the second shrinks toward zero, so multiplying through by $k$ and taking the limit of large $k$:
Figure 8 plots the measured tier fractions by loots used against the
fitted curves, and plots $x_{\text{loot}}$ against kl_usage. One note is that
the clean constants asymptote line for $x_{\text{loot}}$ is only possible because the
clean constants form uses the simpler hyperbolic functions. With the actual fitted function
forms that have a slope slightly above one ($\alpha_a$ and $s_b$), the limit of 15000 would
slowly change - more data past the 100 million loots range would be needed to see if the
linear asymptote holds.
There are two different ways that we can test our model:
A model can be right on average (good validation) but have confidence intervals that are too narrow (bad calibration), and so both should be checked.
We applied three checks for our decline rewards model. First, we compared observed occurrence
rates against model-predicted rates by count_before bucket and bundle size
(§3.4.1). Second, we checked whether observed per-bundle spawn
counts fall within the model's 90% prediction intervals (§3.4.2).
Third, we used the independent :qualityup: preamble emoji rate (which was not part
of any analysis) as a sanity check on the fitted base rates
(§3.4.3).
To validate the fitted models, we compared observed occurrence rates against model-predicted
rates. For each validated bundle, the model predicts a per-loot spawn probability
$p = \bigl[\,b + (1-b)\,b \cdot \text{quality}/100\,\bigr] \times f(\text{count\_before})$
(the conditional quality factor from §2.6). The bundle-level occurrence
probability (whether it shows up or not) is $1-(1-p)^N$ (binomial, where $N$ =
num_loots). We grouped the data by count_before and bundle size, and
for each bucket the observed rate is the fraction of bundles in each bucket with at least one
spawn, and the predicted rate is the mean of the per-bundle binomial predictions.
rolls is excluded - it spawns on every loot, so its occurrence is exactly 100% and
there is nothing to validate (its amount mechanic is checked in
§3.4.2). Figure 9 plots observed vs predicted
per bucket (dot size proportional to the bucket's bundle count): points cluster tightly on the
y=x dashed line, indicating good model fit across all types and buckets.
count_before and bundle size. Dot size is proportional to the bucket's bundle count, and buckets with fewer than 20 bundles are dropped to reduce noise.Beyond checking whether the model predicts the right average rate, we also checked whether it has good prediction ranges (§3.4.1 validates accuracy, this calibrates precision). For each bundle, we used the same ODE solution from §2.3 to predict the range of expected spawn counts from the starting state and bundle size alone. Then we checked the observed actual count and saw if it fell within our 90% confidence interval. The table below shows the fraction of bundles that passed, broken down by reward type and bundle size. Green cells meet or beat the 90% target (given the 90% confidence range, we would expect 90% of bundles to fall within our predictions), and red cells fall short.
| Type | 1 loot | 2-10 loots | 11-100 loots | 100-1000 loots | 1000-10000 loots | 10000+ loots |
|---|---|---|---|---|---|---|
bku | 1.00 | 0.99 | 0.99 | 0.95 | 0.93 | 0.92 |
disable_limits | 0.96 | 0.98 | 0.95 | — | 0.78 | 0.92 |
mudapins | 1.00 | — | — | — | — | — |
permanent_rolls | 1.00 | 1.00 | 1.00 | — | 1.00 | 0.99 |
rolls | 1.00 | 0.95 | 0.94 | — | 0.82 | 0.93 |
rtcd | 0.99 | 0.98 | 0.99 | 1.00 | — | — |
swb | 1.00 | 1.00 | 1.00 | 0.99 | 0.98 | 0.99 |
wlslots | 0.98 | 1.00 | 0.99 | — | 1.00 | 0.99 |
wishprotect | 1.00 | 0.98 | 0.96 | 1.00 | 0.89 | 0.94 |
Buckets were omitted when the validation subsample contained fewer than 10 bundles in that
size range, and so couldn't give a good coverage fraction. Most bundles were either 1-10 loots
or 10,000+, leading to some types missing the 100-1000 loot buckets. rtcd also has
no 1,000+ buckets because its analysis is restricted to uncapped sequences
(§2.1), which contain mostly small bundles. mudapins is
1-loot-only by the same restriction as its rate fit. kakera has no row at all: its
per-spawn amounts are variable rather than a fixed count, so the Poisson count-interval check
doesn't apply, and it is validated via the K estimators in §3.3.1
instead.
Most buckets that did have enough data show good coverage overall, with some drop-off as bundles get larger.
As described in §3.1.3, the agreement between the predicted band
and the observed preamble emoji rate confirms that the fitted base rates in
Table 6 are reasonable. No single base rate can be off by a large factor
without shifting the predicted qualityup band away from the observed data, and the constraint is
particularly sensitive to the high rates of flat types (kakera, wishprotect,
mudapins) that make up the product.
Applying the procedure of §2.4 gives the verdicts in
Table 11. Most types were fairly close to reasonable seeming clean constants,
though disable_limits is a little dubious, and rtcd
did not have enough data to really produce good guesses. The predictive diff column gives the
values of the error comparisons against the best fit parameters. We expect that these values will
shift as we get more data and get better estimates, but as shown by the validation and calibration
they should serve well enough for making predictions.
| Type | MLE fit | Clean constant | Predictive diff | Verdict | ||||
|---|---|---|---|---|---|---|---|---|
| base | k | α | base | k | α | |||
wishprotect | 0.125 | — | — | 1/8 | — | — | 0.0% | adopted |
disable_limits | 0.0361 | 233.5357 | 1.0248 | 1/27 | 200 | 1 | 0.9% | adopted |
permanent_rolls | 0.0017 | 5.8596 | 1 | 1/600 | 6 | 1 | 1.1% | adopted |
wlslots | 0.0029 | 7.0067 | 1 | 1/300 | 6 | 1 | 0.5% | adopted |
bku | 0.0004996 | — | — | 1/2000 | — | — | 0.1% | adopted |
swb | 0.0025 | 19.5901 | 3.5547 | 1/350 | 18 | 3.5 | 0.0% | adopted |
rtcd | 0.0036 | 9998 | 1601 | — | — | — | — | too sparse |
rolls | 1 | — | — | 1 | — | — | — | pinned |
kakera | 0.1674 | — | — | 1/6 | — | — | 0.1% | adopted |
mudapins | 0.1043 | — | — | 1/10 | — | — | 0.1% | adopted |
All of the analysis so far has assumed that rates decline with count obtained. However,
count_before and kl_usage are near-perfectly correlated in the
observed data, which makes sense because obtaining rewards is a direct consequence of buying
loots. The Spearman rank correlation coefficient between count_before and
kl_usage (ρ: a measure of how similarly values of two variables are
ordered - the fifth highest count_before value seen also having the fifth highest
kl_usage value seen, etc) is very
high for all 5 decline types: disable_limits ρ=0.9985, permanent_rolls ρ=0.9996, wlslots ρ=0.9997, swb ρ=0.9901, rtcd ρ=0.7145. The exception is rtcd, which has a lower
ρ because the −15h cumulative cap limits the range of observations. For the other 4
types, fewer than 1% of observations sit off this diagonal (high count_before with
low kl_usage, or vice versa).
This means the alternative - that rates decline with total loots - cannot be ruled out. Both models predict the same declining curve along the trajectory players actually follow. As Figure 10 shows, plotting the cumulative count obtained for each reward against total loots (on log-log axes) gives approximately straight lines for most types, meaning the counts grow as some power of total loots, and a rate stated as a function of count obtained can be restated as a function of loots obtained, and vice versa.
We can still make confident predictions regardless of what the underlying mechanism actually is, but it's important to recognize this ambiguity. Resolving it would require off-diagonal data: players whose per-reward counts are noticeably higher or lower than expected given their total loots purchased.
We also generated best fits of the loots-based model from the data using the same decay function forms and clean constants process. Restating a count-based generalized hyperbolic function in terms of loots gives exponent α/(α+1), and the best fits support that.
| Type | Base | k | α | Best fit exponent | Counts-based predicted exponent |
|---|---|---|---|---|---|
wishprotect | 1/8 | — | — | ||
disable_limits | 1/10 | 100 | 0.5 | 0.503 | 0.506 |
permanent_rolls | 1/2000 | 5000 | 0.5 | 0.498 | 0.500 |
wlslots | 1/2000 | 10000 | 0.5 | 0.500 | 0.500 |
bku | 1/2000 | — | — | ||
swb | 1/1000 | 1000 | 0.75 | 0.746 | 0.780 |
rtcd | 0.0483583765 (no bands) | 0.541387 | 0.541 | 0.999 | |
rolls | 1 (pinned) | ||||
kakera | 1/6 | — | — | ||
mudapins | 1/10 | — | — |
It's tempting to see these clean constants and use their roundness as evidence supporting the loots-based model, but it's important to note that these constants were chosen specifically to be clean, and not the other way around. More data from low count player states and players that are unusually lucky/unlucky are needed to really get better parameter fits.
There are two different kinds of uncertainty that affect how well we're able to fit a model to the data, and consequently how good of a prediction we can make:
These two sources of uncertainty combine to determine the reliability and predictive power of our models: aleatoric uncertainty sets the floor, and epistemic uncertainty widens the prediction interval further on top. It's important to remember that even if we got the true constants that Mudae uses, the accuracy of a prediction range will always trade off with the confidence with which you can make that prediction.
Data limitations:
swb and bku data. Rarer
types should be treated as tentative.Method limitations:
disable_limits, permanent_rolls, and wlslots),
the rate at high count depends only on the product of base × k, and not on
either parameter individually. At low counts, both parameters are distinguishable, but there
is much less data for that region - only 0.1% of permanent_rolls rows and 0.025%
for wlslots have count_before < 5, for example. This means that
for our dataset the optimizer can happily trade off between the base and
k parameters with nearly zero effect on the fit quality. Getting better estimates
of the true parameter values would require more low count observations.Results limitations.
This paper presents an empirical estimation of kakeraloot reward spawn rates in Mudae, cross-validated by two independent methods. The key points are:
gen_hyperbolic decay model - disable_limits, permanent_rolls, wlslots, swb, rtcd all
decline with count_before, described by the generalized hyperbolic $(1+x/k)^{-\alpha}$
with per-type (k, α). Several types converge to special cases of this form:
permanent_rolls/wlslots to hyperbolic (α≈1),
rtcd to a high-α regime (approximating exponential). wishprotect, bku, rolls, kakera, mudapins show no
decay (α=0) and their rates stay constant.count_before or kl_usage is hard to distinguish due to
collinearity (ρ ≈ 0.999). We assume count-obtained, but the total-loots hypothesis
cannot be ruled out - yet.If the Mudae code uses one shared decay form with per-type configuration tables, as the
gen_hyperbolic model suggests, then the following is a guess at approximately
what the kakeraloot spawn logic looks like:
The default version decays with count obtained; the alternative decays with total loots (§4.1.1 covers where its constants come from, and §4.1 why the data cannot tell the two apart).
# Using clean constants (§3.5), else best-fit MLE
CONFIG = {
# "type": ( base , k , alpha )
"disable_limits": ( 1/27 , 200 , 1 ),
"permanent_rolls": ( 1/600 , 6 , 1 ),
"wlslots": ( 1/300 , 6 , 1 ),
"swb": ( 1/350 , 18 , 3.5 ),
"rtcd": ( 0.00364265, 9998.44 , 1600.66 ), # MLE (no clean constants found, use best-fit)
"wishprotect": ( 1/8 , float('inf'), 0 ),
"bku": ( 1/2000 , float('inf'), 0 ),
"rolls": ( 1 , float('inf'), 0 ),
"kakera": ( 1/6 , float('inf'), 0 ),
"mudapins": ( 1/10 , float('inf'), 0 ),
}
# Whether this happens as one loot with modified probability or actually goes
# through a reroll process gives the same results (see §3.1)
def spawn_rate(reward_type, quality_lvl, count_obtained):
base, k, alpha = CONFIG[reward_type]
p = base + (1 - base) * base * quality_lvl / 100
return p * (1 + count_obtained / k) ** (-alpha)
# Rolls tier model with clean constants (see §2.5)
def rolls_tier(kl_usage):
if kl_usage < 500: return 1.0 # 0.7 and 0.4 not possible below 500
a = 1 / (1 + kl_usage / 5000)
if random() < a: return 1.0
if kl_usage < 1000: return 0.7 # 0.4 not possible below 1000
b = 1 / (1 + kl_usage / 5000)
if random() < b: return 0.7
return 0.4
# Per loot:
for reward_type in CONFIG:
count = player.count_obtained[reward_type] # total amount obtained; doubled grants count as 2
p = spawn_rate(reward_type, player.quality_lvl, count)
if random() < p:
quantity_multiplier = 1
if random() < player.quantity_lvl / 100:
quantity_multiplier = 2
amount = BASE_AMOUNT[reward_type] * quantity_multiplier # 1 for most, variable for kakera
if reward_type == "rolls":
amount = rolls_tier(player.kl_usage) * quantity_multiplier
player.grant(reward_type, amount)
player.count_obtained[reward_type] += quantity_multiplier
The combined model for reward type $r$ is:
A calculator using the results of this investigation is at https://colblitz.com/mudae/klcalc
[1] Mudae Wiki, "Kakeraloot" page. Fandom wiki. Accessed 2026-08.
[2] Svessinn, Kakera return formula and kakeraloot rates. https://svessinn.github.io/Mudae/KakeraLoots/. Accessed 2026-08.
Complete NLL ranking of all 7 decay forms per reward type. ΔNLL is
relative to the best-fitting model per type (lower = better).
| Type | Form | Base rate | Parameters | n params | NLL | ΔNLL |
|---|---|---|---|---|---|---|
disable_limits |
gen_hyperbolic |
0.03612680687990851 | k=233.5, α=1.025 | 3 | 12,139,341.500744 | 0 |
disable_limits |
hyperbolic |
0.040424769266356735 | k=187.4 | 2 | 12,139,823.225614 | 481.724870 |
disable_limits |
power |
0.99 | α=0.730 | 2 | 12,252,661.466428 | 113,319.965684 |
disable_limits |
exponential |
0.0011916497556651005 | k=27667.5 | 2 | 12,453,595.767160 | 314,254.266417 |
disable_limits |
logistic |
0.001829815858340056 | k=2045.8, s=24058.1 | 3 | 12,526,476.218111 | 387,134.717367 |
disable_limits |
flat |
0.00027019073579936676 | — | 1 | 13,247,085.342485 | 1,107,743.841741 |
disable_limits |
step |
0.01 | t=50, r=0.050 | 3 | 13,598,808.550795 | 1,459,467.050051 |
permanent_rolls |
gen_hyperbolic |
0.0016932501284512701 | k=6.0, α=1.002 | 3 | 637,504.101048 | 0 |
permanent_rolls |
hyperbolic |
0.00172959521355954 | k=5.9 | 2 | 637,504.201263 | 0.100215 |
permanent_rolls |
power |
0.008057996318929174 | α=0.967 | 2 | 637,575.687367 | 71.586319 |
permanent_rolls |
exponential |
4.335551571250583e-05 | k=1045.8 | 2 | 648,632.009776 | 11,127.908728 |
permanent_rolls |
logistic |
5.297077798267598e-05 | k=377.3, s=916.7 | 3 | 651,940.361027 | 14,436.259979 |
permanent_rolls |
step |
1.003626283148031e-05 | t=5000, r=0.165 | 3 | 677,341.887197 | 39,837.786149 |
permanent_rolls |
flat |
1e-05 | — | 1 | 677,631.493418 | 40,127.392370 |
wlslots |
gen_hyperbolic |
0.002738173229115025 | k=7.5, α=1.005 | 3 | 871,048.176081 | 0 |
wlslots |
hyperbolic |
0.0028760307259104384 | k=7.0 | 2 | 871,049.001994 | 0.825913 |
wlslots |
power |
0.01623866586485867 | α=0.970 | 2 | 871,173.914590 | 125.738509 |
wlslots |
exponential |
6.191090641739043e-05 | k=1462.0 | 2 | 886,875.174327 | 15,826.998246 |
wlslots |
logistic |
0.00010063707748105268 | k=1.0, s=1278.4 | 3 | 890,335.838785 | 19,287.662704 |
wlslots |
flat |
1.4913061991922828e-05 | — | 1 | 928,395.441822 | 57,347.265741 |
wlslots |
step |
0.01 | t=50, r=0.050 | 3 | 3,466,339.829738 | 2,595,291.653657 |
swb |
gen_hyperbolic |
0.002478357965810272 | k=19.6, α=3.555 | 3 | 84,044.210800 | 0 |
swb |
power |
0.131543002088705 | α=2.272 | 2 | 85,032.247825 | 988.037025 |
swb |
exponential |
9.388244558868449e-05 | k=36.2 | 2 | 85,548.292125 | 1,504.081325 |
swb |
logistic |
4.450466900570296e-05 | k=38.6, s=34.6 | 3 | 86,353.119364 | 2,308.908564 |
swb |
hyperbolic |
0.000251778041155995 | k=1.0 | 2 | 89,731.730100 | 5,687.519300 |
swb |
flat |
1e-05 | — | 1 | 130,202.046367 | 46,157.835568 |
swb |
step |
1e-05 | t=500, r=0.500 | 3 | 130,202.046367 | 46,157.835568 |
rtcd |
exponential |
0.003643708860636339 | k=6.2 | 2 | 2,071.801563 | 0 |
rtcd |
gen_hyperbolic |
0.0036426472343890513 | k=9998.4, α=1600.660 | 3 | 2,071.805904 | 0.004340 |
rtcd |
power |
0.009760464343638269 | α=1.070 | 2 | 2,078.363744 | 6.562180 |
rtcd |
hyperbolic |
0.007368203633442207 | k=1.2 | 2 | 2,078.496553 | 6.694990 |
rtcd |
logistic |
0.0012724896149733974 | k=88.5, s=71.9 | 3 | 2,110.976474 | 39.174911 |
rtcd |
flat |
0.0009550063938138117 | — | 1 | 2,112.707329 | 40.905765 |
rtcd |
step |
0.0009550063017672577 | t=500, r=0.500 | 3 | 2,112.707329 | 40.905765 |