-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.html
202 lines (201 loc) · 8.38 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
200
201
202
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Blabrecs</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: Georgia, serif;
font-size: 18px;
margin: 0 auto;
max-width: 80ch;
text-align: center;
}
header {
margin-bottom: 2rem;
margin-top: 2rem;
}
main {
margin-bottom: 2rem;
}
a:hover {
background: black;
color: white;
}
#wordtester {
font-family: Georgia, serif;
font-size: 3rem;
margin-bottom: 0.5rem;
text-align: center;
width: 100%;
}
#wordtester:not(:placeholder-shown) {
text-transform: uppercase;
}
#wordinfo {
font-size: 16px;
margin-bottom: 0.5rem;
}
#wordinfo.ok {
color: forestgreen;
}
#wordinfo.err {
color:darkred;
}
.buttons {
margin-bottom: 2rem;
}
button {
background: #555;
border: none;
border-radius: 0.5rem;
color: white;
font-family: Georgia, serif;
font-size: 1.25rem;
padding: 0.25rem 0.5rem;
}
button:not(:enabled) {
cursor: not-allowed;
opacity: 50%;
}
#playit:enabled {
background: forestgreen;
}
table {
border-collapse: collapse;
width: 100%;
}
#lexicon.disabled {
display: none;
}
th {
background: #555;
color: white;
}
tr:nth-child(odd) {
background: #eee;
}
td:first-child {
font-size: 1.5rem;
text-transform: uppercase;
width: 25%;
}
td:last-child {
width: 75%;
}
textarea {
width: 100%;
}
.longdesc {
line-height: 1.4;
margin: 0 auto;
margin-top: 5rem;
max-width: 60ch;
padding: 0 2ch;
text-align: left;
}
.longdesc h2, .longdesc p {
margin-bottom: 1rem;
}
#loggingheader {
background: cornflowerblue;
border-radius: 0.5rem;
font-size: 0.8rem;
line-height: 1.4;
margin: 1rem;
margin-bottom: 4rem;
padding: 0.5rem;
color: white;
}
#loggingheader a {
color: white;
}
#loggingheader a:hover {
background: none;
color: #eee;
}
</style>
</head>
<body>
<div id="loggingheader">
As part of the <a href="https://neurips-creative-ai.github.io/">NeurIPS 2023 Creative AI exhibition</a>, we're anonymously logging the words and definitions that BLABRECS players create. Our favorites will be showcased. If you want, you can <a href="#" id="optOutLink">opt out of logging</a> or <a href="#" id="dismissLoggingHeaderLink">dismiss this notice</a>.
</div>
<header>
<h1>BLABRECS</h1>
<p>it's like scrabble but worse</p>
</header>
<main>
<input type="text" id="wordtester" placeholder="test a word...">
<p id="wordinfo">can't play that, it's a real word!</p>
<div class="buttons">
<button id="playit">Play It</button>
</div>
<div class="buttons">
<button id="usemarkov" disabled>Use Markov Classifier</button>
<button id="useneural">Use Neural Classifier</button>
</div>
<table id="lexicon" class="disabled">
<tr>
<th>Word</th>
<th>Meaning</th>
</tr>
</table>
<div class="longdesc">
<h2>What's all this, then?</h2>
<p><strong>BLABRECS</strong> is a rules modification for the wordgame SCRABBLE that swaps out the dictionary of real-if-obscure English words for a capricious artificial intelligence. In BLABRECS, real English words aren't allowed! Instead, you have to play nonsense words that <em>sound</em> like English to the AI. These nonsense words are called – you guessed it – BLABRECS.</p>
<h2>How do I play?</h2>
<p>Get together your regular SCRABBLE supplies and pull up this page in a web browser. Then play SCRABBLE as normal, but before you play a word, use the "test a word..." box at the top of the page to check whether the AI will let you play it. Remember, you can only play words that the AI approves!</p>
<p>When you find a legal word to play, hit the "Play It" button to add this word to the lexicon. As you play, you can write in definitions for all the words you've invented in the "Meaning" textbox next to each word.<!--The player who comes up with the best definition for a word can add half of that word's points to their SCRABBLE score at the end of the game.--></p>
<h2>I found a real word that the AI thinks is playable! What should I do?</h2>
<p>This happens often with proper nouns (which SCRABBLE generally disallows) and with inflected forms of base words that are disallowed. I think you should play by the spirit of BLABRECS and not the letter, but what this means is ultimately up to you. Is the game primarily about exploring the vast "shadow English" implied by the statistical distribution of letter sequences, or is it about the inherent absurdity of an external authority presuming to dictate your language to you? Either interpretation seems valid to me.</p>
<h2>How does the AI work?</h2>
<p>The current version of BLABRECS has two AI judges that you can switch between. <a href="https://github.com/mkremins/blabrecs">Source code</a> for both is available if you want to learn more. You can also check out our <a href="https://mkremins.github.io/publications/Blabrecs_NeurIPS2023.pdf">NeurIPS 2023 paper</a> for a detailed writeup.</p>
<p>The original judge uses a Markov chain trained on the <a href="https://www.wordgamedictionary.com/enable/">ENABLE word list</a> used in a number of wordgames. It looks at the statistical patterns of letter sequences in English words and uses this information to determine how likely a sequence of letters is to be a real English word. Then it rejects both real dictionary words and fake words that it deems insufficiently plausible.</p>
<p>The second judge, contributed by <a href="https://isaackarth.com">Isaac Karth</a>, uses a convolutional neural network trained on a substantially larger word list. The features that this judge uses to evaluate words are a bit more opaque, but still ultimately statistical in nature.</p>
<p>I might update BLABRECS to provide a wider range of AI "opponents" using a variety of different technologies in the future. Stay tuned!</p>
<h2>Why is it called BLABRECS?</h2>
<p>I generated every possible permutation of the word SCRABBLE and asked an early version of the AI to sort them from least to most statistically likely. BRABLECS won out by a significant margin, beating not only the real word SCRABBLE but also my hand-designed previous title BESCRALB. Then I switched the L and the R because BLABRECS sounds better and I'm not about to let a computer tell me what to do.</p>
<h2>Who are you?</h2>
<p>I'm <a href="https://mkremins.github.io">Max Kreminski</a>, an artifical intelligence researcher and game designer. I make a lot of weird stuff with AI; if you want to keep tabs on my work, you can <a href="https://twitter.com/maxkreminski">follow me on Twitter</a>. If you enjoyed BLABRECS, you can also <a href="https://mkremins.itch.io/blabrecs">leave me a tip</a> via the "Download Now" button on itch.io.</p>
</div>
</main>
</body>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@2.8.0/dist/tf.min.js"></script>
<script src="out/main.js"></script>
<script>
// Clickable links in logging header
let loggingEnabled = true;
optOutLink.onclick = () => {
loggingEnabled = false;
loggingheader.innerHTML = `Logging disabled. <a href="#" id="dismissLoggingHeaderLink">Dismiss this notice.</a>`;
dismissLoggingHeaderLink.onclick = () => { loggingheader.remove(); }
}
dismissLoggingHeaderLink.onclick = () => { loggingheader.remove(); }
// Log words on page leave
const formID = "1FAIpQLSeqEZUsnrIcPMe8zkEhWq6SF-l-fy0Cxq6mIYXVRrzCiq5A2Q";
const formURL = `https://docs.google.com/forms/d/e/${formID}/formResponse`;
const sessionIDField = "entry.1836109556";
const sessionDataField = "entry.1577951434";
const sessionID = crypto.randomUUID();
document.addEventListener("visibilitychange", () => {
if (document.visibilityState !== "hidden") return;
if (!loggingEnabled) return; // skip logging if user manually disabled it
const defs = [...lexicon.rows].slice(1).map(row => {
return [row.children[0].innerText, row.children[1].children[0].value];
});
if (defs.length === 0) return;
const sessionData = JSON.stringify(defs);
const formParams = new URLSearchParams([
[sessionIDField, sessionID],
[sessionDataField, sessionData],
]);
navigator.sendBeacon(formURL, formParams);
});
</script>
</html>