const moment = require("moment"); const nyoomfactor = { //Percentage of original speed to use when nyoom'ing 1: 1.0, 2: 1.0, 3: 1.0, 4: 1.0, 5: 1.0, 6: 1.0, 7: 1.0, 8: 1.0, 9: 1.0, 10: 1.0, 11: 1.0, 12: 0.9, 13: 0.7, 14: 0.5, 15: 0.3, 16: 0.3, 17: 0.3, 18: 0.3, 19: 0.3, 20: 0.3, 50: 0.81, 100: 0.76, 500: 0.28, 1000: 0.24, 2000: 0.09, 5000: 0.07, 10000: 0.04, 20000: 0.02, }; const slowdownFactor = { //Percentage of slowdown per hour. 1: 0.00001, 2: 0.00003, 3: 0.00003, 4: 0.00005, 5: 0.00005, 6: 0.00005, 7: 0.00005, 8: 0.00005, 9: 0.00005, 10: 0.00005, 11: 0.00005, 12: 0.00006, 13: 0.00007, 14: 0.00008, 15: 0.00009, 16: 0.0001, 17: 0.0001, 18: 0.0001, 19: 0.0001, 20: 0.0001, 50: 0.0002, 100: 0.0003, 500: 0.0004, 1000: 0.0005, 2000: 0.0005, 5000: 0.0005, 10000: 0.0005, 20000: 0.0005, }; class Prediction { chartData = {}; predictionChartData = {}; diffData = []; maxSpeed = 0; constructor(server) { } SetupPredictionModel(eventStart) { this.chartData = {}; this.predictionChartData = {}; if (this.chartData["1"] && this.chartData["1"].length > 400) { this.maxSpeed = Math.floor( this.chartData["1"][400].points / (moment(this.chartData["1"][400].date).diff(eventStart, "minutes") / 60) ); } else if (this.chartData["1"] && this.chartData["1"].length > 0) { this.maxSpeed = Math.floor( this.chartData["1"][this.chartData["1"].length - 1].points / (moment(this.chartData["1"][this.chartData["1"].length - 1].date).diff( eventStart, "minutes" ) / 60) ); } else { this.maxSpeed = 0; } } CreatePrediction(precision, rank, eventStart, eventEnd) { if (!this.chartData[rank]) return []; var startPoint = this.chartData[rank][this.chartData[rank].length - 1]; if (rank <= 20) startPoint = { points: startPoint.points, date: moment() }; var startTime = moment(startPoint.date); if ( startTime.diff(eventStart, "hours") > 36 && moment(startPoint.date).diff(eventStart, "hours") >= 36 ) { //console.log(this.maxSpeed) //Precision is in hours. 1 is default var finalChart = [ { y: this.chartData[rank][this.chartData[rank].length - 1].points, x: this.chartData[rank][this.chartData[rank].length - 1].date, }, ]; //Start from the time of the last reported rank. var myPoints = startPoint.points; var pointSpeed = Math.ceil( this.GetRank(rank) / (moment(startPoint.date).diff(eventStart, "minutes") / 60) ); var speedGoal = this.maxSpeed * nyoomfactor[rank]; while (startTime < eventEnd) { startTime.add(precision, "hours"); myPoints += Math.floor(pointSpeed); if (eventEnd.diff(startTime, "hours") > 11) { pointSpeed -= pointSpeed * (slowdownFactor[rank] * 10) /*CONSTANT for adjustment*/; } else { pointSpeed = Math.max( this.GetRank(rank) / (moment(startPoint.date).diff(eventStart, "minutes") / 60), Math.min( (12 - eventEnd.diff(startTime, "hours")) * (speedGoal / 5), speedGoal ) ); //pointSpeed+=(speedGoal-pointSpeed) //Increase towards final goal. //console.log(pointSpeed) } finalChart = [ ...finalChart, { y: Number.isInteger(myPoints) ? myPoints : "???", x: moment(startTime), }, ]; } this.predictionChartData[rank] = finalChart; return finalChart; } else if (startTime.diff(eventStart, "hours") > 24 && moment(startPoint.date).diff(eventStart, "hours") >= 24 ) { //console.log(this.maxSpeed) //Precision is in hours. 1 is default var finalChart = [ { y: this.chartData[rank][this.chartData[rank].length - 1].points, x: this.chartData[rank][this.chartData[rank].length - 1].date, }, ]; //Start from the time of the last reported rank. var myPoints = startPoint.points; var pointSpeed = this.GetRate(rank); var speedGoal = this.maxSpeed * nyoomfactor[rank]; while (startTime < eventEnd) { startTime.add(precision, "hours"); myPoints += Math.floor(pointSpeed); if (eventEnd.diff(startTime, "hours") > 11) { pointSpeed -= pointSpeed * (slowdownFactor[rank] * 10) /*CONSTANT for adjustment*/; } else { pointSpeed = Math.max( this.GetRank(rank) / (moment(startPoint.date).diff(eventStart, "minutes") / 60), Math.min( (12 - eventEnd.diff(startTime, "hours")) * (speedGoal / 5), speedGoal ) ); //pointSpeed+=(speedGoal-pointSpeed) //Increase towards final goal. //console.log(pointSpeed) } finalChart = [ ...finalChart, { y: Number.isInteger(myPoints) ? myPoints : "???", x: moment(startTime), }, ]; } this.predictionChartData[rank] = finalChart; return finalChart; } else { return []; } } GetRank(rank) { if (this.chartData[rank]) { return this.chartData[rank][this.chartData[rank].length - 1].points; } else { return "???"; } } GetEstimate(rank) { if (this.predictionChartData[rank]) { var currentEstimate = 0; if ( rank > 20 && moment().diff( moment(this.chartData[rank][this.chartData[rank].length - 1].date), "hours" ) > 0.5 ) { for (var i = this.predictionChartData[rank].length - 1; i >= 0; i--) { if (moment(this.predictionChartData[rank][i].x).isAfter(moment())) { currentEstimate = this.predictionChartData[rank][i].y; } else { break; } } return currentEstimate; } else { if (rank > 20) { return this.GetRank(rank); } else { return "---"; } } } else { return "???"; } } GetTime(rank) { if (this.chartData[rank]) { return moment(this.chartData[rank][this.chartData[rank].length - 1].date).fromNow(); } else { return ""; } } GetUpdateColor(rank, en) { if (this.chartData[rank]) { return ( "rgba(255," + Math.max( 255 - moment().diff( moment(this.chartData[rank][this.chartData[rank].length - 1].date), "hours" ) * 3, 0 ) + "," + Math.max( 255 - moment().diff( moment(this.chartData[rank][this.chartData[rank].length - 1].date), "hours" ) * 3, 0 ) + ",1)" ); } else { return ""; } } ChartData(rank, eventStart, eventEnd) { if (!this.chartData[rank]) { return [{ x: 0, y: 0 }]; } if (rank <= 20) { return [ ...this.chartData[rank].map((data) => { return { x: data.date, y: data.points }; }), { x: moment().isBefore(eventEnd) ? moment() : eventEnd, y: this.chartData[rank][this.chartData[rank].length - 1].points, }, ]; } else { return [ { x: eventStart, y: 0 }, ...this.chartData[rank].map((data) => { return { x: data.date, y: data.points }; }), ]; } } GetPointCount(rank) { let pointCount = 1; const RATEDURATION = 1; if (!this.chartData[rank]) { return pointCount; } if (this.chartData[rank].length > 2) { var lastpoint = this.chartData[rank][this.chartData[rank].length - 1]; for (var i = this.chartData[rank].length - 1; i >= 0; i--) { var diff = moment().diff(this.chartData[rank][i].date, "hours"); if (diff >= RATEDURATION) { break; } else { lastpoint = this.chartData[rank][i]; pointCount++; } } return pointCount; } else { if (this.chartData[rank].length > 0) { return this.chartData[rank].length; } else { return pointCount; } } } GetNameData = (t) => { var tempname = ""; if ( this.chartData[t] && this.chartData[t][this.chartData[t].length - 1] && this.chartData[t][this.chartData[t].length - 1].name ) { tempname = this.chartData[t][this.chartData[t].length - 1].name; } var tempdesc = ""; if ( this.chartData[t] && this.chartData[t][this.chartData[t].length - 1] && this.chartData[t][this.chartData[t].length - 1].description ) { tempdesc = this.chartData[t][this.chartData[t].length - 1].description; } return { tempname, tempdesc } } GetRate(rank, eventStart) { const RATEDURATION = 1; //In hours. How much EP/hr is shown. if (this.chartData[rank].length > 2) { var lastpoint = this.chartData[rank][this.chartData[rank].length - 1]; for (var i = this.chartData[rank].length - 1; i >= 0; i--) { var diff = moment().diff(this.chartData[rank][i].date, "hours"); if (diff >= RATEDURATION) { break; } else { lastpoint = this.chartData[rank][i]; } } var timediff = moment( this.chartData[rank][this.chartData[rank].length - 1].date ).diff(moment(lastpoint.date), "minutes"); if (timediff < 120) { if (lastpoint === this.chartData[rank][this.chartData[rank].length - 1]) { return "???"; } else return ( (this.chartData[rank][this.chartData[rank].length - 1].points - lastpoint.points) / RATEDURATION ); } else { return Math.ceil( (this.chartData[rank][this.chartData[rank].length - 1].points - lastpoint.points) / (moment(this.chartData[rank][this.chartData[rank].length - 1].date).diff( moment(lastpoint.date), "minutes" ) / 60) ); } } else { if (this.chartData[rank].length > 0) { var startPoint = this.chartData[rank][this.chartData[rank].length - 1]; return Math.ceil( this.GetRank(rank) / (moment(startPoint.date).diff(eventStart, "minutes") / 60) ); } else { return 0; } } } } exports.Prediction = Prediction