teslo
Version:
Elo rating system
3 lines (2 loc) • 5.87 kB
JavaScript
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,i=this.getExpectedResult(s);return Math.round(this.elo+o*(a-i))}}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(e),this.name=t}}const a={MATCH_COMPLETE:"Match completed",MATCH_INCOMPLETE:"Match incomplete",MAX_PLAYERS:"Cannot add more players",MIN_PLAYERS:"Need more players",MAX_TEAMS:"Cannot add more teams",MIN_TEAMS:"Need more teams",SIZE_MISMATCH:"Calculation does not match contestant size in match",PLAYER_NOT_FOUND:"Player not found",TEAM_NOT_FOUND:"Team not found",MISSING_OPPONENT_ELO:"Could not find opponent elo"};class i extends o{constructor(t){super("MATCH_ERROR",t)}}class r{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 i(a.MATCH_COMPLETE);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 i(a.MATCH_COMPLETE);this._contestants.set(t.id,t)}getResults(){if(!this._completed)throw new i(a.MATCH_INCOMPLETE);return this.contestantMapToResults()}}class c extends r{constructor(t,e){super(Object.assign({minContestants:2,maxContestants:2},e)),t&&this.addPlayers(...t)}static create(t,e){return new c(t,e)}addPlayer(t){if(2===this.size)throw new i(a.MAX_PLAYERS);return this.addContestant(t),this}addPlayers(...t){return t.forEach((t=>this.addPlayer(t))),this}calculate(t){if(this.completed)throw new i(a.MATCH_COMPLETE);if(2!==this.size)throw new i(a.MIN_PLAYERS);if(!this.contestants.has(t))throw new i(a.PLAYER_NOT_FOUND);const e=this.contestantMapToEloMap();for(const[s,n]of this.contestants){const o=n,r=this.findOpponentElos(s,e);if(0===r.length)throw new i(a.MISSING_OPPONENT_ELO);const c=o.calculate(r[0],t===s,this.kFactor);o.elo=c}return this.completed=!0,this.contestantMapToResults()}}class l extends r{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 l(t,e)}addPlayer(t){if(this.size===this.maxContestants)throw new i(a.MAX_PLAYERS);return this.addContestant(t),this}addPlayers(...t){return t.forEach((t=>this.addPlayer(t))),this}calculate(t){if(this.completed)throw new i(a.MATCH_COMPLETE);if(this.size<this.minContestants)throw new i(a.MIN_PLAYERS);if(this.size!==t.length)throw new i(a.SIZE_MISMATCH);const e=this.contestantMapToEloMap();for(let s=0;s<t.length;s++){const n=this.contestants.get(t[s]);if(!n)throw new i(a.PLAYER_NOT_FOUND);let o=0;for(let r=0;r<t.length;r++)if(s!==r){const c=e.get(t[r]);if(void 0===c)throw new i(a.MISSING_OPPONENT_ELO);o+=n.calculate(c,r>s,this.kFactor)}n.elo=Math.floor(o/(t.length-1))}return this.completed=!0,this.contestantMapToResults()}}class h extends r{constructor(t,e){super(Object.assign({minContestants:2,maxContestants:2},e)),t&&this.addTeams(...t)}static create(t,e){return new h(t,e)}addTeam(t){if(2===this.size)throw new i(a.MAX_TEAMS);return this.addContestant(t),this}addTeams(...t){return t.forEach((t=>this.addTeam(t))),this}calculate(t){if(this.completed)throw new i(a.MATCH_COMPLETE);if(2!==this.size)throw new i(a.MIN_TEAMS);if(!this.contestants.has(t))throw new i(a.TEAM_NOT_FOUND);const e=this.contestantMapToEloMap();for(const[s,n]of this.contestants){const o=n,r=this.findOpponentElos(s,e);if(0===r.length)throw new i(a.MISSING_OPPONENT_ELO);for(const[,e]of o.players){const n=e.calculate(r[0],t===s,this.kFactor);e.elo=n}}return this.completed=!0,this.contestantMapToResults()}}class d extends r{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 d(t,e)}addTeam(t){if(this.size===this.maxContestants)throw new i(a.MAX_TEAMS);return this.addContestant(t),this}addTeams(...t){return t.forEach((t=>this.addTeam(t))),this}calculate(t){if(this.completed)throw new i(a.MATCH_COMPLETE);if(this.size<this.minContestants)throw new i(a.MIN_TEAMS);if(this.size!==t.length)throw new i(a.SIZE_MISMATCH);const e=this.contestantMapToEloMap();for(let s=0;s<t.length;s++){const n=this.contestants.get(t[s]);if(!n)throw new i(a.TEAM_NOT_FOUND);for(const[,o]of n.players){let n=0;for(let r=0;r<t.length;r++)if(s!==r){const c=e.get(t[r]);if(void 0===c)throw new i(a.MISSING_OPPONENT_ELO);n+=o.calculate(c,r>s,this.kFactor)}o.elo=Math.floor(n/(t.length-1))}}return this.completed=!0,this.contestantMapToResults()}}export{c as Duel,a as ErrorType,l as FreeForAll,i as MatchError,s as Player,n as Team,h as TeamDuel,d as TeamFreeForAll};
//# sourceMappingURL=index.mjs.map