Source: sim/modes/ElectionSampler.js

/** @module */

import sampleElection from '@paretoman/votekit-sample-election'
import seedrandom from 'seedrandom'

/**
 * Simulate winners from many sampled elections.
 * Candidates are sampled from a distribution.
 * Winners are drawn as points.
 * The simulation is dynamic. More simulations are performed at each frame.
 * @constructor
 */
export default function ElectionSampler() {
    const self = this

    self.update = function (samplingGeometry, changes, optionsBag) {
        if (changes.checkAny()) {
            self.startSim(samplingGeometry)
        }

        const samplingResult = self.addSim(samplingGeometry, optionsBag)
        return samplingResult
    }

    const maxPoints = 5000

    let totalPartyWins
    let totalPoints
    let rng

    self.startSim = function (samplingGeometry) {
        totalPoints = 0
        totalPartyWins = Array(10).fill(0) // TODO: Use number of parties

        const seed = `watermelon${samplingGeometry.samplingSeed}`
        rng = seedrandom(seed)
    }

    self.addSim = function (samplingGeometry, optionsBag) {
        if (totalPoints > maxPoints) return { pointsChanged: false }

        const numSamples = 1
        const sampleResult = sampleElection(samplingGeometry, optionsBag, numSamples, rng)
        const { samplingPointsList, samplingPointsCount, partyWins, error } = sampleResult

        if (error !== undefined) return { pointsChanged: false }
        if (sampleResult.pointsChanged === false) return { pointsChanged: false }

        const numNewPoints = samplingPointsCount.reduce((p, c) => p + c)
        totalPoints += numNewPoints

        for (let i = 0; i < partyWins.length; i++) {
            totalPartyWins[i] += partyWins[i]
        }

        const partyWinFraction = totalPartyWins.map((x) => x / totalPoints)

        const pointsChanged = true
        const samplingResult = { pointsChanged, samplingPointsList, samplingPointsCount, partyWinFraction }
        return samplingResult
    }
}