public static Rune[][] MakeSets(IList <Rune> runes, Predicate <Rune> evens, Predicate <Rune> odds, RuneSet[] reqsets = null) { Rune[][] runesSlot = new Rune[6][]; Predicate <Rune> set = r => true; if (reqsets != null) { int reqCount = 0; foreach (RuneSet s in reqsets) { reqCount += Rune.SetRequired(s); } if (reqCount == 6) { set = r => reqsets.Any(s => s == r.Set); } } runesSlot[0] = runes.Where(r => r.Slot == 1 && !r.Locked && odds.Invoke(r) && set.Invoke(r)).ToArray(); runesSlot[1] = runes.Where(r => r.Slot == 2 && !r.Locked && evens.Invoke(r) && set.Invoke(r)).ToArray(); runesSlot[2] = runes.Where(r => r.Slot == 3 && !r.Locked && odds.Invoke(r) && set.Invoke(r)).ToArray(); runesSlot[3] = runes.Where(r => r.Slot == 4 && !r.Locked && evens.Invoke(r) && set.Invoke(r)).ToArray(); runesSlot[4] = runes.Where(r => r.Slot == 5 && !r.Locked && odds.Invoke(r) && set.Invoke(r)).ToArray(); runesSlot[5] = runes.Where(r => r.Slot == 6 && !r.Locked && evens.Invoke(r) && set.Invoke(r)).ToArray(); return(runesSlot); }
public static Rune[][] MakeSets(IList <Rune> runes, Attr slot2, Attr slot4, Attr slot6, Predicate <Rune> odds, RuneSet[] reqsets = null) { Rune[][] runesSlot = new Rune[6][]; Predicate <Rune> set = r => true; if (reqsets != null) { int reqCount = 0; foreach (RuneSet s in reqsets) { reqCount += Rune.SetRequired(s); } if (reqCount == 6) { set = r => reqsets.Any(s => s == r.Set); } } var unlocked = runes.Where(r => !r.Locked).ToArray(); var sets = unlocked.Where(r => set.Invoke(r)).ToArray(); var odd = sets.Where(r => odds.Invoke(r)).ToArray(); runesSlot[0] = odd.Where(r => r.Slot == 1).ToArray(); runesSlot[1] = sets.Where(r => r.Slot == 2 && (r.MainType == slot2 || slot2 == Attr.Null)).ToArray(); runesSlot[2] = odd.Where(r => r.Slot == 3).ToArray(); runesSlot[3] = sets.Where(r => r.Slot == 4 && (r.MainType == slot4 || slot4 == Attr.Null)).ToArray(); runesSlot[4] = odd.Where(r => r.Slot == 5).ToArray(); runesSlot[5] = sets.Where(r => r.Slot == 6 && (r.MainType == slot6 || slot6 == Attr.Null)).ToArray(); foreach (Rune[] rs in runesSlot.Where(r => r.Length == 0)) { Console.WriteLine("No runes for slot " + (runesSlot.ToList().IndexOf(rs) + 1) + ":("); } return(runesSlot); }
/// <summary> /// Generates builds based on the instances variables. /// </summary> /// <param name="top">If non-zero, runs until N builds are generated</param> /// <param name="time">If non-zero, runs for N seconds</param> /// <param name="printTo">Periodically gives progress% and if it failed</param> /// <param name="progTo">Periodically gives the progress% as a double</param> /// <param name="dumpBads">If true, will only track new builds if they score higher than an other found builds</param> public void GenBuilds(int top = 0, int time = 0, Action <string> printTo = null, Action <double> progTo = null, bool dumpBads = false, bool saveStats = false) { if (runes.Any(r => r == null)) { return; } try { Best = null; SynchronizedCollection <Monster> tests = new SynchronizedCollection <Monster>(); long count = 0; long total = runes[0].Count(); total *= runes[1].Count(); total *= runes[2].Count(); total *= runes[3].Count(); total *= runes[4].Count(); total *= runes[5].Count(); long complete = total; if (total == 0) { if (printTo != null) { printTo.Invoke("0 perms"); } Console.WriteLine("Zero permuations"); return; } if (!AllowBroken && BuildSets.Count == 1 && Rune.SetRequired(BuildSets[0]) == 4) { if (printTo != null) { printTo.Invoke("Bad sets"); } Console.WriteLine("Cannot use 4 set with no broken"); return; } bool hasSort = false; foreach (string stat in statNames) { if (Sort[stat] != 0) { hasSort = true; break; } } foreach (string extra in extraNames) { if (Sort.ExtraGet(extra) != 0) { hasSort = true; break; } } if (top == 0 && !hasSort) { if (printTo != null) { printTo.Invoke("No sort"); } Console.WriteLine("No method of determining best"); return; } DateTime begin = DateTime.Now; DateTime timer = DateTime.Now; Console.WriteLine(count + "/" + total + " " + string.Format("{0:P2}", (count + complete - total) / (double)complete)); // build the scoring function Func <Stats, double> sort = (m) => { double pts = 0; foreach (string stat in statNames) { // if this stat is used for sorting if (Sort[stat] != 0) { // sum points for the stat pts += m[stat] / Sort[stat]; // if exceeding max, subtracted the gained points and then some if (Threshold[stat] != 0) { pts -= Math.Max(0, m[stat] - Threshold[stat]) / Sort[stat]; } } } // look, cool metrics! foreach (string extra in extraNames) { if (Sort.ExtraGet(extra) != 0) { pts += m.ExtraValue(extra) / Sort.ExtraGet(extra); if (Threshold.ExtraGet(extra) != 0) { pts -= Math.Max(0, m.ExtraValue(extra) - Threshold.ExtraGet(extra)) / Sort.ExtraGet(extra); } } } return(pts); }; int[] slotFakes = new int[6]; bool[] slotPred = new bool[6]; // crank the rune prediction for (int i = 0; i < 6; i++) { Rune[] rs = runes[i]; int raiseTo = 0; bool predictSubs = false; // find the largest number to raise to // if any along the tree say to predict, do it if (runePrediction.ContainsKey("g")) { int glevel = runePrediction["g"].Key; if (glevel > raiseTo) { raiseTo = glevel; } predictSubs |= runePrediction["g"].Value; } if (runePrediction.ContainsKey(((i % 2 == 0) ? "o" : "e"))) { int mlevel = runePrediction[((i % 2 == 0) ? "o" : "e")].Key; if (mlevel > raiseTo) { raiseTo = mlevel; } predictSubs |= runePrediction[((i % 2 == 0) ? "o" : "e")].Value; } if (runePrediction.ContainsKey((i + 1).ToString())) { int slevel = runePrediction[(i + 1).ToString()].Key; if (slevel > raiseTo) { raiseTo = slevel; } predictSubs |= runePrediction[(i + 1).ToString()].Value; } slotFakes[i] = raiseTo; slotPred[i] = predictSubs; } // set to running isRun = true; // Parallel the outer loop var loopRes = Parallel.ForEach <Rune>(runes[0], (r0, loopState) => { if (!isRun) { //break; loopState.Break(); } // number of builds ruled out since last sync int kill = 0; // number of builds added since last sync int plus = 0; foreach (Rune r1 in runes[1]) { if (!isRun) // Can't break to a lable, don't want to goto { break; } foreach (Rune r2 in runes[2]) { if (!isRun) { break; } foreach (Rune r3 in runes[3]) { if (!isRun) { break; } foreach (Rune r4 in runes[4]) { if (!isRun) { break; } foreach (Rune r5 in runes[5]) { if (!isRun) { break; } Monster test = new Monster(mon); test.Current.shrines = shrines; test.Current.leader = leader; test.Current.FakeLevel = slotFakes; test.Current.PredictSubs = slotPred; test.ApplyRune(r0); test.ApplyRune(r1); test.ApplyRune(r2); test.ApplyRune(r3); test.ApplyRune(r4); test.ApplyRune(r5); if (saveStats) { foreach (Rune r in test.Current.runes) { r.manageStats_LoadGen++; } } var cstats = test.GetStats(); bool maxdead = false; if (Maximum != null) { foreach (var stat in statNames) { if (Maximum[stat] > 0 && cstats[stat] > Maximum[stat]) { maxdead = true; break; } } } // check if build meets minimum if (Minimum != null && !(cstats > Minimum)) { kill++; } else if (maxdead) { kill++; } // if no broken sets, check for broken sets else if (!AllowBroken && !test.Current.SetsFull) { kill++; } // if there are required sets, ensure we have them else if (RequiredSets != null && RequiredSets.Count > 0 && !RequiredSets.All(s => test.Current.sets.Contains(s))) { kill++; } else { // we found an okay build! plus++; if (saveStats) { foreach (Rune r in test.Current.runes) { r.manageStats_LoadFilt++; } } // if we are to track all good builds, keep it if (!dumpBads) { tests.Add(test); } else { //lock (tests) //{ // if there are currently no good builds, keep it if (tests.FirstOrDefault() == null) { tests.Add(test); Best = test; } else { if (Best.GetStats() < test.GetStats()) { Best = test; tests.Add(test); } // take a snapshot of the builds (multithread /may/ cause a "collection modified" excepion in next step) /*var tt = tests.ToList(); * // if this build is better than any other build, keep it * // can't just keep a copy of Max becaues of threading * if (tt.Max(t => sort(t.GetStats())) < sort(test.GetStats())) * { * tests.Add(test); * }*/ } //} } } // every second, give a bit of feedback to those watching if (DateTime.Now > timer.AddSeconds(1)) { timer = DateTime.Now; Console.WriteLine(count + "/" + total + " " + String.Format("{0:P2}", (double)(count + complete - total) / (double)complete)); if (printTo != null) { printTo.Invoke(String.Format("{0:P2}", (double)(count + complete - total) / (double)complete)); } if (progTo != null) { progTo.Invoke((double)(count + complete - total) / (double)complete); } if (time > 0) { if (DateTime.Now > begin.AddSeconds(time)) { isRun = false; break; } } } } // sum up what work we've done Interlocked.Add(ref total, -kill); kill = 0; Interlocked.Add(ref count, plus); plus = 0; // if we've got enough, stop if (top > 0 && count >= top) { isRun = false; break; } } } } } }); // write out completion Console.WriteLine(isRun + " " + count + "/" + total + " " + String.Format("{0:P2}", (double)(count + complete - total) / (double)complete)); if (printTo != null) { printTo.Invoke("100%"); } if (progTo != null) { progTo.Invoke(1); } // sort *all* the builds loads = tests.Where(t => t != null).OrderByDescending(r => sort(r.GetStats())).Take((top > 0 ? top : 1)); // dump everything to console, if nothing to print to if (printTo == null) { foreach (var l in loads) { Console.WriteLine(l.GetStats().Health + " " + l.GetStats().Attack + " " + l.GetStats().Defense + " " + l.GetStats().Speed + " " + l.GetStats().CritRate + "%" + " " + l.GetStats().CritDamage + "%" + " " + l.GetStats().Resistance + "%" + " " + l.GetStats().Accuracy + "%"); } } // sadface if no builds if (loads.Count() == 0) { Console.WriteLine("No builds :("); if (printTo != null) { printTo.Invoke("Zero :("); } } else { // remember the good one Best = loads.First(); foreach (Rune r in Best.Current.runes) { r.manageStats_In = true; } } } catch (Exception e) { Console.WriteLine("Error " + e); if (printTo != null) { printTo.Invoke(e.ToString()); } } }
/// <summary> /// Fills the instance with acceptable runes from save /// </summary> /// <param name="save">The Save data that contais the runes</param> /// <param name="useLocked">If it should include locked runes</param> /// <param name="useEquipped">If it should include equipped runes (other than the current monster)</param> public void GenRunes(Save save, bool useLocked = false, bool useEquipped = false, bool saveStats = false) { if (save == null) { return; } if (save.Runes == null) { return; } IEnumerable <Rune> rsGlobal = save.Runes; // Only using 'inventory' or runes on mon if (!useEquipped) { rsGlobal = rsGlobal.Where(r => (r.AssignedName == "Unknown name" || r.AssignedName == mon.Name)); } // only if the rune isn't currently locked for another purpose if (!useLocked) { rsGlobal = rsGlobal.Where(r => r.Locked == false); } // Only runes which we've included rsGlobal = rsGlobal.Where(r => BuildSets.Contains(r.Set)); if (saveStats) { foreach (Rune r in rsGlobal) { r.manageStats_Set++; } } int[] slotFakes = new int[6]; bool[] slotPred = new bool[6]; // For each runeslot for (int i = 0; i < 6; i++) { // put the right ones in runes[i] = rsGlobal.Where(r => r.Slot == i + 1).ToArray(); // crank the rune prediction Rune[] rs = runes[i]; int raiseTo = 0; bool predictSubs = false; // find the largest number to raise to // if any along the tree say to predict, do it if (runePrediction.ContainsKey("g")) { int glevel = runePrediction["g"].Key; if (glevel > raiseTo) { raiseTo = glevel; } predictSubs |= runePrediction["g"].Value; } if (runePrediction.ContainsKey(((i % 2 == 0) ? "o" : "e"))) { int mlevel = runePrediction[((i % 2 == 0) ? "o" : "e")].Key; if (mlevel > raiseTo) { raiseTo = mlevel; } predictSubs |= runePrediction[((i % 2 == 0) ? "o" : "e")].Value; } if (runePrediction.ContainsKey((i + 1).ToString())) { int slevel = runePrediction[(i + 1).ToString()].Key; if (slevel > raiseTo) { raiseTo = slevel; } predictSubs |= runePrediction[(i + 1).ToString()].Value; } slotFakes[i] = raiseTo; slotPred[i] = predictSubs; // default fail OR Predicate <Rune> slotTest = r => false; int and = 0; // this means that runes won't get in unless they meet at least 1 criteria // which tab we pulled the filter from string gotScore = ""; // the value to test SUM against int testVal = 0; // TODO: check what inheriting SUM (eg. Odd and 3) does // TODO: check what inheriting AND/OR then SUM (or visa versa) // find the most significant operatand of joining checks if (runeScoring.ContainsKey("g") && runeFilters.ContainsKey("g") && runeFilters["g"].Any(r => r.Value.NonZero)) { var kv = runeScoring["g"]; gotScore = "g"; and = kv.Key; if (kv.Key == 1) { slotTest = r => true; } else if (kv.Key == 2) { testVal = kv.Value; } } // is it and odd or even slot? string tmk = (i % 2 == 1 ? "e" : "o"); if (runeScoring.ContainsKey(tmk) && runeFilters.ContainsKey(tmk) && runeFilters[tmk].Any(r => r.Value.NonZero)) { var kv = runeScoring[tmk]; gotScore = tmk; and = kv.Key; if (kv.Key == 1) { slotTest = r => true; } else if (kv.Key == 2) { testVal = kv.Value; } } // turn the 0-5 to a 1-6 tmk = (i + 1).ToString(); if (runeScoring.ContainsKey(tmk) && runeFilters.ContainsKey(tmk) && runeFilters[tmk].Any(r => r.Value.NonZero)) { var kv = runeScoring[tmk]; gotScore = tmk; and = kv.Key; if (kv.Key == 1) { slotTest = r => true; } else if (kv.Key == 2) { testVal = kv.Value; } } // if an operand was found, ensure the tab contains filter data if (gotScore != "") { if (runeFilters.ContainsKey(gotScore)) { // if all the filters for the tab are zero if (runeFilters[gotScore].All(r => !r.Value.NonZero)) { // set to OR TRUE slotTest = r => true; and = 0; } } } else { // if there wasn't any relevant data for how to pick runes, just take 'em all! slotTest = r => true; } // pull the filters (flat, perc, test) for all the tabs and stats Dictionary <string, RuneFilter> rfG = new Dictionary <string, RuneFilter>(); if (runeFilters.ContainsKey("g")) { rfG = runeFilters["g"]; } Dictionary <string, RuneFilter> rfM = new Dictionary <string, RuneFilter>(); if (runeFilters.ContainsKey((i % 2 == 1 ? "e" : "o"))) { rfM = runeFilters[(i % 2 == 1 ? "e" : "o")]; } Dictionary <string, RuneFilter> rfS = new Dictionary <string, RuneFilter>(); if (runeFilters.ContainsKey((i + 1).ToString())) { rfS = runeFilters[(i + 1).ToString()]; } // if there where no filters with data bool blank = true; Stats rFlat = new Stats(); Stats rPerc = new Stats(); Stats rTest = new Stats(); foreach (string stat in statNames) { RuneFilter rf = new RuneFilter(); if (rfS.ContainsKey(stat)) { rf = rfS[stat]; if (rfM.ContainsKey(stat)) { rf = RuneFilter.Min(rf, rfM[stat]); } if (rfG.ContainsKey(stat)) { rf = RuneFilter.Min(rf, rfG[stat]); } } else { if (rfM.ContainsKey(stat)) { rf = rfM[stat]; if (rfG.ContainsKey(stat)) { rf = RuneFilter.Min(rf, rfG[stat]); } } else { if (rfG.ContainsKey(stat)) { rf = rfG[stat]; } } } if (rf.NonZero) { // put the most relevant divisor in? rFlat[stat] = rf.Flat; rPerc[stat] = rf.Percent; rTest[stat] = rf.Test; blank = false; } } // TODO: seems like it just ignores all the slotTesting before now // no filter data = use all if (blank) { slotTest = r => true; } else { // Set the test based on the type found if (and == 0) { slotTest = r => r.Or(rFlat, rPerc, rTest, raiseTo, predictSubs); } else if (and == 1) { slotTest = r => r.And(rFlat, rPerc, rTest, raiseTo, predictSubs); } else if (and == 2) { slotTest = r => r.Test(rFlat, rPerc, raiseTo, predictSubs) >= testVal; } } runes[i] = runes[i].Where(r => slotTest.Invoke(r)).ToArray(); if (saveStats) { foreach (Rune r in runes[i]) { r.manageStats_RuneFilt++; } } if (i % 2 == 1) // actually evens because off by 1 { // makes sure that the primary stat type is in the selection if (slotStats[i].Count > 0) { runes[i] = runes[i].Where(r => slotStats[i].Contains(r.MainType.ToForms())).ToArray(); } } if (saveStats) { foreach (Rune r in runes[i]) { r.manageStats_TypeFilt++; } } } // Make sure that for each set type, there are enough slots with runes in them // Eg. if only 1,4,5 have Violent, remove all violent runes because you need 4 // for each included set foreach (RuneSet s in BuildSets) { // find how many slots have acceptable runes for it int slots = 0; for (int i = 0; i < 6; i++) { if (runes[i].Any(r => r.Set == s)) { slots += 1; } } // if there isn't enough slots if (slots < Rune.SetRequired(s)) { // remove that set for (int i = 0; i < 6; i++) { runes[i] = runes[i].Where(r => r.Set != s).ToArray(); } } } }
// Check what sets are completed in this build public void CheckSets() { SetsFull = false; // If there are an odd number of runes, don't bother (maybe even check < 6?) if (runeCount % 2 == 1) { return; } // can only have 3 sets max (eg. energy / energy / blade) sets = new RuneSet[3]; // which set we are looking for int setInd = 0; // what slot we are looking at int slotInd = 0; // if we have used this slot in a set yet bool[] used = new bool[6]; // how many runes are in sets int setNums = 0; // Check slots 1 - 5, minimum set size is 2. // Because it will search forward for sets, can't find more from 6 // Eg. starting from 6, working around, find 2 runes? for (; slotInd < 5; slotInd++) { //if there is a uncounted rune in this slot Rune rune = runes[slotInd]; if (rune != null && used[slotInd] == false) { // look for more in the set RuneSet set = rune.Set; // how many runes we need to get int getNum = Rune.SetRequired(set); // how many we got int gotNum = 1; // we have now used this slot used[slotInd] = true; // for the runes after this rune for (int ind = slotInd + 1; ind < 6; ind++) { // if there is a rune in this slot if (runes[ind] != null) { // that hasn't been counted if (used[ind] == false) { // that is the type I want if (runes[ind].Set == set) { used[ind] = true; gotNum++; } } } // if we have more than 1 rune if (gotNum > 1) { // if we have enough runes for a set if (gotNum == getNum) { // log this set sets[setInd] = set; // increase the number of runes in sets setNums += getNum; // look for the next set setInd++; // stop looking forward break; } } } } } // if all runes are in sets if (setNums == 6) { SetsFull = true; } // notify hackers their attempt has failed else if (setNums > 6) { throw new Exception("Wut"); } }