You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

325 lines
14 KiB

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace FabStarterDeckGen
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class DeckConfig
{
[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 partial class CardSpec
{
[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
{
static IEnumerable<IEnumerable<T>> GetPermutations<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)
return list.Select(t => new T[] { t });
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
{
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 }));
}
static IEnumerable<IEnumerable<T>> CartesianProduct<T> (IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> emptyProduct =
new[] { Enumerable.Empty<T>() };
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from accseq in accumulator
from item in sequence
select accseq.Concat(new[] {item}));
}
private List<DeckConfig>? DeckConfigs = new List<DeckConfig>();
public List<Dictionary<string, int>> DeckLists = new List<Dictionary<string, int>>();
private readonly int REASONABLE_MAX = 10;
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 <List<DeckConfig>> (file.ReadToEnd());
}
var three_over_two = GetPermutationsWithRept<int>(new[] {1, 2}, 2).Select(c => c.ToList()).ToList();
three_over_two.RemoveAll(i => i.Sum() != 3);
var three_over_three = GetPermutationsWithRept<int>(new[] {0, 1, 2}, 3).Select(c => c.ToList()).ToList();
three_over_three.RemoveAll(i => i.Sum() != 3);
var two_over_two = GetPermutationsWithRept<int>(new[] {0, 1, 2}, 2).Select(c => c.ToList()).ToList();
two_over_two.RemoveAll(i => i.Sum() != 2);
var two_over_three = GetPermutationsWithRept<int>(new[] {0, 1, 2}, 3).Select(c => c.ToList()).ToList();
two_over_three.RemoveAll(i => i.Sum() != 2);
var one_over_two = GetPermutationsWithRept<int>(new[] {0, 1}, 2).Select(c => c.ToList()).ToList();
one_over_two.RemoveAll(i => i.Sum() != 1);
var one_over_three = GetPermutationsWithRept<int>(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<List<List<int>>> variable_card_perm_lists = new List<List<List<int>>>();
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<List<int>> 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<string, int>();
foreach ( var card in config.Core )
{
if ( card.IsPitchless )
{
card_list.Add(card.Name, int.Parse(card.Count));
}
else
{
card_list.Add(card.Name + " - R", int.Parse(card.R));
card_list.Add(card.Name + " - Y", int.Parse(card.Y));
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);
}
DeckLists.Add(card_list);
}
}
}
public void DetermineMaxBuilds(in Dictionary<string, int> pCollectionCounts)
{
var timer = new Stopwatch();
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 combos = GetKCombsWithRept(range, i).Select(c => c.ToList());
var ints = pCollectionCounts;
Parallel.ForEach(combos, (combo, state) =>
{
var temp_card_counts = new Dictionary<string, int>(ints);
if (ComboIsBuildable(combo, temp_card_counts))
{
// 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();
}
});
/*
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
largest_buildable_combo = new List<int>(combo);
// Move on and enlarge the number of decks we are trying to build
combo_was_built = true;
break;
}
}
*/
if (!combo_was_built)
break;
}
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))
totals_for_combo_build[entry.Key] = 0;
totals_for_combo_build[entry.Key] += entry.Value;
}
var f = Newtonsoft.Json.JsonConvert.SerializeObject(deck, Newtonsoft.Json.Formatting.Indented);
Console.WriteLine(f);
}
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)
{
foreach (var required_count in pDeckCardCounts)
{
if (pCardCounts[required_count.Key] - required_count.Value < 0)
return false;
}
return true;
}
private bool ComboIsBuildable(in List<int> pCombination, Dictionary<string, int> pCardCounts)
{
Console.WriteLine("({1})Testing combo: {0}", String.Join(",", pCombination), Thread.CurrentThread.ManagedThreadId);
foreach (var index in pCombination)
{
var deck = DeckLists[index];
if (!DeckIsBuildable(deck, pCardCounts))
return false;
foreach (var required_count in deck)
pCardCounts[required_count.Key] -= required_count.Value;
}
return true;
}
}
}