Permalink
Browse files

A terrible idea is born

  • Loading branch information...
xori committed Jul 17, 2017
0 parents commit b51b56604e5611a75db4b4702282550b91d903f7
Showing with 160 additions and 0 deletions.
  1. +6 −0 .gitignore
  2. +2 −0 .npmignore
  3. +28 −0 README.md
  4. BIN assets/TestResults.xlsx
  5. BIN assets/graph.png
  6. +33 −0 index.js
  7. +37 −0 package.json
  8. +54 −0 tests/uniform.js
@@ -0,0 +1,6 @@
node_modules/
.*.swp
.DS_Store
config.gypi
npm-debug.log
@@ -0,0 +1,2 @@
tests/*
assets/*
@@ -0,0 +1,28 @@
# The Gambler's Fallacy Dice
> The term *Gambler's fallacy* refers to a misconception about statistics. [...]
In statistics, a random event has a certain probability of occurring. The
fallacy is that if the event has occurred less frequently in the past, it
will be more frequent in the future.
-[Wikipedia](https://simple.wikipedia.org/wiki/Gambler%27s_fallacy)
Well no longer is this a fallacy my friends, these dice are real! If you roll a
20 sided die, and you haven't seen a 20 in a while it is **statistically more
likely** to show up in the next roll with these dice. And the best part, it's
still *uniformly random* for large sample sets!
obligatory chart
![a stupid chart](./assets/graph.png)
## How to Use
```bash
$ npm install --save gamblers-dice
```
```javascript
const RiggedDie = require('gamblers-dice')
const die = new RiggedDie(20) // for a d20
console.log(die.roll()) // 1 -> 20
```
Binary file not shown.
BIN +6.01 KB assets/graph.png
Binary file not shown.
@@ -0,0 +1,33 @@
class Die {
constructor(size) {
size = size || 6
this.state = []
for (let i = 0; i < size; i++) {
this.state[i] = 1
}
}
roll() {
// [ 1, 3, 2]
// [0-1, 1-4, 4-6]
const sum = this.state.reduce((p, c) => p + c, 0)
const r = Math.random() * sum
let runningSum = 0
let result = -1
for (let i = 0; i < this.state.length; i++) {
const mark = this.state[i]
runningSum += mark
if(r < runningSum && result === -1) {
result = i
this.state[i] = 1
} else {
this.state[i]++
}
}
// Add 1, so the die roll is between 1 -> size of die
return (result + 1)
}
}
module.exports = Die
@@ -0,0 +1,37 @@
{
"name": "gamblers-dice",
"version": "1.0.0",
"description": "This set of dice *follows* the Gambler's fallacy.",
"main": "index.js",
"directories": {
"test": "tests"
},
"scripts": {
"test": "tap tests/*"
},
"repository": {
"type": "git",
"url": "git+https://github.com/xori/gamblers-dice.git"
},
"engines": {
"node": ">=4.1.0"
},
"keywords": [
"dice",
"random",
"gambler",
"fallacy",
"terrible-idea",
"monte",
"carlo"
],
"author": "Evan Verworn",
"license": "ISC",
"bugs": {
"url": "https://github.com/xori/gamblers-dice/issues"
},
"homepage": "https://github.com/xori/gamblers-dice#readme",
"devDependencies": {
"tap": "^10.7.0"
}
}
@@ -0,0 +1,54 @@
let BadDie = require('../index.js')
let t = require('tap')
t.test('is uniformly random eventually', function(t) {
let gamblersDie = new BadDie(6)
let result = [0, 0, 0, 0, 0, 0]
// roll a million times and tabulate
for (var i = 0; i < 1000000; i++) {
result[gamblersDie.roll() - 1]++
}
// what the average should be
const avg = 1000000 / 6
// fail if any deviates from the average by more than 0.05%
// anything more and it's suspicious and worth investagating
for (var i = 0; i < result.length; i++) {
t.ok(Math.abs((result[i] - avg) / 1000000) <= 0.0005,
`suspiciously not random. face:${i} ${((result[i] - avg) / 1000000 * 100).toFixed(3)}%`)
}
t.end()
})
t.test('is *consistently* uniformly random eventually', function(t) {
for(let run = 0; run < 10; run++) {
let gamblersDie = new BadDie(6)
let result = [0, 0, 0, 0, 0, 0]
// roll a million times and tabulate
for (var i = 0; i < 1000000; i++) {
result[gamblersDie.roll() - 1]++
}
// what the average should be
const avg = 1000000 / 6
// fail if any deviates from the average by more than 0.05%
// anything more and it's suspicious and worth investagating
for (var i = 0; i < result.length; i++) {
t.ok(Math.abs((result[i] - avg) / 1000000) <= 0.0005,
`suspiciously not random. face:${i} ${((result[i] - avg) / 1000000 * 100).toFixed(2)}%`)
}
}
t.end()
})
t.test('doesn\'t explode if large numbers are used.', function(t) {
let gamblersDie = new BadDie(600)
t.doesNotThrow(gamblersDie.roll.bind(gamblersDie), "The dice exploded.")
t.end()
})
t.test('defaults exist', function(t) {
let gamblersDie = new BadDie()
t.doesNotThrow(gamblersDie.roll.bind(gamblersDie), "The dice exploded.")
t.end()
})

0 comments on commit b51b566

Please sign in to comment.