فهرست منبع

Refactor a few things

Alan Colon 7 سال پیش
والد
کامیت
d68f72a531
6فایلهای تغییر یافته به همراه468 افزوده شده و 25 حذف شده
  1. 58 7
      bot.js
  2. 114 17
      dice.js
  3. 5 1
      package.json
  4. 16 0
      test.js
  5. 223 0
      unicoder.js
  6. 52 0
      yarn.lock

+ 58 - 7
bot.js

@@ -1,4 +1,6 @@
+const Cache = require('cache')
 const { Dice } = require('./dice')
+const SeedRandom = require('seedrandom')
 var { Client, Attachment } = require('discord.js');
 
 var bot = new Client()
@@ -9,22 +11,71 @@ bot.on('ready', function() {
 });
 */
 
-bot.on('message', function(message) {
+setInterval(() => {
+    // Chew through randomness to make it more unpredictable.
+    Math.random()
+})
+
+const cache = new Cache(60 * 60 * 1000)
+
+const formatChatLine = response => response.split('[').join('`').split(']').join('`')
+.replace(/= (\d+)\./g, (a, b) => `= **${b}**.`)
+
+function handleMessage(message) {
     if (message.author.id === bot.user.id) return;
-    const response = Dice.chat(message.content)
+    const session = cache.get(message.id) || { seed: message.id }
+    const rng = new SeedRandom(session.seed)
+
+    const response = Dice.chat(message.content, {
+        rng: () => rng.double()
+    })
     console.log(`message from ${message.author.username}: ${message.content}`)
     if (response) {
         console.log(response)
-        const chatLine = response.split('[').join('`').split(']').join('`')
-          .replace(/= (\d+)\./g, (a, b) => `= **${b}**.`)
+        const chatLine = formatChatLine(response)
         console.log(chatLine)
-        message.reply(chatLine)
+        if (session.reply) {
+            session.reply.edit(chatLine).then(reply => {
+                session.reply = reply
+            })
+        } else {
+            message.reply(chatLine).then(reply => {
+                session.reply = reply
+            })
+        }
+        cache.put(message.id, session)
+    } else {
+        if (session.reply) {
+            session.reply.delete()
+            session.reply = null
+        }
     }
+}
 
-});
+bot.on('message', handleMessage);
 
+bot.on('messageUpdate', function(oldMessage, newMessage) {
+    const session = cache.get(oldMessage.id)
+    if (session) {
+        cache.put(newMessage.id, session)
+    }
+    handleMessage(newMessage)
+})
 
