A suite to track Project Diva score statistics and ratings / D4DJ event data.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
projectdivar/server/routes/utils/prediction.js

400 lines
13 KiB

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