-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathindex.html
199 lines (173 loc) · 8.09 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon"
href="https://expectorant.yootl.es/assets/slot-machine-32.ico" />
<title>Expectorant</title>
<!-- Meta tags for SEO and Open Graph link previews -->
<link rel="canonical" href="https://expectorant.yootl.es/" />
<meta name="description"
content="Random bill splitter and generalized dice roller. Fairness in expectation!" />
<meta name="robots" content="index,follow" />
<meta property="og:title" content="Expectorant" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://expectorant.yootl.es/" />
<meta property="og:description"
content="Generalized dice-roller / Bernoulli-trialler. Exquisite fairness in expectation!" />
<meta property="og:image"
content="https://expectorant.yootl.es/assets/slot-machine-32.ico" />
<meta name="twitter:card" content="summary" />
<script type="text/javascript" id="MathJax-script" async
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js">
</script>
<link rel="stylesheet" href="style.css" />
<script src="main.js" defer></script>
<script src="expectorant.js" defer></script>
<script src="spinner.js" defer></script>
</head>
<body>
<div class='versiontag'>v2023.11.15b </div>
<div class="content">
<h1>Expectorant</h1>
<details open id="beginner-mode">
<summary style="cursor: pointer"><i>beginner mode</i></summary>
<div>
<h2>Exact Change: Repayment with Cash</h2>
<input type="number" class="exact-change" id="expr-repay-owe" type="text" size="80" placeholder="You owe this much"/>
<input type="number" class="exact-change" id="expr-repay-note1" type="text" size="80" placeholder="You have this note"/>
<input type="number" class="exact-change" id="expr-repay-note2" type="text" size="80" placeholder="Optional: you also have this note"/>
</div>
<br>
</details>
<div>
<h2>All payment types</h2>
<input id="expr" type="text" size="80"
placeholder="Expression that evaluates to a probability"/>
<br>
<span id="prob-wrap">p=<span id="prob">0%</span></span>
<span id="counter-wrap">n=<span id="counter">0</span></span>
</div>
<br>
<button id="expectorize"><span class="button-text">Expectorize</span></button>
<audio id="audiotag1" preload="auto"
src="https://expectorant.yootl.es/assets/godmachine3.wav">
</audio>
<div class="spin-wrap"> <!-- initially contains placeholder for spinner -->
<div id="spinneroo">
<svg xmlns="http://www.w3.org/2000/svg"
class="spin" width="100%" height="100%" viewbox="0 0 100 100"></svg>
</div>
<br>
<b><center id="theanswer"> </center></b>
</div>
<input type="checkbox" id="mute" name="mute"/><label for="mute">Mute</label><br>
<details open id="instructions">
<summary style="cursor: pointer"><i>instructions</i></summary>
<p>
Expectorant is a stochastic, nerdtastic restaurant bill splitting app and random number spinner.
Just <i>enter a probability</i> between 0 and 1 or an expression that evaluates to a probability (more on that below) and press <i>Expectorize</i>.
</p>
<h2>Basic use cases</h2>
<p>
Say you owe me $7 for lunch but only have a twenty.
If you give me the twenty with probability 7/20 (35%) then in expectation you've paid me $7!
So type in <code>7/20</code> and spin the spinner.
If it comes up green then I lucked out and get the $20.
</p>
<p>
Or of course there's what the Soule-Reeveses call pumpkin-patching or skyootling where instead of paying someone the $X you owe them, you pay them 10X but with only 1/10 probability, and nothing otherwise.
That's handy for reducing transactional overhead: 90% of the time you don't have to make any money change hands or ledger anything.
It especially saves time if the amount owed hasn't actually been computed yet.
As long as you <i>can</i> unambiguously compute it if/when the 10% expectorization happens, nine times out of ten you don't have to bother.
</p>
<h2>Exact (expected) change
— <code>@</code> syntax</h2>
<p>
Suppose you owe me an especially awkward amount like $8.27 and you have a five and a twenty.
Paying me $5 is <i>kind of</i> close so if you probably give the five and maybe give me the twenty then that could be fair.
Can we make it exactly fair?
Yes we can!
Enter the amount you owe, an @-sign, followed by two amounts
— the first lower than you owe and the second greater.
Expectorant turns that “probably” and “maybe” into exact probabilities.
In this example you'd type
<code>8.27@5,20</code>
which gets turned into
$$\frac{8.27-5}{20-5} \approx 22\%$$
which is the probability \(p\) such that
\((1-p)\cdot 5 + p\cdot 20 = 8.27\).
That's the exquisitely fair probability \(p\) vs \(1-p\) that you should give me the twenty vs the five so that you're giving me exactly $8.27 in expectation!
</p>
<h2>Stochastic, nerdtastic restaurant bill splitting
— <code>:</code> syntax</h2>
<p>
Say the subtotal for your restaurant bill is $100 and the items on the bill are $5, $25, $60, and $10.
Enter <code>100:5</code> and have the person who ordered the $5 item spin the spinner.
If it comes up green (a 5% chance) they get to pay the whole bill!
If not, amend the expression as <code>100:5,25</code> and repeat for the person who got the $25 item.
They'll “win” with probability \(\frac{25}{100-5}\).
If they're off the hook, amend again to <code>100:5,25,60</code>.
This time most likely,
\(p = \frac{60}{100-5-25} \approx 86\%\),
the $60 person will win the honor of paying the bill.
If not, notice that
<code>100:5,25,60,10</code> yields
\(\frac{10}{100-5-25-60} = 1\).
So if the process makes it to the last item on the bill then whoever got that item is it.
</p>
<p>
Mathemagically, it doesn't matter what order you put the items in — each person “wins” (pays the whole bill) with probability equal to their own fair share of the bill.
In other words, you pay in expectation exactly your fair share.
<i>Including tax and tip</i>, even though we never entered those.
Pretty slick!
</p>
<p>
Speaking of tips, you can minimize the hassle by starting with the most expensive items.
Then you don't have to figure out who most of the items belong to.
</p>
<p>
What if three people split the $16
<span id="vittle">nondescript nachos with broken javascript</span>?
Just treat it as 3 items, costing $16/3 each.
And you can literally type the prices as <code>16/3</code> since
Expectorant speaks arithmetic.
</p>
<h2>Credits</h2>
<p>
Built by Christopher Moravec, Bethany Soule, and Daniel Reeves.
Based on an Android app that Soule and Reeves published in 2010 (!) and that's
technically still in Google's app store but doesn't work on modern phones.
Thanks to Dave Pennock who invented the restaurant bill-splitting algorithm.
UI enhancements by Daniel Zwell.
See
<a href="http://messymatters.com/expectorant" title="Stochastic, Nerdtastic Restaurant Bill Splitting on Danny and Sharad's old Messy Matters blog">the original blog post</a>
for more.
</p>
</details>
</div> <!-- end content -->
<script>
// -----------------------------------------------------------------------------
// Stick this Javascript at the end of your html and it will stick a warning at
// the top of the page if it's loaded on an ancient browser that doesn't support
// modern Javascript.
function ancient_browser_check() {
"use strict" // does using "use strict" matter?
try { eval("let foo = (x)=>x+1") } // feature detection: "let" & arrow funcs
catch (e) { return true } // bad browser, not understanding ES6
return false // good browser, not being ancient
}
if (ancient_browser_check()) {
//document.body.insertAdjacentHTML('afterbegin', '<h1>' + s + '</h1>')
// "<h1>Ancient browser warning: nothing's gonna work here!</h1>")
// That works but the following version miiight be safer for ancient browsers:
var body = document.body;
var x = document.createElement("h1");
x.innerHTML = "Ancient browser warning: nothing's gonna work here!";
body.insertBefore(x, body.firstChild);
}
// -----------------------------------------------------------------------------
</script>
</body>
</html>