const _CHANGE_MIN_PERCENT_PODS_ = 1715590917;const ValType = {"Mode":0,"N0":1,"N1":2,"Bool":3,"I":4,"JSON":5};const ConfigMap = {"PointMode":0,"WinPoints":2,"DrawPoints":1,"ByePoints":1,"MaxRoundPoints":2,"RoundTime":2,"DraftPodSize":2,"DraftTime":2,"TableStart":2,"BestOf":2,"ByeMode":0,"IncludeDiscords":3,"PlayerIdentifier":0,"SelfReport":3,"RequireConfirm":3,"HideStandings":3,"ShowDecks":3,"DeckSubmission":3,"Invite":3,"Event":3,"StartPoints":1,"LossPoints":4,"MinGames":1,"FlawlessPoints":2,"ManualConfirm":3,"Breakers":5,"PodSize":2};const ConfigDefaults = {"PointMode":"Standard","PlayerIdentifier":"Name","BestOf":3,"TableStart":1,"ByeMode":"Byes","IncludeDiscords":false,"SelfReport":false,"RequireConfirm":false,"HideStandings":false,"ShowDecks":false,"DeckSubmission":true,"Invite":false,"Event":false,"StartPoints":0,"LossPoints":0,"MinGames":0,"ManualConfirm":false,"Breakers":"legacy","PodSize":4};const BreakerTypes = {"MPTS":{"name":"Match Points","label":"Match Points"},"GWR":{"name":"Game Win Rate","label":"Game Win %"},"OWR":{"name":"Opponent Win Rate (points/max)","label":"Opp Win %"},"OGWR":{"name":"Opponent Game Win Rate (points/max)","label":"Opp Game Win %"},"SR":{"name":"Win Percentage (wins/plays)","label":"Win %"},"OSR":{"name":"Opponent Win Percentage (wins/plays)","label":"Opp Win %"},"OBEAT":{"name":"Opponents Beaten","label":null},"UOPP":{"name":"Unique Opponents","label":null},"GPTS":{"name":"Game Points","label":"Total Points"},"GPP":{"name":"Game Point Percentage","label":"Total Points %"}};const MasterGameLibrary = {"Magic: The Gathering":{"__label":"Magic: The Gathering","__searchTerms":["MTG","Magic","Magic: The Gathering","Magic the Gathering"],"formats":{"EDH":{"label":"MTG: Commander","value":"MTG: Commander","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":5,"DrawPoints":1,"BestOf":1,"RoundTime":80}}},"Pauper EDH":{"label":"MTG: Pauper Commander","value":"MTG: Pauper Commander","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":5,"DrawPoints":1,"BestOf":1,"RoundTime":80}}},"Standard":{"label":"MTG: Standard","value":"MTG: Standard","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}},"Pioneer":{"label":"MTG: Pioneer","value":"MTG: Pioneer","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}},"Modern":{"label":"MTG: Modern","value":"MTG: Modern","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}},"Legacy":{"label":"MTG: Legacy","value":"MTG: Legacy","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}},"Pauper":{"label":"MTG: Pauper","value":"MTG: Pauper","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}},"Vintage":{"label":"MTG: Vintage","value":"MTG: Vintage","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}},"Premodern":{"label":"MTG: Premodern","value":"MTG: Premodern","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}},"Limited":{"label":"MTG: Limited","value":"MTG: Limited","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}},"Duel Commander":{"label":"MTG: Duel Commander","value":"MTG: Duel Commander","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}},"Old School 93/94":{"label":"MTG: Old School 93/94","value":"MTG: Old School 93/94","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}},"Canadian Highlander":{"label":"MTG: Canadian Highlander","value":"MTG: Canadian Highlander","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}},"Tiny Leaders":{"label":"MTG: Tiny Leaders","value":"MTG: Tiny Leaders","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}},"EDH Draft":{"label":"MTG: Commander Draft","value":"MTG: Commander Draft","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":5,"DrawPoints":1,"BestOf":1,"RoundTime":80}}},"Timeless":{"label":"MTG: Timeless","value":"MTG: Timeless","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}},"Historic":{"label":"MTG: Historic","value":"MTG: Historic","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}},"Explorer":{"label":"MTG: Explorer","value":"MTG: Explorer","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}},"7pt Highlander":{"label":"MTG: 7pt Highlander","value":"MTG: 7pt Highlander","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}},"Oathbreaker":{"label":"MTG: Oathbreaker","value":"MTG: Oathbreaker","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":5,"DrawPoints":1,"BestOf":1,"RoundTime":80}}},"Other 1v1":{"label":"MTG: Other 1v1","value":"MTG: Other 1v1","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}}}},"Pokemon":{"__label":"Pokemon","__searchTerms":["Pokemon","PTCG","Pokemon TCG","Pokemon Trading Card Game"],"formats":{"Standard":{"label":"Pokemon: Standard","value":"Pokemon: Standard","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}},"Expanded":{"label":"Pokemon: Expanded","value":"Pokemon: Expanded","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}},"Legacy":{"label":"Pokemon: Legacy","value":"Pokemon: Legacy","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}},"GLC":{"label":"Pokemon: GLC","value":"Pokemon: GLC","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":1,"RoundTime":40}}}}},"Yu-Gi-Oh":{"__label":"Yu-Gi-Oh","__searchTerms":["Yugioh","YGO","Yu-Gi-Oh!"],"formats":{"Advanced":{"label":"Yu-Gi-Oh: Advanced","value":"Yu-Gi-Oh: Advanced","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":45}}},"Edison":{"label":"Yu-Gi-Oh: Edison","value":"Yu-Gi-Oh: Edison","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":45}}},"Goat":{"label":"Yu-Gi-Oh: Goat","value":"Yu-Gi-Oh: Goat","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":45}}},"Domain":{"label":"Yu-Gi-Oh: Domain","value":"Yu-Gi-Oh: Domain","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":5,"DrawPoints":1,"BestOf":1,"RoundTime":80}}}}},"Star Wars Unlimited":{"__label":"Star Wars Unlimited","__searchTerms":["Star Wars","SWU","SW Unlimited"],"formats":{"Premier":{"label":"Star Wars Unlimited: Premier","value":"Star Wars Unlimited: Premier","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":55}}},"Draft":{"label":"Star Wars Unlimited: Draft","value":"Star Wars Unlimited: Draft","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":55}}},"Twin Suns":{"label":"Star Wars Unlimted: Twin Suns","value":"Star Wars Unlimted: Twin Suns","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":5,"DrawPoints":1,"BestOf":1,"RoundTime":80}}}}},"Lorcana":{"__label":"Lorcana","__searchTerms":["Disney Lorcana","Lorcana TCG"],"formats":{"":{"label":"Lorcana","value":"Lorcana","__startup":{"UCONFIG":{"PointMode":"Flawless","WinPoints":3,"DrawPoints":1,"BestOf":2,"RoundTime":50}}}}},"One Piece":{"__label":"One Piece","__searchTerms":["OPTCG","One Piece TCG","One Piece Card Game"],"formats":{"":{"label":"One Piece","value":"One Piece","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":1,"RoundTime":40}}}}},"Digimon":{"__label":"Digimon","__searchTerms":["DTCG","Digimon TCG","Digimon Card Game"],"formats":{"":{"label":"Digimon","value":"Digimon","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}}}},"Marvel Snap":{"__label":"Marvel Snap","__searchTerms":["Snap"],"formats":{"":{"label":"Marvel Snap","value":"Marvel Snap","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":1,"RoundTime":50}}}}},"Shadowverse: Evolve":{"__label":"Shadowverse: Evolve","__searchTerms":["Shadowverse"],"formats":{"":{"label":"Shadowverse: Evolve","value":"Shadowverse: Evolve","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}}}},"Flesh and Blood":{"__label":"Flesh and Blood","__searchTerms":["FAB","Flesh & Blood"],"formats":{"":{"label":"Flesh and Blood","value":"Flesh and Blood","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}}}},"Sorcery: Contested Realm":{"__label":"Sorcery: Contested Realm","__searchTerms":["Sorcery","Contested Realm"],"formats":{"":{"label":"Sorcery: Contested Realm","value":"Sorcery: Contested Realm","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}}}},"Altered":{"__label":"Altered","__searchTerms":["Altered TCG"],"formats":{"Standard":{"label":"Altered: Standard","value":"Altered: Standard","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":1,"RoundTime":50}}},"Draft":{"label":"Altered: Booster Draft","value":"Altered: Booster Draft","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":1,"RoundTime":50}}}}},"Beyblade":{"__label":"Beyblade","__searchTerms":["Beyblade, Beyblade X","3v3","5v5"],"formats":{"3v3":{"label":"Beyblade: 3v3","value":"Beyblade: 3v3","__startup":{"UCONFIG":{"PointMode":"ScoreBased","WinPoints":1,"DrawPoints":0,"BestOf":3,"RoundTime":50,"Breakers":["MPTS","OWR","GPTS"]}}},"5v5":{"label":"Beyblade: 5v5","value":"Beyblade: 5v5","__startup":{"UCONFIG":{"PointMode":"ScoreBased","WinPoints":1,"DrawPoints":0,"BestOf":3,"RoundTime":50,"Breakers":["MPTS","OWR","GPTS"]}}}}},"Settlers of Catan":{"__label":"Settlers of Catan","__searchTerms":["Settlers of Catan","The Settlers of Catan","Catan"],"formats":{"":{"label":"Settlers of Catan","value":"Settlers of Catan","__startup":{"UCONFIG":{"PointMode":"ScoreBased","WinPoints":1,"DrawPoints":0,"BestOf":1,"RoundTime":80,"Breakers":["MPTS","GPTS","GPP"]}}}}},"Gudnak":{"__label":"Gudnak","__searchTerms":["Gudnak"],"formats":{"":{"label":"Gudnak","value":"Gudnak","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":1,"RoundTime":35}}}}},"Other":{"__label":"Other Games","__searchTerms":["Other"],"formats":{"1v1":{"label":"Other (1v1)","value":"Other 1v1","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}},"4p Swiss":{"label":"Other (4p Swiss)","value":"Other 4p Swiss","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":5,"DrawPoints":1,"BestOf":1,"RoundTime":80}}}}}};function ArrayShuffle(array) { for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } return array; }function ArrayCount(array, element){ var counter = 0; for (ele of array) { if (ele == element) { counter++; } }; return counter; }function HI(){ return ValType.N1; }function TDATA(data={}){const ret = {"TEST":"FC"};Object.assign(ret, data);ret.Config = function Config(key, val=undefined){ const ckey = "C:"+key; if(val===undefined){ if (this.hasOwnProperty(ckey)){ return this[ckey]; }else{ if (ConfigDefaults[key] == "legacy"){ //Breakers const ARR = []; if (this.G("Mode") == "Pairs"){ ARR.push("MPTS"); ARR.push("OWR"); ARR.push("GWR"); ARR.push("OGWR"); }else{ //default "Pod" ARR.push("MPTS"); if (this.G("League")){ ARR.push("SR"); ARR.push("OSR"); }else{ ARR.push("OWR"); } ARR.push("OBEAT"); ARR.push("UOPP"); } return ARR; } return ConfigDefaults[key]; } }else{ this["C:"+key] = val; } };ret.G = function G(key){ const G = this.Game; const F = this.Format; if (key == "Mode"){ if (["Settlers of Catan"].includes(G)){ return "Pods"; } if (["EDH", "Pauper EDH", "EDH Draft", "Domain", "4p Swiss", "Twin Suns"].includes(F)){ return "Pods"; } return "Pairs"; }else if (key == "Draft"){ if (["Limited", "EDH Draft", "Draft"].includes(F)){ return true; } return false; }else if (key == "League"){ return this["S1:C:Type"] == "LFG"; } };ret.E = function E(E){ return this["E"+E+":P1"]; };ret.P4E = function P4E(E){ if (this["E"+E+":P1"] == "_x_"){ return { name: "Deleted", discord: "Deleted", discordId: "Deleted", modo: "Deleted", DisplayName: "Deleted", } } const P = this["E"+E+":P1"]; const ret = this.Players[P] || {}; if (this["P:"+P]){ ret.tlock = this["P:"+P].tlock; } if (this.Config("PlayerIdentifier")){ switch(this.Config("PlayerIdentifier")) { case "Name": ret.DisplayName = ret.name; break; case "Discord": ret.DisplayName = (ret.discord !== undefined && ret.discord !== null && ret.discord !== '') ? ret.discord : ret.name; break; case "Modo": ret.DisplayName = (ret.modo !== undefined && ret.modo !== null && ret.modo !== '') ? ret.modo : ret.name; break; default: ret.DisplayName = ret.name; break; } } else if (this.Config("IncludeDiscords")){ ret.DisplayName = (ret.discord !== undefined && ret.discord !== null && ret.discord !== '') ? ret.discord : ret.name; } else { ret.DisplayName = ret.name; } const BADGE_COLOR = '#5A4F9E'; if (ret.pronouns && ret.pronouns !== '') { ret.DisplayName = ` ${ret.DisplayName} ${ret.pronouns} `; } return ret; };ret.E4P = function E4P(P){ var EDEX = 1; while(this.hasOwnProperty("E"+EDEX+":P1")){ if (this["E"+EDEX+":P1"] == P){ return EDEX; } EDEX++; } return 0; };ret.EntityRender = function EntityRender(withStanding=true, supressFin=false){ var ret = [null]; var EDEX = 1; while (this.hasOwnProperty("E"+EDEX+":P1")){ const init = {P:this["E"+EDEX+":P1"]}; init.points = this.Config("StartPoints"); init.opponentHistory = []; init.gamesPlayed = 0; if (this.Config("PointMode") != "Flawless"){ init.gamesWon = 0; init.gamesDrawn = 0; } init.luckLevel = 0; init.byesGotten = 0; init.standing = EDEX; init.E = EDEX; init.DS = this.DropStatus(EDEX)>0?1:0; if (this.G("Mode") == "Pairs"){ if (this.Config("PointMode") == "ScoreBased"){ init.littlePoints = 0; init.littlePointPercents = 0; }else{ init.littleGamesPlayed = 0; init.littleGamesWon = 0; init.littleGamesDrawn = 0; } }else{ if (this.Config("PointMode") == "ScoreBased"){ init.littlePoints = 0; init.littlePointPercents = 0; } init.opponentsBeat = []; } ret.push(init); EDEX++; } const FlawlessPoints = this.Config("FlawlessPoints"); const WinPoints = this.Config("WinPoints"); const ByePoints = this.ByePoints(); const DrawPoints = this.Config("DrawPoints"); const LossPoints = this.Config("LossPoints"); const RenderTable = (table)=>{ if (table.Mute) return; if (this.G("Mode") == "Pairs"){ if (this.PairGrade(table) <= 5) return; }else{ if (this.Config("PointMode") == "ScoreBased" || this.Config("PointMode") == "Custom"){ if (!table.Points) return; }else{ if (!table.Winner) return; } } if (table.Winner == "_x_") return; if (this.G("Mode") == "Pairs"){ const P1E = table.Es[0]; const P2E = table.Es[1]; ret[P1E].gamesPlayed++; ret[P2E].gamesPlayed++; ret[P1E].opponentHistory.push(ret[P2E]); ret[P2E].opponentHistory.push(ret[P1E]); if (table.Points){ ret[P1E].littlePoints += table.Points[0]; ret[P2E].littlePoints += table.Points[1]; }else{ ret[P1E].littleGamesPlayed += table.Wins[0] + table.Wins[1] + table.Draws; ret[P2E].littleGamesPlayed += table.Wins[0] + table.Wins[1] + table.Draws; ret[P1E].littleGamesWon += table.Wins[0]; ret[P2E].littleGamesWon += table.Wins[1]; ret[P1E].littleGamesDrawn += table.Draws; ret[P2E].littleGamesDrawn += table.Draws; } if (this.Config("PointMode") == "Flawless"){ ret[P1E].points += table.Wins[0] * WinPoints; ret[P2E].points += table.Wins[1] * WinPoints; if (this.PairGrade(table) == 10){ ret[P1E].points += FlawlessPoints; }else if (this.PairGrade(table) == 8){ ret[P2E].points += FlawlessPoints; } }else{ if (this.PairGrade(table) >= 9){ ret[P1E].gamesWon++; ret[P1E].points += WinPoints; ret[P2E].points += LossPoints; }else if (this.PairGrade(table) >= 7){ ret[P2E].gamesWon++; ret[P1E].points += LossPoints; ret[P2E].points += WinPoints; }else if (this.PairGrade(table) == 6){ ret[P1E].gamesDrawn++; ret[P2E].gamesDrawn++; ret[P1E].points += DrawPoints; ret[P2E].points += DrawPoints; } } }else{ const TablePoints = (()=>{ if (!table.Points) return 0; var sum = 0; for (let p of table.Points){ sum += p; } return Math.round(sum * (this.Config("PodSize")/table.Points.length)); })() for (var dex = 0; dex < table.Es.length; dex++){ const E = table.Es[dex]; ret[E].gamesPlayed++; for(OE of table.Es){ if (E != OE){ ret[E].opponentHistory.push(ret[OE]); } } ret[E].luckLevel += table.Es.length - 1 - dex; if (table.Points){ const MeDex = table.Es.indexOf(E); ret[E].littlePoints += table.Points[MeDex]; ret[E].littlePointPercents += table.Points[MeDex] / TablePoints; if (this.Config("PointMode") == "Custom"){ const Max = this.Config("MaxRoundPoints"); if (table.Points && table.Points.length >= table.Es.length){ if(table.Points[MeDex] >= Max){ ret[E].gamesWon++; } ret[E].points += table.Points[MeDex]; //~ for(E of table.Es){ //~ if (E != table.Winner){ //~ ret[table.Winner].opponentsBeat.push(ret[E]); //~ ret[E].points += LossPoints; //~ } //~ } } } if (this.Config("PointMode") == "ScoreBased"){ const top = Math.max(...table.Points); const winners = []; for (let p = 0; p < table.Points.length; p++){ if (table.Points[p] == top) winners.push(table.Es[p]); } const MyPoints = table.Points[MeDex]; if (winners.includes(E)){ if (winners.length == 1){ ret[E].gamesWon++; ret[E].points += WinPoints; for(let OE of table.Es){ if (E != OE){ ret[E].opponentsBeat.push(ret[OE]); ret[OE].points += LossPoints; } } }else{ ret[E].gamesDrawn++; ret[E].points += DrawPoints; for(let OE of table.Es){ if (!winners.includes(OE)){ ret[E].opponentsBeat.push(ret[OE]); ret[OE].points += LossPoints; } } } } } }else{ if (table.Winner == "_DRAW_"){ if (!table.Losers.includes(E)){ ret[E].gamesDrawn++; ret[E].points += DrawPoints; } } } } if (table.Winner && table.Winner != "_DRAW_"){ ret[table.Winner].gamesWon++; ret[table.Winner].points += WinPoints; for(let E of table.Es){ if (E != table.Winner){ ret[table.Winner].opponentsBeat.push(ret[E]); ret[E].points += LossPoints; } } } } } this.ITER({}, (SDEX)=>{ if (this["S"+SDEX+":C:Type"] == "GRP"){ this.ITER({SDEX}, (RDEX)=>{ if (this.hasOwnProperty("S"+SDEX+":R"+RDEX+":End")){ this.ITER({SDEX, RDEX}, (TDEX)=>{ const table = this["S"+SDEX+":R"+RDEX+":T"+TDEX]; RenderTable(table); }); if (this.hasOwnProperty("S"+SDEX+":R"+RDEX+":TB")){ for (E of this["S"+SDEX+":R"+RDEX+":TB"].Es){ ret[E].byesGotten++; ret[E].points += ByePoints; if (this.Config("PointMode") == "Flawless"){ ret[E].points += this.Config("WinPoints") * Math.ceil(this.Config("BestOf")*0.51); } } } if (this.hasOwnProperty("S"+SDEX+":R"+RDEX+":TL")){ for (E of this["S"+SDEX+":R"+RDEX+":TL"]){ ret[E].gamesPlayed++; } } } }) }else if (this["S"+SDEX+":C:Type"] == "LFG"){ this.ITER({SDEX, RDEX:null}, (TDEX)=>{ const table = this["S"+SDEX+":T"+TDEX]; RenderTable(table); }); } }) ret.shift(); if (withStanding){ for(let dex = ret.length-1; dex >= 0; dex--){ if (this.End){ delete ret[dex].DS; }else{ if (ret[dex].P == "_x_"){ ret.splice(dex, 1); } } } if (this.End && supressFin==false){ if (this.G("Mode") == "Pairs"){ var Fins = []; const SDEX = this.ITER(); if (this["S"+SDEX+":C:Type"] == "BRKT"){ const RDEX = this.ITER({SDEX}); for(let R = RDEX; R > 0; R--){ const ChunkW = []; const ChunkL = []; var TDEX = 1; this.ITER({SDEX, RDEX:R}, (TDEX)=>{ const table = this["S"+SDEX+":R"+R+":T"+TDEX]; var WinE; var LossE; if (this.PairGrade(table) >= 9){ WinE = ret[table.Es[0]-1]; LossE = ret[table.Es[1]-1]; }else if (this.PairGrade(table) >= 7){ WinE = ret[table.Es[1]-1]; LossE = ret[table.Es[0]-1]; } if (!Fins.includes(WinE)){ ChunkW.push(WinE); } if (!Fins.includes(LossE)){ ChunkL.push(LossE); } }); ChunkW.sort(this.SortByRanking.bind(this)); ChunkL.sort(this.SortByRanking.bind(this)); Fins = Fins.concat(ChunkW); Fins = Fins.concat(ChunkL); } } ret.sort(this.SortByRanking.bind(this)); for (let E of ret){ if (E.P != "_x_" && !Fins.includes(E)){ Fins.push(E); } } ret = Fins; }else{ var Fins = []; const SDEX = this.ITER(); if (this["S"+SDEX+":C:Type"] == "BRKT"){ const RDEX = this.ITER({SDEX}); for(let R = RDEX; R > 0; R--){ const Chunk = []; this.ITER({SDEX, RDEX:R}, (TDEX)=>{ const table = this["S"+SDEX+":R"+R+":T"+TDEX]; const WinE = table.Winner; if (!Fins.includes(ret[WinE-1])){ Chunk.push(ret[WinE-1]); } }); if (this.hasOwnProperty("S"+SDEX+":R"+R+":TB")){ for (let Bye of this["S"+SDEX+":R"+R+":TB"].Es){ if (!Fins.includes(ret[Bye-1])){ Chunk.push(ret[Bye-1]); } } } Chunk.sort(this.SortByRanking.bind(this)); Fins = Fins.concat(Chunk); } } ret.sort(this.SortByRanking.bind(this)); for (let E of ret){ if (E.P != "_x_" && !Fins.includes(E)){ Fins.push(E); } } ret = Fins; } }else{ ret.sort(this.SortByRanking.bind(this)); } for(var s = 0; s < ret.length; s++){ ret[s].standing = s+1; } } return ret; };ret.PerformActions = async function PerformActions(_ACTS, CBS={}){ if(!Array.isArray(_ACTS)){ _ACTS = [_ACTS]; } for(let ACTS of _ACTS){ //~ console.log("ACTS", ACTS); const Set =(key, val)=>{ this[key] = val; if (CBS.OnSet) CBS.OnSet(key, val); } const Del = (key)=>{ delete this[key]; if (CBS.OnDelete) CBS.OnDelete(key); } if (ACTS.UCONFIG){ for(Config of Object.entries(ACTS.UCONFIG)){ if (ConfigDefaults[Config[0]] == Config[1]){ if (this.hasOwnProperty("C:" + Config[0])){ Del("C:" + Config[0]); } }else{ Set("C:" + Config[0], Config[1]); } } } if (ACTS.UEVENT) { const { Name, StartDate, EndDate, DeckDeadline } = ACTS.UEVENT; if (Name !== undefined && Name !== this.Name) { Set("Name", Name); } if (StartDate !== undefined && StartDate != this.StartDate) { Set("StartDate", StartDate); } if (EndDate !== undefined && EndDate != this.EndDate) { Set("EndDate", EndDate); } if (DeckDeadline !== undefined && DeckDeadline != this.DeckDeadline){ Set("DeckDeadline", DeckDeadline); } } if (ACTS.UTIER) { const Tier = ACTS.UTIER; if (!this.Tier2025 && Tier) { Set("Tier2025", Tier); } } if (Array.isArray(ACTS.ENEW)){ var EDEX = 1; while(this.hasOwnProperty("E" + EDEX + ":P1")){ const dex = ACTS.ENEW.indexOf(this["E" + EDEX + ":P1"]); if (dex > -1){ ACTS.ENEW.splice(dex, 1); } EDEX++; } for (ADD of ACTS.ENEW){ Set("E" + EDEX + ":P1", ADD); EDEX++; } } if (Array.isArray(ACTS.EDEL)){ var EDEX = 1; while(this.hasOwnProperty("E" + EDEX + ":P1")){ if (ACTS.EDEL.includes(this["E" + EDEX + ":P1"])){ Set("E" + EDEX + ":P1", "_x_"); } EDEX++; } } //~ const DVAL = (E, Key, Val)=>{ //~ var DEX = 1; //~ while(true){ //~ if (!this["E"+E+":D:"+Key+DEX]){ //~ break; //~ } //~ DEX++; //~ } //~ var Set = []; //~ if (Array.isArray(Val)){ //~ Set = [...Val]; //~ }else{ //~ Set = [Val]; //~ } //~ Set.unshift(Date.now()); //~ this["E"+E+":D:"+Key+DEX] = Set; //~ } const DTOG = (On, Off, E, D)=>{ if (D > 0){ if (!this.hasOwnProperty("E"+E+":D:"+On+D)){ if (D == 1 || this.hasOwnProperty("E"+E+":D:"+On+(D-1))){ Set("E"+E+":D:"+On+D, Date.now()); } } }else if (D < 0){ if (!this.hasOwnProperty("E"+E+":D:"+Off+(-D))){ if (this.hasOwnProperty("E"+E+":D:"+On+(-D))){ Set("E"+E+":D:"+Off+(-D), Date.now()); } } } } const DADD = (On, Off, E, D)=>{ if (D > 0){ if (!this.hasOwnProperty("E"+E+":D:"+On+D)){ if (D == 1 || this.hasOwnProperty("E"+E+":D:"+On+(D-1))){ Set("E"+E+":D:"+On+D, Date.now()); } } }else if (D < 0){ if (!this.hasOwnProperty("E"+E+":D:"+Off+(-D))){ if (D == -1 || this.hasOwnProperty("E"+E+":D:"+Off+(-D-1))){ Set("E"+E+":D:"+Off+(-D), Date.now()); } } } } if (Array.isArray(ACTS.EDROP)){ for (DROP of ACTS.EDROP){ DTOG("Drop", "Undrop", DROP[0], DROP[1]); } } if (ACTS.ELOSS){ DADD("Loss", "Unloss", ACTS.ELOSS[0], ACTS.ELOSS[1]); } if (ACTS.EBYE){ DADD("Bye", "Unbye", ACTS.EBYE[0], ACTS.EBYE[1]); } if (Array.isArray(ACTS.TLOCK)){ for (L of ACTS.TLOCK){ if (L.length > 1){ if (L[1] != null){ Set("P:"+L[0], {tlock: L[1]}); }else{ Del("P:"+L[0]); } } } } if(Array.isArray(ACTS.SNEW)){ const S = ACTS.SNEW[0]; const Type = ACTS.SNEW[1]; if (!this.hasOwnProperty(["S"+S+":C:Type"])){ if(S==1 && ["SCI", "POOL"].includes(Type) && !this.hasOwnProperty("S0:C:Type")){ Set("S0:C:Type", Type); Set("S0:R0:C:Hide", true); // for now }else if (S==1 || this.hasOwnProperty(["S"+(S-1)+":C:Type"])){ if (S > 1){ //Check the last stage isn't empty or in progress } Set("S"+S+":C:Type", Type); } } } if(Array.isArray(ACTS.RNEW)){ const S = ACTS.RNEW[0]; const R = ACTS.RNEW[1]; if (this.hasOwnProperty("S"+S+":C:Type")){ const SType = this["S"+S+":C:Type"]; if (R==1 || this.hasOwnProperty(["S"+S+":R"+(R-1)+":C:Type"])){ if (!this.hasOwnProperty(["S"+S+":R"+R+":C:Type"])){ var RType = ACTS.RNEW[2]; const Pools = []; if (typeof(RType) === "object"){ //Pool Stage Is Involved const TypeData = RType; RType = TypeData.type; const G = TypeData.G; const Mode = TypeData.mode; const Es = this.EntityRender(false); //keep E order if(Mode == 0){ const Pool = []; this.ITER({SDEX:G,RDEX:0},(TDEX)=>{ for (E of this["S"+G+":R0:T"+TDEX].Es){ Pool.push(Es[E-1]); } }) Pools.push(Pool); }else{ this.ITER({SDEX:G,RDEX:0},(TDEX)=>{ const Pool = []; for (E of this["S"+G+":R0:T"+TDEX].Es){ Pool.push(Es[E-1]); } Pools.push(Pool); }) } } if (RType == "A"){ if (R > 1){ // "Pairs" const LastType = this["S"+S+":R"+(R-1)+":C:Type"]; if (this.G("Mode") == "Pairs"){ if (typeof(RType === "string") && typeof(LastType) === "string"){ const DE = LastType.split("-"); const U = parseInt(DE[0]); const L = parseInt(DE[1]); if (this.BracketDex(LastType) == 0){ const Ls = U/2; RType = Ls + "-" + (Ls+L); }else{ RType = U + "-" + L/2; } }else{ RType = LastType / 2; } }else{ if (LastType % 4 == 0 && LastType != 40){ RType = LastType / 4; }else if (LastType == 13){ RType = 4; }else if (LastType == 10){ RType = 4; }else if (LastType == 40){ RType = 16; } } } } Set("S"+S+":R"+R+":C:Type", RType); Set("S"+S+":R"+R+":C:Hide", true); if (CBS.OverMatch){ CBS.OverMatch(S, SType, R, RType); }else if (ACTS.RNEW[3]){ //left empty }else{ if(Pools.length > 0){ var GX = 0; const BTs = []; for(let g = 0; g < Pools.length; g++){ var GC = 0; const Setter = (T, val)=>{ if (T == "B"){ BTs.push(...val.Es); }else{ Set("S"+S+":R"+R+":T"+(T+GX), val); GC++; } } this.MakeNewTables(S, SType, R, RType, Setter, Pools[g]); GX += GC; } if (BTs.length > 0){ Set("S"+S+":R"+R+":TB", {Es:BTs}); } }else{ const Setter = (T, val)=>{ Set("S"+S+":R"+R+":T"+T, val); } this.MakeNewTables(S, SType, R, RType, Setter); } } } } } } if(ACTS.RDEL){ const S = ACTS.RDEL[0]; const R = ACTS.RDEL[1]; const SR = "S"+S+":R"+R; if (this.hasOwnProperty(SR+":C:Type")){ Del(SR+":C:Type"); Del(SR+":Start"); Del(SR+":End"); var TDEX = 1; while(this.hasOwnProperty(SR+":T"+TDEX)){ Del(SR+":T"+TDEX); TDEX++; } Del(SR+":TB"); } if (R == 1){ Del("S"+S+":C:Type"); } //delete tourney End? } if(ACTS.RPUB){ const S = ACTS.RPUB[0]; const R = ACTS.RPUB[1]; const SR = "S"+S+":R"+R; if (this.hasOwnProperty(SR+":C:Hide")){ Del(SR+":C:Hide"); } } if(ACTS.RUNPUB){ const S = ACTS.RUNPUB[0]; const R = ACTS.RUNPUB[1]; const SR = "S"+S+":R"+R; if (!this.hasOwnProperty(SR+":C:Hide")){ Set(SR+":C:Hide", true); } } if(ACTS.RSTART){ const S = ACTS.RSTART[0]; const R = ACTS.RSTART[1]; const TSOR = ACTS.RSTART[2]; const SR = "S"+S+":R"+R; if (this.hasOwnProperty(SR+":C:Type")){ if (!this.hasOwnProperty(SR+":Start")){ Set(SR+":Start", TSOR==null?Date.now():TSOR); } } } const RTOG = (On, Off, S, R, D, TSOR)=>{ const SR = "S"+S+":R"+R; if (this.hasOwnProperty(SR+":C:Type")){ if (this.hasOwnProperty(SR+":Start")){ if (D > 0){ if (!this.hasOwnProperty(SR+":D:"+On+D)){ if (D == 1 || this.hasOwnProperty(SR+":D:"+On+(D-1))){ Set(SR+":D:"+On+D, TSOR==null?Date.now():TSOR); } } }else if (D < 0){ if (!this.hasOwnProperty(SR+":D:"+Off+(-D))){ if (this.hasOwnProperty(SR+":D:"+On+(-D))){ Set(SR+":D:"+Off+(-D), TSOR==null?Date.now():TSOR); } } } } } } if (ACTS.RPAUSE){ const S = ACTS.RPAUSE[0]; const R = ACTS.RPAUSE[1]; const D = ACTS.RPAUSE[2]; RTOG("Pause", "Unpause", S, R, D, ACTS.RPAUSE[3]); } if(ACTS.RSTOP){ const S = ACTS.RSTOP[0]; const R = ACTS.RSTOP[1]; const SR = "S"+S+":R"+R; if (this.hasOwnProperty(SR+":C:Type")){ if (this.hasOwnProperty(SR+":Start")){ Del(SR+":Start"); } } } const T_OP = (ACTS, CB)=>{ const S = ACTS[0]; const R = ACTS[1]; const T = ACTS[2]; //eventually make client have to pass null R and do R ? SRT : ST var KEY = "S"+S+":R"+R+":T"+T; if(!this.hasOwnProperty(KEY)){ KEY = "S"+S+":T"+T; } if (this.hasOwnProperty(KEY)){ CB(KEY); } } if(ACTS.TWIN){ T_OP(ACTS.TWIN, (KEY)=>{ //check validity based on config const change = this[KEY]; if (change.hasOwnProperty("Points")){ change.Points = ACTS.TWIN[3]; }else{ change.Winner = ACTS.TWIN[3]; } delete change.SelfReport; change.End = Date.now(); Set(KEY, change); }) } if(ACTS.TLOSS){ T_OP(ACTS.TLOSS, (KEY)=>{ //check validity based on config const change = this[KEY]; if (!change.Losers.includes(ACTS.TLOSS[3])){ change.Losers.push(ACTS.TLOSS[3]); Set(KEY, change); } }) } if(ACTS.TUNLOSS){ T_OP(ACTS.TUNLOSS, (KEY)=>{ //check validity based on config const change = this[KEY]; if (change.Losers.includes(ACTS.TUNLOSS[3])){ change.Losers.splice(change.Losers.indexOf(ACTS.TUNLOSS[3]), 1); Set(KEY, change); } }) } if(ACTS.TWLD){ T_OP(ACTS.TWLD, (KEY)=>{ //check validity based on config const change = this[KEY]; const WLD = ACTS.TWLD[3]; change.Wins = [WLD[0], WLD[1]]; change.Draws = WLD[2]; delete change.SelfReport; change.End = Date.now(); Set(KEY, change); }) } if(ACTS.TSETS){ const S = ACTS.TSETS[0]; const R = ACTS.TSETS[1]; const Ts = ACTS.TSETS[2]; for (TEs of Ts){ const T = TEs[0]; const KEY = "S"+S+(R=="active"?"":(":R"+R))+":T"+T; if (TEs[1].length == 0){ if (this.hasOwnProperty(KEY) && !this[KEY].End){ Del(KEY); } }else{ if (this.hasOwnProperty(KEY)){ const change = this[KEY]; change.Es = TEs[1]; Set(KEY, change); }else{ var SetMe = {Es:TEs[1]}; if (this["S"+S+":R"+R+":C:Type"] != "Draft"){ if (this.G("Mode") == "Pairs"){ SetMe = this.MakePairTable(TEs[1]); }else{ if (this.Config("PointMode") == "ScoreBased" || this.Config("PointMode") == "Custom"){ SetMe = {Es:TEs[1], Points:null}; }else{ SetMe = {Es:TEs[1], Winner:null, Losers:[]}; } } if (R == "active"){ SetMe.Start = Date.now(); } } Set(KEY, SetMe); } } } } if(ACTS.TRESET){ T_OP(ACTS.TRESET, (KEY)=>{ //check validity based on config const change = this[KEY]; if (this.G("Mode") == "Pairs"){ if (change.Wins) change.Wins = [0, 0]; if (change.Points) change.Points = [null, null]; if (change.Draws) change.Draws = 0; }else{ if (this.Config("PointMode") == "ScoreBased" || this.Config("PointMode") == "Custom"){ change.Points = null; }else{ change.Winner = null; change.Losers = []; } } delete change.SelfReport; if (this["S"+ACTS.TRESET[0]+":C:Type"] == "LFG"){ change.End = "_x_"; }else{ delete change.End; } Set(KEY, change); }) } if(ACTS.TMUTE){ T_OP(ACTS.TMUTE, (KEY)=>{ //check validity based on config const change = this[KEY]; change.Mute = true; Set(KEY, change); }) } if(ACTS.TTIME){ T_OP(ACTS.TTIME, (KEY)=>{ const change = this[KEY]; change.ExtraTime = ACTS.TTIME[3]; Set(KEY, change); }) } if(ACTS.REND){ const S = ACTS.REND[0]; const R = ACTS.REND[1]; const TSOR = ACTS.REND[2]; const SR = "S"+S+":R"+R; if (this.hasOwnProperty(SR+":C:Type")){ //TODO check that it's actually done Set(SR+":End", TSOR==null?Date.now():TSOR); } } if(ACTS.hasOwnProperty("SSTART")){ const S = ACTS.SSTART; if (this.hasOwnProperty("S"+S+":C:Type")){ if (this["S"+S+":C:Type"] == "LFG"){ Set("S"+S+":Start", this.StartDate); }else{ if (!this.hasOwnProperty("S"+S+":Start")){ Set("S"+S+":Start", Date.now()); } } } } if(ACTS.hasOwnProperty("SEND")){ const S = ACTS.SEND; if (this.hasOwnProperty("S"+S+":C:Type")){ //TODO check that it's actually done Set("S"+S+":End", Date.now()); } } if(ACTS.FIN){ //TODO check if something is outstanding Set("End", Date.now()); } if(ACTS.LFGS){ const S = ACTS.LFGS; if (this["S"+S+":C:Type"] == "LFG"){ if (CBS.OverMatch){ CBS.OverMatch(); }else{ const Es = this.EntityRender(false); //keep E order var TDEX = 1; while (this.hasOwnProperty("S"+S+":T"+TDEX)){ const table = this["S"+S+":T"+TDEX]; if (!table.End && !table.Mute){ for(E of table.Es){ Es[E-1] = null; } } TDEX++; } var EDEX = 0; while(EDEX < Es.length){ if (!Es[EDEX] || this.DropStatus(Es[EDEX].E) > 0){ Es.splice(EDEX, 1); }else{ EDEX++; } } ArrayShuffle(Es); if (this.G("Mode") == "Pods"){ EDEX = 0; var NewPod = []; while(EDEX < Es.length-3){ NewPod.push(Es[EDEX].E); NewPod.push(Es[EDEX+1].E); NewPod.push(Es[EDEX+2].E); NewPod.push(Es[EDEX+3].E); Set("S"+S+":T"+TDEX, {Es:NewPod, Winner:null, Losers:[], Start:Date.now()}); NewPod = []; TDEX++; EDEX+=4; } }else{ EDEX = 0; var NewPair = []; while(EDEX < Es.length-1){ NewPair.push(Es[EDEX].E); NewPair.push(Es[EDEX+1].E); Set("S"+S+":T"+TDEX, Object.assign(this.MakePairTable(NewPair), {Start:Date.now()})); NewPair = []; TDEX++; EDEX+=2; } } Set("S"+S+":Lobby", []); } } } } };ret.H = function H(){ return "Frooty Loops " + HI() + this.TEST; };ret.ITER = function ITER(paras={}, foo=null){ if (!paras.hasOwnProperty("SDEX")){ var SDEX = null; if (this.hasOwnProperty("S0:C:Type")){ if (foo) foo(0); SDEX = 1; } if (this.hasOwnProperty("S1:C:Type")){ SDEX = 1; while (this.hasOwnProperty("S"+SDEX+":C:Type")){ if (foo) foo(SDEX); SDEX++; } } return SDEX == null ? null : SDEX - 1; }else if (!paras.hasOwnProperty("RDEX")){ var RDEX = 1; while (this.hasOwnProperty("S"+paras.SDEX+":R"+RDEX+":C:Type")){ if (foo) foo(RDEX); RDEX++; } return RDEX-1; }else if (!paras.hasOwnProperty("TDEX")){ var TDEX = 1; if (paras.RDEX == null){ while (this.hasOwnProperty("S"+paras.SDEX+":T"+TDEX)){ if (foo) foo(TDEX); TDEX++; } }else{ while (this.hasOwnProperty("S"+paras.SDEX+":R"+paras.RDEX+":T"+TDEX)){ if (foo) foo(TDEX); TDEX++; } } return TDEX-1; } return null; };ret.PodSizes = function PodSizes(pCount){ if (pCount < 3){ console.log("There are too few players to start a round"); return [0, 0]; } const r = pCount % 4; var fours = (pCount-r) / 4; const byeMode = this.Config("ByeMode"); if (byeMode == "Byes"){ return [fours, r]; }else{ if (byeMode == "High3s" || byeMode == "Low3s"){ switch(r){ case 3: return [fours, 1]; case 2: return [fours-1, 2]; case 1: return [fours-2, 3]; default: return [fours, 0]; } }else{ if (pCount == 6){ return [0, 0, 2]; }else if (pCount == 7){ return [0, 1, 1]; }else if (pCount == 11){ return [0, 2, 1]; }else{ switch(r){ case 3: return [3, fours-3, 0]; case 2: return [2, fours-2, 0]; case 1: return [1, fours-1, 0]; default: return [0, fours, 0]; } } } } };ret.InitPods = function InitPods(Es, pcount=0){ const Pods = []; const PodCounts = this.PodSizes(Es.length); function PodOfSize(n){ Pods.push({Es:[], Size:n}); } const byeMode = this.Config("ByeMode"); if (pcount){ for (var c = 0; c < pcount/4; c++){ PodOfSize(4); } }else{ if (byeMode == "Byes"){ for (var p = 1; p <= PodCounts[0]; p++){ PodOfSize(4) } }else{ if (byeMode == "High3s"){ for (var p = 1; p <= PodCounts[1]; p++){ PodOfSize(3); } for (var p = 1; p <= PodCounts[0]; p++){ PodOfSize(4); } }else if (byeMode == "Low3s"){ for (var p = 1; p <= PodCounts[0]; p++){ PodOfSize(4); } for (var p = 1; p <= PodCounts[1]; p++){ PodOfSize(3); } }else if (byeMode == "High5s"){ for (var p = 1; p <= PodCounts[0]; p++){ PodOfSize(5); } for (var p = 1; p <= PodCounts[1]; p++){ PodOfSize(4); } for (var p = 1; p <= PodCounts[2]; p++){ PodOfSize(3); } }else if (byeMode == "Low5s"){ for (var p = 1; p <= PodCounts[2]; p++){ PodOfSize(3); } for (var p = 1; p <= PodCounts[1]; p++){ PodOfSize(4) } for (var p = 1; p <= PodCounts[0]; p++){ PodOfSize(5) } } } } return Pods; };ret.DoMatchmaking = function DoMatchmaking(S, R, RType, OverEs=null){ const Byes = []; const Losses = []; const byeMode = this.Config("ByeMode"); const Since = this["S"+S+":R"+(R-1)+":Start"] || 0; const Memo = {}; const TLocks = []; var Fin = []; const ProcEss = (Es)=>{ var EDEX = 0; while(EDEX < Es.length){ const E = Es[EDEX]; if (this.DropStatus(E.E) > 0 || E.P == "_x_"){ Es.splice(EDEX, 1); continue; } if (this.ByeStack(E.E, Since) > 0){ Byes.push(Es.splice(EDEX, 1)[0].E); continue; } if (this.LossStack(E.E, Since) > 0){ Losses.push(Es.splice(EDEX, 1)[0].E); continue; } const tlock = this.P4E(E.E).tlock; if(tlock){ E.tlock = tlock; TLocks.push(E); } Memo["E"+E.E] = E; EDEX++; } if (RType != "X"){ ArrayShuffle(Es); } function ShuffleWithinPointTiers(){ function RangeShuffle(start, end){ const slice = []; for (let x = start; x <= end; x++){ slice.push(Es[x]); } ArrayShuffle(slice); for (let x = start; x <= end; x++){ Es[x] = slice[x-start]; } } var sDex = 0; var eDex = 0; while (eDex < Es.length){ if (Es[sDex].points != Es[eDex].points){ RangeShuffle(sDex, eDex-1); sDex = eDex; } eDex++; } eDex--; if (sDex != eDex){ RangeShuffle(sDex, eDex); } } var Ret = []; if (this.G("Mode")=="Pairs"){ function PowerPairing(Set, reLimit=Infinity){ const set = [...Set]; const ret = []; while (set.length > 0){ if (set.length > 1){ var cert = false; var reLimiter = 0; while(!cert){ for (fR = 1; fR < set.length; fR++){ if (!set[0].opponentHistory || ArrayCount(set[0].opponentHistory, set[fR]) <= reLimiter){ ret.push(set[0]); ret.push(set[fR]); set.splice(fR, 1); set.splice(0, 1); cert = true; break; } } if (!cert){ reLimiter++; if (reLimiter > reLimit) return null; } } }else{ ret.push(set[0]); set.splice(0, 1); } } return ret; } function FreshPairing(_Set, reLimit=Infinity){ var ret = null; var Best = Infinity; function Recurse(Set, AX, BX){ if (Set.length > 1){ for (var fR = 1; fR < Set.length; fR++){ const rees = ArrayCount(Set[0].opponentHistory, Set[fR]); if (rees <= reLimit){ const bx = rees + BX; if (bx < Best){ const set = [...Set]; const ax = [...AX].concat([set[0], set[fR]]); set.splice(fR, 1); set.splice(0, 1); Recurse(set, ax, bx); } } } return null; }else{ ret = AX.concat(Set); Best = BX; } } Recurse(_Set, [], 0); return ret; } function NewPair(p1, p2){ return { Es: [p1, p2], } } var FinList = null; if (RType == "X"){ if (Es.length % 2 == 1){ Byes.push(Es.splice(Es.length-1, 1)[0].E); } const half = Es.length/2; for (let d = 0; d < half; d++){ Ret.push(NewPair(Es[d].E, Es[d+half].E)); } }else if (RType == "Random"){ EDEX = 0; while(EDEX < Es.length-1){ Ret.push(NewPair(Es[EDEX].E, Es[EDEX+1].E)); EDEX+=2; } }else{ if (RType == "Power"){ if (R > 1) Es.sort(this.SortByRanking.bind(this)); FinList = PowerPairing(Es); }else{ if (R > 1){ Es.sort(this.SortByMatching.bind(this)); ShuffleWithinPointTiers(); } const BaseTiers = []; var Bye = null; const Clone = [...Es]; if (Clone.length%2 == 1){ var maxbyes = 0; var findex = null; while (findex == null){ var dex = Clone.length-1; while (dex > 0){ if (Clone[dex].byesGotten <= maxbyes){ findex = dex; break; } dex--; } maxbyes++; } Bye = Clone.splice(findex, 1)[0]; } while (Clone.length > 0){ const tier = this.MatchPoints(Clone[1]); var dex = 2; while (dex < Clone.length && this.MatchPoints(Clone[dex]) == tier && this.MatchPoints(Clone[dex+1]) == tier){ dex+=2; } BaseTiers.push(Clone.splice(0, dex)); } function SpliceArray(arr, x, y){ const S = x > y ? y : x; const E = x > y ? x : y; arr[S] = arr[S].concat(arr.splice(E, 1)[0]); } var reLimit = 0; function RecurseTiers(Tiers){ if (Tiers.length == 1){ return FreshPairing(Tiers[0], reLimit); } //Trim Ends const Front = FreshPairing(Tiers[0], reLimit); const Back = FreshPairing(Tiers[Tiers.length-1], reLimit); if (Front && Back){ var ret = []; for (var t = 0; t < Tiers.length; t++){ tier = Tiers[t]; const fresh = FreshPairing(tier, reLimit); if (fresh){ ret = ret.concat(fresh); }else{ SpliceArray(Tiers, t, t+1); return RecurseTiers(Tiers); } } return ret; }else{ if (Tiers.length == 2){ SpliceArray(Tiers, 0, 1); return RecurseTiers(Tiers); }else if (Tiers.length == 3){ if (Front){ SpliceArray(Tiers, 1, 2); return RecurseTiers(Tiers); }else{ SpliceArray(Tiers, 0, 1); return RecurseTiers(Tiers); } }else{ if (!Front){ SpliceArray(Tiers, 0, 1); } if (!Back){ SpliceArray(Tiers, Tiers.length-2, Tiers.length-1); } return RecurseTiers(Tiers); } } } do { const Clone = []; for (Tier of BaseTiers){ Clone.push([...Tier]); } FinList = RecurseTiers(Clone); reLimit++; }while (FinList == null) if (Bye) FinList.push(Bye); } for (var p = 0; p < FinList.length; p+=2){ if (p+1 < FinList.length){ const newpair = NewPair(FinList[p].E,FinList[p+1].E); Ret.push(newpair); }else{ Byes.push(FinList[p].E); } } } }else{ if (RType == "X"){ const Rem = Es.length % 4; if (Rem > 0){ const byEs = Es.splice(Es.length-Rem, Rem); const tab = []; for (E of byEs){ Byes.push(E.E); } } const pods = Es.length/4; const End = Es.length-1; var TDEX = 1; for (let p = pods-1; p >= 0; p--){ const tab = [Es[End-p].E, Es[End-pods-p].E, Es[End-2*pods-p].E, Es[End-3*pods-p].E]; Ret.push({Es:tab}); TDEX++; } }else if (RType == "Random"){ EDEX = 0; while(EDEX < Es.length-3){ const tab = [Es[EDEX].E, Es[EDEX+1].E, Es[EDEX+2].E, Es[EDEX+3].E] Ret.push({Es:tab}); EDEX+=4; } } else { Ret = this.InitPods(Es); if (RType == "Bubble") { if (R > 1) Es.sort(this.SortByRanking.bind(this)); for (E of Es) { var FullPods = true; for (pod of Ret) { if (pod.Es.length == pod.Size) { continue; } pod.Es.push(E.E); FullPods = false; break; } if (FullPods && byeMode == "Byes") { Byes.push(E.E); } } } else if (RType == "Snake") { if (R > 1) Es.sort(this.SortByMatching.bind(this)); const winners = Es.filter(e => e.gamesWon === 1 || e.byesGotten === 1); console.log(winners.length); const nonWinners = Es.filter(e => e.gamesWon === 0 && e.byesGotten === 0); console.log(nonWinners.length); // Distribute winners evenly across pods let winnerIndex = 0; for (let pod of Ret) { if (winnerIndex < winners.length) { pod.Es.push(winners[winnerIndex].E); winnerIndex++; } } // Fill remaining spots with non-winners for (let E of [...winners.slice(winnerIndex), ...nonWinners]) { const PodScores = []; var FullPods = true; for (let pod of Ret) { if (pod.Es.length == pod.Size) { PodScores.push(-Infinity); continue; } var score = 0; for (let pp of pod.Es) { var ACount = 0; for (let OH of E.opponentHistory) { if (OH.E == pp) { ACount++; } } score -= ACount ** 2; } PodScores.push(score); FullPods = false; } if (FullPods && byeMode == "Byes") { Byes.push(E.E); } else { const pdex = PodScores.indexOf(Math.max(...PodScores)); Ret[pdex].Es.push(E.E); } } } else if (RType == "JustWin") { if (R > 1) Es.sort(this.SortByMatching.bind(this)); // Get top 16 players const topPlayers = new Set(Es.slice(0, 16).map(e => e.E)); for (E of Es) { const PodScores = []; var FullPods = true; for (pod of Ret) { if (pod.Es.length == pod.Size) { PodScores.push(-Infinity); continue; } var score = 0; // Check opponent history for (pp of pod.Es) { var ACount = 0; for (OH of E.opponentHistory) { if (OH.E == pp) { ACount++; } } score -= ACount ** 3 * 1000; } // Adjust score based on top players in the pod const topPlayersInPod = pod.Es.filter(p => topPlayers.has(p)).length; if (topPlayers.has(E.E)) { // If current player is a top player if (topPlayersInPod == 0) { score += 500; // Strongly encourage first top player in a pod } else if (topPlayersInPod == 1) { score += 1000; // Very strongly encourage second top player in a pod } else { score -= 2000; // Very strongly discourage more than 2 top players in a pod } } else { // If current player is not a top player if (topPlayersInPod == 0) { score -= 500; // Discourage non-top players in pods without top players } else if (topPlayersInPod == 1) { score += 250; // Encourage non-top players to join pods with 1 top player } else if (topPlayersInPod == 2) { score += 1000; // Strongly encourage non-top players to fill pods with 2 top players } } PodScores.push(score); FullPods = false; } if (FullPods && byeMode == "Byes") { Byes.push(E.E); } else { const pdex = PodScores.indexOf(Math.max(...PodScores)); Ret[pdex].Es.push(E.E); } } } else { //Pure Swiss/Power if (R > 1) Es.sort(this.SortByRanking.bind(this)); if (RType == "Swiss"){ ShuffleWithinPointTiers(); } function PodStats(arr){ const ret = {}; var Sum = 0; for(let x of arr){ Sum += x; } const Avg = Sum/arr.length; var Sum2 = 0; var Sum3 = 0; var Sum4 = 0; for(let x of arr){ const diff = x - Avg; Sum2 += diff * diff; Sum3 += diff * diff * diff; Sum4 += diff * diff * diff * diff; } const Vari = Sum2/arr.length; const StdDev = Math.sqrt(Vari); const Skew = (Sum3/arr.length)/Math.pow(StdDev, 3); const Kurt = (Sum4/arr.length)/Math.pow(StdDev, 4); return {Avg, Vari, StdDev, Skew, Kurt}; } function PodPermutations(size, bubbles, cutoff, returnCut=false){ const Ret = []; const Start = []; const Scratch = []; for(let x = 1; x <= size; x++){ Start.push(x); Scratch.push(0); } Start[size-1] += bubbles; Scratch.pop(); Scratch.pop(); while(true){ const Stands = [...Start]; for (let x = 0; x < Scratch.length; x++){ Stands[x+1] += Scratch[x]; } Stands.Stats = PodStats(Stands); Stands.Stats.Outlier = Stands[Stands.length-1] > (Stands.Stats.Avg * cutoff); if (!Stands.Stats.Outlier){ Ret.push(Stands); var Cert = false; for(let x = Scratch.length-1; x >= 0; x--){ if(Scratch[x] >= bubbles){ continue; } Scratch[x]++; for(let s = 1; s < Scratch.length-x; s++){ Scratch[s+x] = Scratch[x] ; } Cert = true; break; } if (!Cert){ break; } }else{ //Get Next Not Outlier const CurrentSum = (()=>{ var Sum = 0; for(let x of Stands){ Sum += x; } return Sum; })() var Shift = ((size + bubbles) / cutoff * size) - CurrentSum; var Dex = 0; while(Shift > 0){ const Cur = Scratch[Scratch.length-1-Dex]; const New = Math.min(bubbles, Cur+Shift); Scratch[Scratch.length-1-Dex] = New; Shift -= New-Cur; Dex++; } } } return Ret; } function GetPodSpectrum(size, bubbles, cutoff, showo=false){ const Perms = PodPermutations(size, bubbles, cutoff, showo); Perms.sort(function(a, b){ if (a.Stats["Avg"] != b.Stats["Avg"]){ return a.Stats["Avg"] - b.Stats["Avg"]; } if (a.Stats["Skew"] != b.Stats["Skew"]){ return a.Stats["Skew"] - b.Stats["Skew"]; } if (a.Stats["StdDev"] != b.Stats["StdDev"]){ return a.Stats["StdDev"] - b.Stats["StdDev"]; } if (a.Stats["Kurt"] != b.Stats["Kurt"]){ return a.Stats["Kurt"] - b.Stats["Kurt"]; } return 0; }); return Perms; } const BubbleSpec = { P4B0: [[1, 2, 3, 4]] }; function GetBubbles(size, count, dex){ //~ console.log("GB", size, count, dex); const key = "P"+size+"B"+count; if (!BubbleSpec.hasOwnProperty(key)){ BubbleSpec[key] = GetPodSpectrum(size, count, 2); } //~ console.log(BubbleSpec); const ret = BubbleSpec[key][dex]; //~ console.log("GETBUBBLESRET", ret); return ret || null; } //~ EDEX = 0; //~ while(EDEX < Es.length-3){ //~ const tab = [Es[EDEX].E, Es[EDEX+1].E, Es[EDEX+2].E, Es[EDEX+3].E] //~ Ret.push({Es:tab}); //~ EDEX+=4; //~ } var COUT = 0; function Recurse(pdex){ //~ console.log("RePairs", RePairs); //~ console.log("RECURSE", COUT); COUT++; //~ if (COUT > 12) return true function GetUnseated(dexi){ //~ console.log("dexi", dexi); //~ console.log("Es", Es); //~ console.log("EsAsdfasdfasdf"); var clicks = 0; const ret = []; //~ console.log("LLL", Es.length); for (E of Es){ //~ console.log("click", clicks); if (!E.ScratchSeat){ if (clicks > 0){ clicks--; }else{ ret.push(E); //~ console.log("ret", ret); if(ret.length == dexi.length){ return ret; }else{ //~ console.log("dexi", dexi); clicks = dexi[ret.length] - dexi[ret.length-1] - 1; } } } } //if we get here we didn't have enough players to get all the dexi //~ console.log("ret", ret); return null; } var PEs = []; //~ var SUM = 0; //~ for (var x = 0; x < Ret[pdex].Size; x++){ //~ PEs.push(null); //~ SUM += x; //~ } var BCount = 0; var Dex = 0; while (true){ const Ds = GetBubbles(Ret[pdex].Size, BCount, Dex); if (Ds == null){ BCount++; //~ if (BCount > 10) break; Dex = 0; continue; } //~ console.log("BCOUNT", BCount); //Get the earliest unseated players PEs = GetUnseated(Ds); //~ console.log("PEs", PEs); //~ if (pdex > 4) break; if (PEs == null){ //you are asking to use a dex that doesn't exist //the fact you asked for this means you need it //therefore the current recurse is fucked return false; } //check for replays var Cert = true; for (E1 of PEs){ if (Cert){ for (E2 of PEs){ if (E1 != E2){ var Left = E1; var Right = E2; for (let x = 0; x < 2; x++){ if (Left.opponentHistory.includes(Right)){ if (RePairs > 0){ var C = 0; for (E of Left.opponentHistory){ if (E == Right){ C++; } } if (C > RePairs){ Cert = false; break; } }else{ Cert = false; //~ console.log(Left.E + " played " + Right.E); break; } } } } } } } //Try to recurse if (Cert){ for (E of PEs){ E.ScratchSeat = true; } if (Ret.length > pdex+1){ if(RType == "Power"){ RePairs = 0; while(!Recurse(pdex+1)){ RePairs++; } }else{ Cert = Recurse(pdex+1); } } //validate double byes here I think } //if still cert we are done if (Cert){ break; }else{ //otherwise, try a different set of Es for (E of PEs){ E.ScratchSeat = false; } Dex++; } } //~ console.log("JOB DONE"); //If we get here job done for (E of PEs){ Ret[pdex].Es.push(E.E); } return true; } var RePairs = 0; while(Recurse(0) == false){ RePairs++; } //Bye the leftovers for (E of Es){ if (!E.ScratchSeat){ Byes.push(E.E); } } //~ console.log("RECSER", Recurse(0)); //~ console.log("REERET", Ret); } for (pod of Ret){ const order = []; const players = []; for (E of pod.Es){ players.push({E:E, luck:Memo["E"+E].luckLevel}); } function SortByLuck(a, b){ La = a.luck; Lb = b.luck; return La - Lb; } players.sort(SortByLuck); const D = pod.Es.length * 2; for (var d = 0; d < pod.Es.length; d++){ var rand = Math.random(); if (rand < 0.5){ const dex = Math.floor(rand * (D - d*2)); order.push(players.splice(dex, 1)[0].E); }else{ rand = (rand - 0.5) * 2; var reposte = 1; while (reposte < players.length && players[reposte-1].luck == players[reposte].luck){ reposte++; } const dex = Math.floor(rand * reposte); order.push(players.splice(dex, 1)[0].E); } } pod.Es = order; } } } Fin = Fin.concat(Ret); } if (this.G("Draft")){ var SDEX = 1; const Es = this.EntityRender(false); //stays in E order var DSDEX = 0; while(this.hasOwnProperty("S"+SDEX+":C:Type")){ const SType = this["S"+SDEX+":C:Type"]; if (SType == "DRFT"){ DSDEX = SDEX } SDEX++; } if (DSDEX > 0){ var SDEX = DSDEX; var RDEX = 1; while(this.hasOwnProperty("S"+SDEX+":R"+RDEX+":C:Type")){ const RType = this["S"+SDEX+":R"+RDEX+":C:Type"]; if (RType == "Draft"){ RDEX++; }else{ break; } } RDEX--; if (RDEX > 0){ var TDEX = 1; while(this.hasOwnProperty("S"+SDEX+":R"+RDEX+":T"+TDEX)){ const table = this["S"+SDEX+":R"+RDEX+":T"+TDEX]; const tEs = []; for(E of table.Es){ tEs.push(Es[E-1]); } ProcEss(tEs); TDEX++; } } } }else{ ProcEss(OverEs || this.EntityRender()); } for (var l = 0; l < TLocks.length; l++){ const lock = TLocks[l]; if (lock.tlock <= Fin.length){ for(var t = 0; t < Fin.length; t++){ const table = Fin[t]; if (table.Es.includes(lock.E)){ var cert = true; for (var c = l-1; c >= 0; c--){ if (table.Es.includes(TLocks[c].E)){ cert = false; break; } } if (cert){ const swap = Fin[lock.tlock-1]; Fin[lock.tlock-1] = table; Fin[t] = swap; } break; } } } } return [Fin, Byes, Losses]; };ret.MakePairTable = function MakePairTable(Es){ if (this.Config("PointMode") == "ScoreBased"){ return {Es, Points:[null, null]}; }else{ return {Es, Wins:[0, 0], Draws:0}; } };ret.MakeNewTables = function MakeNewTables(S, SType, R, RType, Setter, OverEs=null){ if (RType == "Draft"){ const Es = this.EntityRender(); var EDEX = 0; while(EDEX < Es.length){ const E = Es[EDEX]; if (this.DropStatus(E.E) > 0 || E.P == "_x_"){ Es.splice(EDEX, 1); continue; } EDEX++; } ArrayShuffle(Es); const Size = this.Config("DraftPodSize"); var TDEX = 1; while (Es.length > 0){ const Fin = []; const pEs = Es.length>=Size ? Es.splice(0, Size) : Es.splice(0, Es.length); for (E of pEs){ Fin.push(E.E); } //~ Set("S"+S+":R"+R+":T"+TDEX, {Es:Fin}); Setter(TDEX, {Es:Fin}); TDEX++; } }else if (this.G("Mode") == "Pairs"){ if (SType == "GRP"){ const PBL = this.DoMatchmaking(S, R, RType, OverEs); const Pairs = PBL[0]; for(var x = 1; x <= Pairs.length; x++){ //~ Set("S"+S+":R"+R+":T"+x, {Es:Pairs[x-1].Es, Wins:[0, 0], Draws:0}); Setter(x, this.MakePairTable(Pairs[x-1].Es)); } if (PBL[1].length > 0){ //~ Set("S"+S+":R"+R+":TB", {Es:PBL[1]}); Setter("B", {Es:PBL[1]}); } if (PBL[2].length > 0){ //~ Set("S"+S+":R"+R+":TL", PBL[2]); Setter("L", PBL[2]); } }else if (SType == "BRKT"){ const Pods = this.BracketPairs(S, R, RType, OverEs); for(var x = 1; x <= Pods.Ts.length; x++){ //~ Set("S"+S+":R"+R+":T"+x, {Es:Pods.Ts[x-1], Wins:[0, 0], Draws:0}); Setter(x, this.MakePairTable(Pods.Ts[x-1])); } if (Pods.TB){ //~ Set("S"+S+":R"+R+":TB", {Es:Pods.TB}); Setter("B", {Es:Pods.TB}); } } }else{ if (SType == "GRP"){ const PBL = this.DoMatchmaking(S, R, RType); const Pods = PBL[0]; for(var x = 1; x <= Pods.length; x++){ if (this.Config("PointMode") == "ScoreBased" || this.Config("PointMode") == "Custom"){ //~ Set("S"+S+":R"+R+":T"+x, {Es:Pods[x-1].Es, Points:null}); Setter(x, {Es:Pods[x-1].Es, Points:null}); }else{ //~ Set("S"+S+":R"+R+":T"+x, {Es:Pods[x-1].Es, Winner:null, Losers:[]}); Setter(x, {Es:Pods[x-1].Es, Winner:null, Losers:[]}); } } if (PBL[1].length > 0){ //~ Set("S"+S+":R"+R+":TB", {Es:PBL[1]}); Setter("B", {Es:PBL[1]}); } if (PBL[2].length > 0){ //~ Set("S"+S+":R"+R+":TL", PBL[2]); Setter("L", PBL[2]); } }else if (SType == "BRKT"){ const Pods = this.BracketPods(S, R, RType); for(var x = 1; x <= Pods.Ts.length; x++){ //~ Set("S"+S+":R"+R+":T"+x, {Es:Pods.Ts[x-1], Winner:null, Losers:[]}); Setter(x, {Es:Pods.Ts[x-1], Winner:null, Losers:[]}); } if (Pods.TB){ //~ Set("S"+S+":R"+R+":TB", {Es:Pods.TB}); Setter("B", {Es:Pods.TB}); } } } };ret.BracketDex = function BracketDex(TX){ if (this.G("Mode") == "Pairs"){ const DE = TX.split("-"); const U = parseInt(DE[0]); const L = parseInt(DE[1]); if (U > 1){ const NextL = U/2 + L; if (NextL & (NextL - 1)){ return 1; }else{ return 0; } }else{ if (L == 2){ return 1; }else{ return 0; } } }else{ return 0; } };ret.BracketPods = function BracketPods(S, R, TopX){ const ret = { Ts: [], }; var Es = this.EntityRender(false); //stays in E order if (R == 1){ var EDEX = 0; while(EDEX < Es.length){ const E = Es[EDEX]; if (this.DropStatus(E.E) > 0 || E.P == "_x_"){ Es.splice(EDEX, 1); continue; } EDEX++; } if (Es.length < TopX){ return ret; } ArrayShuffle(Es); }else{ const newEs = []; var TDEX = 1; while(this.hasOwnProperty("S"+S+":R"+(R-1)+":T"+TDEX)){ newEs.push(Es[this["S"+S+":R"+(R-1)+":T"+TDEX].Winner-1]); TDEX++; } if (this["S"+S+":R"+(R-1)+":TB"]){ for (Bye of this["S"+S+":R"+(R-1)+":TB"].Es){ newEs.push(Es[Bye-1]); } } Es = newEs; } Es.sort(this.SortByRanking.bind(this)); var pow4 = 4; while (pow4 < TopX){ pow4 *= 4; } var b = 0; if (pow4 != TopX){ if (TopX == 13){ pow4 = 12; } if (TopX == 10){ pow4 = 8; }else if (TopX == 40){ pow4 = 32; } ret.TB = []; for (let dex = 0; dex < TopX-pow4; dex++){ ret.TB.push(Es.splice(0, 1)[0].E); } } for (let x = 1; x <= pow4/4; x++){ ret.Ts.push([]); } const numPlayers = pow4; const halfPlayers = numPlayers / 2; for (let i = 0; i < ret.Ts.length; i++) { ret.Ts[i].push(Es[i+b].E); ret.Ts[i].push(Es[halfPlayers+b - 1 - i].E); ret.Ts[i].push(Es[i+b + halfPlayers].E); ret.Ts[i].push(Es[numPlayers+b - 1 - i].E); } return ret; };ret.BracketPairs = function BracketPairs(S, R, TX, OverEs=null){ const ret = { Ts: [], }; function NewPair(p1, p2){ return [p1, p2]; } var Es = this.EntityRender(false); //stays in E order if (R == 1){ if (OverEs){ Es = OverEs; }else{ var EDEX = 0; while(EDEX < Es.length){ const E = Es[EDEX]; if (this.DropStatus(E.E) > 0 || E.P == "_x_"){ Es.splice(EDEX, 1); continue; } EDEX++; } } var TopU = TX; var TopL = 0; if (typeof(TopU) === "string"){ const UL = TopU.split("-"); TopU = parseInt(UL[0]); TopL = parseInt(UL[1]); } if (Es.length < TopU + TopL){ console.log("NOT ENOUGH TO CUT"); return ret; } if (S == 1 || this["S"+(S-1)+":C:Type"] != "GRP"){ ArrayShuffle(Es); } else{ Es.sort(this.SortByRanking.bind(this)); } var Seeds = [0]; while (Seeds.length < TopU){ const Double = []; for (let s = 0; s < Seeds.length; s++){ if (s < Seeds.length/2){ Double.push(Seeds[s]); Double.push(Seeds.length*2-1 - Seeds[s]); }else{ Double.push(Seeds.length*2-1 - Seeds[s]); Double.push(Seeds[s]); } } Seeds = Double; } for (let s = 0; s < Seeds.length; s+=2){ const A = Seeds[s]; const B = Seeds[s+1]; ret.Ts.push(NewPair(Es[Math.min(A, B)].E, Es[Math.max(A, B)].E)); } }else{ if (typeof(TX) === "string"){ const DE = TX.split("-"); const U = parseInt(DE[0]); const L = parseInt(DE[1]); const winners = []; const losers = []; var WinR = 0; var LoseR = 0; if (U == L){ if (U+L == 2){ //Grand Finals const TL = this["S"+S+":R"+(R-1)+":T1"]; const TW = this["S"+S+":R"+(R-2)+":T1"]; var E1; var E2; if (this.PairGrade(TL) >= 9){ E2 = TL.Es[0]; }else{ E2 = TL.Es[1]; } if (this.PairGrade(TW) >= 9){ E1 = TW.Es[0]; }else{ E1 = TW.Es[1]; } ret.Ts.push(NewPair(E1, E2)); return ret; }else{ if (R == 2){ LoseR = 1; }else{ WinR = 1; } } }else if (U < L){ WinR = 2; LoseR = 1; }else{ if (R == 3){ WinR = 2; }else{ WinR = 3; } if (this.BracketDex(TX) == 1){ LoseR = 1; } } var TDEX = 1; if (R-WinR < 1){ //initial cut had people go straight to losers, so pull them from the bracket const QuickEs = [...Es].sort(this.SortByRanking.bind(this)); for (let s = 0; s < L/2; s+=2){ //~ if (s < L/4){ winners.push(QuickEs[L+s].E); winners.push(QuickEs[L + L/2 - 1 - s].E); //~ }else{ //~ winners.push(QuickEs[L + L/2 - 1 - s].E); //~ winners.push(QuickEs[L+s].E); //~ } } }else{ while(this.hasOwnProperty("S"+S+":R"+(R-WinR)+":T"+TDEX)){ const table = this["S"+S+":R"+(R-WinR)+":T"+TDEX]; if (this.PairGrade(table) >= 9){ winners.push(table.Es[0]); }else{ winners.push(table.Es[1]); } TDEX++; } } TDEX = 1; while(this.hasOwnProperty("S"+S+":R"+(R-LoseR)+":T"+TDEX)){ const table = this["S"+S+":R"+(R-LoseR)+":T"+TDEX]; if (this.PairGrade(table) >= 9){ losers.push(table.Es[1]); }else{ losers.push(table.Es[0]); } TDEX++; } if (losers.length == 0){ for (let p = 0; p < winners.length; p+=2){ if (p < winners.length/2){ ret.Ts.push(NewPair(winners[p], winners[p+1])); }else{ ret.Ts.push(NewPair(winners[p+1], winners[p])); } } }else if (winners.length == 0){ for (let p = 0; p < losers.length; p+=2){ if (p < losers.length/2){ ret.Ts.push(NewPair(losers[p+1], losers[p])); }else{ ret.Ts.push(NewPair(losers[p], losers[p+1])); } } }else if (losers.length == winners.length){ if (winners.length == 1){ ret.Ts.push(NewPair(losers[0], winners[0])); }else{ //losers here are from the winners bracket, so they are first seat for (let p = 0; p < winners.length; p+=2){ ret.Ts.push(NewPair(losers[p], winners[p])); ret.Ts.push(NewPair(losers[p+1], winners[p+1])); } } }else{ console.log(winners, losers); console.log("HOOOOOOOOOOOOOOLD UP"); } }else{ const winners = []; const newEs = []; var TDEX = 1; while(this.hasOwnProperty("S"+S+":R"+(R-1)+":T"+TDEX)){ const table = this["S"+S+":R"+(R-1)+":T"+TDEX]; if (this.PairGrade(table) >= 9){ winners.push(table.Es[0]); }else{ winners.push(table.Es[1]); } TDEX++; } for (let p = 0; p < winners.length; p+=2){ if (p < winners.length/2){ ret.Ts.push(NewPair(winners[p], winners[p+1])); }else{ ret.Ts.push(NewPair(winners[p+1], winners[p])); } } } } return ret; };ret.D = function D(E, Key){ var DEX = 1; while(true){ if (!this["E"+E+":D:"+Key+DEX]){ DEX--; break; } DEX++; } const stuff = this["E"+E+":D:"+Key+DEX]; if (stuff.length == 2){ return stuff[1]; }else{ const ret = [...stuff]; ret.shift(); //remove timestamp return ret; } };ret.DSTAT = function DSTAT(On, Off, E, val=null){ var DEX = 1; while(true){ const onstamp = this["E"+E+":D:"+On+DEX]; if (onstamp){ const offstamp = this["E"+E+":D:"+Off+DEX]; if (offstamp){ //empty }else{ return DEX; } }else{ return -(DEX-1); } DEX++; } };ret.DSTACK = function DSTACK(On, Off, E, since){ const SINCE = since || 0; var DEX = 1; var ON = 0; while(this.hasOwnProperty("E"+E+":D:"+On+DEX)){ if (this["E"+E+":D:"+On+DEX] > since){ ON++; } DEX++; } DEX = 1; var OFF = 0; while(this.hasOwnProperty("E"+E+":D:"+Off+DEX)){ if (this["E"+E+":D:"+Off+DEX] > since){ OFF++; } DEX++; } return [ON, OFF]; };ret.DropStatus = function DropStatus(E){ return this.DSTAT("Drop", "Undrop", E); };ret.LossStatus = function LossStatus(E, since=null){ return this.DSTACK("Loss", "Unloss", E, since); };ret.LossStack = function LossStack(E, since=null){ const Loss = this.LossStatus(E, since); return Loss[0] - Loss[1]; };ret.ByeStatus = function ByeStatus(E, since=null){ return this.DSTACK("Bye", "Unbye", E, since); };ret.ByeStack = function ByeStack(E, since=null){ const Bye = this.ByeStatus(E, since); return Bye[0] - Bye[1]; };ret.RSTAT = function RSTAT(S, R, On, Off){ var DEX = 1; const SR = "S"+S+":R"+R; var ret = 0; while(true){ const onstamp = this[SR+":D:"+On+DEX]; if (onstamp){ const offstamp = this[SR+":D:"+Off+DEX]; if (offstamp){ ret += offstamp - onstamp; }else{ return [DEX, onstamp, ret]; } }else{ return [-(DEX-1), onstamp, ret]; } DEX++; } };ret.PauseStatus = function PauseStatus(S, R){ return this.RSTAT(S, R, "Pause", "Unpause")[0]; };ret.LastStart = function LastStart(){ const SDEX = this.ITER(); const RDEX = this.ITER({SDEX}); if(this.hasOwnProperty("S"+SDEX+":R"+RDEX+":Start")){ return this["S"+SDEX+":R"+RDEX+":Start"]; }else if (RDEX == 1){ return 0; }else{ return this["S"+SDEX+":R"+RDEX+":Start"]; } };ret.LastPool = function LastPool(S=1){ var SDEX = S-1; while(this.hasOwnProperty("S"+SDEX+":C:Type")){ const SType = this["S"+SDEX+":C:Type"]; if (SType == "POOL"){ return SDEX; } SDEX--; } return -1; };ret.ByePoints = function ByePoints(){ if (this.Config("PointMode") == "Standard" || this.Config("PointMode") == "ScoreBased"){ return this.Config("WinPoints"); }else if (this.Config("PointMode") == "Flawless"){ return this.Config("FlawlessPoints"); }else{ return this.Config("ByePoints"); } };ret.PairGrade = function PairGrade(Pair){ if (Pair.Void) return 0; if (Pair.Points){ if (Pair.Points[0] == null || Pair.Points[1] == null){ return 5; } if(Pair.Points[0] > Pair.Points[1]){ return 10 } if(Pair.Points[0] < Pair.Points[1]){ return 8; } if(Pair.Points[0] == Pair.Points[1]){ return 6; } return 5; } const BestOf = this.Config("BestOf"); const MaxWins = Math.ceil(BestOf*0.51); const P1 = Pair.Wins[0]; const P2 = Pair.Wins[1]; const D = Pair.Draws; if (P1 > MaxWins || P2 > MaxWins || P1+D > BestOf || P2+D > BestOf || P1+P2 > BestOf){ return 0; } if (P1 == 0 && P2 == 0 && D == 0){ return 5; } if (P1 == P2){ return 6; }else if(Pair.Wins[0] > Pair.Wins[1]){ if (Pair.Wins[0] == MaxWins && Pair.Wins[1] == 0){ return 10 }else{ return 9 } }else if(Pair.Wins[0] < Pair.Wins[1]){ if (Pair.Wins[1] == MaxWins && Pair.Wins[0] == 0){ return 8; }else{ return 7; } } return 5; };ret.ActiveEntities = function ActiveEntities(){ var EDEX = 1; var ret = 0; while (this.hasOwnProperty("E"+EDEX+":P1")){ if (this["E"+EDEX+":P1"] != "_x_" && this.DropStatus(EDEX)<=0){ ret++; } EDEX++; } return ret; };ret.MatchPoints = function MatchPoints(Player){ return Player.points; };ret.WinRate = function WinRate(Player){ if (Player.gamesPlayed == 0 && Player.byesGotten == 0) { return 0; }else{ if (this.Config("PointMode") == "Custom"){ const ret = (Player.points - this.Config("StartPoints")) / (Player.gamesPlayed*this.Config("MaxRoundPoints")); return ret; }else{ var N = this.MatchPoints(Player) - this.Config("StartPoints"); var D = 0; if (this.Config("PointMode") == "Flawless"){ D += this.Config("WinPoints") * Player.littleGamesPlayed; D += this.Config("WinPoints") * Player.byesGotten * Math.ceil(this.Config("BestOf")*0.51); D += this.Config("FlawlessPoints") * (Player.gamesPlayed + Player.byesGotten); }else{ D += (Player.gamesPlayed + Player.byesGotten)*this.Config("WinPoints"); } return N/D; } } };ret.OpponentWinRate = function OpponentWinRate(Player){ const MinWin = (this.G("Mode") == "Pods" && this.StartDate > _CHANGE_MIN_PERCENT_PODS_) ? 20 : 33 const Opps = new Set(Player.opponentHistory); if (Opps.size == 0){ if (Player.byesGotten > 0){ return MinWin/100; }else{ return 0; } } var Ax = 0; for (O of Opps){ Ax += Math.max(this.WinRate(O), MinWin/100); } if (Player.byesGotten > 0 && this.G("Mode") == "Pods"){ return (Ax+((MinWin/100)*3))/(Opps.size+3); }else{ return Ax / Opps.size; } };ret.SuccessRate = function SuccessRate(Player){ if (Player.gamesPlayed == 0 && Player.byesGotten == 0) { return 0; }else{ if (this.Config("PointMode") == "Custom"){ return (Player.points - this.Config("StartPoints")) / (Player.gamesPlayed*this.Config("MaxRoundPoints")); }else{ return Player.gamesWon / Player.gamesPlayed; } } };ret.OpponentSuccessRate = function OpponentSuccessRate(Player){ const Opps = new Set(Player.opponentHistory); if (Opps.size == 0){ return 0; } var Ax = 0; for (O of Opps){ Ax += this.SuccessRate(O); } return Ax / Opps.size; };ret.GamePoints = function GamePoints(Player){ if (Player.littlePoints) return Player.littlePoints; var points = this.Config("WinPoints") * Player.littleGamesWon; points += this.Config("WinPoints") * Player.byesGotten * Math.ceil(this.Config("BestOf")*0.51); if (Player.littleGamesDrawn){ points += this.Config("DrawPoints") * Player.littleGamesDrawn; } return points ? points : 0; };ret.GameWinRate = function GameWinRate(Player){ if (Player.littleGamesPlayed == 0 && Player.byesGotten == 0) { return 0; }else{ const N = this.GamePoints(Player); var D = Player.littleGamesPlayed*this.Config("WinPoints"); D += this.Config("WinPoints") * Player.byesGotten * Math.ceil(this.Config("BestOf")*0.51); return N/D; } };ret.OpponentGameWinRate = function OpponentGameWinRate(Player){ const MinWin = (this.G("Mode") == "Pods" && this.StartDate > _CHANGE_MIN_PERCENT_PODS_) ? 20 : 33 const Opps = new Set(Player.opponentHistory); if (Opps.size == 0){ if (Player.byesGotten > 0){ return MinWin/100; }else{ return 0; } } var Ax = 0; for (O of Opps){ Ax += Math.max(this.GameWinRate(O), MinWin/100); } return Ax / Opps.size; };ret.SortByRanking = function SortByRanking(Pa, Pb){ //Drop Status Moves to Last if (Pa.DS != Pb.DS){ return Pa.DS - Pb.DS; } const Breakers = this.Config("Breakers"); for (Breaker of Breakers){ switch(Breaker){ case "MPTS": MPa = this.MatchPoints(Pa); MPb = this.MatchPoints(Pb); if (MPa != MPb){ return MPb - MPa; } break; case "GWR": GWa = Math.round(this.GameWinRate(Pa) * 10000)/100; GWb = Math.round(this.GameWinRate(Pb) * 10000)/100; if (GWa != GWb){ return GWb - GWa; } break; case "OWR": OWa = Math.round(this.OpponentWinRate(Pa) * 10000)/100; OWb = Math.round(this.OpponentWinRate(Pb) * 10000)/100; if (OWa != OWb){ return OWb - OWa; } break; case "OGWR": OGWa = Math.round(this.OpponentGameWinRate(Pa) * 10000)/100; OGWb = Math.round(this.OpponentGameWinRate(Pb) * 10000)/100; if (OGWa != OGWb){ return OGWb - OGWa; } break; case "SR": WRa = Math.round(this.SuccessRate(Pa) * 10000)/100; WRb = Math.round(this.SuccessRate(Pb) * 10000)/100; if (WRa != WRb){ return WRb - WRa; } break case "OSR": ORa = Math.round(this.OpponentSuccessRate(Pa) * 10000)/100; ORb = Math.round(this.OpponentSuccessRate(Pb) * 10000)/100; if (ORa != ORb){ return ORb - ORa; } break case "OBEAT": const OBa = new Set(Pa.opponentsBeat); const OBb = new Set(Pb.opponentsBeat); if (OBa.size != OBb.size){ return OBb.size - OBa.size; } break case "UOPP": const Ua = (new Set(Pa.opponentHistory)).size; const Ub = (new Set(Pb.opponentHistory)).size; if (Ua != Ub){ return Ub - Ua; } break case "GPTS": GPa = this.GamePoints(Pa); GPb = this.GamePoints(Pb); if (GPa != GPb){ return GPb - GPa; } break; case "GPP": GPPa = Pa.littlePointPercents; GPPb = Pb.littlePointPercents; if (GPPa != GPPb){ return GPPb - GPPa; } break; default: console.error("Breaker type '" + Breaker + "' does not exist"); return 0; } } //First Come, First Serve return Pa.E - Pb.E; };ret.SortByMatching = function SortByMatching(Pa, Pb){ if (this.G("Mode") == "Pairs"){ return this.SortByRanking(Pa, Pb); }else{ //default "Pods" //More Points Higher In List if (Pa.points != Pb.points){ return Pb.points - Pa.points; } //Less Games Played Higher In List if (Pa.gamesPlayed != Pb.gamesPlayed){ return Pa.gamesPlayed - Pb.gamesPlayed; } //Less Unique Opponents Higher In List const Ua = (new Set(Pa.opponentHistory)).size; const Ub = (new Set(Pb.opponentHistory)).size; if (Ua != Ub){ return Ua - Ub; } //Lower Opponent Winrate Higher in List OWa = this.OpponentWinRate(Pa); OWb = this.OpponentWinRate(Pb); if (OWa != OWb){ return OWa - OWb; } } return 0; };return ret;}function PDATA(data={}){const ret = {};Object.assign(ret, data);ret.PlayerActions = function PlayerActions(ACTS, CBS={}, UID=""){ const Set =(P, key, val)=>{ if (CBS.OnSet) CBS.OnSet(P, key, val); } const Del = (P, key)=>{ if (CBS.OnDelete) CBS.OnDelete(P, key); } const Push = (P, key, val)=>{ if (CBS.OnPush) CBS.OnPush(P, key, val); } const Remove = (P, key, dex, len)=>{ if (CBS.OnRemove) CBS.OnRemove(P, key, dex, len); } if (Array.isArray(ACTS.CHECKIN)){ for(P of ACTS.CHECKIN){ Set(P, "CheckedIn", true); } } if (Array.isArray(ACTS.CHECKOFF)){ for(P of ACTS.CHECKOFF){ Del(P, "CheckedIn"); } } if (Array.isArray(ACTS.DCHECKON)){ for(P of ACTS.DCHECKON){ Set(P, "DeckChecked", true); } } if (Array.isArray(ACTS.DCHECKOFF)){ for(P of ACTS.DCHECKOFF){ Del(P, "DeckChecked"); } } if (Array.isArray(ACTS.PAYYES)){ for(P of ACTS.PAYYES){ Set(P, "paid", true); } } if (Array.isArray(ACTS.PAYNO)){ for(P of ACTS.PAYNO){ Del(P, "paid"); } } if (Array.isArray(ACTS.DECKSET)){ for(PD of ACTS.DECKSET){ if (PD.length > 1){ Set(PD[0], "decklist", PD[1]); Del(PD[0], "decklock"); } } } if (Array.isArray(ACTS.SETNAME)){ for(PD of ACTS.SETNAME){ if (PD.length > 1){ Set(PD[0], "name", PD[1]); Set(PD[0], "discord", PD[1]); Set(PD[0], "modo", PD[1]); } } } if (Array.isArray(ACTS.PENALTY)){ Push(ACTS.PENALTY[0], "Penalties", { Name: ACTS.PENALTY[1], Description: ACTS.PENALTY[2], timestamp: Math.round(Date.now()/1000), User: UID, }); } if (Array.isArray(ACTS.NOPENALTY) && ACTS.NOPENALTY.length >= 3){ Remove(ACTS.NOPENALTY[0], "Penalties", ACTS.NOPENALTY[1], ACTS.NOPENALTY[2]); } };ret.All = function All(){ const ret = []; for (PE of Object.entries(this)){ if (typeof PE[1] === "function") continue; PE[1].P = PE[0]; ret.push(PE[1]); } return ret; };return ret;}