Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resin predict #2253

Merged
merged 11 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Snap.Hutao/.editorconfig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[*]
[*]
charset = utf-8

[*.cs]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ internal static class IntrinsicFrozen
SH.ModelMetadataMaterialWeaponAscensionMaterial,
];

public static FrozenSet<string> ResinMaterialTypeDescriptions { get; } =
[
SH.ModelMetadataMaterialCharacterTalentMaterial,
SH.ModelMetadataMaterialCharacterLevelUpMaterial,
SH.ModelMetadataMaterialWeaponAscensionMaterial,
];

private static FrozenSet<string> NamesFromEnum<TEnum>(Func<TEnum, string?> selector)
where TEnum : struct, Enum
{
Expand Down
2 changes: 2 additions & 0 deletions src/Snap.Hutao/Snap.Hutao/Model/Metadata/Item/DisplayItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ internal class DisplayItem

public required string Name { get; init; }

public uint Rank { get; init; }

public string? Description { get; init; }

public string? TypeDescription { get; init; }
Expand Down
17 changes: 17 additions & 0 deletions src/Snap.Hutao/Snap.Hutao/Model/Metadata/Item/Material.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,23 @@ public bool IsInventoryItem()
return IntrinsicFrozen.MaterialTypeDescriptions.Contains(TypeDescription);
}

public bool IsResinItem()
{
// 摩拉 大英雄的经验
if (Id == 202U || Id == 104003U)
{
return true;
}

// 无类型 智识之冕
if (TypeDescription is null || Id == 104319U)
{
return false;
}

return IntrinsicFrozen.ResinMaterialTypeDescriptions.Contains(TypeDescription);
}

