Пример #1
0
		public void GenRunes(Save save) {
			if (save?.Runes == null)
				return;

			if (!getRunningHandle())
				return;
			try {

				if (Type == BuildType.Lock) {
					foreach (var r in Mon.Current.Runes) {
						if (r != null) {
							if (r.Locked)
								runes[r.Slot - 1] = new Rune[0];
							else
								runes[r.Slot - 1] = new Rune[] { r };
						}
					}
					return;
				}

				if (Type == BuildType.Link && LinkBuild == null) {
					for (int i = 0; i < 6; i++)
						runes[i] = new Rune[0];
					return;
				}

				// todo: less .ToArray-ing
				ParallelQuery<Rune> rsGlobal = save.Runes.AsParallel();

				// if not saving stats, cull unusable here
				if (!BuildSaveStats) {
					// Only using 'inventory' or runes on mon
					// also, include runes which have been unequipped (should only look above)
					if (!RunesUseEquipped || RunesOnlyFillEmpty)
						rsGlobal = rsGlobal.Where(r => r.IsUnassigned || r.AssignedId == Mon.Id || r.Swapped);
					// only if the rune isn't currently locked for another purpose
					if (!RunesUseLocked)
						rsGlobal = rsGlobal.Where(r => !r.Locked);
					rsGlobal = rsGlobal.Where(r => !BannedRuneId.Any(b => b == r.Id) && !BannedRunesTemp.Any(b => b == r.Id));
				}

				if ((BuildSets.Any() || RequiredSets.Any()) && BuildSets.All(s => Rune.SetRequired(s) == 4) && RequiredSets.All(s => Rune.SetRequired(s) == 4)) {
					// if only include/req 4 sets, include all 2 sets autoRuneSelect && ()
					rsGlobal = rsGlobal.Where(r => BuildSets.Contains(r.Set) || RequiredSets.Contains(r.Set) || Rune.SetRequired(r.Set) == 2);
				}
				else if (BuildSets.Any() || RequiredSets.Any()) {
					rsGlobal = rsGlobal.Where(r => BuildSets.Contains(r.Set) || RequiredSets.Contains(r.Set));
					// Only runes which we've included
				}

				if (BuildSaveStats) {
					foreach (Rune r in rsGlobal) {
						r.manageStats.AddOrUpdate("currentBuildPoints", 0, (k, v) => 0);
						if (!BuildGoodRunes)
							r.manageStats.AddOrUpdate("Set", 1, (s, d) => { return d + 1; });
						else
							r.manageStats.AddOrUpdate("Set", 0.001, (s, d) => { return d + 0.001; });
					}
				}

				int?[] slotFakes = new int?[6];
				bool[] slotPred = new bool[6];
				getPrediction(slotFakes, slotPred);

				// Set up each runeslot
				for (int i = 0; i < 6; i++) {
					// put the right ones in
					runes[i] = rsGlobal.Where(r => r.Slot == i + 1).ToArray();

					// makes sure that the primary stat type is in the selection
					if (i % 2 == 1 && SlotStats[i].Count > 0) // actually evens because off by 1
					{
						runes[i] = runes[i].AsParallel().Where(r => SlotStats[i].Contains(r.Main.Type.ToForms())).ToArray();
					}

					if (BuildSaveStats) {
						foreach (Rune r in runes[i]) {
							if (!BuildGoodRunes)
								r.manageStats.AddOrUpdate("TypeFilt", 1, (s, d) => { return d + 1; });
							else
								r.manageStats.AddOrUpdate("TypeFilt", 0.001, (s, d) => { return d + 0.001; });
						}
						// cull here instead
						if (!RunesUseEquipped || RunesOnlyFillEmpty)
							runes[i] = runes[i].AsParallel().Where(r => r.IsUnassigned || r.AssignedId == Mon.Id || r.Swapped).ToArray();
						if (!RunesUseLocked)
							runes[i] = runes[i].AsParallel().Where(r => !r.Locked).ToArray();

					}
				}

				// clean out runes which won't make complete sets
				cleanBroken();

				// clean out runes which won't pass the minimum
				cleanMinimum();

				if (AutoRuneSelect) {
					// TODO: triple pass: start at needed for min, but each pass reduce the requirements by the average of the chosen runes for that pass, increase it by build scoring

					var needed = NeededForMin(slotFakes, slotPred);
					if (needed == null)
						AutoRuneSelect = false;

					if (AutoRuneSelect) {
						var needRune = new Stats(needed) / 6;

						// Auto-Rune select picking N per RuneSet should be fine to pick more because early-out should keep times low.
						// reduce number of runes to 10-15

						// odds first, then evens
						foreach (int i in new int[] { 0, 2, 4, 5, 3, 1 }) {
							Rune[] rr = new Rune[0];
							foreach (var rs in RequiredSets) {
								rr = rr.Concat(runes[i].AsParallel().Where(r => r.Set == rs).OrderByDescending(r => runeVsStats(r, needRune) * 10 + runeVsStats(r, Sort)).Take(AutoRuneAmount / 2).ToArray()).ToArray();
							}
							if (rr.Length < AutoRuneAmount)
								rr = rr.Concat(runes[i].AsParallel().Where(r => !rr.Contains(r)).OrderByDescending(r => runeVsStats(r, needRune) * 10 + runeVsStats(r, Sort)).Take(AutoRuneAmount - rr.Length).ToArray()).Distinct().ToArray();

							runes[i] = rr;
						}

						cleanBroken();
					}
				}
				if (!AutoRuneSelect) {
					// TODO: Remove
#if BUILD_RUNE_LOGGING
					//var tmp = RuneLog.logTo;
					//using (var fs = new System.IO.FileStream("sampleselect.log", System.IO.FileMode.Create))
					//using (var sw = new System.IO.StreamWriter(fs)) {
						RuneLog.logTo = sw;
#else
					{
#endif
						// Filter each runeslot
						for (int i = 0; i < 6; i++) {
							// default fail OR
							Predicate<Rune> slotTest = MakeRuneScoring(i + 1, slotFakes[i] ?? 0, slotPred[i]);

							runes[i] = runes[i].AsParallel().Where(r => slotTest.Invoke(r)).OrderByDescending(r => r.manageStats.GetOrAdd("testScore", 0)).ToArray();
							var filt = LoadFilters(i + 1);
							if (filt.Count != null) {

								var tSets = RequiredSets.Count + BuildSets.Except(RequiredSets).Count();
								var perc = RequiredSets.Count / (float)tSets;
								var reqLoad = Math.Max(2,(int)((filt.Count ?? AutoRuneAmount ) * perc));

								var rr = runes[i].AsParallel().Where(r => RequiredSets.Contains(r.Set)).GroupBy(r => r.Set).SelectMany(r => r).Take(reqLoad).ToArray();

								var incLoad = (filt.Count ?? AutoRuneAmount) - rr.Count();
								runes[i] = rr.Concat(runes[i].AsParallel().Where(r => !RequiredSets.Contains(r.Set)).Take(incLoad)).ToArray();

								// TODO: pick 20% per required set
								// Then fill remaining with the best from included
								// Go around checking if there are enough runes from each set to complete it (if NonBroken)
								// Check if removing N other runes of SCORE will permit finishing set
								// Remove rune add next best in slot
							}

							if (BuildSaveStats) {
								foreach (Rune r in runes[i]) {
									if (!BuildGoodRunes)
										r.manageStats.AddOrUpdate("RuneFilt", 1, (s, d) => d + 1);
									else
										r.manageStats.AddOrUpdate("RuneFilt", 0.001, (s, d) => d + 0.001);
								}
							}
						}
					}
				}
				if (RunesDropHalfSetStat) {
					for (int i = 0; i < 6; i++) {
						double rmm = 0;
						var runesForSlot = runes[i];
						var outRunes = new List<Rune>();
						var runesBySet = runesForSlot.GroupBy(r => r.Set);
						foreach (var rsg in runesBySet) {
							var runesByMain = rsg.GroupBy(r => r.Main.Type);
							foreach (var rmg in runesByMain) {
								rmm = rmg.Max(r => r.manageStats.GetOrAdd("testScore", 0)) * 0.6;
								if (rmm > 0) {
									outRunes.AddRange(rmg.Where(r => r.manageStats.GetOrAdd("testScore", 0) > rmm));
								}
							}
						}
						if (rmm > 0)
							runes[i] = outRunes.ToArray();
					}
				}
				// if we are only to fill empty slots
				if (RunesOnlyFillEmpty) {
					for (int i = 0; i < 6; i++) {
						if (Mon.Current.Runes[i] != null && (!Mon.Current.Runes[i]?.Locked ?? false)) {
							runes[i] = new Rune[0];
						}
					}
				}
				// always try to put the current rune back in
				for (int i = 0; i < 6; i++) {
					var r = Mon.Current.Runes[i];
					if (r == null)
						continue;

					bool isGoodType = true;
					if (i % 2 == 1 && SlotStats[i].Count > 0) {
						isGoodType = SlotStats[i].Contains(r.Main.Type.ToForms());
					}
					if (!runes[i].Contains(r) && !r.Locked && isGoodType) {
						var tl = runes[i].ToList();
						tl.Add(r);
						runes[i] = tl.ToArray();
					}
				}

				grinds = runes.SelectMany(rg => rg.SelectMany(r => r.FilterGrinds(save.Crafts).Concat(r.FilterEnchants(save.Crafts)))).Distinct().ToArray();
			}
			finally {
				IsRunning = false;
			}
		}