-/*
+bot.on('messageDelete', function(message) {
+    const session = cache.get(message.id)
+    if (session && session.reply) {
+        session.reply.delete()
+        session.reply = null
+    }
+})
+
+bot.on('error', function(err) {
+    console.error(err)
+    process.exit(1)
+})
+
+/*l
 bot.on('channelCreate', function() { console.log('channelCreate', Array.from(arguments)) })
 bot.on('channelDelete', function() { console.log('channelDelete', Array.from(arguments)) })
 bot.on('channelPinsUpdate', function() { console.log('channelPinsUpdate', Array.from(arguments)) })

+ 114 - 17
dice.js

@@ -1,7 +1,66 @@
 const diceRegex = () => /\b(\d*)[dD](\d+)\b(?:\s?([+-])\s?(\d+))?/g
 const rerollRegex = () => /\b(reroll|re-roll)\s+((?:(?:\S*|\d+s|\d+'s|\d+)\s*)+)/
 const numberRegex = () => /\b(one|two|three|four|five|six|seven|eight|nine|ten|ones|twos|threes|fours|fives|sixes|sevens|eights|nines|tens|one's|two's|three's|four's|five's|seven's|eight's|nine's|ten's|\d+)\b/gi
-const advRegex = () => /(disadvantage|advantage)/i
+const advRegex = () => /\b(disadvantage|advantage)\b/i
+const percentRegex = () => /\b(percentile|percentiles|percent)\b/i
+const dicebotRegex = () => /\b(dicebot|dice bot)\b/i
+const unicoder = require('./unicoder')
+
+const d6 = {
+  stringify: n => unicoder.codesets.dice[n],
+  total: (ns, bonus) => ns.reduce((a, b) => a + b, bonus).toString()
+}
+
+const coin = {
+  stringify: (n, i, total) => `${n === 1 ? unicoder.codesets.circles.red : n === 2 ? unicoder.codesets.circles.blue : unicoder.codesets.circles.black}${total === 1 ? (n === 1 ? ' Heads' : n === 2 ? ' Tails' : ' Sides') : ''}`,
+  total: ns => {
+    const heads = ns.filter(x => x === 1).length
+    const tails = ns.filter(x => x === 2).length
+
+    if (heads === 1 && tails === 0) return 'Heads'
+    if (heads === 0 && tails === 1) return 'Tails'
+    if (heads === 0 && tails > 0) return `${tails} Tails and no Heads`
+    if (heads > 0 && tails === 0) return `${heads} Heads and no Tails`
+    if (heads > 0 && tails > 0) return `${heads} Heads and ${tails} Tails`
+    return 'The coin disappears'
+  }
+}
+
+const d10 = {
+  stringify: (n, i, total, { percent = false } = {}) => {
+    if (total === 2 && percent) {
+      if (i === 0) {
+        return n === 10 ? '00' : `${n}0`
+      } else {
+        return n === 10 ? '0' : n.toString()
+      }
+    } else {
+      return unicoder.codesets.negRound[n === 10 ? '0' : n.toString()]
+    }
+  },
+  total: (ns, bonus, { percent = false } = {}) => {
+    if (ns.length === 2 & percent) {
+      if (ns[0] === 0 && ns[1] === 0) return '100'
+      return ((ns[0] === 10 ? 0 : ns[0]) * 10 + (ns[1] % 10)).toString()
+    }
+    return ns.reduce((a, b) => a + b, bonus).toString()
+  },
+  bonusText: (x) => {
+    const {percent = false, values = []} = x || {}
+
+    if (values.length === 2 && !percent) return '_Did you mean "percentile"?_'
+  }
+}
+
+const d20 = {
+  stringify: n => unicoder.codesets.negRound[n],
+  total: (ns, bonus) => ns.reduce((a, b) => a + b, bonus).toString()
+}
+
+const ascii = {
+  stringify: n => n.toString(),
+  total: (ns, bonus) => ns.reduce((a, b) => a + b, bonus).toString()
+}
 
 class Roll {
   constructor(roll) {
@@ -11,14 +70,14 @@ class Roll {
     return this.values.reduce((a, b) => a + b, this.bonus || 0)
   }
   toString() {
-    let str = this.values.map(val => `[${val}]`).join(' + ')
+    let str = this.values.map((val, i) => this.formatter && this.formatter.stringify ? this.formatter.stringify(val, i, this.values.length, this) : `[${val}]`).join(' + ')
     if (this.bonus < 0) {
       str += ` - ${Math.abs(this.bonus)}`
     } else if (this.bonus > 0) {
       str += ` + ${this.bonus}`
     }
     if (this.values.length > 1 || this.bonus) {
-      str += ` = ${this.value()}`
+      str += ` = ${this.formatter && this.formatter.total ? this.formatter.total(this.values, this.bonus || 0, this) : this.value()}`
     }
     if (this.rerolls === 1) {
       str += ` with 1 reroll`
@@ -38,11 +97,19 @@ class BadRoll extends Roll {
   }
 }
 
-const random = n => Math.floor(Math.random() * n) + 1
+const random = (rng, n) => Math.floor(rng() * n) + 1
 
 class Dice {
   constructor(dice) {
     if (dice) Object.assign(this, dice)
+    if (!this.formatter) {
+      if (this.sides === 2) this.formatter = coin
+      else if (this.sides === 6) this.formatter = d6
+      else if (this.sides === 10) this.formatter = d10
+      else if (this.sides <= 20) this.formatter = d20
+      else this.formatter = ascii
+    }
+    if (!this.rng) this.rng = () => Math.random()
   }
   roll() {
     if (this.count > 100) return new BadRoll(`I lost count rolling ${this}`)
@@ -55,11 +122,11 @@ class Dice {
     let values = []
     for (let i = 0; i < this.count; i++) {
       if (this.modifier === 'advantage') {
-        values.push(Math.max(random(this.sides), random(this.sides)))
+        values.push(Math.max(random(this.rng, this.sides), random(this.rng, this.sides)))
       } else if (this.modifier === 'disadvantage') {
-        values.push(Math.min(random(this.sides), random(this.sides)))
+        values.push(Math.min(random(this.rng, this.sides), random(this.rng, this.sides)))
       } else {
-        values.push(random(this.sides))
+        values.push(random(this.rng, this.sides))
       }
     }
     let bonus = 0
@@ -73,7 +140,7 @@ class Dice {
       for (let d = 0; d < values.length; d++) {
         const v = values[d]
         if (this.rerollNumbers && this.rerollNumbers.includes(v)) {
-          values[d] = random(this.sides)
+          values[d] = random(this.rng, this.sides)
           foundAny = true
           rerolls++
         }
@@ -83,11 +150,20 @@ class Dice {
     return new Roll({
       values,
       bonus,
-      rerolls
+      rerolls,
+      percent: this.percent,
+      formatter: this.formatter
     })
   }
   toString() {
-    let str = `${this.count}d${this.sides}`
+    let str =
+      this.sides === 2
+      ? (
+        this.count === 1 ? 'a coin' : `${this.count} coins`
+      )
+      : (this.count === 2 && this.percent)
+      ? 'percentile dice'
+      :`${this.count}d${this.sides}`
     if (this.bonusSign) {
       str += this.bonusSign + this.bonus
     }
@@ -105,7 +181,7 @@ class Dice {
   }
 }
 
-Dice.parse = str => {
+Dice.parse = (str, opts) => {
   const rrRegex = rerollRegex()
   const rerolls = []
   const reroll = rrRegex.exec(str)
@@ -165,14 +241,24 @@ Dice.parse = str => {
 
   const aRegex = advRegex()
   const ad = aRegex.exec(str)
+  const dicebot = dicebotRegex().test(str)
   
   const modifier = ad ? (
     ad[1].toLowerCase()[0] === 'a' ? 'advantage' : 'disadvantage'
   ) : null
 
+  const percent = percentRegex().test(str)
+
   const regex = diceRegex()
   const ret = []
   let dice
+  if (percent) {
+    ret.push(new Dice({
+      count: 2,
+      sides: 10,
+      percent: true
+    }))
+  }
   while (dice = regex.exec(str)) {
     ret.push(new Dice({
       match: dice,
@@ -181,22 +267,33 @@ Dice.parse = str => {
       bonusSign: dice[3] || null,
       bonus: dice[4] === undefined ? null : parseFloat(dice[4]),
       rerollNumbers,
-      modifier
+      modifier,
+      percent,
+      dicebot,
+      rng: opts && opts.rng
     }))
   }
   return ret
 }
 
-Dice.chat = chat => {
-  const dice = Dice.parse(chat)
+Dice.chat = (chat, opts) => {
+  const dice = Dice.parse(chat, opts)
   if (dice.length) {
     const diceStrings = dice.map(x => x.toString())
     if (diceStrings.length > 1) {
       diceStrings[diceStrings.length - 1] = `and ${diceStrings[diceStrings.length - 1]}`
     }
-    const rollingString = `Rolling ${diceStrings.join(', ')}...`
-    const rollsStrings = dice.map(die => die.roll().toString())
-    return `${rollingString} ${rollsStrings.join(', ')}.`
+    const isCoin = dice.length === 1 && dice[0].sides === 2
+    const rollingString = `${isCoin ? 'Flipping' : 'Rolling'} ${diceStrings.join(', ')}...`
+    const rolls = dice.map(die => die.roll())
+    const rollsStrings = rolls.map(roll => roll.toString())
+    const bonusString = rolls
+      .filter(x => x.formatter && x.formatter.bonusText)
+      .map(x => x.formatter.bonusText(x))
+      .filter(x => x)
+      .map(x => '\n' + x)
+      .join('')
+    return `${rollingString} ${rollsStrings.join(', ')}.${bonusString}`
   }
 }
 

+ 5 - 1
package.json

@@ -5,6 +5,10 @@
   "license": "MIT",
   "dependencies": {
     "await-file": "^2.2.0",
-    "discord.js": "^11.4.2"
+    "cache": "^2.1.0",
+    "discord.js": "^11.4.2",
+    "fifo": "^2.3.0",
+    "random": "^2.0.12",
+    "seedrandom": "^2.4.4"
   }
 }

+ 16 - 0
test.js

@@ -0,0 +1,16 @@
+const db = require('./dice')
+const seedrandom = require('seedrandom')
+const sr = () => {
+  const rng = seedrandom(1)
+  return () => rng.double()
+}
+const n = 2
+Array(n).fill().map(x => console.log(db.Dice.chat('roll 2d10', {rng: sr()})))
+Array(n).fill().map(x => console.log(db.Dice.chat('roll 1d10', {rng: sr()})))
+Array(n).fill().map(x => console.log(db.Dice.chat('roll 3d10', {rng: sr()})))
+Array(n).fill().map(x => console.log(db.Dice.chat('roll 3d6', {rng: sr()})))
+Array(n).fill().map(x => console.log(db.Dice.chat('roll 1d6', {rng: sr()})))
+Array(n).fill().map(x => console.log(db.Dice.chat('roll 1d2', {rng: sr()})))
+Array(n).fill().map(x => console.log(db.Dice.chat('roll 5d2', {rng: sr()})))
+Array(n).fill().map(x => console.log(db.Dice.chat('roll 5d12', {rng: sr()})))
+Array(n).fill().map(x => console.log(db.Dice.chat('roll percentile', {rng: sr()})))

+ 223 - 0
unicoder.js

@@ -0,0 +1,223 @@
+const codesets = [
+  {
+    name: 'paren',
+    sets: [
+      { code: 0x1f110, ref: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' },
+      { code: 0x249c, ref: 'abcdefghijklmnopqrstuvwxyz' },
+      { code: 0x2474, ref: '123456789' }
+    ]
+  },
+  {
+    name: 'whiteSquare',
+    sets: [
+      { code: 0x1f130, ref: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' },
+      { code: 0x1f130, ref: 'abcdefghijklmnopqrstuvwxyz' }
+    ]
+  },
+  {
+    name: 'dice',
+    sets: [
+      { code: 0x2680, ref: '123456'}
+    ]
+  },
+  {
+    name: 'round',
+    sets: [
+      { code: 0x2460, ref: '123456789' },
+      { code: 0x24ea, ref: '0' },
+      { code: 0x24b6, ref: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' },
+      { code: 0x24d0, ref: 'abcdefghijklmnopqrstuvwxyz' }
+    ],
+    '10': String.fromCodePoint(0x2469),
+    '11': String.fromCodePoint(0x246a),
+    '12': String.fromCodePoint(0x246b),
+    '13': String.fromCodePoint(0x246c),
+    '14': String.fromCodePoint(0x246d),
+    '15': String.fromCodePoint(0x246e),
+    '16': String.fromCodePoint(0x246f),
+    '17': String.fromCodePoint(0x2470),
+    '18': String.fromCodePoint(0x2471),
+    '19': String.fromCodePoint(0x2472),
+    '20': String.fromCodePoint(0x2473)
+  },
+  {
+    name: 'recycling',
+    sets: [
+      { code: 0x2673, ref: '1234567' }
+    ]
+  },
+  {
+    name: 'negRound',
+    sets: [
+      { code: 0x1f150, ref: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' },
+      { code: 0x1f150, ref: 'abcdefghijklmnopqrstuvwxyz' },
+      { code: 0x2776, ref: '123456789'},
+      { code: 0x24ff, ref: '0' }
+    ],
+    '10': String.fromCodePoint(0x277f),
+    '11': String.fromCodePoint(0x24eb),
+    '12': String.fromCodePoint(0x24ec),
+    '13': String.fromCodePoint(0x24ed),
+    '14': String.fromCodePoint(0x24ee),
+    '15': String.fromCodePoint(0x24ef),
+    '16': String.fromCodePoint(0x24f0),
+    '17': String.fromCodePoint(0x24f1),
+    '18': String.fromCodePoint(0x24f2),
+    '19': String.fromCodePoint(0x24f3),
+    '20': String.fromCodePoint(0x24f4)
+  },
+  {
+    name: 'negSquare',
+    sets: [
+      { code: 0x1f170, ref: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' },
+      { code: 0x1f170, ref: 'abcdefghijklmnopqrstuvwxyz' }
+    ]
+  },
+  {
+    name: 'indicator',
+    sets: [
+      { code: 0x1f1e6, ref: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' },
+      { code: 0x1f1e6, ref: 'abcdefghijklmnopqrstuvwxyz' }
+    ]
+  },
+  {
+    name: 'doubleStruck',
+    sets: [
+      { code: 0x1d538, ref: 'AB DEFG IJKLM O   STUVWXY ' },
+      { code: 0x1d552, ref: 'abcdefghijklmnopqrstuvwxyz' },
+      { code: 0x1d7d8, ref: '0123456789'},
+      { code: 0x2102, ref: 'C' },
+      { code: 0x210D, ref: 'H' },
+      { code: 0x2115, ref: 'N' },
+      { code: 0x2119, ref: 'P' },
+      { code: 0x211A, ref: 'Q' },
+      { code: 0x211D, ref: 'R' },
+      { code: 0x2124, ref: 'Z' }
+    ]
+  },
+  {
+    name: 'fraktur',
+    sets: [
+      { code: 0x1d56c, ref: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' },
+      { code: 0x1d586, ref: 'abcdefghijklmnopqrstuvwxyz' }
+    ]
+  },
+  {
+    name: 'sansSerif',
+    sets: [
+      { code: 0x1d5a0, ref: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' },
+      { code: 0x1d5ba, ref: 'abcdefghijklmnopqrstuvwxyz' },
+      { code: 0x1d7e2, ref: '0123456789'}
+    ]
+  },
+  {
+    name: 'bold',
+    sets: [
+      { code: 0x1d400, ref: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' },
+      { code: 0x1d41a, ref: 'abcdefghijklmnopqrstuvwxyz' },
+      { code: 0x1d7ce, ref: '0123456789' }
+    ]
+  },
+  {
+    name: 'sansSerifBold',
+    sets: [
+      { code: 0x1d5d4, ref: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' },
+      { code: 0x1d5ee, ref: 'abcdefghijklmnopqrstuvwxyz' },
+      { code: 0x1d7ce, ref: '0123456789' }
+    ]
+  },
+  {
+    name: 'italic',
+    sets: [
+      { code: 0x1d434, ref: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' },
+      { code: 0x1d622, ref: 'abcdefghijklmnopqrstuvwxyz' }
+    ]
+  },
+  {
+    name: 'sansSerifItalic',
+    sets: [
+      { code: 0x1d608, ref: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' },
+      { code: 0x1d622, ref: 'abcdefghijklmnopqrstuvwxyz' }
+    ]
+  },
+  {
+    name: 'boldItalic',
+    sets: [
+      { code: 0x1d468, ref: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' },
+      { code: 0x1d482, ref: 'abcdefghijklmnopqrstuvwxyz' }
+    ]
+  },
+  {
+    name: 'sansSerifBoldItalic',
+    sets: [
+      { code: 0x1d63c, ref: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' },
+      { code: 0x1d656, ref: 'abcdefghijklmnopqrstuvwxyz' }
+    ]
+  },
+  {
+    name: 'monospace',
+    sets: [
+      { code: 0x1d670, ref: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' },
+      { code: 0x1d68a, ref: 'abcdefghijklmnopqrstuvwxyz' },
+      { code: 0x1d7f6, ref: '0123456789' }
+    ]
+  },
+  {
+    name: 'script',
+    sets: [
+      { code: 0x1d49c, ref: 'A CD  G  JK  NOPQ STUVWXYZ' },
+      { code: 0x1d4b6, ref: 'abcd f hijklmn pqrstuvwxyz' }
+    ]
+  },
+  {
+    name: 'circles',
+    black: String.fromCodePoint(0x26ab),
+    white: String.fromCodePoint(0x26aa),
+    red: String.fromCodePoint(0x1f534),
+    blue: String.fromCodePoint(0x1f535)
+  }
+]
+codesets.forEach(cs => {
+  codesets[cs.name] = cs
+
+  if (cs.sets) {
+    cs.sets.forEach(set => {
+      set.ref.split('').forEach((c, i) => {
+        if (c !== ' ') {
+          cs[c] = String.fromCodePoint(set.code + i)
+        }
+      })
+    })
+  }
+})
+
+const convertAlpha = (codeset, string) => {
+  if (typeof codeset === 'string' || typeof codeset === 'number') codeset = codesets[codeset]
+  if (!codeset) return null
+  const codes = []
+  for (let i = 0; i < string.length; i++) {
+    const char = string[i]
+    if (char === ' ') {
+      codes.push(32)
+      continue
+    }
+    let found = false
+    for (var c = 0; c < codeset.sets.length; c++) {
+      const set = codeset.sets[c]
+      const index = set.ref.indexOf(char)
+      if (index !== -1) {
+        codes.push(set.code + index)
+        found = true
+        break
+      }
+    }
+    if (!found) codes.push(char.charCodeAt())
+  }
+  
+  return String.fromCodePoint(...codes)
+}
+
+module.exports = {
+  codesets,
+  convertAlpha
+}

+ 52 - 0
yarn.lock

@@ -14,6 +14,14 @@ await-file@^2.2.0:
   dependencies:
     rimraf "^2.5.2"
 
+babel-runtime@^6.26.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
+  integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
+  dependencies:
+    core-js "^2.4.0"
+    regenerator-runtime "^0.11.0"
+
 balanced-match@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
@@ -27,11 +35,21 @@ brace-expansion@^1.1.7:
     balanced-match "^1.0.0"
     concat-map "0.0.1"
 
+cache@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/cache/-/cache-2.1.0.tgz#ec970ce99a3d1f12e191e0abb02dedc71d2743c8"
+  integrity sha1-7JcM6Zo9HxLhkeCrsC3txx0nQ8g=
+
 concat-map@0.0.1:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
   integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
 
+core-js@^2.4.0:
+  version "2.6.0"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.0.tgz#1e30793e9ee5782b307e37ffa22da0eacddd84d4"
+  integrity sha512-kLRC6ncVpuEW/1kwrOXYX6KQASCVtrh1gQr/UiaVgFlf9WE5Vp+lNe5+h3LuMr5PAucWnnEXwH0nQHRH/gpGtw==
+
 discord.js@^11.4.2:
   version "11.4.2"
   resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-11.4.2.tgz#54586981926521572051f2a30b984aad2b49786e"
@@ -43,6 +61,11 @@ discord.js@^11.4.2:
     tweetnacl "^1.0.0"
     ws "^4.0.0"
 
+fifo@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/fifo/-/fifo-2.3.0.tgz#182de8dd0632aa47cf6816d9bc4323317d5259dc"
+  integrity sha1-GC3o3QYyqkfPaBbZvEMjMX1SWdw=
+
 fs.realpath@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -92,6 +115,16 @@ once@^1.3.0:
   dependencies:
     wrappy "1"
 
+ow-lite@^0.0.2:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/ow-lite/-/ow-lite-0.0.2.tgz#df9f500e6740b6590a1e9a965730d49a8eb597d1"
+  integrity sha1-359QDmdAtlkKHpqWVzDUmo61l9E=
+
+ow@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/ow/-/ow-0.4.0.tgz#119a846d23738ce11677331fef8a3fbe019eb05b"
+  integrity sha512-kJNzxUgVd6EF5LoGs+s2/etJPwjfRDLXPTCfEgV8At77sRrV+PSFA8lcoW2HF15Qd455mIR2Stee/2MzDiFBDA==
+
 path-is-absolute@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
@@ -102,6 +135,20 @@ prism-media@^0.0.3:
   resolved "https://registry.yarnpkg.com/prism-media/-/prism-media-0.0.3.tgz#8842d4fae804f099d3b48a9a38e3c2bab6f4855b"
   integrity sha512-c9KkNifSMU/iXT8FFTaBwBMr+rdVcN+H/uNv1o+CuFeTThNZNTOrQ+RgXA1yL/DeLk098duAeRPP3QNPNbhxYQ==
 
+random@^2.0.12:
+  version "2.0.12"
+  resolved "https://registry.yarnpkg.com/random/-/random-2.0.12.tgz#8b33686639fd26959b40ff11f9186a43c1272f3c"
+  integrity sha1-izNoZjn9JpWbQP8R+RhqQ8EnLzw=
+  dependencies:
+    babel-runtime "^6.26.0"
+    ow "^0.4.0"
+    ow-lite "^0.0.2"
+
+regenerator-runtime@^0.11.0:
+  version "0.11.1"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
+  integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
+
 rimraf@^2.5.2:
   version "2.6.2"
   resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
@@ -114,6 +161,11 @@ safe-buffer@~5.1.0:
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
   integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
 
+seedrandom@^2.4.4:
+  version "2.4.4"
+  resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-2.4.4.tgz#b25ea98632c73e45f58b77cfaa931678df01f9ba"
+  integrity sha512-9A+PDmgm+2du77B5i0Ip2cxOqqHjgNxnBgglxLcX78A2D6c2rTo61z4jnVABpF4cKeDMDG+cmXXvdnqse2VqMA==
+
 snekfetch@^3.6.4:
   version "3.6.4"
   resolved "https://registry.yarnpkg.com/snekfetch/-/snekfetch-3.6.4.tgz#d13e80a616d892f3d38daae4289f4d258a645120"