teslo
Version:
Elo rating system
3 lines (2 loc) • 5.28 kB
JavaScript
"use strict";const t=1,e=0;class s{constructor(t,e){this.id=t,this.elo=e}static create(t,e){return new s(t,e)}getExpectedResult(t){return 1/(1+Math.pow(10,(t-this.elo)/400))}calculate(s,n,o){const a=n?t:e,r=this.getExpectedResult(s);return Math.round(this.elo+o*(a-r))}}class n{constructor(t,e){this.id=t,this.players=new Map,e&&this.addPlayers(...e)}static create(t,e){return new n(t,e)}addPlayer(t){return this.players.set(t.id,t),this}addPlayers(...t){return t.forEach((t=>this.addPlayer(t))),this}getAverageElo(){if(0===this.players.size)return 0;let t=0;for(const e of this.players.values())t+=e.elo;return t/this.players.size}}class o extends Error{constructor(t,e){super(),this.name=t,this.message=e}}const a="Match completed",r="Match incomplete",i="Cannot add more players",l="Need more players",c="Cannot add more teams",h="Need more teams",d="Calculation does not match contestant size in match",u="Player not found",p="Team not found",f="Could not find opponent elo";class m extends o{constructor(t){super("MATCH_ERROR",t)}}class w{constructor(t){var e,s,n;this._contestants=new Map,this._completed=!1,this.minContestants=null!==(e=null==t?void 0:t.minContestants)&&void 0!==e?e:2,this.maxContestants=null!==(s=null==t?void 0:t.maxContestants)&&void 0!==s?s:256,this.kFactor=null!==(n=null==t?void 0:t.kFactor)&&void 0!==n?n:32}get contestants(){return this._contestants}get size(){return this._contestants.size}get completed(){return this._completed}set completed(t){if(this._completed)throw new m(a);this._completed=t}contestantMapToEloMap(){const t=new Map;for(const[e,o]of this._contestants)o instanceof s?t.set(e,o.elo):o instanceof n&&t.set(e,o.getAverageElo());return t}contestantMapToResults(){const t=[];for(const[e,o]of this._contestants)if(o instanceof s){const s={id:e,elo:o.elo};t.push(s)}else if(o instanceof n){const s={id:e,players:[...o.players.values()].map((t=>({id:t.id,elo:t.elo})))};t.push(s)}return t}findOpponentElos(t,e){const s=[];for(const[n,o]of e)n!==t&&s.push(o);return s}addContestant(t){if(this._completed)throw new m(a);this._contestants.set(t.id,t)}getResults(){if(!this._completed)throw new m(r);return this.contestantMapToResults()}}class g extends w{constructor(t,e){super(Object.assign({minContestants:2,maxContestants:2},e)),t&&this.addPlayers(...t)}static create(t,e){return new g(t,e)}addPlayer(t){if(2===this.size)throw new m(i);return this.addContestant(t),this}addPlayers(...t){return t.forEach((t=>this.addPlayer(t))),this}calculate(t){if(this.completed)throw new m(a);if(2!==this.size)throw new m(l);const e=this.contestantMapToEloMap();for(const[s,n]of this.contestants){const o=n,a=this.findOpponentElos(s,e);if(0===a.length)throw new m(f);const r=o.calculate(a[0],t===s,this.kFactor);o.elo=r}return this.completed=!0,this.contestantMapToResults()}}class y extends w{constructor(t,e){super(Object.assign({minContestants:null==e?void 0:e.minPlayers,maxContestants:null==e?void 0:e.maxPlayers},e)),t&&this.addPlayers(...t)}static create(t,e){return new y(t,e)}addPlayer(t){if(this.size===this.maxContestants)throw new m(i);return this.addContestant(t),this}addPlayers(...t){return t.forEach((t=>this.addPlayer(t))),this}calculate(t){if(this.completed)throw new m(a);if(this.size<this.minContestants)throw new m(l);if(this.size!==t.length)throw new m(d);const e=this.contestantMapToEloMap();for(let s=0;s<t.length;s++){const n=this.contestants.get(t[s]);if(!n)throw new m(u);let o=0;for(let a=0;a<t.length;a++)if(s!=a){const r=e.get(t[a]);if(!r)throw new m(f);o+=n.calculate(r,a>s,this.kFactor)}n.elo=Math.floor(o/(t.length-1))}return this.completed=!0,this.contestantMapToResults()}}class C extends w{constructor(t,e){super(Object.assign({minContestants:2,maxContestants:2},e)),t&&this.addTeams(...t)}static create(t,e){return new C(t,e)}addTeam(t){if(2===this.size)throw new m(c);return this.addContestant(t),this}addTeams(...t){return t.forEach((t=>this.addTeam(t))),this}calculate(t){if(this.completed)throw new m(a);if(2!==this.size)throw new m(h);const e=this.contestantMapToEloMap();for(const[s,n]of this.contestants){const o=n,a=this.findOpponentElos(s,e);if(0===a.length)throw new m(f);for(const[,e]of o.players){const n=e.calculate(a[0],t===s,this.kFactor);e.elo=n}}return this.completed=!0,this.contestantMapToResults()}}class M extends w{constructor(t,e){super(Object.assign({minContestants:null==e?void 0:e.minTeams,maxContestants:null==e?void 0:e.maxTeams},e)),t&&this.addTeams(...t)}static create(t,e){return new M(t,e)}addTeam(t){if(this.size===this.maxContestants)throw new m(c);return this.addContestant(t),this}addTeams(...t){return t.forEach((t=>this.addTeam(t))),this}calculate(t){if(this.completed)throw new m(a);if(this.size<this.minContestants)throw new m(h);if(this.size!==t.length)throw new m(d);const e=this.contestantMapToEloMap();for(let s=0;s<t.length;s++){const n=this.contestants.get(t[s]);if(!n)throw new m(p);for(const[,o]of n.players){let n=0;for(let a=0;a<t.length;a++)if(s!=a){const r=e.get(t[a]);if(!r)throw new m(f);n+=o.calculate(r,a>s,this.kFactor)}o.elo=Math.floor(n/(t.length-1))}}return this.completed=!0,this.contestantMapToResults()}}exports.Duel=g,exports.FreeForAll=y,exports.Player=s,exports.Team=n,exports.TeamDuel=C,exports.TeamFreeForAll=M;
//# sourceMappingURL=index.js.map