Skip to content

Commit

Permalink
material trader suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
msarilar committed Apr 30, 2018
1 parent 711b2be commit 78dd21a
Show file tree
Hide file tree
Showing 13 changed files with 538 additions and 67 deletions.
2 changes: 2 additions & 0 deletions EDEngineer.Models/EDEngineer.Models.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="MaterialTrading\MaterialTrade.cs" />
<Compile Include="MaterialTrading\MaterialTrader.cs" />
<Compile Include="State\CommanderAggregation.cs" />
<Compile Include="State\StateAggregation.cs" />
<Compile Include="BlueprintCategory.cs" />
Expand Down
24 changes: 24 additions & 0 deletions EDEngineer.Models/MaterialTrading/MaterialTrade.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace EDEngineer.Models
{
public class MaterialTrade
{
public MaterialTrade(Entry traded, Entry needed, int tradedNeeded, int missing, bool willBeEnough, int alreadyNeeded)
{
Traded = traded;
Needed = needed;
TradedNeeded = tradedNeeded;
Missing = missing;
WillBeEnough = willBeEnough;
AlreadyNeeded = alreadyNeeded;
}

public Entry Traded { get; }
public Entry Needed { get; }
public int TradedNeeded { get; }
public int Missing { get; }
public bool WillBeEnough { get; }
public int AlreadyNeeded { get; }

public decimal? Consumption => Traded.Count == AlreadyNeeded ? (decimal?) null : (TradedNeeded / (decimal) (Traded.Count - AlreadyNeeded)) * 100m;
}
}
133 changes: 133 additions & 0 deletions EDEngineer.Models/MaterialTrading/MaterialTrader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
using System.Linq;
using EDEngineer.Models.State;
using EDEngineer.Models.Utils;

namespace EDEngineer.Models
{
public static class MaterialTrader
{
public static IEnumerable<MaterialTrade> FindPossibleTrades(StateCargo cargo, Dictionary<Entry, int> missingIngredients, Dictionary<EntryData, int> deduced)
{
var ingredients = cargo.Ingredients.Values
.Where(i => i.Count > 0 &&
i.Data.Group.HasValue &&
i.Data.Rarity.Rank() != null &&
!missingIngredients.ContainsKey(i)).ToList();

var allTrades = AllTrades(missingIngredients, ingredients, deduced).ToList();

var coveredTrades = allTrades.Where(trade => trade.WillBeEnough).OrderBy(trade => trade.TradedNeeded).ToList();
var incompleteTrades = allTrades.Where(trade => !trade.WillBeEnough).OrderBy(trade => trade.TradedNeeded).ToList();

var coveredIngredients = new HashSet<EntryData>();
foreach (var currentTrade in coveredTrades)
{
if (currentTrade.Traded.Count - currentTrade.TradedNeeded - deduced.GetOrDefault(currentTrade.Traded.Data) < 0)
{
continue;
}

coveredIngredients.Add(currentTrade.Needed.Data);

if (deduced.ContainsKey(currentTrade.Traded.Data))
{
deduced[currentTrade.Traded.Data] += currentTrade.TradedNeeded;
}
else
{
deduced[currentTrade.Traded.Data] = currentTrade.TradedNeeded;
}

yield return currentTrade;
}

foreach (var currentTrade in incompleteTrades)
{
if (coveredIngredients.Contains(currentTrade.Needed.Data))
{
continue;
}

if (deduced.ContainsKey(currentTrade.Traded.Data))
{
deduced[currentTrade.Traded.Data] += currentTrade.TradedNeeded;
}
else
{
deduced[currentTrade.Traded.Data] = currentTrade.TradedNeeded;
}

yield return currentTrade;
}
}

private static IEnumerable<MaterialTrade> AllTrades(Dictionary<Entry, int> missingIngredients,
List<Entry> ingredients, Dictionary<EntryData, int> deduced)
{
foreach (var missingIngredient in missingIngredients)
{
var ingredient = missingIngredient.Key;
var missingSize = missingIngredient.Value;

if (missingSize <= 0 ||
!ingredient.Data.Group.HasValue ||
ingredient.Data.Group.In(Group.Thargoid, Group.Guardian, Group.Commodities) ||
!ingredient.Data.Rarity.Rank().HasValue)
{
continue;
}

var group = ingredient.Data.Group.Value;
// ReSharper disable PossibleInvalidOperationException
var targetRank = ingredient.Data.Rarity.Rank().Value;

// find same group ingredients
var sameGroupIngredients = ingredients.Where(i => i.Data.Group == group).ToList();
foreach (var sameGroup in sameGroupIngredients)
{
var sourceRank = sameGroup.Data.Rarity.Rank().Value;
var rankDifference = sourceRank - targetRank;
int needed;
if (rankDifference > 0)
{
needed = (int) Math.Ceiling(missingSize / Math.Pow(3, rankDifference));
}
else
{
needed = (int) (Math.Pow(6, Math.Abs(rankDifference)) * missingSize);
}

var willBeEnough = needed + deduced.GetOrDefault(sameGroup.Data) <= sameGroup.Count;
yield return new MaterialTrade(sameGroup, ingredient, needed, missingSize, willBeEnough, deduced.GetOrDefault(sameGroup.Data));
}

// find other group ingredients
var differentGroupIngredients = ingredients.Where(i => i.Data.Group != group && i.Data.Kind == ingredient.Data.Kind && i.Data.Subkind == ingredient.Data.Subkind).ToList();
foreach (var otherGroup in differentGroupIngredients)
{
var sourceRank = otherGroup.Data.Rarity.Rank().Value;
var rankDifference = sourceRank - targetRank;
int needed;
if (rankDifference > 0)
{
needed = (int)(2 * missingSize * Math.Pow(3, rankDifference));
}
else if (rankDifference == 0)
{
needed = 6 * missingSize;
}
else
{
continue;
}

var willBeEnough = needed + deduced.GetOrDefault(otherGroup.Data) <= otherGroup.Count;
yield return new MaterialTrade(otherGroup, ingredient, needed, missingSize, willBeEnough, deduced.GetOrDefault(otherGroup.Data));
}
// ReSharper restore PossibleInvalidOperationException
}
}
}
}
48 changes: 48 additions & 0 deletions EDEngineer.Models/Utils/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,43 @@ public static string Initials(this string self)
return string.Join("", initials);
}

