using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace FabStarterDeckGen { using System; using System.Collections.Generic; using Newtonsoft.Json; public class DeckConfig { public DeckConfig(string name, string hero, CardSpec[] core, CardSpec[] variables) { Name = name; Hero = hero; Core = core; Variables = variables; } [JsonProperty("Name")] public string Name { get; set; } [JsonProperty("Hero")] public string Hero { get; set; } [JsonProperty("Core")] public CardSpec[] Core { get; set; } [JsonProperty("Variables")] public CardSpec[] Variables { get; set; } public List LargestBuildableCombination = new List(); } 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")] public string Name { get; set; } [JsonProperty("IsPitchless")] public bool IsPitchless { get; set; } [JsonProperty("Count", NullValueHandling = NullValueHandling.Ignore)] public string Count { get; set; } [JsonProperty("R", NullValueHandling = NullValueHandling.Ignore)] public string R { get; set; } [JsonProperty("Y", NullValueHandling = NullValueHandling.Ignore)] public string Y { get; set; } [JsonProperty("B", NullValueHandling = NullValueHandling.Ignore)] public string B { get; set; } } public class DeckGenerator { private static IEnumerable> GetPermutationsWithRept(IEnumerable list, int length) { if (length == 1) return list.Select(t => new T[] { t }); return GetPermutationsWithRept(list, length - 1).SelectMany(t => list, (t1, t2) => t1.Concat(new T[] { t2 })); } private static IEnumerable> GetKCombsWithRept(IEnumerable list, int length) where T : IComparable { if (length == 1) return list.Select(t => new T[] { t }); return GetKCombsWithRept(list, length - 1) .SelectMany(t => list.Where(o => o.CompareTo(t.Last()) >= 0), (t1, t2) => t1.Concat(new T[] { t2 })); } private static IEnumerable> CartesianProduct (IEnumerable> sequences) { IEnumerable> emptyProduct = new[] { Enumerable.Empty() }; return sequences.Aggregate( emptyProduct, (accumulator, sequence) => from accseq in accumulator from item in sequence select accseq.Concat(new[] {item})); } private List? DeckConfigs = new List(); public List> DeckLists = new List>(); private Dictionary>> PerHeroLists = new Dictionary>>(); private const int REASONABLE_MAX = 6; private bool IsAssortedThree(string pValue) { return pValue == "3*";} private bool IsAssortedTwo(string pValue) { return pValue == "2*";} private bool IsAssortedOne(string pValue) { return pValue == "1*";} private bool ProcessCountString(string count, ref int rAssorted3Count, ref int rAssorted2Count, ref int rAssorted1Count) { if ( count == "" ) return false; if ( IsAssortedThree(count) ) { rAssorted3Count++; return true; } if ( IsAssortedTwo(count) ) { rAssorted2Count++; return true; } if ( IsAssortedOne(count) ) { rAssorted1Count++; return true; } return false; } public void LoadDeckConfigs(string pFilepath) { using (StreamReader file = File.OpenText(pFilepath)) { DeckConfigs = JsonConvert.DeserializeObject > (file.ReadToEnd()); } var three_over_two = GetPermutationsWithRept(new[] {1, 2}, 2).Select(c => c.ToList()).ToList(); three_over_two.RemoveAll(i => i.Sum() != 3); var three_over_three = GetPermutationsWithRept(new[] {0, 1, 2}, 3).Select(c => c.ToList()).ToList(); three_over_three.RemoveAll(i => i.Sum() != 3); var two_over_two = GetPermutationsWithRept(new[] {0, 1, 2}, 2).Select(c => c.ToList()).ToList(); two_over_two.RemoveAll(i => i.Sum() != 2); var two_over_three = GetPermutationsWithRept(new[] {0, 1, 2}, 3).Select(c => c.ToList()).ToList(); two_over_three.RemoveAll(i => i.Sum() != 2); var one_over_two = GetPermutationsWithRept(new[] {0, 1}, 2).Select(c => c.ToList()).ToList(); one_over_two.RemoveAll(i => i.Sum() != 1); var one_over_three = GetPermutationsWithRept(new[] {0, 1}, 3).Select(c => c.ToList()).ToList(); one_over_three.RemoveAll(i => i.Sum() != 1); Debug.Assert(DeckConfigs != null, nameof(DeckConfigs) + " != null"); foreach ( var config in DeckConfigs ) { List>> variable_card_perm_lists = new List>>(); foreach ( var variable_card in config.Variables ) { int assorted_3_count = 0; int assorted_2_count = 0; int assorted_1_count = 0; bool spans_red = ProcessCountString(variable_card.R, ref assorted_3_count, ref assorted_2_count, ref assorted_1_count); bool spans_blue = ProcessCountString(variable_card.B, ref assorted_3_count, ref assorted_2_count, ref assorted_1_count); ProcessCountString(variable_card.Y, ref assorted_3_count, ref assorted_2_count, ref assorted_1_count); List> counts = null; if ( assorted_3_count == 2 ) counts = three_over_two.Select(x => x.ToList()).ToList(); else if (assorted_3_count == 3) counts = three_over_three.Select(x => x.ToList()).ToList(); else if (assorted_2_count == 2) counts = two_over_two.Select(x => x.ToList()).ToList(); else if (assorted_2_count == 3) counts = two_over_three.Select(x => x.ToList()).ToList(); else if (assorted_1_count == 2) counts = one_over_two.Select(x => x.ToList()).ToList(); else if (assorted_1_count == 3) counts = one_over_three.Select(x => x.ToList()).ToList(); if ( (!spans_red || !spans_blue) && counts != null ) { for ( int i = 0; i < counts.Count; i++ ) { if ( spans_red ) counts[i].Add(int.Parse(variable_card.B)); if (spans_blue) counts[i].Insert(0,int.Parse(variable_card.R)); } } variable_card_perm_lists.Add(counts); } var lists = CartesianProduct(variable_card_perm_lists).ToList(); Console.WriteLine(lists.Count); foreach ( var variation in lists ) { var card_list = new Dictionary(); foreach ( var card in config.Core ) { if ( card.IsPitchless ) { card_list.Add(card.Name, int.Parse(card.Count)); } else { if(int.Parse(card.R) != 0) card_list.Add(card.Name + " - R", int.Parse(card.R)); 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)); } } var variation_counts = variation.ToList(); for ( int i = 0; i < config.Variables.Length; i++ ) { var card = config.Variables[i]; var counts = variation_counts[i]; if(counts[0] > 0) card_list.Add(card.Name + " - R", counts[0]); if(counts[1] > 0) card_list.Add(card.Name + " - Y", counts[1]); if(counts[2] > 0) card_list.Add(card.Name + " - B", counts[2]); } int card_count = card_list.Sum(i => i.Value); if ( card_count != 45 ) { Console.WriteLine(card_list.ToString()); Debug.Assert(card_count == 45); } if ( !PerHeroLists.ContainsKey(config) ) PerHeroLists[config] = new List>(); PerHeroLists[config].Add(card_list); DeckLists.Add(card_list); } } } public void DetermineMaxBuilds(in Dictionary pCollectionCounts) { foreach ( var hero_config in PerHeroLists ) { var timer = new Stopwatch(); timer.Start(); var num_lists_for_hero = Enumerable.Range(0, hero_config.Value.Count); for (int i = 1; i <= REASONABLE_MAX; i++) { bool combo_was_built = false; var combos = GetKCombsWithRept(num_lists_for_hero, i).Select(c => c.ToList()); var ints = pCollectionCounts; Parallel.ForEach(combos, (combo, state) => { var temp_card_counts = new Dictionary(ints); if (ComboIsBuildable(hero_config.Value, combo, temp_card_counts)) { hero_config.Key.LargestBuildableCombination = new List(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; } var f = string.Join(",", hero_config.Key.LargestBuildableCombination); Dictionary totals_for_combo_build = new Dictionary(); foreach (var index in hero_config.Key.LargestBuildableCombination) { var deck = hero_config.Value[index]; foreach (var entry in deck) { 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); } 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); timer.Stop(); TimeSpan timeTaken = timer.Elapsed; Console.WriteLine("Time taken: " + timeTaken.ToString(@"m\:ss\.fff")); } } private bool DeckIsBuildable(in Dictionary pDeckCardCounts, in Dictionary pCardCounts) { 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) return false; } return true; } private bool ComboIsBuildable(in List> lists, in List pCombination, Dictionary pCardCounts) { Console.WriteLine("({1})Testing combo: {0}", String.Join(",", pCombination), Thread.CurrentThread.ManagedThreadId); foreach (var index in pCombination) { var deck = lists[index]; if (!DeckIsBuildable(deck, pCardCounts)) return false; foreach (var required_count in deck) pCardCounts[required_count.Key] -= required_count.Value; } return true; } } }