public bool IsTodaysItem(bool treatSundayAsTrue = false)
{
// TODO: support different time zone
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.

using System.Collections.Immutable;

namespace Snap.Hutao.Model.Metadata.Item;

internal sealed class MaterialDropDistribution
{
public static readonly MaterialDropDistribution Nine = new(9, 60000D, 122500D, 10.12, 16.708, 3, 2.4);
public static readonly MaterialDropDistribution Eight = new(8, 60000D, 122500D, 10.12, 16.708, 2.56, 2.4);
public static readonly MaterialDropDistribution Seven = new(7, 60000D, 122500D, 10.12, 16.708, 2.56, 2.1);
public static readonly MaterialDropDistribution Six = new(6, 60000D, 122500D, 10.12, 16.708, 2.2, 1.55);
public static readonly MaterialDropDistribution Five = new(5, 52000D, 102500D, 7.8, 16.708, 2.2, 1D);

private MaterialDropDistribution(int worldLevel, double blossomOfWealth, double blossomOfRevelation, double talentBooks, double weaponAscension, double normalBoss, double weeklyBoss)
{
WorldLevel = worldLevel;
BlossomOfWealth = blossomOfWealth;
BlossomOfRevelation = blossomOfRevelation;
TalentBooks = talentBooks;
WeaponAscension = weaponAscension;
NormalBoss = normalBoss;
WeeklyBoss = weeklyBoss;
}

public static ImmutableArray<MaterialDropDistribution> Distributions { get; } = [Five, Six, Seven, Eight, Nine];

public int WorldLevel { get; }

public double BlossomOfWealth { get; }

public double BlossomOfRevelation { get; }

public double TalentBooks { get; }

public double WeaponAscension { get; }

public double NormalBoss { get; }

public double WeeklyBoss { get; }
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1817,6 +1817,27 @@
<data name="ViewModelCultivationRemoveProjectTitle" xml:space="preserve">
<value>确认要删除当前计划吗?</value>
</data>
<data name="ViewModelCultivationResinStatisticsBlossomOfRevelationTitle" xml:space="preserve">
<value>启示之花</value>
</data>
<data name="ViewModelCultivationResinStatisticsBlossomOfWealthTitle" xml:space="preserve">
<value>藏金之花</value>
</data>
<data name="ViewModelCultivationResinStatisticsItemRemainDays" xml:space="preserve">
<value>还需 {0} 天</value>
</data>
<data name="ViewModelCultivationResinStatisticsNormalBossTitle" xml:space="preserve">
<value>世界 BOSS</value>
</data>
<data name="ViewModelCultivationResinStatisticsTalentAscensionTitle" xml:space="preserve">
<value>角色天赋素材</value>
</data>
<data name="ViewModelCultivationResinStatisticsWeaponAscensionTitle" xml:space="preserve">
<value>武器突破素材</value>
</data>
<data name="ViewModelCultivationResinStatisticsWeeklyBossTitle" xml:space="preserve">
<value>周常 BOSS</value>
</data>
<data name="ViewModelDailyNoteConfigWebhookUrlComplete" xml:space="preserve">
<value>实时便笺 Webhook Url 配置成功</value>
</data>
Expand Down Expand Up @@ -2264,6 +2285,12 @@
<data name="ViewPageCultivationMaterialListIncompleteFirstLabel" xml:space="preserve">
<value>未集齐优先</value>
</data>
<data name="ViewPageCultivationMaterialListResinStatisticsLabel" xml:space="preserve">
<value>树脂预估</value>
</data>
<data name="ViewPageCultivationMaterialListWorldLevelTitle" xml:space="preserve">
<value>世界等级</value>
</data>
<data name="ViewPageCultivationMaterialStatistics" xml:space="preserve">
<value>材料统计</value>
</data>
Expand Down
100 changes: 100 additions & 0 deletions src/Snap.Hutao/Snap.Hutao/Service/Cultivation/AlchemyCrafting.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.

using System.Runtime.CompilerServices;

namespace Snap.Hutao.Service.Cultivation;

internal static class AlchemyCrafting
{
public static double UnWeighted(double orangeItems, double purpleItems, double blueItems, double greenItems)
{
return Weighted(orangeItems * 27, purpleItems * 9, blueItems * 3, greenItems);
}

public static double Weighted(double orangeItems, double purpleItems, double blueItems, double greenItems)
{
if (orangeItems > 0)
{
if (purpleItems > 0)
{
if (blueItems > 0)
{
if (greenItems > 0)
{
return orangeItems + purpleItems + blueItems + greenItems;
}

return Delta(orangeItems + purpleItems + blueItems, -greenItems);
}

if (greenItems > 0)
{
return Delta(orangeItems + purpleItems, -blueItems) + greenItems;
}

return Delta(orangeItems + purpleItems, -(blueItems + greenItems));
}

if (blueItems > 0)
{
if (greenItems > 0)
{
return Delta(orangeItems, -purpleItems) + blueItems + greenItems;
}

return Delta(Delta(orangeItems, -purpleItems) + blueItems, -greenItems);
}

if (greenItems > 0)
{
return Delta(orangeItems, -(purpleItems + blueItems)) + greenItems;
}

return Delta(orangeItems, -(purpleItems + blueItems + greenItems));
}

if (purpleItems > 0)
{
if (blueItems > 0)
{
if (greenItems > 0)
{
return purpleItems + blueItems + greenItems;
}

return Delta(purpleItems + blueItems, -greenItems);
}

if (greenItems > 0)
{
return Delta(purpleItems, -blueItems) + greenItems;
}

return Delta(purpleItems, -(blueItems + greenItems));
}

if (blueItems > 0)
{
if (greenItems > 0)
{
return blueItems + greenItems;
}

return Delta(blueItems, -greenItems);
}

if (greenItems > 0)
{
return greenItems;
}

return 0D;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static double Delta(double expect, double actual)
{
return expect - Math.Min(expect, actual);
}
}
106 changes: 103 additions & 3 deletions src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
// Copyright (c) DGP Studio. All rights reserved.
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.

using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Model.Entity.Primitive;
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata.Item;
using Snap.Hutao.Service.Cultivation.Consumption;
using Snap.Hutao.Service.Inventory;
using Snap.Hutao.Service.Metadata.ContextAbstraction;
using Snap.Hutao.ViewModel.Cultivation;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using System.Diagnostics;
using ModelItem = Snap.Hutao.Model.Item;

namespace Snap.Hutao.Service.Cultivation;
Expand Down Expand Up @@ -91,7 +95,7 @@ public async ValueTask<ObservableCollection<StatisticsCultivateItem>> GetStatist

if (resultItems.SingleOrDefault(i => i.Inner.Id == item.ItemId) is { } existedItem)
{
existedItem.Count += item.Count;
existedItem.Count += (int)item.Count;
}
else
{
Expand All @@ -104,13 +108,109 @@ public async ValueTask<ObservableCollection<StatisticsCultivateItem>> GetStatist
{
if (resultItems.SingleOrDefault(i => i.Inner.Id == inventoryItem.ItemId) is { } existedItem)
{
existedItem.TotalCount += inventoryItem.Count;
existedItem.Current += (int)inventoryItem.Count;
}
}

return resultItems.SortBy(item => item.Inner.Id, MaterialIdComparer.Shared).ToObservableCollection();
}

public async ValueTask<ResinStatistics> GetResinStatisticsAsync(ObservableCollection<StatisticsCultivateItem> statisticsCultivateItems, CancellationToken token)
{
await taskContext.SwitchToBackgroundAsync();
ResinStatistics statistics = new();

IEnumerable<IGrouping<uint, StatisticsCultivateItem>> groupedItems = statisticsCultivateItems
.Where(i => i.Inner.IsResinItem())
.GroupBy(i => i.Inner.Rank);

foreach ((uint rank, IEnumerable<StatisticsCultivateItem> items) in groupedItems)
{
// 摩拉
if (rank is 10U)
{
StatisticsCultivateItem item = items.Single();
if (item.IsFinished)
{
continue;
}

double times = item.Count - item.Current;
statistics.BlossomOfWealth.RawItemCount += times;
continue;
}

// 经验
if (rank is 100U)
{
StatisticsCultivateItem item = items.Single();
Debug.Assert(item.Inner.RankLevel is QualityType.QUALITY_PURPLE, "经验书必须是紫色品质");
if (item.IsFinished)
{
continue;
}

double times = (item.Count - item.Current) * 20000D;
statistics.BlossomOfRevelation.RawItemCount += times;
continue;
}

// BOSS 掉落
if (rank is 11101U)
{
foreach (StatisticsCultivateItem item in items)
{
if (item.IsFinished)
{
continue;
}

double times = item.Count - item.Current;
_ = item.Inner.RankLevel switch
{
QualityType.QUALITY_PURPLE => statistics.NormalBoss.RawItemCount += times,
QualityType.QUALITY_ORANGE => statistics.WeeklyBoss.RawItemCount += times,
_ => throw HutaoException.NotSupported(),
};
}

continue;
}

// 天赋书,武器突破材料
// items.Count in [0..4]
double greenItems = 0D;
double blueItems = 0D;
double purpleItems = 0D;
double orangeItems = 0D;

// ABCDE -> B
ResinStatisticsItem targetStatisticsItem = ((rank / 1000) % 10) switch
{
3 => statistics.TalentAscension,
5 => statistics.WeaponAscension,
_ => throw HutaoException.NotSupported(),
};

foreach (StatisticsCultivateItem item in items)
{
double times = item.Count - item.Current;
_ = item.Inner.RankLevel switch
{
QualityType.QUALITY_GREEN => greenItems += times,
QualityType.QUALITY_BLUE => blueItems += times,
QualityType.QUALITY_PURPLE => purpleItems += times,
QualityType.QUALITY_ORANGE => orangeItems += times,
_ => throw HutaoException.NotSupported(),
};
}

targetStatisticsItem.RawItemCount += AlchemyCrafting.UnWeighted(orangeItems, purpleItems, blueItems, greenItems);
}

return statistics;
}

public async ValueTask RemoveCultivateEntryAsync(Guid entryId)
{
// Invalidate cache
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ internal interface ICultivationService

ValueTask<ObservableCollection<CultivateEntryView>> GetCultivateEntryCollectionAsync(CultivateProject cultivateProject, ICultivationMetadataContext context);

ValueTask<ObservableCollection<StatisticsCultivateItem>> GetStatisticsCultivateItemCollectionAsync(
CultivateProject cultivateProject, ICultivationMetadataContext context, CancellationToken token);
ValueTask<ObservableCollection<StatisticsCultivateItem>> GetStatisticsCultivateItemCollectionAsync(CultivateProject cultivateProject, ICultivationMetadataContext context, CancellationToken token);

ValueTask<ResinStatistics> GetResinStatisticsAsync(ObservableCollection<StatisticsCultivateItem> statisticsCultivateItems, CancellationToken token);

ValueTask RemoveCultivateEntryAsync(Guid entryId);

Expand Down
Loading
Loading