public static bool In<T>(this T value, params T[] values)
{
return values.Contains(value);
}

public static int? Rank(this Rarity rarity)
{
switch (rarity)
{
case Rarity.VeryCommon:
return 1;
case Rarity.Common:
return 2;
case Rarity.Standard:
return 3;
case Rarity.Rare:
return 4;
case Rarity.VeryRare:
return 5;
default:
case Rarity.None:
case Rarity.Commodity:
return null;
}
}

public static TValue GetOrDefault<TKey, TValue>(this Dictionary<TKey, TValue> dict,
TKey key)
{
if (!dict.ContainsKey(key))
{
return default(TValue);
}

return dict[key];
}

public static TValue GetOrAdd<TKey, TValue>(this Dictionary<TKey, TValue> dict,
TKey key,
Func<TKey, TValue> adder)
Expand Down Expand Up @@ -45,6 +82,17 @@ public static bool IsIn(this string target, string field)
return evaluator(o.Value);
}

public static TResult Map<TInput, TResult>(this TInput o, Func<TInput, TResult> evaluator)
where TResult : class
{
if (o == null)
{
return null;
}

return evaluator(o);
}

public static string Description(this Enum value)
{
if (value == null)
Expand Down
1 change: 1 addition & 0 deletions EDEngineer.Tests/EDEngineer.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="MaterialTraderTests.cs" />
<Compile Include="ReferenceDataIntegrityTests.cs" />
<Compile Include="LogParsingTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
Expand Down
89 changes: 89 additions & 0 deletions EDEngineer.Tests/MaterialTraderTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Linq;
using EDEngineer.Models;
using EDEngineer.Models.State;
using EDEngineer.Utils.System;
using Moq;
using Newtonsoft.Json;
using NFluent;
using NUnit.Framework;

namespace EDEngineer.Tests
{
[TestFixture]
public class MaterialTraderTests
{
private StateCargo cargo;
private List<EntryData> entries;

[SetUp]
public void Setup()
{
entries = JsonConvert.DeserializeObject<List<EntryData>>(IOUtils.GetEntryDatasJson());
cargo = new StateCargo(entries, Mock.Of<ILanguage>(), StateCargo.COUNT_COMPARER);
}

[TestCase(1, 6)]
[TestCase(2, 36)]
[TestCase(3, 216)]
[TestCase(4, 1296)]
public void Simple_upgrade_trade(int rank, int expected)
{
var group = Group.Alloys;
var alloys = entries.Where(e => e.Group == group)
.OrderBy(e => e.Rarity)
.ToList();

var firstGrade = alloys[0];
var secondGrade = new Entry(alloys[rank]);

cargo.IncrementCargo(firstGrade.Name, expected * 2);

var missingIngredients = new Dictionary<Entry, int>
{
[secondGrade] = 1
};

var trades = MaterialTrader.FindPossibleTrades(cargo, missingIngredients, new Dictionary<EntryData, int>()).ToList();

Check.That(trades.Count).IsEqualTo(1);

var trade = trades[0];

Check.That(trade.Traded.Data).IsEqualTo(firstGrade);
Check.That(trade.TradedNeeded).IsEqualTo(expected);
}

[TestCase(1, 3)]
[TestCase(2, 9)]
[TestCase(3, 27)]
[TestCase(4, 81)]
public void Simple_downgrade_trade(int rank, int expected)
{
var group = Group.Alloys;
var alloys = entries.Where(e => e.Group == group)
.OrderBy(e => e.Rarity)
.ToList();

var firstGrade = alloys[rank];
var secondGrade = new Entry(alloys[0]);

cargo.IncrementCargo(firstGrade.Name, expected * 2);

var missingIngredients = new Dictionary<Entry, int>
{
[secondGrade] = 1
};

var trades = MaterialTrader.FindPossibleTrades(cargo, missingIngredients, new Dictionary<EntryData, int>()).ToList();

Check.That(trades.Count).IsEqualTo(1);

var trade = trades[0];

Check.That(trade.Traded.Data).IsEqualTo(firstGrade);
Check.That(trade.TradedNeeded).IsEqualTo(expected);
}
}
}
6 changes: 6 additions & 0 deletions EDEngineer.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_IFELSE_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_WHILE_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INDENT_ANONYMOUS_METHOD_BLOCK/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AROUND_MULTIPLICATIVE_OP/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_SINGLE_LINE_ARRAY_INITIALIZER_BRACES/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHING_EMPTY_BRACES/@EntryValue">True</s:Boolean>
Expand Down Expand Up @@ -65,7 +66,12 @@
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=NAMESPACE_005FALIAS/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FFIELD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FRESOURCE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EXml_002ECodeStyle_002EFormatSettingsUpgrade_002EXmlMoveToCommonFormatterSettingsUpgrade/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
17 changes: 11 additions & 6 deletions EDEngineer/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
<SolidColorBrush x:Key="TechnologyBrush" Color="MediumVioletRed"/>
<SolidColorBrush x:Key="ExperimentalBrush" Color="DarkOliveGreen"/>
<SolidColorBrush x:Key="UnlockBrush" Color="MediumPurple"/>
<SolidColorBrush x:Key="GreenBrush" Color="Green"/>

<converters:RarityToIconConverter x:Key="RarityToIcon" />
<converters:SubkindToIcon x:Key="SubkindToIcon" />
Expand Down Expand Up @@ -93,6 +94,15 @@
<converters:BooleanToSpecialColorConverter x:Key="BooleanToShoppingListBlockHighlightColor"
SpecialColor="{StaticResource IngredientHighlightedBrush}"
NormalColor="{StaticResource SynthesisBlueBrush}"/>
<converters:BooleanToSpecialColorConverter x:Key="BooleanToWillBeEnoughColor"
SpecialColor="{StaticResource ExperimentalBrush}"
NormalColor="{StaticResource IngredientHighlightedBrush}"/>
<converters:BooleanToSpecialColorConverter x:Key="BooleanToWillBeEnoughForegroundColor"
SpecialColor="{StaticResource GreenBrush}"
NormalColor="{StaticResource EliteOrangeBrush}"/>
<converters:BooleanToSpecialColorConverter x:Key="BooleanToWillBeEnoughTextColor"
SpecialColor="{StaticResource BackgroundBrush}"
NormalColor="{StaticResource AccentBackgroundBrush}"/>
<converters:BooleanToSpecialColorConverter x:Key="BooleanToGoodEffectColorConverter"
SpecialColor="Green"
NormalColor="Red"/>
Expand Down Expand Up @@ -888,12 +898,7 @@
<Setter.Value>
<DataTemplate>
<Grid Background="{TemplateBinding Tag}"
Margin="0"
Width="{Binding
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type Expander}},
Path=ActualWidth}">
Margin="30,0,0,0">
<ContentPresenter Content="{Binding}"
HorizontalAlignment="Center"
Margin="-18,0,0,0"/>
Expand Down
Loading

0 comments on commit 78dd21a

Please sign in to comment.