private RequirementGroupViewModel FindBestMatch(RequirementGroupViewModel needle, IEnumerable <RequirementGroupViewModel> haystack) { RequirementGroupViewModel bestMatch = null; int bestMatchCount = 0; var requirementExs = RequirementEx.Combine(needle.Requirements.Select(r => r.Requirement)); foreach (var potentialMatch in haystack) { // convert to requirement clauses and compare var compareRequirementExs = RequirementEx.Combine(potentialMatch.Requirements.Select(r => r.Requirement)); int matchCount = compareRequirementExs.Count; foreach (var requirementEx in requirementExs) { for (int i = 0; i < compareRequirementExs.Count; i++) { if (compareRequirementExs[i] == requirementEx) { compareRequirementExs.RemoveAt(i); break; } } } matchCount -= compareRequirementExs.Count; if (matchCount > bestMatchCount) { bestMatchCount = matchCount; bestMatch = potentialMatch; } } return(bestMatch); }
protected override ParseErrorExpression ModifyRequirements(AchievementBuilder builder) { var requirementsEx = RequirementEx.Combine(builder.CoreRequirements); foreach (var requirementEx in requirementsEx) { var lastCondition = requirementEx.Requirements.Last(); if (lastCondition.Type != RequirementType.None) { return(new ParseErrorExpression(string.Format("Cannot apply '{0}' to condition already flagged with {1}", Name.Name, lastCondition.Type))); } lastCondition.Type = RequirementType.SubHits; } return(null); }
public bool RequirementsAreEqual(RequirementGroupViewModel that) { // quick check to make sure the same number of requirements exist if (Requirements.Count() != that.Requirements.Count()) { return(false); } // convert to requirement clauses and compare var requirementExs = RequirementEx.Combine(Requirements.Select(r => r.Requirement)); var compareRequirementExs = RequirementEx.Combine(that.Requirements.Select(r => r.Requirement)); if (requirementExs.Count != compareRequirementExs.Count) { return(false); } foreach (var requirementEx in requirementExs) { bool matched = false; for (int i = 0; i < compareRequirementExs.Count; i++) { if (compareRequirementExs[i] == requirementEx) { compareRequirementExs.RemoveAt(i); matched = true; break; } } if (!matched) { return(false); } } return(true); }
public void TestCombine(string input, string expected) { var achievement = new AchievementBuilder(); achievement.ParseRequirements(Tokenizer.CreateTokenizer(input)); var groups = RequirementEx.Combine(achievement.CoreRequirements); var builder = new StringBuilder(); foreach (var group in groups) { if (builder.Length > 0) { builder.Append('|'); } group.AppendString(builder, NumberFormat.Decimal); } Assert.That(builder.ToString(), Is.EqualTo(expected)); // make sure we didn't modify the source requirements Assert.That(achievement.SerializeRequirements(), Is.EqualTo(input)); }
private static bool CanUseAddHits(List <ICollection <Requirement> > requirements) { // make sure each clause ends with a value comparison foreach (var requirement in requirements) { if (requirement.Last().Right.Type != FieldType.Value) { return(false); } // cannot change OrNext to AddHits if AddHits already exists if (requirement.Any(r => r.Type == RequirementType.AddHits)) { return(false); } } // find the first condition that doesn't evaluate to always_false List <RequirementEx> first; var firstIndex = 0; do { first = RequirementEx.Combine(requirements[firstIndex]); if (first.Count >= 1) { break; } // condition is always_false, try next ++firstIndex; } while (firstIndex < requirements.Count); if (first.Count != 1) { return(false); } var firstOperator = first[0].Requirements.Last().Operator; for (int i = firstIndex + 1; i < requirements.Count; ++i) { var requirementEx = RequirementEx.Combine(requirements[i]); if (requirementEx.Count == 0) { continue; } if (requirementEx.Count > 1) { return(false); } var right = requirementEx[0]; if (right.Evaluate() == false) { continue; } if (right.Requirements.Last().Operator != firstOperator || right.Requirements.Last().Right.Type != FieldType.Value || !right.LeftEquals(first[0])) { // if both sides are not making the same comparison to different values, they // could occur in the same frame. don't change the OrNext to an AddHits return(false); } } return(true); }
public RequirementGroupViewModel(string label, IEnumerable <Requirement> requirements, IEnumerable <Requirement> compareRequirements, NumberFormat numberFormat, IDictionary <int, string> notes) { Label = label; var requirementExs = RequirementEx.Combine(requirements); var compareRequirementExs = RequirementEx.Combine(compareRequirements); var unmatchedCompareRequirementExs = new List <RequirementEx>(compareRequirementExs); var matches = new Dictionary <RequirementEx, RequirementEx>(); var unmatchedRequirementExs = new List <RequirementEx>(); // first pass: find exact matches foreach (var requirementEx in requirementExs) { bool matched = false; for (int i = 0; i < unmatchedCompareRequirementExs.Count; i++) { if (unmatchedCompareRequirementExs[i] == requirementEx) { matches[requirementEx] = unmatchedCompareRequirementExs[i]; unmatchedCompareRequirementExs.RemoveAt(i); matched = true; break; } } if (!matched) { unmatchedRequirementExs.Add(requirementEx); } } // second pass: find close matches while (unmatchedRequirementExs.Count > 0) { var bestScore = MinimumMatchingScore - 1; var matchIndex = -1; var compareIndex = -1; for (var i = 0; i < unmatchedRequirementExs.Count; i++) { var requirementEx = unmatchedRequirementExs[i]; var evaluation = requirementEx.Evaluate(); if (evaluation != null) { for (var j = 0; j < unmatchedCompareRequirementExs.Count; j++) { if (unmatchedCompareRequirementExs[j].Evaluate() == evaluation) { bestScore = 1000; matchIndex = i; compareIndex = j; break; } } } for (var j = 0; j < unmatchedCompareRequirementExs.Count; j++) { var score = CalculateScore(requirementEx, unmatchedCompareRequirementExs[j]); if (score > bestScore) { bestScore = score; matchIndex = i; compareIndex = j; } } } if (matchIndex == -1) { break; } matches[unmatchedRequirementExs[matchIndex]] = unmatchedCompareRequirementExs[compareIndex]; unmatchedRequirementExs.RemoveAt(matchIndex); unmatchedCompareRequirementExs.RemoveAt(compareIndex); } // construct the output list from the requirements var pairs = new List <Tuple <RequirementEx, RequirementEx> >(matches.Count + unmatchedCompareRequirementExs.Count); foreach (var requirementEx in requirementExs) { RequirementEx match; matches.TryGetValue(requirementEx, out match); pairs.Add(new Tuple <RequirementEx, RequirementEx>(requirementEx, match)); } // allow an always_true() group to match an empty group if (pairs.Count == 0 && unmatchedCompareRequirementExs.Count == 1 && unmatchedCompareRequirementExs[0].Evaluate() == true) { pairs.Add(new Tuple <RequirementEx, RequirementEx>(unmatchedCompareRequirementExs[0], unmatchedCompareRequirementExs[0])); unmatchedCompareRequirementExs.Clear(); } // third pass: insert any unmatched comparison requirements if (unmatchedCompareRequirementExs.Count > 0) { var indices = new int[compareRequirementExs.Count + 2]; for (int i = 1; i < indices.Length - 1; ++i) { indices[i] = -2; } indices[0] = -1; indices[compareRequirementExs.Count + 1] = compareRequirementExs.Count + 1; for (int i = 0; i < requirementExs.Count; i++) { RequirementEx match; if (matches.TryGetValue(requirementExs[i], out match)) { indices[compareRequirementExs.IndexOf(match) + 1] = i; } } foreach (var requirementEx in unmatchedCompareRequirementExs) { var insertIndex = pairs.Count; var requirementIndex = compareRequirementExs.IndexOf(requirementEx); if (requirementIndex < compareRequirementExs.Count - 1) { for (int i = 1; i < indices.Length - 1; i++) { if (indices[i - 1] == requirementIndex - 1 || indices[i + 1] == requirementIndex + 1) { insertIndex = i - 1; break; } } } if (insertIndex < pairs.Count) { for (int i = 0; i < indices.Length; i++) { if (indices[i] >= insertIndex) { indices[i]++; } } } if (insertIndex < indices.Length - 1) { indices[insertIndex + 1] = requirementIndex; } pairs.Insert(insertIndex, new Tuple <RequirementEx, RequirementEx>(null, requirementEx)); } } // convert RequirementEx pairs to RequirementComparisonViewModels var list = new List <RequirementViewModel>(); foreach (var pair in pairs) { AppendRequirements(list, pair.Item1, pair.Item2, numberFormat, notes); } // attempt to merge requirements that may have been separated into separate RequirementExs int leftIndex, rightIndex; while (GetBestMerge(list, out leftIndex, out rightIndex)) { list[leftIndex] = new RequirementComparisonViewModel(list[leftIndex].Requirement, ((RequirementComparisonViewModel)list[rightIndex]).CompareRequirement, numberFormat, notes); list.RemoveAt(rightIndex); } Requirements = list; }