6 <div id=
"elo-content"></div>
7 <script type=
"text/javascript">
11 for (var i
= 0, l
= arr
.length
; i
< l
; ++i
) {
12 if (Object
.prototype.hasOwnProperty
.call(u
, arr
[i
])) continue;
23 function EloPlayer() {
24 this.score
= initScore
;
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
);
43 this.elo
= new EloPlayer();
51 this.root
= document
.getElementById(eltId
);
56 var e
= document
.createElement('div');
61 function Sequence(items
) {
66 Sequence
.prototype.next = function () {
67 if (this.current
>= this.items
.length
) return null;
69 return this.items
[this.current
++];
72 function getPairs(items
) {
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
]]);
84 X
.prototype.moveTo = function (stage
, arg
) {
90 this.items
= getItems(arg
).map(function (s
) { return new Item(s
) });
91 this.sequence
= new Sequence(getPairs(this.items
));
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 () {
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';
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
); });
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
);
153 X
.prototype.renderCompare = function () {
156 this.root
.innerHTML
= '';
158 var item
= this.sequence
.next();
161 this.moveTo(display
);
168 if (Math
.random() < .5) {
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 () {
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)));
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();
209 case compare
: this.renderCompare();
211 case display
: this.renderDisplay();
219 var v
= new X('elo-content');