Hexagonal two-type tiles
[zzandy.git] / elo.html
blob43123abe085b4217341aca392c1cbce864e29e35
1 <html>
2 <head>
3 <title>Elo</title>
4 </head>
5 <body>
6 <div id="elo-content"></div>
7 <script type="text/javascript">
9 function unique(arr) {
10 var u = {}, a = [];
11 for (var i = 0, l = arr.length; i < l; ++i) {
12 if (Object.prototype.hasOwnProperty.call(u, arr[i])) continue;
13 a.push(arr[i]);
14 u[arr[i]] = 1;
16 return a;
19 var initScore = 1400;
20 var x10dif = 400;
21 var kFactor = 10;
23 function EloPlayer() {
24 this.score = initScore;
25 this.numMatches = 0;
28 function eloMatch(winner, looser, tie) {
29 winner.Update(looser, tie ? .5 : 1);
30 looser.Update(winner, tie ? .5 : 0);
33 EloPlayer.prototype.Update = function (other, result) {
34 var expected = 1 / (1 + Math.pow(10, (this.score - other.score) / x10dif));
36 var delta = kFactor * (result - expected);
37 this.score = Math.max(0, this.score + delta);
38 this.numMatches++;
41 function Item(name) {
42 this.name = name;
43 this.elo = new EloPlayer();
46 var collect = {};
47 var compare = {};
48 var display = {};
50 function X(eltId) {
51 this.root = document.getElementById(eltId);
52 this.stage = collect;
55 function div(elt) {
56 var e = document.createElement('div');
57 e.appendChild(elt);
58 return e;
61 function Sequence(items) {
62 this.items = items;
63 this.current = 0;
66 Sequence.prototype.next = function () {
67 if (this.current >= this.items.length) return null;
69 return this.items[this.current++];
72 function getPairs(items) {
73 var a = [];
75 for (var i = 0; i < items.length; ++i) {
76 for (var j = i + 1; j < items.length; ++j) {
77 a.push([items[i], items[j]]);
81 return a;
84 X.prototype.moveTo = function (stage, arg) {
85 switch (stage) {
86 case collect:
87 this.items = null;
88 break;
89 case compare:
90 this.items = getItems(arg).map(function (s) { return new Item(s) });
91 this.sequence = new Sequence(getPairs(this.items));
92 break;
93 case display:
94 this.sequence = null;
95 break;
98 this.stage = stage;
99 this.render();
102 function fac(n) {
103 return n <= 1 ? 1 : n * fac(n - 1);
106 function numPairs(n) {
107 return fac(n) / (fac(n - 2) * 2);
110 function getItems(text) {
111 return unique(text.split('\n').map(function (s) { return s.trim(); }).filter(function (s) { return s.length > 0 }));
114 X.prototype.updateItemsList = function (text, elt, nfo) {
115 var lines = getItems(text);
116 elt.disabled = lines.length < 2;
118 var n = numPairs(lines.length);
119 nfo.innerHTML = lines.length + ' items, ' + n + ' pairs';
122 X.prototype.renderCollect = function () {
123 var self = this;
124 this.root.innerHTML = '';
125 var ta = document.createElement('textarea');
126 var ok = document.createElement('button');
127 var nfo = document.createElement('div');
129 ta.placeholder = 'Enter pairs';
130 ta.rows = 10;
131 ta.addEventListener('keyup', function () { self.updateItemsList(ta.value, ok, nfo) });
133 ok.appendChild(document.createTextNode('Done'));
134 ok.addEventListener('click', function () { self.moveTo(compare, ta.value); });
135 ok.disabled = true;
137 var bottom = div(nfo);
138 bottom.appendChild(ok);
140 this.root.appendChild(div(ta))
141 this.root.appendChild(bottom);
144 function button(text, code) {
145 var b = document.createElement('button');
147 b.appendChild(document.createTextNode(text));
148 b.addEventListener('click', code);
150 return b;
153 X.prototype.renderCompare = function () {
155 var self = this;
156 this.root.innerHTML= '';
158 var item = this.sequence.next();
160 if (item == null) {
161 this.moveTo(display);
162 return;
165 var a = item[0];
166 var b = item[1];
168 if (Math.random() < .5) {
169 var c = a;
170 a = b;
171 b = c;
174 console.log(a, b);
176 var left = button(a.name, function () { eloMatch(a.elo, b.elo, false); self.render() });
177 var tie = button('tie', function () { eloMatch(a.elo, b.elo, true); self.render() });
178 var right = button(b.name, function () { eloMatch(b.elo, a.elo, false); self.render() });
180 this.root.appendChild(left);
181 this.root.appendChild(tie);
182 this.root.appendChild(right);
183 this.root.appendChild(div(document.createTextNode(this.sequence.current + '/' + this.sequence.items.length)));
186 X.prototype.renderDisplay = function () {
187 var self = this;
188 this.root.innerHTML = '';
190 var list = document.createElement('ol');
192 this.items.sort(function (a, b) { return b.elo.score - a.elo.score });
194 this.items.forEach(function (item, n) {
195 var e = document.createElement('li');
196 e.appendChild(document.createTextNode(item.name + ' ' + item.elo.score.toFixed(2)));
197 list.appendChild(e);
200 this.root.appendChild(list);
201 this.root.appendChild(button('Again', function(){self.moveTo(collect)}));
204 X.prototype.render = function () {
206 switch (this.stage) {
207 case collect: this.renderCollect();
208 break;
209 case compare: this.renderCompare();
210 break;
211 case display: this.renderDisplay();
212 break;
219 var v = new X('elo-content');
220 v.render();
222 </script>
223 </body>
224 </html>