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"],"__logo":"magic-the-gathering.webp","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"],"__logo":"pokemon.webp","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!"],"__logo":"yu-gi-oh.webp","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"],"__logo":"star-wars-unlimited.webp","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"],"__logo":"lorcana.webp","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"],"__logo":"one-piece.webp","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"],"__logo":"digimon.webp","formats":{"":{"label":"Digimon","value":"Digimon","__startup":{"UCONFIG":{"PointMode":"Standard","WinPoints":3,"DrawPoints":1,"BestOf":3,"RoundTime":50}}}}},"Marvel Snap":{"__label":"Marvel Snap","__searchTerms":["Snap"],"__logo":"marvel-snap.webp","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"],"__logo":"shadowverse-evolve.webp","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"],"__logo":"flesh-and-blood.webp","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"],"__logo":"sorcery-contested-realm.webp","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"],"__logo":"altered.webp","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"],"__logo":"beyblade.webp","formats":{"X":{"label":"Beyblade X","value":"Beyblade X","__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"],"__logo":"catan.webp","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"],"__logo":"gudnak.webp","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 (R == 1 && SType == "BRKT" && Pools.length > 1){
if (typeof(RType) === "string"){
const DE = RType.split("-");
const U = parseInt(DE[0]);
const L = parseInt(DE[1]);
Set("S"+S+":C:Mod", U+L);
}else{
Set("S"+S+":C:Mod", RType);
}
}
}
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+":C:Hide");
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");
Del("S"+S+":C:Mod");
}
//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;}