diff --git a/DragonFruit.Six.Api.Tests/Utils/SeasonalRanksTests.cs b/DragonFruit.Six.Api.Tests/Utils/SeasonalRanksTests.cs index ae34a74b..26aafd75 100644 --- a/DragonFruit.Six.Api.Tests/Utils/SeasonalRanksTests.cs +++ b/DragonFruit.Six.Api.Tests/Utils/SeasonalRanksTests.cs @@ -15,7 +15,7 @@ public class SeasonalRanksTests [TestCase(6050, 23)] public void TestMMR(int mmr, byte expectedRankId) { - var rank = SeasonalRanks.GetFromMMR(mmr); + var rank = SeasonalRanks.GetRank(mmr, 22, true); Assert.AreEqual(expectedRankId, rank.Id); } @@ -25,8 +25,15 @@ public void TestMMR(int mmr, byte expectedRankId) [TestCase(4800, 20)] public void TestLegacyMMR(int mmr, byte expectedRankId) { - var rank = SeasonalRanks.GetFromMMR(mmr, true); + var rank = SeasonalRanks.GetRank(mmr, 12, true); Assert.AreEqual(expectedRankId, rank.Id); } + + [Test] + public void TestInvalidRank() + { + var rank = SeasonalRanks.GetRank(100); + Assert.AreEqual(rank.Name, "Unranked"); + } } } diff --git a/DragonFruit.Six.Api/Containers/RankInfo.cs b/DragonFruit.Six.Api/Containers/RankInfo.cs index 198bd31c..5d97e739 100644 --- a/DragonFruit.Six.Api/Containers/RankInfo.cs +++ b/DragonFruit.Six.Api/Containers/RankInfo.cs @@ -1,11 +1,12 @@ // Dragon6 API Copyright 2020 DragonFruit Network // Licensed under Apache-2. Please refer to the LICENSE file for more info +using System; using Newtonsoft.Json; namespace DragonFruit.Six.Api.Containers { - public class RankInfo + public struct RankInfo : IEquatable, IEquatable { internal RankInfo(byte id, string name, string iconUrl, int? minMMR, int? maxMMR) { @@ -46,5 +47,8 @@ internal RankInfo(byte id, string name, string iconUrl, int? minMMR, int? maxMMR /// [JsonProperty("mmr_max")] public int? MaxMMR { get; set; } + + public bool Equals(int other) => Id == other; + public bool Equals(RankInfo other) => Equals(other.Id); } } diff --git a/DragonFruit.Six.Api/Entities/SeasonStats.cs b/DragonFruit.Six.Api/Entities/SeasonStats.cs index 26b3fc71..ba5f2287 100644 --- a/DragonFruit.Six.Api/Entities/SeasonStats.cs +++ b/DragonFruit.Six.Api/Entities/SeasonStats.cs @@ -14,7 +14,7 @@ namespace DragonFruit.Six.Api.Entities [JsonObject(MemberSerialization.OptIn)] public class SeasonStats : StatsBase, IAssociatedWithAccount, IStatsResponse { - private RankInfo _rankInfo, _maxRankInfo, _mmrRankInfo; + private RankInfo? _rankInfo, _maxRankInfo, _mmrRankInfo; [JsonProperty("profile")] internal string ProfileId { get; set; } @@ -75,10 +75,9 @@ public class SeasonStats : StatsBase, IAssociatedWithAccount, IStatsResponse #endregion - public bool IsLegacySeason => SeasonId < 14; - public RankInfo RankInfo => _rankInfo ??= SeasonalRanks.GetFromId(Rank, IsLegacySeason); - public RankInfo MaxRankInfo => _maxRankInfo ??= SeasonalRanks.GetFromId(MaxRank, IsLegacySeason); - public RankInfo MMRRankInfo => _mmrRankInfo ??= SeasonalRanks.GetFromMMR((int)MMR, IsLegacySeason); + public RankInfo RankInfo => _rankInfo ??= SeasonalRanks.GetRank(Rank, SeasonId); + public RankInfo MaxRankInfo => _maxRankInfo ??= SeasonalRanks.GetRank(MaxRank, SeasonId); + public RankInfo MMRRankInfo => _mmrRankInfo ??= SeasonalRanks.GetRank((int)MMR, SeasonId, true); public bool IsAssociatedWithAccount(AccountInfo account) => account.Identifiers.Profile.Equals(ProfileId); } diff --git a/DragonFruit.Six.Api/Utils/SeasonalRanks.cs b/DragonFruit.Six.Api/Utils/SeasonalRanks.cs index 48ae8485..431789ef 100644 --- a/DragonFruit.Six.Api/Utils/SeasonalRanks.cs +++ b/DragonFruit.Six.Api/Utils/SeasonalRanks.cs @@ -9,29 +9,88 @@ namespace DragonFruit.Six.Api.Utils public static class SeasonalRanks { /// - /// Gets the for the current rank based on id + /// Gets the for the provided rank and season /// - public static RankInfo GetFromId(int rankId, bool legacy = false) => GetRank(rankId, legacy); + /// The id of the rank to return + /// The id of the season (optional, -1 for the latest season) + public static RankInfo GetFromId(int id, int season = -1) => GetRank(id, season); /// - /// Gets the for the current rank based on the MMR value + /// Gets the for the provided mmr and season /// - public static RankInfo GetFromMMR(int mmr, bool legacy = false) => GetRank(mmr, legacy, true); + /// The mmr of the rank to return + /// The id of the season (optional, -1 for the latest season) + public static RankInfo GetFromMMR(int mmr, int season = -1) => GetRank(mmr, season, true); - private static RankInfo GetRank(int identifier, bool isLegacyRanks = false, bool isMMR = false) + /// + /// Gets the for the identifier and season + /// + /// The id of the rank or the mmr + /// The id of the season (optional, -1 for the latest season) + /// Whether the represents mmr. + public static RankInfo GetRank(int identifier, int season = -1, bool isMMR = false) { - var itemsSource = isLegacyRanks ? LegacyRanks : Ranks; - - return isMMR switch + var itemsSource = season switch { - false => itemsSource.ElementAtOrDefault(identifier) ?? Ranks[0], - true => itemsSource.Where(x => (x.MinMMR ?? int.MinValue) <= identifier && (x.MaxMMR ?? int.MaxValue) >= identifier).OrderByDescending(x => x.Id).First() + // season 1-14 + >= 01 and <= 14 => RankingV1, + + // season 15-22 + >= 15 and <= 22 => RankingV2, + + // season 23- (incl. latest season identifier) + _ => RankingV3 }; + + if (isMMR) + { + return itemsSource.Where(x => (x.MinMMR ?? int.MinValue) <= identifier && (x.MaxMMR ?? int.MaxValue) >= identifier).OrderByDescending(x => x.Id).First(); + } + + // if the rank isn't in the array, return generic unranked one + return identifier + 1 <= itemsSource.Length ? itemsSource[identifier] : RankingV3[0]; } - #region Data Source + #region Data Sources + + private static readonly RankInfo[] RankingV3 = + { + new(0, "Unranked", "/rank/v3/0.svg", null, null), + + new(1, "Copper 5", "/rank/v3/1.svg", null, 1199), + new(2, "Copper 4", "/rank/v3/2.svg", 1200, 1299), + new(3, "Copper 3", "/rank/v3/3.svg", 1300, 1399), + new(4, "Copper 2", "/rank/v3/4.svg", 1400, 1499), + new(5, "Copper 1", "/rank/v3/5.svg", 1500, 1599), + + new(6, "Bronze 5", "/rank/v3/6.svg", 1600, 1699), + new(7, "Bronze 4", "/rank/v3/7.svg", 1700, 1799), + new(8, "Bronze 3", "/rank/v3/8.svg", 1800, 1899), + new(9, "Bronze 2", "/rank/v3/9.svg", 1900, 1999), + new(10, "Bronze 1", "/rank/v3/10.svg", 2000, 2099), + + new(11, "Silver 5", "/rank/v3/11.svg", 2100, 2199), + new(12, "Silver 4", "/rank/v3/12.svg", 2200, 2299), + new(13, "Silver 3", "/rank/v3/13.svg", 2300, 2399), + new(14, "Silver 2", "/rank/v3/14.svg", 2400, 2499), + new(15, "Silver 1", "/rank/v3/15.svg", 2500, 2599), + + new(16, "Gold 3", "/rank/v3/16.svg", 2600, 2799), + new(17, "Gold 2", "/rank/v3/17.svg", 2800, 2999), + new(18, "Gold 1", "/rank/v3/18.svg", 3000, 3199), + + new(19, "Platinum 3", "/rank/v3/19.svg", 3200, 3499), + new(20, "Platinum 2", "/rank/v3/20.svg", 3500, 3799), + new(21, "Platinum 1", "/rank/v3/21.svg", 3800, 4099), + + new(22, "Diamond 3", "/rank/v3/22.svg", 4100, 4399), + new(23, "Diamond 2", "/rank/v3/23.svg", 4400, 4699), + new(24, "Diamond 1", "/rank/v3/24.svg", 4700, 4999), + + new(25, "Champion", "/rank/v3/25.svg", 5000, null) + }; - private static readonly RankInfo[] Ranks = + private static readonly RankInfo[] RankingV2 = { new(0, "Unranked", "/rank/v2/0.svg", null, null), @@ -65,7 +124,7 @@ private static RankInfo GetRank(int identifier, bool isLegacyRanks = false, bool new(23, "Champion", "/rank/v2/23.svg", 5000, null) }; - private static readonly RankInfo[] LegacyRanks = + private static readonly RankInfo[] RankingV1 = { new(0, "Unranked", "/rank/v1/0.svg", null, null),