From e952edb7dcc7866e29006e3b31ee886c1112994b Mon Sep 17 00:00:00 2001 From: dan63047 Date: Mon, 11 Mar 2024 01:34:30 +0300 Subject: [PATCH] Comparing against rank averages --- lib/data_objects/tetrio.dart | 2 +- lib/views/main_view.dart | 7 ++++-- lib/views/rank_averages_view.dart | 20 +++++++++------- lib/views/settings_view.dart | 4 ++-- lib/widgets/stat_sell_num.dart | 22 ++++++++++++----- lib/widgets/tl_thingy.dart | 39 +++++++++++++++++++------------ 6 files changed, 60 insertions(+), 34 deletions(-) diff --git a/lib/data_objects/tetrio.dart b/lib/data_objects/tetrio.dart index f5cffb2..d67a38b 100644 --- a/lib/data_objects/tetrio.dart +++ b/lib/data_objects/tetrio.dart @@ -1851,7 +1851,7 @@ class TetrioPlayersLeaderboard { Stats.app, Stats.vsapm, Stats.dss, Stats.dsp, Stats.appdsp, Stats.cheese, Stats.gbe, Stats.nyaapp, Stats.area, Stats.eTR, Stats.acceTR]; List results = []; for (Stats stat in stats) { - List sortedLeaderboard = getStatRanking(fakePositions ? copyOfLeaderboard : leaderboard, stat, reversed: false); + List sortedLeaderboard = getStatRanking(fakePositions ? copyOfLeaderboard : leaderboard, stat, reversed: stat == Stats.cheese ? true : false); int position = sortedLeaderboard.indexWhere((element) => element.userId == user.userId) + 1; if (position == 0) { results.add(null); diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index 1a576fd..1819f64 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -32,6 +32,7 @@ import 'package:go_router/go_router.dart'; Future me = Future.delayed(const Duration(seconds: 60), () => [null, null, null, null, null, null]); // I love lists shut up TetrioPlayersLeaderboard? everyone; PlayerLeaderboardPosition? meAmongEveryone; +TetraLeagueAlpha? rankAverages; String _searchFor = "6098518e3d5155e6ec429cdc"; // who we looking for String _titleNickname = "dan63047"; final TetrioService teto = TetrioService(); // thing, that manadge our local DB @@ -73,14 +74,14 @@ String get40lTime(int microseconds){ String readableTimeDifference(Duration a, Duration b){ Duration result = a - b; - return "${NumberFormat("0.000s;0.000s", LocaleSettings.currentLocale.languageCode).format(result.inMilliseconds/1000)}"; + return NumberFormat("0.000s;0.000s", LocaleSettings.currentLocale.languageCode).format(result.inMilliseconds/1000); } /// Readable [a] - [b], without sign String readableIntDifference(int a, int b){ int result = a - b; - return "${NumberFormat("#,###;#,###", LocaleSettings.currentLocale.languageCode).format(result)}"; + return NumberFormat("#,###;#,###", LocaleSettings.currentLocale.languageCode).format(result); } class _MainState extends State with TickerProviderStateMixin { @@ -178,6 +179,8 @@ class _MainState extends State with TickerProviderStateMixin { if (meAmongEveryone != null) teto.cacheLeaderboardPositions(me.userId, meAmongEveryone!); } + if (everyone != null && me.tlSeason1.gamesPlayed > 9) rankAverages = everyone?.averages[me.tlSeason1.percentileRank]?[0]; + // Making list of Tetra League matches List tlMatches = []; bool isTracking = await teto.isPlayerTracking(me.userId); diff --git a/lib/views/rank_averages_view.dart b/lib/views/rank_averages_view.dart index 96ffe5c..19651d5 100644 --- a/lib/views/rank_averages_view.dart +++ b/lib/views/rank_averages_view.dart @@ -16,6 +16,7 @@ Stats _chartsX = Stats.tr; Stats _chartsY = Stats.apm; List _itemStats = [for (MapEntry e in chartsShortTitles.entries) DropdownMenuItem(value: e.key, child: Text(e.value))]; Stats _sortBy = Stats.tr; +late List they; bool _reversed = false; List _itemCountries = [for (MapEntry e in t.countries.entries) DropdownMenuItem(value: e.key, child: Text(e.value))]; String _country = ""; @@ -61,6 +62,7 @@ class RankState extends State with SingleTickerProviderStateMixin { } super.initState(); previousAxisTitles = _chartsX.toString()+_chartsY.toString(); + they = TetrioPlayersLeaderboard("lol", []).getStatRanking(widget.rank[1]["entries"]!, _sortBy, reversed: _reversed, country: _country); recalculateBoundaries(); resetScale(); } @@ -73,7 +75,7 @@ class RankState extends State with SingleTickerProviderStateMixin { } else { return element; } - }).getStatByEnum(_chartsX) as double; + }).getStatByEnum(_chartsX).toDouble(); actualMaxX = (widget.rank[1]["entries"] as List).reduce((value, element) { num n = max(value.getStatByEnum(_chartsX), element.getStatByEnum(_chartsX)); if (value.getStatByEnum(_chartsX) == n) { @@ -81,7 +83,7 @@ class RankState extends State with SingleTickerProviderStateMixin { } else { return element; } - }).getStatByEnum(_chartsX) as double; + }).getStatByEnum(_chartsX).toDouble(); actualMinY = (widget.rank[1]["entries"] as List).reduce((value, element) { num n = min(value.getStatByEnum(_chartsY), element.getStatByEnum(_chartsY)); if (value.getStatByEnum(_chartsY) == n) { @@ -89,7 +91,7 @@ class RankState extends State with SingleTickerProviderStateMixin { } else { return element; } - }).getStatByEnum(_chartsY) as double; + }).getStatByEnum(_chartsY).toDouble(); actualMaxY = (widget.rank[1]["entries"] as List).reduce((value, element) { num n = max(value.getStatByEnum(_chartsY), element.getStatByEnum(_chartsY)); if (value.getStatByEnum(_chartsY) == n) { @@ -97,7 +99,7 @@ class RankState extends State with SingleTickerProviderStateMixin { } else { return element; } - }).getStatByEnum(_chartsY) as double; + }).getStatByEnum(_chartsY).toDouble(); } void resetScale(){ @@ -164,7 +166,7 @@ class RankState extends State with SingleTickerProviderStateMixin { previousAxisTitles = _chartsX.toString()+_chartsY.toString(); } final t = Translations.of(context); - List they = TetrioPlayersLeaderboard("lol", []).getStatRanking(widget.rank[1]["entries"]!, _sortBy, reversed: _reversed, country: _country); + //they = TetrioPlayersLeaderboard("lol", []).getStatRanking(widget.rank[1]["entries"]!, _sortBy, reversed: _reversed, country: _country); return Scaffold( appBar: AppBar( title: Text(widget.rank[1]["everyone"] ? t.everyoneAverages : t.rankAverages(rank: widget.rank[0].rank.toUpperCase())), @@ -327,8 +329,8 @@ class RankState extends State with SingleTickerProviderStateMixin { for (TetrioPlayerFromLeaderboard entry in widget.rank[1]["entries"]) if (entry.apm != 0.0 && entry.vs != 0.0) // prevents from ScatterChart "Offset argument contained a NaN value." exception _MyScatterSpot( - entry.getStatByEnum(_chartsX) as double, - entry.getStatByEnum(_chartsY) as double, + entry.getStatByEnum(_chartsX).toDouble(), + entry.getStatByEnum(_chartsY).toDouble(), entry.userId, entry.username, dotPainter: FlDotCirclePainter(color: rankColors[entry.rank]??Colors.white, radius: 3)) @@ -403,7 +405,9 @@ class RankState extends State with SingleTickerProviderStateMixin { value: _sortBy, onChanged: ((value) { _sortBy = value; - setState(() {}); + setState(() { + they = TetrioPlayersLeaderboard("lol", []).getStatRanking(widget.rank[1]["entries"]!, _sortBy, reversed: _reversed, country: _country); + }); }), ), ], diff --git a/lib/views/settings_view.dart b/lib/views/settings_view.dart index 97e3c70..3aa1c11 100644 --- a/lib/views/settings_view.dart +++ b/lib/views/settings_view.dart @@ -266,8 +266,8 @@ class SettingsState extends State { onTap: () { Navigator.pushNamed(context, "/customization"); },), - ListTile(title: Text("Show LB position for each stat"), - subtitle: Text("That will impact on app performance..."), + ListTile(title: Text("Show leaderboard based stats"), + subtitle: Text("That will impact on loading times, but will allow you to see position on LB by stats and comparison with average values"), trailing: Switch(value: showPositions, onChanged: (bool value){ prefs.setBool("showPositions", value); setState(() { diff --git a/lib/widgets/stat_sell_num.dart b/lib/widgets/stat_sell_num.dart index 09bead9..839457a 100644 --- a/lib/widgets/stat_sell_num.dart +++ b/lib/widgets/stat_sell_num.dart @@ -10,11 +10,12 @@ class StatCellNum extends StatelessWidget { required this.playerStat, required this.playerStatLabel, required this.isScreenBig, + this.smallDecimal = true, this.alertWidgets, this.fractionDigits, this.oldPlayerStat, required this.higherIsBetter, - this.okText, this.alertTitle, this.pos}); + this.okText, this.alertTitle, this.pos, this.averageStat}); final num playerStat; final num? oldPlayerStat; @@ -22,10 +23,22 @@ class StatCellNum extends StatelessWidget { final String playerStatLabel; final String? okText; final bool isScreenBig; + final bool smallDecimal; final String? alertTitle; final List? alertWidgets; final int? fractionDigits; final LeaderboardPosition? pos; + final num? averageStat; + + Color getStatColor(){ + if (averageStat == null) return Colors.white; + num percentile = (higherIsBetter ? playerStat / averageStat! : averageStat! / playerStat).abs(); + if (percentile > 1.50) return Colors.purpleAccent; + else if (percentile > 1.20) return Colors.blueAccent; + else if (percentile > 0.90) return Colors.greenAccent; + else if (percentile > 0.70) return Colors.yellowAccent; + else return Colors.redAccent; + } @override Widget build(BuildContext context) { @@ -33,20 +46,17 @@ class StatCellNum extends StatelessWidget { NumberFormat fractionf = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: fractionDigits ?? 0)..maximumIntegerDigits = 0; num fraction = playerStat.isNegative ? 1 - (playerStat - playerStat.floor()) : playerStat - playerStat.floor(); int integer = playerStat.isNegative ? (playerStat + fraction).toInt() : (playerStat - fraction).toInt(); - // String valueAsString = fractionDigits == null ? f.format(playerStat.floor()) : f.format(playerStat); - // var exploded = valueAsString.split("."); return Column( children: [ RichText( text: TextSpan(text: intf.format(integer), children: [ - TextSpan(text: fractionf.format(fraction).substring(1), style: const TextStyle(fontSize: 16)) + TextSpan(text: fractionf.format(fraction).substring(1), style: smallDecimal ? const TextStyle(fontSize: 16) : null) ], style: TextStyle( fontFamily: "Eurostile Round Extended", - //fontWeight: FontWeight.bold, fontSize: isScreenBig ? 32 : 24, - color: Colors.white + color: getStatColor() ) ) ), diff --git a/lib/widgets/tl_thingy.dart b/lib/widgets/tl_thingy.dart index ecd13ee..6db515d 100644 --- a/lib/widgets/tl_thingy.dart +++ b/lib/widgets/tl_thingy.dart @@ -4,6 +4,7 @@ import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:syncfusion_flutter_gauges/gauges.dart'; import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/utils/numers_formats.dart'; +import 'package:tetra_stats/views/main_view.dart'; import 'package:tetra_stats/widgets/gauget_num.dart'; import 'package:tetra_stats/widgets/graphs.dart'; import 'package:tetra_stats/widgets/stat_sell_num.dart'; @@ -25,7 +26,8 @@ class TLThingy extends StatefulWidget { final bool guest; final double? topTR; final PlayerLeaderboardPosition? lbPositions; - 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}); + final TetraLeagueAlpha? averages; + 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}); @override State createState() => _TLThingyState(); @@ -150,7 +152,7 @@ class _TLThingyState extends State { softWrap: true, textAlign: TextAlign.center, style: TextStyle( - fontFamily: "Eurostile Round Extended", + fontFamily: "Eurostile Round", fontSize: bigScreen ? 42 : 28, overflow: TextOverflow.visible, )), @@ -163,13 +165,13 @@ class _TLThingyState extends State { crossAxisAlignment: WrapCrossAlignment.start, clipBehavior: Clip.hardEdge, children: [ - if (currentTl.apm != null) StatCellNum(playerStat: currentTl.apm!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.apm, higherIsBetter: true, oldPlayerStat: oldTl?.apm, pos: widget.lbPositions?.apm), - if (currentTl.pps != null) StatCellNum(playerStat: currentTl.pps!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.pps, higherIsBetter: true, oldPlayerStat: oldTl?.pps, pos: widget.lbPositions?.pps), - if (currentTl.vs != null) StatCellNum(playerStat: currentTl.vs!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.vs, higherIsBetter: true, oldPlayerStat: oldTl?.vs, pos: widget.lbPositions?.vs), + if (currentTl.apm != null) StatCellNum(playerStat: currentTl.apm!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.apm, higherIsBetter: true, oldPlayerStat: oldTl?.apm, pos: widget.lbPositions?.apm, averageStat: rankAverages?.apm), + if (currentTl.pps != null) StatCellNum(playerStat: currentTl.pps!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.pps, higherIsBetter: true, oldPlayerStat: oldTl?.pps, pos: widget.lbPositions?.pps, averageStat: rankAverages?.pps, smallDecimal: false), + if (currentTl.vs != null) StatCellNum(playerStat: currentTl.vs!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.vs, higherIsBetter: true, oldPlayerStat: oldTl?.vs, pos: widget.lbPositions?.vs, averageStat: rankAverages?.vs), if (currentTl.standingLocal > 0) StatCellNum(playerStat: currentTl.standingLocal, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.lbpc, higherIsBetter: false, oldPlayerStat: oldTl?.standingLocal), StatCellNum(playerStat: currentTl.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.gamesPlayed, higherIsBetter: true, oldPlayerStat: oldTl?.gamesPlayed, pos: widget.lbPositions?.gamesPlayed), StatCellNum(playerStat: currentTl.gamesWon, isScreenBig: bigScreen, playerStatLabel: t.statCellNum.gamesWonTL, higherIsBetter: true, oldPlayerStat: oldTl?.gamesWon, pos: widget.lbPositions?.gamesWon), - StatCellNum(playerStat: currentTl.winrate * 100, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.winrate, higherIsBetter: true, oldPlayerStat: oldTl != null ? oldTl!.winrate*100 : null, pos: widget.lbPositions?.winrate), + StatCellNum(playerStat: currentTl.winrate * 100, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.winrate, higherIsBetter: true, oldPlayerStat: oldTl != null ? oldTl!.winrate*100 : null, pos: widget.lbPositions?.winrate, averageStat: rankAverages != null ? rankAverages!.winrate * 100 : null), ], ), ), @@ -214,7 +216,8 @@ class _TLThingyState extends State { clipBehavior: Clip.hardEdge, children: [ StatCellNum(playerStat: currentTl.nerdStats!.dss, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.dss, - pos: widget.lbPositions?.dss, + pos: widget.lbPositions?.dss, + averageStat: rankAverages?.nerdStats?.dss, smallDecimal: false, alertWidgets: [Text(t.statCellNum.dssDescription), Text("${t.formula}: (VS / 100) - (APM / 60)"), Text("${t.exactValue}: ${currentTl.nerdStats!.dss}"),], @@ -222,7 +225,8 @@ class _TLThingyState extends State { higherIsBetter: true, oldPlayerStat: oldTl?.nerdStats?.dss,), StatCellNum(playerStat: currentTl.nerdStats!.dsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.dsp, - pos: widget.lbPositions?.dsp, + pos: widget.lbPositions?.dsp, + averageStat: rankAverages?.nerdStats?.dsp, smallDecimal: false, alertWidgets: [Text(t.statCellNum.dspDescription), Text("${t.formula}: DS/S / PPS"), Text("${t.exactValue}: ${currentTl.nerdStats!.dsp}"),], @@ -230,7 +234,8 @@ class _TLThingyState extends State { higherIsBetter: true, oldPlayerStat: oldTl?.nerdStats?.dsp,), StatCellNum(playerStat: currentTl.nerdStats!.appdsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.appdsp, - pos: widget.lbPositions?.appdsp, + pos: widget.lbPositions?.appdsp, + averageStat: rankAverages?.nerdStats?.appdsp, smallDecimal: false, alertWidgets: [Text(t.statCellNum.appdspDescription), Text("${t.formula}: APP + DS/P"), Text("${t.exactValue}: ${currentTl.nerdStats!.appdsp}"),], @@ -238,15 +243,17 @@ class _TLThingyState extends State { higherIsBetter: true, oldPlayerStat: oldTl?.nerdStats?.appdsp,), StatCellNum(playerStat: currentTl.nerdStats!.cheese, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.cheese, - pos: widget.lbPositions?.cheese, + pos: widget.lbPositions?.cheese, + averageStat: rankAverages?.nerdStats?.cheese, alertWidgets: [Text(t.statCellNum.cheeseDescription), Text("${t.formula}: (DS/P * 150) + ((VS/APM - 2) * 50) + (0.6 - APP) * 125"), Text("${t.exactValue}: ${currentTl.nerdStats!.cheese}"),], okText: t.popupActions.ok, - higherIsBetter: true, + higherIsBetter: false, oldPlayerStat: oldTl?.nerdStats?.cheese,), StatCellNum(playerStat: currentTl.nerdStats!.gbe, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.gbe, - pos: widget.lbPositions?.gbe, + pos: widget.lbPositions?.gbe, + averageStat: rankAverages?.nerdStats?.gbe, smallDecimal: false, alertWidgets: [Text(t.statCellNum.gbeDescription), Text("${t.formula}: APP * DS/P * 2"), Text("${t.exactValue}: ${currentTl.nerdStats!.gbe}"),], @@ -254,7 +261,8 @@ class _TLThingyState extends State { higherIsBetter: true, oldPlayerStat: oldTl?.nerdStats?.gbe,), StatCellNum(playerStat: currentTl.nerdStats!.nyaapp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.nyaapp, - pos: widget.lbPositions?.nyaapp, + pos: widget.lbPositions?.nyaapp, + averageStat: rankAverages?.nerdStats?.nyaapp, smallDecimal: false, alertWidgets: [Text(t.statCellNum.nyaappDescription), Text("${t.formula}: APP - 5 * tan(radians((Cheese Index / -30) + 1))"), Text("${t.exactValue}: ${currentTl.nerdStats!.nyaapp}"),], @@ -262,7 +270,8 @@ class _TLThingyState extends State { higherIsBetter: true, oldPlayerStat: oldTl?.nerdStats?.nyaapp,), StatCellNum(playerStat: currentTl.nerdStats!.area, isScreenBig: bigScreen, fractionDigits: 1, playerStatLabel: t.statCellNum.area, - pos: widget.lbPositions?.area, + pos: widget.lbPositions?.area, + averageStat: rankAverages?.nerdStats?.area, alertWidgets: [Text(t.statCellNum.areaDescription), Text("${t.formula}: APM * 1 + PPS * 45 + VS * 0.444 + APP * 185 + DS/S * 175 + DS/P * 450 + Garbage Effi * 315"), Text("${t.exactValue}: ${currentTl.nerdStats!.area}"),], @@ -314,7 +323,7 @@ class _TLThingyState extends State { RichText( text: TextSpan( text: (currentTl.esttracc != null) ? intFDiff.format(currentTl.esttracc!.truncate()) : "-", - style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: FontWeight.w500), + style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 36, fontWeight: FontWeight.w500), children: [ TextSpan(text: (currentTl.esttracc != null) ? fractionfEstTRAcc.format(currentTl.esttracc!.isNegative ? 1 - (currentTl.esttracc! - currentTl.esttracc!.truncate()) : (currentTl.esttracc! - currentTl.esttracc!.truncate())).substring(1) : ".---", style: TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100)) ]