fortuna v2

Weighted gacha system.

Usage

Create an item using GachaMachine.createItem

More weight = more common

const items = [
    { result: "SSR cool character", chance: 1 },
    { result: "Kinda rare character", chance: 3 },
    { result: "Mob character", chance: 5 },
    { result: "Mob character", chance: 5 },
    { result: "Mob character", chance: 5 },
]

const machine = new GachaMachine(items)

machine.get(10) // Rolls 10x 

/*
    My result:
    [
      "Kinda rare character",
      "Mob character",
      "Mob character",
      "Mob character",
      "Mob character",
      "Kinda rare character",
      "Mob character",
      "Mob character",
      "Mob character",
      "Mob character"
    ]
*/

Plain weighted random selection

You probably don't need all complicated stuff. Here's a quick way to just create a simple weight-based gacha system: (Only works on v1.1.0 and above)

import { GachaMachine } from 'https://deno.land/x/fortuna/mod.ts' // wherever you are importing from. 

const items = [
    { result: "SSR cool character", chance: 1 },
    { result: "Kinda rare character", chance: 3 },
    { result: "Mob character", chance: 5 },
    { result: "Mob character", chance: 5 },
    { result: "Mob character", chance: 5 },
]

GachaMachine.rollWithLinearSearch(items) // Rolls one item from the list of items using linear search.

GachaMachine#get() works using Binary Search by default. Using the Binary Search method explicitly requires a different structure of data for input.

import { GachaMachine } from 'https://deno.land/x/fortuna/mod.ts' // wherever you are importing from. 

const items = [
    { result: "SSR cool character", cumulativeChance: 1 },
    { result: "Kinda rare character", cumulativeChance: 4 },
    { result: "Mob character", cumulativeChance: 9 },
    { result: "Mob character", cumulativeChance: 14 },
    { result: "Mob character", cumulativeChance: 19 },
]

GachaMachine.rollWithBinarySearch(items) // Rolls one item from the list of items using linear search.

How it works

Fortuna has two algorithms, one being a static method in the GachaMachine class and one being a standalone function.

Algorithm 1

Fortuna provides a simple function roll that generates a pseudo random number and performs linear search on the provided data in order to find the proper item.

// JS PSEUDOCODE

// Each choice is an object with 
//     chance: number
//     result: T (type parameter)
// 
// total chance can be supplied
// manually or computed from data
function roll(choices, total) {
    // runs a loop to compute total
    if(!total) total = sum_by_chance(data)

    // generate random number for choosing
    let rng = random_number(0, total)

    // run a loop to find the choice
    let current = 0.0;
    for (choice in data) {
        current = current + choice.chance;
        if(rng < current) return choice.result;
    }
}

Fortuna's default GachaMachine.get() uses a more complex, but faster approach.

  • When the GachaMachine class is instantiated, data is transformed into a form suitable for binary search.

  • When GachaMachine.get() is run, Fortuna performs binary search on this transformed data.

The roll function is more suitable when the weighted data is used only once.

The get method is suitable when the weighted data is reused. It comes at extra cost during initialization but compensates for it with better performance when sampling.

Documentation

Documentation for the latest version can be found in https://doc.deno.land/https://deno.land/x/fortuna/mod.ts

A guide for usage can be found in docs.nekooftheabyss.moe (not updated for v3 yet).