WeightedRandom

Installation

def deps do
  [
    {:weighted_random, "~> 0.4.2"}
  ]
end

Introduction

Sometimes random is too random. Use this to add a bias toward a certain value (or values) Also supports such values impacting their neighbours

Not intended to be cryptographically secure. Also not nearly as performant as a simple Enum.random/1, so consider whether you actually need this.

Quick Example

iex> import WeightedRandom
iex> # Pick a random number between 1..10, but 4 is 35x more likely than
iex. # any other given number
iex> range = 1..10
iex> weight1 = %{target: 4, weight: 35}
iex> rand(range, weight1)
4
iex> # Multiple weights are supported
iex> # You can even set a radius so that neighbouring values also get
iex> # some added weight
iex> weight1 = %{target: 7, weight: 15, radius: 4, curve: :ease_in_sine}
iex> weight2 = %{target: 1, weight: 35}
iex> weights = [weight1, weight2]
iex> s = Stream.repeatedly(fn -> rand(range, weights, index: false) end)
iex> Enum.take(s, 10)
[8, 1, 1, 5, 8, 6, 7, 7, 1, 10]

Visual example

Here I demonstrate picking 10_000 random numbers, and count how many times each number came up.

range = 1..100
target = 45
weight = 15
radius = 25

Enum.random(1..100)

Image

Ease In

Image

Ease Out

Image

Dice

WeightedRandom also includes a Dice rolling module.

iex> :rand.seed(:exsss, {100, 231, 302})
iex> import WeightedRandom.Dice
iex> d6 = ~d{6}
iex> d6.total
6
iex> d6 = Dice.roll(d6)
iex> d6.total
3
iex> # You might know this as 4d8+1.
iex> d8s =  ~d{4, 8, 1}
iex> d8s.total
27
iex> d8s = Dice.roll(d8s)
iex> d8s.total
15
iex> mixed_dice = Dice.merge_dice([d6, d8s])
iex> mixed_dice.total == d6.total + d8s.total
true
iex> heavy_d4 = Dice.add_weight(~d{4}, %{weight: 400, target: 4})
iex> heavy_d4.total == 4
iex> mixed_dice = Dice.merge_dice(mixed_dice, heavy_d4)
iex> mixed_dice.total
3 + 15 + 4