dicedicedice/distribution.js

210 lines
4.9 KiB
JavaScript

export class DenominatorTooLargeError extends Error {}
export function Distribution(floor, ceil, denominator, fill) {
if(denominator > Number.MAX_SAFE_INTEGER) {
throw DenominatorTooLargeError("Distribution denominator unsafe")
}
let length = (ceil + 1) - floor
let buffer = new ArrayBuffer(length * 4)
let data = new Uint32Array(buffer)
if(Number.isInteger(fill)) {
data.fill(fill)
}
return {
floor,
ceil,
denominator,
data
}
}
export function Die(size) {
return Distribution(1, size, size, 1)
}
export function Index(dist, result) {
return result - dist.floor
}
export function Set(dist, result, prob) {
return dist.data.set([ prob ], Index(dist, result))
}
export function Get(dist, result) {
return dist.data.at(Index(dist, result))
}
export function Inc(dist, result, amount = 1) {
let i = Index(dist, result)
return dist.data.set([ dist.data.at(i) + amount ], i)
}
export function Includes(dist, result) {
return dist.floor <= result && result <= dist.ceil
}
export function Convolve(d1, d2) {
let ax1 = d1.floor + d2.floor
let ax2 = d1.ceil + d2.ceil
let Pc = Distribution(ax1, ax2, d1.denominator * d2.denominator, 0)
for(let S = ax1; S <= ax2; S++) {
for(let r1 = d1.floor; r1 <= d1.ceil; r1++) {
let r2 = S - r1
if(Includes(d2, r2)) {
Inc(Pc, S, Get(d1, r1) * Get(d2, r2))
}
}
}
return Pc
}
export function Highest(d1, d2) {
let hd, ld
if(d1.ceil >= d2.ceil) {
hd = d1, ld = d2
} else {
hd = d2, ld = d1
}
let Ph = Distribution(Math.max(d1.floor, d2.floor), hd.ceil, d1.denominator * d2.denominator, 0)
for(let r1 = hd.floor; r1 <= hd.ceil; r1++) {
for(let r2 = ld.floor; r2 <= ld.ceil; r2++) {
let S = Math.max(r2, r1)
Inc(Ph, S, Get(hd, r1) * Get(ld, r2))
}
}
return Ph
}
export function Lowest(d1, d2) {
let ld, hd
if(d1.floor <= d2.floor) {
ld = d1, hd = d2
} else {
ld = d2, hd = d1
}
let Ph = Distribution(ld.floor, Math.min(d1.ceil, d2.ceil), d1.denominator * d2.denominator, 0)
for(let r1 = ld.floor; r1 <= ld.ceil; r1++) {
for(let r2 = hd.floor; r2 <= hd.ceil; r2++) {
let S = Math.min(r2, r1)
Inc(Ph, S, Get(ld, r1) * Get(hd, r2))
}
}
return Ph
}
export function ForEach(dist, func) {
for(let i = 0; i < dist.data.length; i++) {
func(i + dist.floor, dist.data.at(i))
}
}
export function Floats(dist) {
let buf = new ArrayBuffer(dist.data.length * 4)
let view = new Float32Array(buf)
ForEach(dist, (i) => {
view[Index(dist, i)] = Get(dist, i) / dist.denominator
})
return view
}
export function Count(dist, amount) {
let counter = amount
for(let i = 0; i < dist.data.length; i++) {
counter -= dist.data.at(i)
if(counter <= 0) {
return i + dist.floor
}
}
}
const MAX_32_BIT_INT = 4294967295
export function Roll(dist) {
let i = crypto.getRandomValues(new Uint32Array(1))[0]
i = Math.ceil((i / MAX_32_BIT_INT) * dist.denominator)
return Count(dist, i)
}
/*
2 11.11% ████████████
3 22.22% ████████████
# 4 33.33% ████████████
5 22.22% ████████████
6 1.11% ████████████
*/
export function Visualize(dist, highlight = 13, tableWidth = 13) {
let largestProb = 0
let items = []
ForEach(dist, (i, v) => {
if(v > largestProb) largestProb = v
items.push([ i, v ])
})
let v = ''
let probUnits = tableWidth / largestProb
for(let [ res, prob ] of items) {
let percentage = prob / dist.denominator
percentage *= 100
v += res === highlight ? '#' : ' '
v += res.toString().padStart(3, ' ')
v += ' '
v += percentage.toFixed(2).padStart(5) + '%'
v += ' '
let u = prob * probUnits
let fb = Math.floor(u)
let r = u - fb
v += ('\u2588'.repeat(fb) + fractionalBlockCharacter(r)).padEnd(tableWidth, '\u2500')
v += '\n'
}
return v
}
export function fractionalBlockCharacter(float) {
if(float >= 7/8) return '\u2589'
if(float >= 3/4) return '\u258A'
if(float >= 5/8) return '\u258B'
if(float >= 1/2) return '\u258C'
if(float >= 3/8) return '\u258D'
if(float >= 1/4) return '\u258E'
if(float >= 1/8) return '\u258F'
return ''
}
// console.log(
// )
let exD = Die(6)
exD.floor += 3
exD.ceil += 3
// TODO: Change ceil back to a function so this is less weird
console.log(
// Die(6).data.length
Visualize(
// Convolve(
// Die(4),
Highest(Die(8), exD)
// )
)
)