|
|
@ -1,8 +1,6 @@ |
|
|
using System.Diagnostics; |
|
|
using System.Diagnostics; |
|
|
using System.Diagnostics.CodeAnalysis; |
|
|
|
|
|
using System.IO; |
|
|
using System.IO; |
|
|
using System.Linq; |
|
|
using System.Linq; |
|
|
using System.Runtime.InteropServices; |
|
|
|
|
|
using System.Threading; |
|
|
using System.Threading; |
|
|
using System.Threading.Tasks; |
|
|
using System.Threading.Tasks; |
|
|
|
|
|
|
|
|
@ -10,13 +8,18 @@ namespace FabStarterDeckGen |
|
|
{ |
|
|
{ |
|
|
using System; |
|
|
using System; |
|
|
using System.Collections.Generic; |
|
|
using System.Collections.Generic; |
|
|
|
|
|
|
|
|
using System.Globalization; |
|
|
|
|
|
using Newtonsoft.Json; |
|
|
using Newtonsoft.Json; |
|
|
using Newtonsoft.Json.Converters; |
|
|
|
|
|
|
|
|
|
|
|
public partial class DeckConfig |
|
|
public class DeckConfig |
|
|
{ |
|
|
{ |
|
|
|
|
|
public DeckConfig(string name, string hero, CardSpec[] core, CardSpec[] variables) |
|
|
|
|
|
{ |
|
|
|
|
|
Name = name; |
|
|
|
|
|
Hero = hero; |
|
|
|
|
|
Core = core; |
|
|
|
|
|
Variables = variables; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
[JsonProperty("Name")] |
|
|
[JsonProperty("Name")] |
|
|
public string Name { get; set; } |
|
|
public string Name { get; set; } |
|
|
|
|
|
|
|
|
@ -28,10 +31,22 @@ namespace FabStarterDeckGen |
|
|
|
|
|
|
|
|
[JsonProperty("Variables")] |
|
|
[JsonProperty("Variables")] |
|
|
public CardSpec[] Variables { get; set; } |
|
|
public CardSpec[] Variables { get; set; } |
|
|
|
|
|
|
|
|
|
|
|
public List<int> LargestBuildableCombination = new List<int>(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public partial class CardSpec |
|
|
public class CardSpec |
|
|
{ |
|
|
{ |
|
|
|
|
|
public CardSpec(string name, bool isPitchless, string count, string r, string y, string b) |
|
|
|
|
|
{ |
|
|
|
|
|
Name = name; |
|
|
|
|
|
IsPitchless = isPitchless; |
|
|
|
|
|
Count = count; |
|
|
|
|
|
R = r; |
|
|
|
|
|
Y = y; |
|
|
|
|
|
B = b; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
[JsonProperty("Name")] |
|
|
[JsonProperty("Name")] |
|
|
public string Name { get; set; } |
|
|
public string Name { get; set; } |
|
|
|
|
|
|
|
|
@ -53,29 +68,22 @@ namespace FabStarterDeckGen |
|
|
|
|
|
|
|
|
public class DeckGenerator |
|
|
public class DeckGenerator |
|
|
{ |
|
|
{ |
|
|
static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<T> list, int length) |
|
|
private static IEnumerable<IEnumerable<T>> GetPermutationsWithRept<T>(IEnumerable<T> list, int length) |
|
|
{ |
|
|
|
|
|
if (length == 1) |
|
|
|
|
|
return list.Select(t => new T[] { t }); |
|
|
|
|
|
return GetPermutations(list, length - 1).SelectMany(t => list.Where(o => !t.Contains(o)), (t1, t2) => t1.Concat(new T[] { t2 })); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static IEnumerable<IEnumerable<T>> GetPermutationsWithRept<T>(IEnumerable<T> list, int length) |
|
|
|
|
|
{ |
|
|
{ |
|
|
if (length == 1) |
|
|
if (length == 1) |
|
|
return list.Select(t => new T[] { t }); |
|
|
return list.Select(t => new T[] { t }); |
|
|
return GetPermutationsWithRept(list, length - 1).SelectMany(t => list, (t1, t2) => t1.Concat(new T[] { t2 })); |
|
|
return GetPermutationsWithRept(list, length - 1).SelectMany(t => list, (t1, t2) => t1.Concat(new T[] { t2 })); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static IEnumerable<IEnumerable<T>> GetKCombsWithRept<T>(IEnumerable<T> list, int length) where T : IComparable |
|
|
private static IEnumerable<IEnumerable<T>> GetKCombsWithRept<T>(IEnumerable<T> list, int length) where T : IComparable |
|
|
{ |
|
|
{ |
|
|
if (length == 1) return list.Select(t => new T[] { t }); |
|
|
if (length == 1) return list.Select(t => new T[] { t }); |
|
|
return GetKCombsWithRept(list, length - 1) |
|
|
return GetKCombsWithRept(list, length - 1) |
|
|
.SelectMany(t => list.Where(o => o.CompareTo(t.Last()) >= 0), |
|
|
.SelectMany(t => list.Where(o => o.CompareTo(t.Last()) >= 0), |
|
|
(t1, t2) => t1.Concat(new T[] { t2 })); |
|
|
(t1, t2) => t1.Concat(new T[] { t2 })); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static IEnumerable<IEnumerable<T>> CartesianProduct<T> (IEnumerable<IEnumerable<T>> sequences) |
|
|
private static IEnumerable<IEnumerable<T>> CartesianProduct<T> (IEnumerable<IEnumerable<T>> sequences) |
|
|
{ |
|
|
{ |
|
|
IEnumerable<IEnumerable<T>> emptyProduct = |
|
|
IEnumerable<IEnumerable<T>> emptyProduct = |
|
|
new[] { Enumerable.Empty<T>() }; |
|
|
new[] { Enumerable.Empty<T>() }; |
|
|
@ -90,7 +98,9 @@ namespace FabStarterDeckGen |
|
|
private List<DeckConfig>? DeckConfigs = new List<DeckConfig>(); |
|
|
private List<DeckConfig>? DeckConfigs = new List<DeckConfig>(); |
|
|
public List<Dictionary<string, int>> DeckLists = new List<Dictionary<string, int>>(); |
|
|
public List<Dictionary<string, int>> DeckLists = new List<Dictionary<string, int>>(); |
|
|
|
|
|
|
|
|
private readonly int REASONABLE_MAX = 10; |
|
|
private Dictionary<DeckConfig, List<Dictionary<string, int>>> PerHeroLists = new Dictionary<DeckConfig, List<Dictionary<string, int>>>(); |
|
|
|
|
|
|
|
|
|
|
|
private const int REASONABLE_MAX = 6; |
|
|
|
|
|
|
|
|
private bool IsAssortedThree(string pValue) { return pValue == "3*";} |
|
|
private bool IsAssortedThree(string pValue) { return pValue == "3*";} |
|
|
private bool IsAssortedTwo(string pValue) { return pValue == "2*";} |
|
|
private bool IsAssortedTwo(string pValue) { return pValue == "2*";} |
|
|
@ -120,9 +130,9 @@ namespace FabStarterDeckGen |
|
|
|
|
|
|
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
|
public void LoadDeckConfigs(string pfilePath) |
|
|
public void LoadDeckConfigs(string pFilepath) |
|
|
{ |
|
|
{ |
|
|
using (StreamReader file = File.OpenText(pfilePath)) |
|
|
using (StreamReader file = File.OpenText(pFilepath)) |
|
|
{ |
|
|
{ |
|
|
DeckConfigs = JsonConvert.DeserializeObject <List<DeckConfig>> (file.ReadToEnd()); |
|
|
DeckConfigs = JsonConvert.DeserializeObject <List<DeckConfig>> (file.ReadToEnd()); |
|
|
} |
|
|
} |
|
|
@ -199,9 +209,12 @@ namespace FabStarterDeckGen |
|
|
} |
|
|
} |
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
|
card_list.Add(card.Name + " - R", int.Parse(card.R)); |
|
|
if(int.Parse(card.R) != 0) |
|
|
card_list.Add(card.Name + " - Y", int.Parse(card.Y)); |
|
|
card_list.Add(card.Name + " - R", int.Parse(card.R)); |
|
|
card_list.Add(card.Name + " - B", int.Parse(card.B)); |
|
|
if(int.Parse(card.Y) != 0) |
|
|
|
|
|
card_list.Add(card.Name + " - Y", int.Parse(card.Y)); |
|
|
|
|
|
if(int.Parse(card.B) != 0) |
|
|
|
|
|
card_list.Add(card.Name + " - B", int.Parse(card.B)); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -224,7 +237,11 @@ namespace FabStarterDeckGen |
|
|
Console.WriteLine(card_list.ToString()); |
|
|
Console.WriteLine(card_list.ToString()); |
|
|
Debug.Assert(card_count == 45); |
|
|
Debug.Assert(card_count == 45); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if ( !PerHeroLists.ContainsKey(config) ) |
|
|
|
|
|
PerHeroLists[config] = new List<Dictionary<string, int>>(); |
|
|
|
|
|
|
|
|
|
|
|
PerHeroLists[config].Add(card_list); |
|
|
DeckLists.Add(card_list); |
|
|
DeckLists.Add(card_list); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -232,87 +249,76 @@ namespace FabStarterDeckGen |
|
|
|
|
|
|
|
|
public void DetermineMaxBuilds(in Dictionary<string, int> pCollectionCounts) |
|
|
public void DetermineMaxBuilds(in Dictionary<string, int> pCollectionCounts) |
|
|
{ |
|
|
{ |
|
|
var timer = new Stopwatch(); |
|
|
foreach ( var hero_config in PerHeroLists ) |
|
|
timer.Start(); |
|
|
|
|
|
|
|
|
|
|
|
// Limit this to per hero once i have more specs written
|
|
|
|
|
|
var range = Enumerable.Range(0, DeckLists.Count); |
|
|
|
|
|
List<int> largest_buildable_combo = new List<int>(); |
|
|
|
|
|
for (int i = 1; i <= REASONABLE_MAX; i++) |
|
|
|
|
|
{ |
|
|
{ |
|
|
bool combo_was_built = false; |
|
|
var timer = new Stopwatch(); |
|
|
var combos = GetKCombsWithRept(range, i).Select(c => c.ToList()); |
|
|
timer.Start(); |
|
|
var ints = pCollectionCounts; |
|
|
|
|
|
|
|
|
|
|
|
Parallel.ForEach(combos, (combo, state) => |
|
|
var num_lists_for_hero = Enumerable.Range(0, hero_config.Value.Count); |
|
|
|
|
|
for (int i = 1; i <= REASONABLE_MAX; i++) |
|
|
{ |
|
|
{ |
|
|
var temp_card_counts = new Dictionary<string, int>(ints); |
|
|
bool combo_was_built = false; |
|
|
if (ComboIsBuildable(combo, temp_card_counts)) |
|
|
var combos = GetKCombsWithRept(num_lists_for_hero, i).Select(c => c.ToList()); |
|
|
{ |
|
|
var ints = pCollectionCounts; |
|
|
// Cache it off somehwere as the largest buildable combo
|
|
|
|
|
|
largest_buildable_combo = new List<int>(combo); |
|
|
|
|
|
// Move on and enlarge the number of decks we are trying to build
|
|
|
|
|
|
combo_was_built = true; |
|
|
|
|
|
state.Break(); |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
Parallel.ForEach(combos, (combo, state) => |
|
|
foreach (var combo in combos) |
|
|
|
|
|
{ |
|
|
|
|
|
// Multithread!
|
|
|
|
|
|
var temp_card_counts = new Dictionary<string, int>(pCollectionCounts); |
|
|
|
|
|
if (ComboIsBuildable(combo, temp_card_counts)) |
|
|
|
|
|
{ |
|
|
{ |
|
|
// Cache it off somehwere as the largest buildable combo
|
|
|
var temp_card_counts = new Dictionary<string, int>(ints); |
|
|
largest_buildable_combo = new List<int>(combo); |
|
|
if (ComboIsBuildable(hero_config.Value, combo, temp_card_counts)) |
|
|
// Move on and enlarge the number of decks we are trying to build
|
|
|
{ |
|
|
combo_was_built = true; |
|
|
hero_config.Key.LargestBuildableCombination = new List<int>(combo); |
|
|
|
|
|
// Move on and enlarge the number of decks we are trying to build
|
|
|
|
|
|
combo_was_built = true; |
|
|
|
|
|
state.Break(); |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
if (!combo_was_built) |
|
|
break; |
|
|
break; |
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
if (!combo_was_built) |
|
|
var f = string.Join(",", hero_config.Key.LargestBuildableCombination); |
|
|
break; |
|
|
Dictionary<string, int> totals_for_combo_build = new Dictionary<string, int>(); |
|
|
} |
|
|
foreach (var index in hero_config.Key.LargestBuildableCombination) |
|
|
Console.WriteLine(string.Join(",", largest_buildable_combo)); |
|
|
|
|
|
Dictionary<string, int> totals_for_combo_build = new Dictionary<string, int>(); |
|
|
|
|
|
foreach (var index in largest_buildable_combo) |
|
|
|
|
|
{ |
|
|
|
|
|
var deck = DeckLists[index]; |
|
|
|
|
|
foreach (var entry in deck) |
|
|
|
|
|
{ |
|
|
{ |
|
|
if (!totals_for_combo_build.ContainsKey(entry.Key)) |
|
|
var deck = hero_config.Value[index]; |
|
|
totals_for_combo_build[entry.Key] = 0; |
|
|
foreach (var entry in deck) |
|
|
totals_for_combo_build[entry.Key] += entry.Value; |
|
|
{ |
|
|
|
|
|
if (!totals_for_combo_build.ContainsKey(entry.Key)) |
|
|
|
|
|
totals_for_combo_build[entry.Key] = 0; |
|
|
|
|
|
totals_for_combo_build[entry.Key] += entry.Value; |
|
|
|
|
|
} |
|
|
|
|
|
f += Newtonsoft.Json.JsonConvert.SerializeObject(deck, Newtonsoft.Json.Formatting.Indented); |
|
|
|
|
|
Console.WriteLine(f); |
|
|
} |
|
|
} |
|
|
var f = Newtonsoft.Json.JsonConvert.SerializeObject(deck, Newtonsoft.Json.Formatting.Indented); |
|
|
f += Newtonsoft.Json.JsonConvert.SerializeObject(totals_for_combo_build, Newtonsoft.Json.Formatting.Indented); |
|
|
|
|
|
File.WriteAllText(@"D:\Dev\FABStarterDeckGenerator\Out\" + hero_config.Key.Hero + ".json", f); |
|
|
Console.WriteLine(f); |
|
|
Console.WriteLine(f); |
|
|
|
|
|
timer.Stop(); |
|
|
|
|
|
TimeSpan timeTaken = timer.Elapsed; |
|
|
|
|
|
Console.WriteLine("Time taken: " + timeTaken.ToString(@"m\:ss\.fff")); |
|
|
} |
|
|
} |
|
|
var total_counts = Newtonsoft.Json.JsonConvert.SerializeObject(totals_for_combo_build, Newtonsoft.Json.Formatting.Indented); |
|
|
|
|
|
Console.WriteLine(total_counts); |
|
|
|
|
|
timer.Stop(); |
|
|
|
|
|
TimeSpan timeTaken = timer.Elapsed; |
|
|
|
|
|
Console.WriteLine("Time taken: " + timeTaken.ToString(@"m\:ss\.fff")); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private bool DeckIsBuildable(in Dictionary<string, int> pDeckCardCounts, in Dictionary<string, int> pCardCounts) |
|
|
private bool DeckIsBuildable(in Dictionary<string, int> pDeckCardCounts, in Dictionary<string, int> pCardCounts) |
|
|
{ |
|
|
{ |
|
|
foreach (var required_count in pDeckCardCounts) |
|
|
foreach (var required_count in pDeckCardCounts) |
|
|
{ |
|
|
{ |
|
|
|
|
|
if (required_count.Value == 0 && !pCardCounts.ContainsKey(required_count.Key)) |
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
if (pCardCounts[required_count.Key] - required_count.Value < 0) |
|
|
if (pCardCounts[required_count.Key] - required_count.Value < 0) |
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return true; |
|
|
return true; |
|
|
} |
|
|
} |
|
|
private bool ComboIsBuildable(in List<int> pCombination, Dictionary<string, int> pCardCounts) |
|
|
private bool ComboIsBuildable(in List<Dictionary<string, int>> lists, in List<int> pCombination, Dictionary<string, int> pCardCounts) |
|
|
{ |
|
|
{ |
|
|
Console.WriteLine("({1})Testing combo: {0}", String.Join(",", pCombination), Thread.CurrentThread.ManagedThreadId); |
|
|
Console.WriteLine("({1})Testing combo: {0}", String.Join(",", pCombination), Thread.CurrentThread.ManagedThreadId); |
|
|
foreach (var index in pCombination) |
|
|
foreach (var index in pCombination) |
|
|
{ |
|
|
{ |
|
|
var deck = DeckLists[index]; |
|
|
var deck = lists[index]; |
|
|
if (!DeckIsBuildable(deck, pCardCounts)) |
|
|
if (!DeckIsBuildable(deck, pCardCounts)) |
|
|
return false; |
|
|
return false; |
|
|
|
|
|
|
|
|
|