public void Dispose() { MatchBuffer?.Dispose(); MetaEnumerator?.Dispose(); MatchBuffer = null; MetaEnumerator = null; }
public GroupSearchData(int matchBufferCapacity, int[] memberIndices, Dictionary <int, float>[] allMemberRatings) { MatchBuffer = new SetMatchesBuffer(memberIndices.Length, matchBufferCapacity); MemberRatings = new Dictionary <int, float> [memberIndices.Length]; ClaimedCombination = new int[memberIndices.Length]; // gather set member ratings from global collections & compact it into local form for (var i = 0; i < memberIndices.Length; i++) { MemberRatings[i] = allMemberRatings[memberIndices[i]]; } MetaEnumerator = new RatingMetaEnumerator(MemberRatings); }
void ProcessStage(List <int> workingIndices, List <int> outputIndices, int[][] allSetMemberIndices, RelationDataPair[][] allLocalRelationIndexPairs, RelationRatingsData[] allRelationRatings, float[] allSetOrderWeights, GroupSearchData[] allGroupSearchData, RelationMembership[][] allRelationMemberships, Exclusivity[] allMemberExclusivities, Dictionary <int, float>[] allMemberRatings, ref int[] allMemberAssignments) { if (workingIndices.Count == 0) { IsComplete = true; return; } if (m_TickCount == 0) { outputIndices.Clear(); m_MemberAssignments.Clear(); // Determine the order we are going to answer Groups. CalculateSolveOrder(allSetOrderWeights, AllGroupPriorities, workingIndices, m_AllOrderPairs); var setCount = m_AllOrderPairs.Count; m_SetsPerTick = Mathf.Clamp(Mathf.CeilToInt(setCount / (float)FrameBudget), 1, setCount); } // if we need to force completion, setting the end index to the last one will force all groups to solve var endIndex = NeedsCompletion ? m_AllOrderPairs.Count : Mathf.Clamp(m_SolveIndex + m_SetsPerTick, m_SolveIndex, m_AllOrderPairs.Count); for (var solveIndex = m_SolveIndex; solveIndex < endIndex; solveIndex++) { var kvp = m_AllOrderPairs[solveIndex]; var setIndex = kvp.Key; var queryId = SetMatchIds[setIndex].queryID; var memberIndices = allSetMemberIndices[setIndex]; var localPairs = allLocalRelationIndexPairs[setIndex]; var relationRatings = allRelationRatings[setIndex]; var searchData = allGroupSearchData[setIndex]; var matchBuffer = searchData.MatchBuffer; matchBuffer.Reset(); // determine how many options we should try for each member before stopping var hasRelations = localPairs.Length > 0; var searchSpaceSize = GetGroupSearchSpaceSize(memberIndices, allMemberRatings); // there's no options available for this group, skip it. if (searchSpaceSize == 0) { continue; } var portion = GetSearchPortion(searchSpaceSize, SearchSpacePortionCurve, hasRelations); GetIterationTargets(portion, searchData.MemberRatings, searchData.MetaEnumerator.TargetDepths); m_MetaEnumerator = searchData.MetaEnumerator; m_MetaEnumerator.RefreshEnumerators(); m_MetaEnumerator.Reset(); m_MetaEnumerator.MoveNext(); // get enumerator into initial valid state if (!PreviousMatches.TryGetValue(queryId, out var record)) { record = new PreviousSetMatches(memberIndices.Length); PreviousMatches.Add(queryId, record); } /* The critical loop where we search the set's combination space happens here, in either function. * There are 2 different versions of our inner combination loop. * * 1) for Sets with at least one Relation, we filter during the loop only based on whether * the combination satisfies all Relations. We only do duplicate assignment & * data availability checks later, when picking the best match. * * 2) for Sets with no Relations, we check duplicate assignment & previous use during the loop, * and anything that passes those checks is valid. */ var matchFound = hasRelations ? TryCombinations(searchData.MatchBuffer, relationRatings, localPairs) : TryCombinationsWithoutRelations(searchData, record); // if we didn't find any matches for this set during our search, give up and move on. if (!matchFound) { continue; } // if we did find matches, find the highest rated one and do any picking between ties necessary var chosenIndex = BestAvailableMatch(matchBuffer, record, hasRelations); if (chosenIndex < 0) { continue; } // take our chosen match and assign its keys to set members in a staging collection - // this isn't final assignment, since we might have to roll back later. var begin = chosenIndex * matchBuffer.SetSize; var end = begin + matchBuffer.SetSize; for (var i = begin; i < end; i++) { var assignedDataId = matchBuffer.DataIds[i]; var globalMemberIndex = memberIndices[i - begin]; m_MemberAssignments[globalMemberIndex] = assignedDataId; } matchBuffer.CombinationAtRatingIndex(chosenIndex, searchData.ClaimedCombination); // the following stages - marking data used & filling results - will use these as their working indices if (!outputIndices.Contains(setIndex)) { outputIndices.Add(setIndex); } // if this is anything except the last Set to be solved in the cycle, // we need to remove the appropriate possibilities from the remaining sets if (solveIndex >= m_AllOrderPairs.Count - 1) { continue; } for (var i = solveIndex + 1; i < m_AllOrderPairs.Count; i++) { var otherSetIndex = m_AllOrderPairs[i].Key; RemoveClaimedData(searchData.ClaimedCombination, memberIndices, allSetMemberIndices[otherSetIndex], allMemberExclusivities, allMemberRatings); } } m_SolveIndex = endIndex; m_TickCount++; if (endIndex == m_AllOrderPairs.Count) { // finalize our assignments foreach (var kvp in m_MemberAssignments) { allMemberAssignments[kvp.Key] = kvp.Value; } IsComplete = true; } }