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
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;
|
|
}
|
|
}
|
|
}
|
|
|