diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ICultivationItemsAccess.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ICultivationItemsAccess.cs index 050f1ba8..dd1fab0b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ICultivationItemsAccess.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Abstraction/ICultivationItemsAccess.cs @@ -7,5 +7,7 @@ namespace Snap.Hutao.Model.Metadata.Abstraction; internal interface ICultivationItemsAccess { + string Name { get; } + List CultivationItems { get; } -} +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs index a400fcac..5452b29a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/InventoryService.cs @@ -59,9 +59,11 @@ internal sealed partial class InventoryService : IInventoryService List cultivationItemsEntryList = [ .. await metadataService.GetAvatarListAsync().ConfigureAwait(false), - .. (await metadataService.GetWeaponListAsync().ConfigureAwait(false)).Where(weapon => weapon.Quality >= Model.Intrinsic.QualityType.QUALITY_BLUE), + .. await metadataService.GetWeaponListAsync().ConfigureAwait(false), ]; + cultivationItemsEntryList = MinimalPromotionDelta.Find(cultivationItemsEntryList); + BatchConsumption? batchConsumption = default; using (IServiceScope scope = serviceScopeFactory.CreateScope()) { diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs new file mode 100644 index 00000000..7ea09766 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Inventory/MinimalPromotionDelta.cs @@ -0,0 +1,57 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Google.OrTools.LinearSolver; +using Snap.Hutao.Core.ExceptionService; +using Snap.Hutao.Model.Metadata.Abstraction; +using Snap.Hutao.Model.Primitive; +using System.Runtime.InteropServices; + +namespace Snap.Hutao.Service.Inventory; + +internal static class MinimalPromotionDelta +{ + public static List Find(List cultivationItems) + { + using (Solver? solver = Solver.CreateSolver("SCIP")) + { + ArgumentNullException.ThrowIfNull(solver); + + Objective objective = solver.Objective(); + objective.SetMinimization(); + + Dictionary itemVariableMap = []; + foreach (ref readonly ICultivationItemsAccess item in CollectionsMarshal.AsSpan(cultivationItems)) + { + Variable variable = solver.MakeBoolVar(item.Name); + itemVariableMap[item] = variable; + objective.SetCoefficient(variable, 1); + } + + Dictionary materialConstraintMap = []; + foreach (ref readonly ICultivationItemsAccess item in CollectionsMarshal.AsSpan(cultivationItems)) + { + foreach (ref readonly MaterialId materialId in CollectionsMarshal.AsSpan(item.CultivationItems)) + { + ref Constraint? constraint = ref CollectionsMarshal.GetValueRefOrAddDefault(materialConstraintMap, materialId, out _); + constraint ??= solver.MakeConstraint(1, double.PositiveInfinity, $"{materialId}"); + constraint.SetCoefficient(itemVariableMap[item], 1); + } + } + + Solver.ResultStatus status = solver.Solve(); + HutaoException.ThrowIf(status != Solver.ResultStatus.OPTIMAL, "Unable to solve minimal item set"); + + List results = []; + foreach ((ICultivationItemsAccess item, Variable variable) in itemVariableMap) + { + if (variable.SolutionValue() > 0.5) + { + results.Add(item); + } + } + + return results; + } + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj index 40283196..8c589fbd 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -313,6 +313,7 @@ + all