TLThingy bug fix + progress bar now can appoximate number of wins/losses
This commit is contained in:
parent
7ffe909c2d
commit
6195705dcb
|
@ -0,0 +1,147 @@
|
||||||
|
import 'dart:math';
|
||||||
|
// I reimplemented kenany/glicko2-lite in dart lol
|
||||||
|
// Don't look here lol
|
||||||
|
|
||||||
|
List<double> scale(double rating, double rd, double options) {
|
||||||
|
double mu = (rating - options) / 173.7178;
|
||||||
|
double phi = rd / 173.7178;
|
||||||
|
return [ mu, phi ];
|
||||||
|
}
|
||||||
|
|
||||||
|
double g(phi) {
|
||||||
|
return 1 / sqrt(1 + 3 * pow(phi, 2) / pow(pi, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
double e(double mu, double muj, double phij) {
|
||||||
|
return 1 / (1 + exp(-g(phij) * (mu - muj)));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Map<String, double>> scaleOpponents(double mu, List<List<double>> opponents, double rating) {
|
||||||
|
return opponents.map((opp) {
|
||||||
|
var scaled = scale(opp[0], opp[1], rating);
|
||||||
|
return {
|
||||||
|
"muj": scaled[0],
|
||||||
|
"phij": scaled[1],
|
||||||
|
"gphij": g(scaled[1]),
|
||||||
|
"emmp": e(mu, scaled[0], scaled[1]),
|
||||||
|
"score": opp[2]
|
||||||
|
};
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
double updateRating(List<Map<String, double>> opponents) {
|
||||||
|
double value = pow(opponents.first["gphij"]!, 2) * opponents.first["emmp"]! * (1 - opponents.first["emmp"]!);
|
||||||
|
opponents.skip(1).forEach((element) {
|
||||||
|
value += pow(element["gphij"]!, 2) * element["emmp"]! * (1 - element["emmp"]!);
|
||||||
|
});
|
||||||
|
return 1 / value;
|
||||||
|
}
|
||||||
|
|
||||||
|
double computeDelta(v, List<Map<String, double>> opponents) {
|
||||||
|
double value = opponents.first["gphij"]! * (opponents.first["score"]! - opponents.first["emmp"]!);
|
||||||
|
opponents.skip(1).forEach((element) {
|
||||||
|
value += opponents.first["gphij"]! * (opponents.first["score"]! - opponents.first["emmp"]!);
|
||||||
|
});
|
||||||
|
return v * value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Function volF(double phi, double v, double delta, double a, double tau) {
|
||||||
|
num phi2 = pow(phi, 2);
|
||||||
|
num d2 = pow(delta, 2);
|
||||||
|
|
||||||
|
return (x) {
|
||||||
|
double ex = exp(x);
|
||||||
|
double a2 = phi2 + v + ex;
|
||||||
|
double p2 = (x - a) / pow(tau, 2);
|
||||||
|
double p1 = (ex * (d2 - phi2 - v - ex)) / (2 * pow(a2, 2));
|
||||||
|
return p1 - p2;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
double computeVolatility(double sigma, double phi, double v, double delta, double options) {
|
||||||
|
// 5.1
|
||||||
|
double a = log(pow(sigma, 2));
|
||||||
|
Function f = volF(phi, v, delta, a, options);
|
||||||
|
|
||||||
|
// 5.2
|
||||||
|
double b;
|
||||||
|
if (pow(delta, 2) > pow(phi, 2) + v) {
|
||||||
|
b = log(pow(delta, 2) - pow(phi, 2) - v);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
double k = 1;
|
||||||
|
while (f(a - k * options) < 0) {
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
b = a - k * options;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5.3
|
||||||
|
double fa = f(a);
|
||||||
|
double fb = f(b);
|
||||||
|
|
||||||
|
// 5.4
|
||||||
|
while ((b - a).abs() > 0.000001) {
|
||||||
|
double c = a + (a - b) * fa / (fb - fa);
|
||||||
|
double fc = f(c);
|
||||||
|
|
||||||
|
if (fc * fb <= 0) {
|
||||||
|
a = b;
|
||||||
|
fa = fb;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fa /= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
b = c;
|
||||||
|
fb = fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5.5
|
||||||
|
return exp(a / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
double phiStar(sigmap, phi) {
|
||||||
|
return sqrt(pow(sigmap, 2) + pow(phi, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, double> newRating(phis, mu, v, opponents) {
|
||||||
|
double phip = 1 / sqrt(1 / pow(phis, 2) + 1 / v);
|
||||||
|
double value = opponents.first["gphij"]! * (opponents.first["score"]! - opponents.first["emmp"]!);
|
||||||
|
opponents.skip(1).forEach((element) {
|
||||||
|
value += element["gphij"]! * (element["score"]! - element["emmp"]!);
|
||||||
|
});
|
||||||
|
double mup = mu + pow(phip, 2) * value;
|
||||||
|
return { "mu": mup, "phi": phip };
|
||||||
|
}
|
||||||
|
|
||||||
|
List<double> unscale(mup, phip, options) {
|
||||||
|
double rating = 173.7178 * mup + options["rating"];
|
||||||
|
double rd = 173.7178 * phip;
|
||||||
|
return [ rating, rd ];
|
||||||
|
}
|
||||||
|
|
||||||
|
List<double> rate(double rating, double rd, double sigma, List<List<double>> opponents, Map<String, double> options) {
|
||||||
|
Map<String, double> opts = { "rating": options["rating"]??1500, "tau": options["tau"]??0.5 };
|
||||||
|
|
||||||
|
// Step 2
|
||||||
|
List<double> scaled = scale(rating, rd, opts["rating"]!);
|
||||||
|
List<Map<String, double>> scaledOpponents = scaleOpponents(scaled[0], opponents, opts["rating"]!);
|
||||||
|
|
||||||
|
// Step 3
|
||||||
|
double v = updateRating(scaledOpponents);
|
||||||
|
|
||||||
|
// Step 4
|
||||||
|
double delta = computeDelta(v, scaledOpponents);
|
||||||
|
|
||||||
|
// Step 5
|
||||||
|
double sigmap = computeVolatility(sigma, scaled[1], v, delta, opts["tau"]!);
|
||||||
|
|
||||||
|
// Step 6
|
||||||
|
double phis = phiStar(sigmap, scaled[1]);
|
||||||
|
|
||||||
|
// Step 7
|
||||||
|
Map<String, double> updated = newRating(phis, scaled[0], v, scaledOpponents);
|
||||||
|
|
||||||
|
return unscale(updated['mu'], updated['phi'], opts)..add(sigmap);
|
||||||
|
}
|
|
@ -1856,11 +1856,12 @@ class TetrioPlayersLeaderboard {
|
||||||
"avgStride": avgStride,
|
"avgStride": avgStride,
|
||||||
"avgInfDS": avgInfDS,
|
"avgInfDS": avgInfDS,
|
||||||
"toEnterTR": rank.toLowerCase() != "z" ? leaderboard[(leaderboard.length * rankCutoffs[rank]!).floor()-1].rating : lowestTR,
|
"toEnterTR": rank.toLowerCase() != "z" ? leaderboard[(leaderboard.length * rankCutoffs[rank]!).floor()-1].rating : lowestTR,
|
||||||
|
"toEnterGlicko": rank.toLowerCase() != "z" ? leaderboard[(leaderboard.length * rankCutoffs[rank]!).floor()-1].glicko : 0,
|
||||||
"entries": filtredLeaderboard
|
"entries": filtredLeaderboard
|
||||||
}];
|
}];
|
||||||
}else{
|
}else{
|
||||||
return [TetraLeagueAlpha(timestamp: DateTime.now(), apm: 0, pps: 0, vs: 0, glicko: 0, rd: noTrRd, gamesPlayed: 0, gamesWon: 0, bestRank: rank, decaying: false, rating: 0, rank: rank, percentileRank: rank, percentile: rankCutoffs[rank]!, standing: -1, standingLocal: -1, nextAt: -1, prevAt: -1),
|
return [TetraLeagueAlpha(timestamp: DateTime.now(), apm: 0, pps: 0, vs: 0, glicko: 0, rd: noTrRd, gamesPlayed: 0, gamesWon: 0, bestRank: rank, decaying: false, rating: 0, rank: rank, percentileRank: rank, percentile: rankCutoffs[rank]!, standing: -1, standingLocal: -1, nextAt: -1, prevAt: -1),
|
||||||
{"players": filtredLeaderboard.length, "lowestTR": 0, "toEnterTR": 0}];
|
{"players": filtredLeaderboard.length, "lowestTR": 0, "toEnterTR": 0, "toEnterGlicko": 0}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1929,6 +1930,26 @@ class TetrioPlayersLeaderboard {
|
||||||
'd': getAverageOfRank("d")[1]["toEnterTR"]
|
'd': getAverageOfRank("d")[1]["toEnterTR"]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Map<String, double> get cutoffsGlicko => {
|
||||||
|
'x': getAverageOfRank("x")[1]["toEnterGlicko"],
|
||||||
|
'u': getAverageOfRank("u")[1]["toEnterGlicko"],
|
||||||
|
'ss': getAverageOfRank("ss")[1]["toEnterGlicko"],
|
||||||
|
's+': getAverageOfRank("s+")[1]["toEnterGlicko"],
|
||||||
|
's': getAverageOfRank("s")[1]["toEnterGlicko"],
|
||||||
|
's-': getAverageOfRank("s-")[1]["toEnterGlicko"],
|
||||||
|
'a+': getAverageOfRank("a+")[1]["toEnterGlicko"],
|
||||||
|
'a': getAverageOfRank("a")[1]["toEnterGlicko"],
|
||||||
|
'a-': getAverageOfRank("a-")[1]["toEnterGlicko"],
|
||||||
|
'b+': getAverageOfRank("b+")[1]["toEnterGlicko"],
|
||||||
|
'b': getAverageOfRank("b")[1]["toEnterGlicko"],
|
||||||
|
'b-': getAverageOfRank("b-")[1]["toEnterGlicko"],
|
||||||
|
'c+': getAverageOfRank("c+")[1]["toEnterGlicko"],
|
||||||
|
'c': getAverageOfRank("c")[1]["toEnterGlicko"],
|
||||||
|
'c-': getAverageOfRank("c-")[1]["toEnterGlicko"],
|
||||||
|
'd+': getAverageOfRank("d+")[1]["toEnterGlicko"],
|
||||||
|
'd': getAverageOfRank("d")[1]["toEnterGlicko"]
|
||||||
|
};
|
||||||
|
|
||||||
TetrioPlayersLeaderboard.fromJson(List<dynamic> json, String t, DateTime ts) {
|
TetrioPlayersLeaderboard.fromJson(List<dynamic> json, String t, DateTime ts) {
|
||||||
type = t;
|
type = t;
|
||||||
timestamp = ts;
|
timestamp = ts;
|
||||||
|
|
|
@ -86,6 +86,8 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
||||||
TetraLeagueAlpha? rankAverages;
|
TetraLeagueAlpha? rankAverages;
|
||||||
double? thatRankCutoff;
|
double? thatRankCutoff;
|
||||||
double? nextRankCutoff;
|
double? nextRankCutoff;
|
||||||
|
double? thatRankGlickoCutoff;
|
||||||
|
double? nextRankGlickoCutoff;
|
||||||
String _searchFor = "6098518e3d5155e6ec429cdc"; // who we looking for
|
String _searchFor = "6098518e3d5155e6ec429cdc"; // who we looking for
|
||||||
String _titleNickname = "";
|
String _titleNickname = "";
|
||||||
/// Each dropdown menu item contains list of dots for the graph
|
/// Each dropdown menu item contains list of dots for the graph
|
||||||
|
@ -198,8 +200,11 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
||||||
}
|
}
|
||||||
if (me.tlSeason1.gamesPlayed > 9) {
|
if (me.tlSeason1.gamesPlayed > 9) {
|
||||||
thatRankCutoff = everyone!.cutoffs[me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank];
|
thatRankCutoff = everyone!.cutoffs[me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank];
|
||||||
|
thatRankGlickoCutoff = everyone!.cutoffsGlicko[me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank];
|
||||||
nextRankCutoff = everyone!.cutoffs[ranks.elementAtOrNull(ranks.indexOf(me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank)+1)];
|
nextRankCutoff = everyone!.cutoffs[ranks.elementAtOrNull(ranks.indexOf(me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank)+1)];
|
||||||
|
nextRankGlickoCutoff = everyone!.cutoffsGlicko[ranks.elementAtOrNull(ranks.indexOf(me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank)+1)];
|
||||||
nextRankCutoff = nextRankCutoff??25000;
|
nextRankCutoff = nextRankCutoff??25000;
|
||||||
|
nextRankGlickoCutoff = nextRankGlickoCutoff??double.infinity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +277,6 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
||||||
// Also i need previous Tetra League State for comparison if avaliable
|
// Also i need previous Tetra League State for comparison if avaliable
|
||||||
if (uniqueTL.length >= 2){
|
if (uniqueTL.length >= 2){
|
||||||
compareWith = uniqueTL.toList().elementAtOrNull(uniqueTL.length - 2);
|
compareWith = uniqueTL.toList().elementAtOrNull(uniqueTL.length - 2);
|
||||||
//chartsData = [for (var tl in uniqueTL) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, [tl.rating, tl.glicko, tl.rd, tl.apm, tl.pps, tl.vs, tl.nerdStats?.app, tl.nerdStats?.dss, tl.nerdStats?.dsp, tl.nerdStats?.appdsp, tl.nerdStats?.vsapm, tl.nerdStats?.cheese, tl.nerdStats?.gbe, tl.nerdStats?.nyaapp, tl.nerdStats?.area, tl.estTr?.esttr, tl.esttracc, tl.playstyle?.opener, tl.playstyle?.plonk, tl.playstyle?.infds, tl.playstyle?.stride])];
|
|
||||||
chartsData = <DropdownMenuItem<List<_HistoryChartSpot>>>[ // Dumping charts data into dropdown menu items, while cheking if every entry is valid
|
chartsData = <DropdownMenuItem<List<_HistoryChartSpot>>>[ // Dumping charts data into dropdown menu items, while cheking if every entry is valid
|
||||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.rating)], child: Text(t.statCellNum.tr)),
|
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.rating)], child: Text(t.statCellNum.tr)),
|
||||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.glicko!)], child: const Text("Glicko")),
|
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.glicko!)], child: const Text("Glicko")),
|
||||||
|
@ -455,9 +459,11 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
||||||
bot: snapshot.data![0].role == "bot",
|
bot: snapshot.data![0].role == "bot",
|
||||||
guest: snapshot.data![0].role == "anon",
|
guest: snapshot.data![0].role == "anon",
|
||||||
thatRankCutoff: thatRankCutoff,
|
thatRankCutoff: thatRankCutoff,
|
||||||
|
thatRankCutoffGlicko: thatRankGlickoCutoff,
|
||||||
thatRankTarget: snapshot.data![0].tlSeason1.rank != "z" ? rankTargets[snapshot.data![0].tlSeason1.rank] : null,
|
thatRankTarget: snapshot.data![0].tlSeason1.rank != "z" ? rankTargets[snapshot.data![0].tlSeason1.rank] : null,
|
||||||
nextRankCutoff: nextRankCutoff,
|
nextRankCutoff: nextRankCutoff,
|
||||||
nextRankTarget: snapshot.data![0].tlSeason1.rank != "z" || snapshot.data![0].tlSeason1.rank != "x" ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(snapshot.data![0].tlSeason1.rank)+1)] : null,
|
nextRankCutoffGlicko: nextRankGlickoCutoff,
|
||||||
|
nextRankTarget: (snapshot.data![0].tlSeason1.rank != "z" && snapshot.data![0].tlSeason1.rank != "x") ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(snapshot.data![0].tlSeason1.rank)+1)] : null,
|
||||||
averages: rankAverages,
|
averages: rankAverages,
|
||||||
lbPositions: meAmongEveryone
|
lbPositions: meAmongEveryone
|
||||||
),
|
),
|
||||||
|
@ -934,26 +940,44 @@ class _HistoryChartThigyState extends State<_HistoryChartThigy> {
|
||||||
child: SfCartesianChart(
|
child: SfCartesianChart(
|
||||||
tooltipBehavior: _tooltipBehavior,
|
tooltipBehavior: _tooltipBehavior,
|
||||||
zoomPanBehavior: _zoomPanBehavior,
|
zoomPanBehavior: _zoomPanBehavior,
|
||||||
primaryXAxis: DateTimeAxis(),
|
primaryXAxis: _gamesPlayedInsteadOfDateAndTime ? NumericAxis() : DateTimeAxis(),
|
||||||
primaryYAxis: NumericAxis(
|
primaryYAxis: NumericAxis(
|
||||||
rangePadding: ChartRangePadding.additional,
|
rangePadding: ChartRangePadding.additional,
|
||||||
),
|
),
|
||||||
series: <CartesianSeries>[
|
series: <CartesianSeries>[
|
||||||
StepLineSeries<_HistoryChartSpot, DateTime>(
|
if (_gamesPlayedInsteadOfDateAndTime) StepLineSeries<_HistoryChartSpot, int>(
|
||||||
enableTooltip: true,
|
enableTooltip: true,
|
||||||
// splineType: SplineType.cardinal,
|
// splineType: SplineType.cardinal,
|
||||||
// cardinalSplineTension: 0.2,
|
// cardinalSplineTension: 0.2,
|
||||||
dataSource: widget.data,
|
dataSource: widget.data,
|
||||||
animationDuration: 0,
|
animationDuration: 0,
|
||||||
opacity: 1,
|
opacity: _smooth ? 0 : 1,
|
||||||
|
xValueMapper: (_HistoryChartSpot data, _) => data.gamesPlayed,
|
||||||
|
yValueMapper: (_HistoryChartSpot data, _) => data.stat,
|
||||||
|
trendlines:<Trendline>[
|
||||||
|
Trendline(
|
||||||
|
isVisible: _smooth,
|
||||||
|
period: (widget.data.length/175).floor(),
|
||||||
|
type: TrendlineType.movingAverage,
|
||||||
|
color: Colors.blue)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
else StepLineSeries<_HistoryChartSpot, DateTime>(
|
||||||
|
enableTooltip: true,
|
||||||
|
// splineType: SplineType.cardinal,
|
||||||
|
// cardinalSplineTension: 0.2,
|
||||||
|
dataSource: widget.data,
|
||||||
|
animationDuration: 0,
|
||||||
|
opacity: _smooth ? 0 : 1,
|
||||||
xValueMapper: (_HistoryChartSpot data, _) => data.timestamp,
|
xValueMapper: (_HistoryChartSpot data, _) => data.timestamp,
|
||||||
yValueMapper: (_HistoryChartSpot data, _) => data.stat,
|
yValueMapper: (_HistoryChartSpot data, _) => data.stat,
|
||||||
// trendlines:<Trendline>[
|
trendlines:<Trendline>[
|
||||||
// Trendline(
|
Trendline(
|
||||||
// period: (widget.data.length/175).floor(),
|
isVisible: _smooth,
|
||||||
// type: TrendlineType.movingAverage,
|
period: (widget.data.length/175).floor(),
|
||||||
// color: Colors.blue)
|
type: TrendlineType.movingAverage,
|
||||||
// ],
|
color: Colors.blue)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -200,7 +200,7 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
|
||||||
direction: Axis.horizontal,
|
direction: Axis.horizontal,
|
||||||
alignment: WrapAlignment.center,
|
alignment: WrapAlignment.center,
|
||||||
crossAxisAlignment: WrapCrossAlignment.end,
|
crossAxisAlignment: WrapCrossAlignment.end,
|
||||||
spacing: 25,
|
spacing: 20,
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
|
|
|
@ -98,7 +98,7 @@ class GaugetNum extends StatelessWidget {
|
||||||
oldPlayerStat! > playerStat ? Colors.redAccent : Colors.greenAccent :
|
oldPlayerStat! > playerStat ? Colors.redAccent : Colors.greenAccent :
|
||||||
oldPlayerStat! < playerStat ? Colors.redAccent : Colors.greenAccent
|
oldPlayerStat! < playerStat ? Colors.redAccent : Colors.greenAccent
|
||||||
),),
|
),),
|
||||||
if ((oldTl != null && oldTl!.gamesPlayed > 0) && pos != null) const TextSpan(text: " • "),
|
if (oldPlayerStat != null && pos != null) const TextSpan(text: " • "),
|
||||||
if (pos != null) TextSpan(text: pos!.position >= 1000 ? "${t.top} ${f2.format(pos!.percentage*100)}%" : "№${pos!.position}", style: TextStyle(color: getColorOfRank(pos!.position)))
|
if (pos != null) TextSpan(text: pos!.position >= 1000 ? "${t.top} ${f2.format(pos!.percentage*100)}%" : "№${pos!.position}", style: TextStyle(color: getColorOfRank(pos!.position)))
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
|
|
@ -3,27 +3,26 @@ import 'dart:math';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:syncfusion_flutter_gauges/gauges.dart';
|
import 'package:syncfusion_flutter_gauges/gauges.dart';
|
||||||
|
import 'package:tetra_stats/data_objects/glicko.dart';
|
||||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||||
import 'package:tetra_stats/gen/strings.g.dart';
|
import 'package:tetra_stats/gen/strings.g.dart';
|
||||||
import 'package:tetra_stats/utils/numers_formats.dart';
|
import 'package:tetra_stats/utils/numers_formats.dart';
|
||||||
|
|
||||||
class TLProgress extends StatelessWidget{
|
class TLProgress extends StatelessWidget{
|
||||||
final double tr;
|
final TetraLeagueAlpha tlData;
|
||||||
final String rank;
|
|
||||||
final int position;
|
|
||||||
final String? nextRank;
|
final String? nextRank;
|
||||||
final String? previousRank;
|
final String? previousRank;
|
||||||
final int nextRankPosition;
|
|
||||||
final int previousRankPosition;
|
|
||||||
final double? nextRankTRcutoff;
|
final double? nextRankTRcutoff;
|
||||||
final double? previousRankTRcutoff;
|
final double? previousRankTRcutoff;
|
||||||
|
final double? nextRankGlickoCutoff;
|
||||||
|
final double? previousGlickoCutoff;
|
||||||
final double? nextRankTRcutoffTarget;
|
final double? nextRankTRcutoffTarget;
|
||||||
final double? previousRankTRcutoffTarget;
|
final double? previousRankTRcutoffTarget;
|
||||||
|
|
||||||
const TLProgress({super.key, required this.tr, required this.rank, required this.position, required this.nextRankPosition, required this.previousRankPosition, this.nextRank, this.previousRank, this.nextRankTRcutoff, this.previousRankTRcutoff, this.nextRankTRcutoffTarget, this.previousRankTRcutoffTarget});
|
const TLProgress({super.key, required this.tlData, this.nextRank, this.previousRank, this.nextRankTRcutoff, this.previousRankTRcutoff, this.nextRankGlickoCutoff, this.previousGlickoCutoff, this.nextRankTRcutoffTarget, this.previousRankTRcutoffTarget});
|
||||||
|
|
||||||
double getBarPosition(){
|
double getBarPosition(){
|
||||||
return min(max(0, 1 - (position - nextRankPosition)/(previousRankPosition - nextRankPosition)), 1);
|
return min(max(0, 1 - (tlData.standing - tlData.nextAt)/(tlData.prevAt - tlData.nextAt)), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
double? getBarTR(double tr){
|
double? getBarTR(double tr){
|
||||||
|
@ -32,45 +31,51 @@ class TLProgress extends StatelessWidget{
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// return Container(
|
final glickoForWin = rate(tlData.glicko!, tlData.rd!, 0.06, [[tlData.glicko!, tlData.rd!, 1]], {})[0]-tlData.glicko!;
|
||||||
// alignment: Alignment.centerLeft,
|
|
||||||
// height: 50,
|
|
||||||
// width: MediaQuery.of(context).size.width,
|
|
||||||
// color: Colors.blue,
|
|
||||||
// child: Container(
|
|
||||||
// width: MediaQuery.of(context).size.width / 2,
|
|
||||||
// height: 50,
|
|
||||||
// color: Colors.red,
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: SfLinearGauge(
|
child: Column(
|
||||||
minimum: 0,
|
mainAxisSize: MainAxisSize.min,
|
||||||
maximum: 1,
|
children: [
|
||||||
interval: 1,
|
SfLinearGauge(
|
||||||
ranges: [
|
minimum: 0,
|
||||||
if (previousRankTRcutoff != null && nextRankTRcutoff != null) LinearGaugeRange(endValue: getBarTR(tr)!, color: Colors.cyanAccent, position: LinearElementPosition.cross),
|
maximum: 1,
|
||||||
if (position != -1) LinearGaugeRange(endValue: getBarPosition(), color: Colors.cyanAccent, position: LinearElementPosition.cross),
|
interval: 1,
|
||||||
if (previousRankTRcutoff != null && previousRankTRcutoffTarget != null) LinearGaugeRange(endValue: getBarTR(previousRankTRcutoffTarget!)!, color: Colors.greenAccent, position: LinearElementPosition.inside),
|
ranges: [
|
||||||
if (nextRankTRcutoff != null && nextRankTRcutoffTarget != null) LinearGaugeRange(startValue: getBarTR(nextRankTRcutoffTarget!)!, endValue: 1, color: Colors.yellowAccent, position: LinearElementPosition.inside)
|
if (previousRankTRcutoff != null && nextRankTRcutoff != null) LinearGaugeRange(endValue: getBarTR(tlData.rating)!, color: Colors.cyanAccent, position: LinearElementPosition.cross)
|
||||||
|
else if (tlData.standing != -1) LinearGaugeRange(endValue: getBarPosition(), color: Colors.cyanAccent, position: LinearElementPosition.cross),
|
||||||
|
if (previousRankTRcutoff != null && previousRankTRcutoffTarget != null) LinearGaugeRange(endValue: getBarTR(previousRankTRcutoffTarget!)!, color: Colors.greenAccent, position: LinearElementPosition.inside),
|
||||||
|
if (nextRankTRcutoff != null && nextRankTRcutoffTarget != null) LinearGaugeRange(startValue: getBarTR(nextRankTRcutoffTarget!)!, endValue: 1, color: Colors.yellowAccent, position: LinearElementPosition.inside)
|
||||||
|
],
|
||||||
|
markerPointers: [
|
||||||
|
LinearShapePointer(value: (previousRankTRcutoff != null && nextRankTRcutoff != null) ? getBarTR(tlData.rating)! : getBarPosition(), position: LinearElementPosition.cross, shapeType: LinearShapePointerType.diamond, color: Colors.white, height: 20),
|
||||||
|
LinearWidgetPointer(offset: 4, position: LinearElementPosition.outside, value: (previousRankTRcutoff != null && nextRankTRcutoff != null) ? getBarTR(tlData.rating)! : getBarPosition(), child: Text("№ ${NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0).format(tlData.standing)}"))
|
||||||
|
],
|
||||||
|
isMirrored: true,
|
||||||
|
showTicks: true,
|
||||||
|
axisLabelStyle: TextStyle(),
|
||||||
|
onGenerateLabels: () => [
|
||||||
|
LinearAxisLabel(text: "${tlData.prevAt > 0 ? "№ ${f0.format(tlData.prevAt)}" : ""}\n ${intf.format(previousRankTRcutoff)} TR", value: 0),
|
||||||
|
LinearAxisLabel(text: "${tlData.nextAt > 0 ? "№ ${f0.format(tlData.nextAt)}" : ""}\n ${intf.format(nextRankTRcutoff)} TR", value: 1),
|
||||||
|
],
|
||||||
|
// labelFormatterCallback: (value) {
|
||||||
|
// if (value == "0") return "${f0.format(previousRankPosition)}\n 26,700 TR";
|
||||||
|
// else return f0.format(nextRankPosition);
|
||||||
|
// },
|
||||||
|
showLabels: true
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
height: 20,
|
||||||
|
child: Stack(
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [
|
||||||
|
Positioned(child: Text("${f2.format(tlData.rating-previousRankTRcutoff!)} (${f2.format((tlData.glicko!-previousGlickoCutoff!)/glickoForWin)} losses)"), left: 0,),
|
||||||
|
Positioned(child: Text("${f2.format(nextRankTRcutoff!-tlData.rating)} (${f2.format((nextRankGlickoCutoff!-tlData.glicko!)/glickoForWin)} wins)"), right: 0,)
|
||||||
|
],),
|
||||||
|
)
|
||||||
|
|
||||||
],
|
],
|
||||||
markerPointers: [
|
|
||||||
LinearShapePointer(value: getBarPosition(), position: LinearElementPosition.cross, shapeType: LinearShapePointerType.diamond, color: Colors.white, height: 20),
|
|
||||||
LinearWidgetPointer(offset: 4, position: LinearElementPosition.outside, value: getBarPosition(), child: Text("№ ${NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0).format(position)}"))
|
|
||||||
],
|
|
||||||
isMirrored: true,
|
|
||||||
showTicks: true,
|
|
||||||
axisLabelStyle: TextStyle(),
|
|
||||||
onGenerateLabels: () => [
|
|
||||||
LinearAxisLabel(text: "№ ${f0.format(previousRankPosition)}\n ${intf.format(previousRankTRcutoff)} TR", value: 0),
|
|
||||||
LinearAxisLabel(text: "№ ${f0.format(nextRankPosition)}\n ${intf.format(nextRankTRcutoff)} TR", value: 1),
|
|
||||||
],
|
|
||||||
// labelFormatterCallback: (value) {
|
|
||||||
// if (value == "0") return "${f0.format(previousRankPosition)}\n 26,700 TR";
|
|
||||||
// else return f0.format(nextRankPosition);
|
|
||||||
// },
|
|
||||||
showLabels: true
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,6 @@ import 'package:tetra_stats/widgets/tl_progress_bar.dart';
|
||||||
var fDiff = NumberFormat("+#,###.###;-#,###.###");
|
var fDiff = NumberFormat("+#,###.###;-#,###.###");
|
||||||
var intFDiff = NumberFormat("+#,###;-#,###");
|
var intFDiff = NumberFormat("+#,###;-#,###");
|
||||||
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
|
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
|
||||||
late RangeValues _currentRangeValues;
|
|
||||||
TetraLeagueAlpha? oldTl;
|
|
||||||
late TetraLeagueAlpha currentTl;
|
|
||||||
late List<TetrioPlayer> sortedStates;
|
|
||||||
|
|
||||||
class TLThingy extends StatefulWidget {
|
class TLThingy extends StatefulWidget {
|
||||||
final TetraLeagueAlpha tl;
|
final TetraLeagueAlpha tl;
|
||||||
|
@ -30,10 +26,12 @@ class TLThingy extends StatefulWidget {
|
||||||
final PlayerLeaderboardPosition? lbPositions;
|
final PlayerLeaderboardPosition? lbPositions;
|
||||||
final TetraLeagueAlpha? averages;
|
final TetraLeagueAlpha? averages;
|
||||||
final double? thatRankCutoff;
|
final double? thatRankCutoff;
|
||||||
|
final double? thatRankCutoffGlicko;
|
||||||
final double? thatRankTarget;
|
final double? thatRankTarget;
|
||||||
final double? nextRankCutoff;
|
final double? nextRankCutoff;
|
||||||
|
final double? nextRankCutoffGlicko;
|
||||||
final double? nextRankTarget;
|
final double? nextRankTarget;
|
||||||
const TLThingy({super.key, required this.tl, required this.userID, required this.states, this.showTitle = true, this.bot=false, this.guest=false, this.topTR, this.lbPositions, this.averages, this.nextRankCutoff = 25000, this.thatRankCutoff = 0, this.nextRankTarget = 25000, this.thatRankTarget = 0});
|
const TLThingy({super.key, required this.tl, required this.userID, required this.states, this.showTitle = true, this.bot=false, this.guest=false, this.topTR, this.lbPositions, this.averages, this.nextRankCutoff = 25000, this.thatRankCutoff = 0, this.thatRankCutoffGlicko = 0, this.nextRankCutoffGlicko = double.infinity, this.nextRankTarget = 25000, this.thatRankTarget = 0});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<TLThingy> createState() => _TLThingyState();
|
State<TLThingy> createState() => _TLThingyState();
|
||||||
|
@ -41,6 +39,10 @@ class TLThingy extends StatefulWidget {
|
||||||
|
|
||||||
class _TLThingyState extends State<TLThingy> {
|
class _TLThingyState extends State<TLThingy> {
|
||||||
late bool oskKagariGimmick;
|
late bool oskKagariGimmick;
|
||||||
|
late TetraLeagueAlpha? oldTl;
|
||||||
|
late TetraLeagueAlpha currentTl;
|
||||||
|
late RangeValues _currentRangeValues;
|
||||||
|
late List<TetrioPlayer> sortedStates;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -161,14 +163,13 @@ class _TLThingyState extends State<TLThingy> {
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
TLProgress(
|
TLProgress(
|
||||||
tr: currentTl.rating,
|
tlData: currentTl,
|
||||||
rank: currentTl.rank != "z" ? currentTl.rank : currentTl.percentileRank,
|
|
||||||
position: currentTl.standing,
|
|
||||||
nextRankPosition: currentTl.nextAt,
|
|
||||||
previousRankPosition: currentTl.prevAt,
|
|
||||||
previousRankTRcutoff: widget.thatRankCutoff,
|
previousRankTRcutoff: widget.thatRankCutoff,
|
||||||
|
previousGlickoCutoff: widget.thatRankCutoffGlicko,
|
||||||
|
previousRank: widget.tl.prevRank,
|
||||||
previousRankTRcutoffTarget: widget.thatRankTarget,
|
previousRankTRcutoffTarget: widget.thatRankTarget,
|
||||||
nextRankTRcutoff: widget.nextRankCutoff,
|
nextRankTRcutoff: widget.nextRankCutoff,
|
||||||
|
nextRankGlickoCutoff: widget.nextRankCutoffGlicko,
|
||||||
nextRankTRcutoffTarget: widget.nextRankTarget,
|
nextRankTRcutoffTarget: widget.nextRankTarget,
|
||||||
nextRank: widget.tl.nextRank
|
nextRank: widget.tl.nextRank
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in New Issue