jeffdeon

Balanced Dice Algorithm

Sep 27th, 2020
576
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. interface IStandardDiceDeck {
  2.     totalDice: number
  3.     dicePairs: IDicePair[],
  4. }
  5.  
  6. interface IWeightedDiceDeck {
  7.     totalDice: number,
  8.     dicePairs: IDicePair[],
  9.     probabilityWeighting: number,
  10.     recentlyRolledCount: number,
  11. }
  12.  
  13. export interface IDicePair {
  14.     dice1: number
  15.     dice2: number
  16. }
  17.  
  18. export abstract class DiceController {
  19.     abstract throwDice(): IDicePair
  20. }
  21.  
  22. export class WeightedDiceDeckController extends DiceController {
  23.  
  24.     private readonly minimumCardsBeforeReshuffling: number
  25.     private readonly probabilityReductionForRecentlyRolled: number
  26.  
  27.     private weightedDiceDeck: IWeightedDiceDeck[]
  28.     private cardsLeftInDeck: number
  29.     private recentRolls: number[]
  30.     private maximumRecentRollMemory: number
  31.  
  32.     constructor() {
  33.         super()
  34.         this.initWeightedDiceDeck()
  35.         this.reshuffleWeightedDiceDeck()
  36.         this.updateWeightedDiceDeckProbabilities()
  37.  
  38.         this.minimumCardsBeforeReshuffling = 13
  39.         this.probabilityReductionForRecentlyRolled = 0.3
  40.  
  41.         this.recentRolls = []
  42.         this.maximumRecentRollMemory = 5
  43.     }
  44.  
  45.     throwDice(): IDicePair {
  46.         return this.drawWeightedCard()
  47.     }
  48.  
  49.     private drawWeightedCard(): IDicePair {
  50.         if(this.cardsLeftInDeck < this.minimumCardsBeforeReshuffling) this.reshuffleWeightedDiceDeck()
  51.         this.updateWeightedDiceDeckProbabilities()
  52.         this.adjustWeightedDiceDeckBasedOnRecentRolls()
  53.         return this.getWeightedDice()
  54.     }
  55.  
  56.     private initWeightedDiceDeck() {
  57.         this.weightedDiceDeck = []
  58.         this.weightedDiceDeck.push({totalDice: 2, dicePairs: [], probabilityWeighting: 0, recentlyRolledCount: 0})
  59.         this.weightedDiceDeck.push({totalDice: 3, dicePairs: [], probabilityWeighting: 0, recentlyRolledCount: 0})
  60.         this.weightedDiceDeck.push({totalDice: 4, dicePairs: [], probabilityWeighting: 0, recentlyRolledCount: 0})
  61.         this.weightedDiceDeck.push({totalDice: 5, dicePairs: [], probabilityWeighting: 0, recentlyRolledCount: 0})
  62.         this.weightedDiceDeck.push({totalDice: 6, dicePairs: [], probabilityWeighting: 0, recentlyRolledCount: 0})
  63.         this.weightedDiceDeck.push({totalDice: 7, dicePairs: [], probabilityWeighting: 0, recentlyRolledCount: 0})
  64.         this.weightedDiceDeck.push({totalDice: 8, dicePairs: [], probabilityWeighting: 0, recentlyRolledCount: 0})
  65.         this.weightedDiceDeck.push({totalDice: 9, dicePairs: [], probabilityWeighting: 0, recentlyRolledCount: 0})
  66.         this.weightedDiceDeck.push({totalDice: 10, dicePairs: [], probabilityWeighting: 0, recentlyRolledCount: 0})
  67.         this.weightedDiceDeck.push({totalDice: 11, dicePairs: [], probabilityWeighting: 0, recentlyRolledCount: 0})
  68.         this.weightedDiceDeck.push({totalDice: 12, dicePairs: [], probabilityWeighting: 0, recentlyRolledCount: 0})
  69.     }
  70.  
  71.     private reshuffleWeightedDiceDeck() {
  72.         const standardDiceDeck = this.getStandardDiceDeck()
  73.  
  74.         for(const [totalDiceIndex, dicePairsForTotalDice] of standardDiceDeck.entries()) {
  75.             this.weightedDiceDeck[totalDiceIndex].dicePairs = dicePairsForTotalDice.dicePairs
  76.         }
  77.  
  78.         const totalCombinations = 36
  79.         this.cardsLeftInDeck = totalCombinations
  80.     }
  81.  
  82.     private updateWeightedDiceDeckProbabilities() {
  83.         for(const diceDeckForTotalDice of this.weightedDiceDeck) {
  84.             diceDeckForTotalDice.probabilityWeighting = diceDeckForTotalDice.dicePairs.length / this.cardsLeftInDeck
  85.         }
  86.     }
  87.  
  88.     private getWeightedDice(): IDicePair {
  89.         const totalProbabilityWeight = this.getTotalProbabilityWeight()
  90.  
  91.         let targetRandomNumber = Math.random() * totalProbabilityWeight
  92.         for(const diceDeckForTotalDice of this.weightedDiceDeck) {
  93.             if(targetRandomNumber <= diceDeckForTotalDice.probabilityWeighting) {
  94.                 const drawnCard = randomElementFromArray(diceDeckForTotalDice.dicePairs)
  95.                 removeElementFromArray(diceDeckForTotalDice.dicePairs, drawnCard)
  96.  
  97.                 this.recentRolls.push(diceDeckForTotalDice.totalDice)
  98.                 diceDeckForTotalDice.recentlyRolledCount ++
  99.                 this.cardsLeftInDeck --
  100.  
  101.                 if(this.recentRolls.length > this.maximumRecentRollMemory) this.updateRecentlyRolled()
  102.                 return drawnCard
  103.             }
  104.             targetRandomNumber -= diceDeckForTotalDice.probabilityWeighting
  105.         }
  106.  
  107.         JL4('Something seriously wrong with weighted dice deck')
  108.         const defaultRollIfError = {dice1: 3, dice2: 4}
  109.         return defaultRollIfError
  110.     }
  111.  
  112.     private getTotalProbabilityWeight(): number {
  113.         let totalProbabilityWeight = 0
  114.         for(const dicePairs of this.weightedDiceDeck) {
  115.             totalProbabilityWeight += dicePairs.probabilityWeighting
  116.         }
  117.  
  118.         return totalProbabilityWeight
  119.     }
  120.  
  121.     private updateRecentlyRolled() {
  122.         const ignore0and1 = 2
  123.         const totalDiceFiveRollsAgo = this.recentRolls[0]
  124.         this.weightedDiceDeck[totalDiceFiveRollsAgo - ignore0and1].recentlyRolledCount --
  125.         this.recentRolls.shift()
  126.     }
  127.  
  128.     private adjustWeightedDiceDeckBasedOnRecentRolls() {
  129.         for(const diceDeckForTotalDice of this.weightedDiceDeck) {
  130.             const probabilityReduction = (diceDeckForTotalDice.recentlyRolledCount * this.probabilityReductionForRecentlyRolled)
  131.             const probabilityMultiplier = 1 - probabilityReduction
  132.             diceDeckForTotalDice.probabilityWeighting *= probabilityMultiplier
  133.             if(diceDeckForTotalDice.probabilityWeighting < 0) diceDeckForTotalDice.probabilityWeighting = 0
  134.         }
  135.     }
  136.  
  137.     private getStandardDiceDeck(): IStandardDiceDeck[] {
  138.         const standardDiceDeck: IStandardDiceDeck[] = []
  139.         standardDiceDeck.push({totalDice: 2, dicePairs: [{dice1: 1, dice2: 1}]})
  140.         standardDiceDeck.push({totalDice: 3, dicePairs: [{dice1: 1, dice2: 2}, {dice1: 2, dice2: 1}]})
  141.         standardDiceDeck.push({totalDice: 4, dicePairs: [{dice1: 1, dice2: 3}, {dice1: 2, dice2: 2}, {dice1: 3, dice2: 1}]})
  142.         standardDiceDeck.push({totalDice: 5, dicePairs: [{dice1: 1, dice2: 4}, {dice1: 2, dice2: 3}, {dice1: 3, dice2: 2}, {dice1: 4, dice2: 1}]})
  143.         standardDiceDeck.push({totalDice: 6, dicePairs: [{dice1: 1, dice2: 5}, {dice1: 2, dice2: 4}, {dice1: 3, dice2: 3}, {dice1: 4, dice2: 2}, {dice1: 5, dice2: 1}]})
  144.         standardDiceDeck.push({totalDice: 7, dicePairs: [{dice1: 1, dice2: 6}, {dice1: 2, dice2: 5}, {dice1: 3, dice2: 4}, {dice1: 4, dice2: 3}, {dice1: 5, dice2: 2}, {dice1: 6, dice2: 1}]})
  145.         standardDiceDeck.push({totalDice: 8, dicePairs: [{dice1: 2, dice2: 6}, {dice1: 3, dice2: 5}, {dice1: 4, dice2: 4}, {dice1: 5, dice2: 3}, {dice1: 6, dice2: 2}]})
  146.         standardDiceDeck.push({totalDice: 9, dicePairs: [{dice1: 3, dice2: 6}, {dice1: 4, dice2: 5}, {dice1: 5, dice2: 4}, {dice1: 6, dice2: 3}]})
  147.         standardDiceDeck.push({totalDice: 10, dicePairs: [{dice1: 4, dice2: 6}, {dice1: 5, dice2: 5}, {dice1: 6, dice2: 4}]})
  148.         standardDiceDeck.push({totalDice: 11, dicePairs: [{dice1: 5, dice2: 6}, {dice1: 6, dice2: 5}]})
  149.         standardDiceDeck.push({totalDice: 12, dicePairs: [{dice1: 6, dice2: 6}]})
  150.  
  151.         return standardDiceDeck
  152.     }
  153. }
  154.  
  155. export function randomElementFromArray<T>(array: T[]): T {
  156.     return array[Math.floor(Math.random() * array.length)]
  157. }
  158.  
  159. export function removeElementFromArray<T>(array: T[], element: T): boolean {
  160.     const index = array.indexOf(element)
  161.     if(index > -1) {
  162.         array.splice(index, 1)
  163.         return true
  164.     }
  165.     return false
  166. }
RAW Paste Data

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×