private static Tuple<string, WinningBean[]> GetWinnersSprtLineage(string lineage, Tuple<string, WinningBean[]> existing, WinningBean[] candidates, int maxPerLineage, bool tracksLivingInstances) { List<WinningBean> distinctBeans = new List<WinningBean>(); if (tracksLivingInstances) { // Existing and candidate lists could have the same instance of a bean (from different moments in time). Group by // bean instances var beanGroups = UtilityCore.Iterate(existing == null ? null : existing.Item2, candidates). GroupBy(o => o.Ship.PhysicsBody). // the physics body implements IComparable (compares on token) ToArray(); // Now pull only the best example from each instance of a bean foreach (var group in beanGroups) { distinctBeans.Add(group.OrderByDescending(o => o.Score).First()); } } else { // DNA should never be shared between existing and new, so everything is unique distinctBeans = UtilityCore.Iterate(existing == null ? null : existing.Item2, candidates).ToList(); } // Sort by score, and take the top performers WinningBean[] retVal = distinctBeans. OrderByDescending(o => o.Score). Take(maxPerLineage). ToArray(); // Exit Function return Tuple.Create(lineage, retVal); }
/// <summary> /// This merges the current tree with the beans passed in. Only keeps the top performers, independent of where they came from /// </summary> private static Tuple<DateTime, WinningSet[]> GetWinners(Tuple<DateTime, WinningSet[]> existing, WinningBean[] candidates, int maxLineages, int maxPerLineage, bool tracksLivingInstances) { List<WinningSet> retVal = new List<WinningSet>(); #region Validate candidates if (candidates != null) { if (tracksLivingInstances) { if (candidates.Any(o => o.DNA != null)) { throw new ArgumentException("When tracking live instances, raw dna shouldn't be passed in"); } } else { if (candidates.Any(o => o.Ship != null)) { throw new ArgumentException("When tracking dna, live instances shouldn't be passed in"); } } } #endregion // Shoot through all the unique names across exising and candidate foreach (string name in UtilityCore.Iterate( existing == null ? null : existing.Item2.Select(o => o.ShipName), tracksLivingInstances ? candidates.Select(o => o.Ship.Name) : candidates.Select(o => o.DNA.ShipName) ).Distinct()) { // Each name is treated independently of the other names. So one name could have far worse winners than // others, and it wouldn't matter retVal.Add(GetWinnersSprtName(name, existing == null ? null : existing.Item2.Where(o => o.ShipName == name).FirstOrDefault(), tracksLivingInstances ? candidates.Where(o => o.Ship.Name == name).ToArray() : candidates.Where(o => o.DNA.ShipName == name).ToArray(), maxLineages, maxPerLineage, tracksLivingInstances)); } // Sort the list by the highest performer return Tuple.Create(DateTime.UtcNow, retVal.OrderByDescending(o => o.BeansByLineage.Max(p => p.Item2.Max(q => q.Score))). ToArray()); }
private static WinningSet GetWinnersSprtName(string name, WinningSet existing, WinningBean[] candidates, int maxLineages, int maxPerLineage, bool tracksLivingInstances) { if (candidates == null) { #region Validate Existing //TODO: See if the constraints are more restrictive //if (existing != null) //{ // if (existing.BeansByLineage.Length > maxLineages) // { // } //} #endregion return existing; } // Group the candidates up by lineage var candidateLineage = candidates.GroupBy(o => tracksLivingInstances ? o.Ship.Lineage : o.DNA.ShipLineage).ToArray(); List<Tuple<string, WinningBean[]>> retVal = new List<Tuple<string, WinningBean[]>>(); // Shoot through all the unique lineages across existing and candidate foreach (string lineage in UtilityCore.Iterate( existing == null ? null : existing.BeansByLineage.Select(o => o.Item1), candidateLineage.Select(o => o.Key)). Distinct()) { var matchingCandidate = candidateLineage.Where(o => o.Key == lineage).FirstOrDefault(); // Get the top beans for this lineage retVal.Add(GetWinnersSprtLineage(lineage, existing == null ? null : existing.BeansByLineage.Where(o => o.Item1 == lineage).FirstOrDefault(), matchingCandidate == null ? null : matchingCandidate.ToArray(), maxPerLineage, tracksLivingInstances)); } // Sort, take top return new WinningSet(name, retVal.OrderByDescending(o => o.Item2.Max(p => p.Score)). Take(maxLineages). ToArray()); }
private static WinningBean[] RemoveBeansSprtArgs(List<Tuple<long, WinningBean>> returnList, WinningBean[] beans, long[] removals) { if (beans == null) { return null; } List<WinningBean> remaining = new List<WinningBean>(); for (int cntr = 0; cntr < beans.Length; cntr++) { long token = beans[cntr].Ship.PhysicsBody.Token; if (removals.Contains(token)) { returnList.Add(Tuple.Create(token, beans[cntr])); } else { remaining.Add(beans[cntr]); } } return remaining.ToArray(); }
/// <summary> /// This pulls beans out of the list passed in and the current tree /// </summary> private Tuple<long, WinningBean>[] RemoveBeans(out WinningBean[] remaining, WinningBean[] beans, long[] removals) { List<Tuple<long, WinningBean>> retVal = new List<Tuple<long, WinningBean>>(); // Reduce the list of beans passed in remaining = RemoveBeansSprtArgs(retVal, beans, removals); //NOTE: Since there could be other threads working this tree, it's possible for removed beans to reemerge. But that would //only happen if StoreWinners is called faster than a thread can process, which shouldn't happen in reality _current = RemoveBeansSprtTree(retVal, _current, removals); // Exit Function return retVal.ToArray(); }
private Tuple<long, WinningBean>[] StoreWinnersSprtDoIt(WinningBean[] beans, long[] removals) { if ((removals == null || removals.Length == 0) && (beans == null || beans.Length == 0)) { return null; } // Remove - this thread Tuple<long, WinningBean>[] retVal = null; WinningBean[] reducedBeans = beans; if (removals != null && removals.Length > 0) { retVal = RemoveBeans(out reducedBeans, beans, removals); } if (reducedBeans != null && reducedBeans.Length > 0) { int maxLineages = this.MaxLineages; int maxPerLineage = this.MaxPerLineage; bool tracksLiveInstances = this.TracksLivingInstances; #region Merge - other thread // Merge the scores passed in with the current winners var task = Task.Factory.StartNew(() => { // Merge return GetWinners(_current, reducedBeans, maxLineages, maxPerLineage, tracksLiveInstances); }); #endregion #region Store results - this thread // Store the results task.ContinueWith(result => { var prev = _current; // since it's volatile, only read once // Store if newer if (prev == null || result.Result.Item1 > prev.Item1) { _current = result.Result; } }, TaskScheduler.FromCurrentSynchronizationContext()); #endregion } // Exit Function return retVal; }
/// <summary> /// This method is only used for live tracking. You can pass in tokens of ships that have died, and this will remove them /// from the list, as well as return the final score of any that were actually removed. /// </summary> public Tuple<long, WinningBean>[] StoreWinners(WinningBean[] adds, long[] removals) { if (!this.TracksLivingInstances) { throw new InvalidOperationException("This method can only be used when tracking live instances"); } return StoreWinnersSprtDoIt(adds, removals); }
public void StoreWinners(WinningBean[] beans) { StoreWinnersSprtDoIt(beans, null); }