public void Match_shape_grants_one_copy_of_corresponding_tool(Vector2Int[] horizontalMatches, Vector2Int[] verticalMatches, Tool awardedTool) { Toolbox toolbox = CreateToolbox(); MatchStep step = MatchStepFromMatches(1, horizontalMatches, verticalMatches); toolbox.RewardMatches(step); Assert.That(toolbox.GetAvailableUses(awardedTool), Is.EqualTo(1)); }
private async Task ShowMatchShapeTutorialAsync(MatchStep matchStep, List <Tool> newTools) { var matchedRects = matchStep.LeftEndsOfHorizontalMatches .Select(pos => new GridRect(pos, pos + new Vector2Int(2, 0))) .Concat(matchStep.BottomEndsOfVerticalMatches .Select(pos => new GridRect(pos, pos + new Vector2Int(0, 2)))) .ToList(); await TutorialManager.Instance.ShowTutorialIfNewAsync(TutorialID.MatchShapes, matchedRects, newTools); }
public void Increasing_chain_length_still_rewards_tools(Vector2Int[] horizontalMatches, Vector2Int[] verticalMatches, Tool awardedTool) { Toolbox toolbox = CreateToolbox(); MatchStep step = MatchStepFromMatches(1, horizontalMatches, verticalMatches); toolbox.RewardMatches(step); step = MatchStepFromMatches(2, horizontalMatches, verticalMatches); toolbox.RewardMatches(step); Assert.That(toolbox.GetAvailableUses(awardedTool), Is.EqualTo(2)); }
public void Chain_length_1_rewards_tools_only_once(Vector2Int[] horizontalMatches, Vector2Int[] verticalMatches, Tool awardedTool) { Toolbox toolbox = CreateToolbox(); MatchStep step = MatchStepFromMatches(1, horizontalMatches, verticalMatches); toolbox.RewardMatches(step); step = MatchStepFromMatches(1, horizontalMatches, verticalMatches); toolbox.RewardMatches(step); Assert.That(toolbox.GetAvailableUses(awardedTool), Is.EqualTo(1)); }
public async Task RewardMatches(MatchStep matchStep) { List <Tool> newTools = toolbox.RewardMatches(matchStep); if (newTools.Count > 0) { sessionMetrics.RegisterToolRewards(newTools); await ShowMatchShapeTutorialAsync(matchStep, newTools); } UpdateUI(); }
/// <summary> /// Build a plan for processing the LIKE functionality. /// </summary> protected internal virtual void BuildPlan() { string pattern = Pattern; if (pattern == null) { // the result of "v LIKE NULL" is false for all values of "v" m_plan = OptimizationPlan.AlwaysFalse; return; } char[] patternChars = pattern.ToCharArray(); int patternCharsLength = patternChars.Length; char escapeChar = EscapeChar; bool isEscape = false; bool ignoreCase = IgnoreCase; StringBuilder sb = null; BitArray bits = null; IList list = new ArrayList(); // parse the pattern into a list of steps for (int of = 0; of < patternCharsLength; ++of) { char ch = patternChars[of]; if (isEscape) { isEscape = false; } else if (ch == escapeChar) { isEscape = true; continue; } else if (ch == '%') { if (sb != null) { list.Add(new MatchStep(this, sb, bits)); sb = null; bits = null; } if ((list.Count == 0) || list[list.Count - 1] != ANY) { list.Add(ANY); } continue; } else if (ch == '_') { if (bits == null) { bits = new BitArray(64); } CollectionUtils.SetBit(bits, sb == null ? 0 : sb.Length, true); } if (sb == null) { sb = new StringBuilder(); } sb.Append(ch); } // check for unclosed escape if (isEscape) { throw new ArgumentException("pattern ends with an unclosed escape: \"" + pattern + "\""); } // store off the last match step (if there is one) if (sb != null) { list.Add(new MatchStep(this, sb, bits)); } // check for simple optimizations switch (list.Count) { case 0: // case sensistive case insensitive pattern // ------------------ ------------------ ------- // OptimizationPlan.ExactMatch OptimizationPlan.ExactMatch "" m_plan = OptimizationPlan.ExactMatch; m_part = ""; return; case 1: // case sensistive case insensitive pattern // ------------------ ------------------ ------- // OptimizationPlan.ExactMatch OptimizationPlan.InsensMatch "xyz" (no wildcards) // OptimizationPlan.AlwaysTrue OptimizationPlan.AlwaysTrue "%" (only '%' wildcards) { object o = list[0]; if (o == ANY) { m_plan = OptimizationPlan.AlwaysTrue; return; } var matchstep = (MatchStep)o; if (matchstep.IsLiteral) { m_plan = ignoreCase ? OptimizationPlan.InsensMatch : OptimizationPlan.ExactMatch; // matchstep may contain escaped chars (such as '_') m_part = matchstep.String; return; } } break; case 2: // case sensistive case insensitive pattern // ------------------ ------------------ ------- // OptimizationPlan.StartsWithChar OptimizationPlan.StartsWithInsens "x%" // OptimizationPlan.StartsWithString OptimizationPlan.StartsWithInsens "xyz%" // OptimizationPlan.EndsWithChar OptimizationPlan.EndsWithInsens "%x" // OptimizationPlan.EndsWithString OptimizationPlan.EndsWithInsens "%xyz" { MatchStep matchStep; bool startsWith; object o = list[0]; if (o == ANY) { startsWith = false; matchStep = (MatchStep)list[1]; } else { startsWith = true; matchStep = (MatchStep)o; } if (matchStep.IsLiteral) { if (ignoreCase) { m_plan = startsWith ? OptimizationPlan.StartsWithInsens : OptimizationPlan.EndsWithInsens; m_part = matchStep.String; } else if (matchStep.Length == 1) { m_plan = startsWith ? OptimizationPlan.StartsWithChar : OptimizationPlan.EndsWithChar; m_partChar = matchStep.String[0]; } else { m_plan = startsWith ? OptimizationPlan.StartsWithString : OptimizationPlan.EndsWithString; m_part = matchStep.String; } return; } } break; case 3: // case sensistive case insensitive pattern // ------------------ ------------------ ------- // OptimizationPlan.ContainsChar n/a "%x%" // OptimizationPlan.ContainsString n/a "%xyz%" { if (!ignoreCase) { object o = list[1]; if (o != ANY) { var matchstep = (MatchStep)o; if (matchstep.IsLiteral) { if (matchstep.Length == 1) { m_plan = OptimizationPlan.ContainsChar; m_partChar = matchstep.String[0]; } else { m_plan = OptimizationPlan.ContainsString; m_part = matchstep.String; } return; } } } } break; } // build iterative plan // # steps description // ------- -------------------------------------------------------- // 1 match with '_' // 2 starts with or ends with match with '_' // 3 starts and ends with matches, or contains match with '_' // 4+ alternating % and matches, potentially starting with // and/or ending with matches, each could have '_' m_plan = OptimizationPlan.IterativeEval; switch (list.Count) { case 0: throw new Exception("assertion failed"); case 1: m_stepFront = (MatchStep)list[0]; m_isTrailingTextAllowed = false; break; case 2: { object step1 = list[0]; object step2 = list[1]; // should not have two "ANYs" in a row, but one must be ANY Debug.Assert(step1 == ANY ^ step2 == ANY); if (step1 == ANY) { m_stepBack = (MatchStep)step2; m_isTrailingTextAllowed = false; } else { m_stepFront = (MatchStep)step1; m_isTrailingTextAllowed = true; } } break; default: { int matchStepsCount = list.Count; // figure out where the "middle" is; the "middle" is // defined as those steps that occur after one or more // '%' matches and before one or more '%' matches int ofStartMiddle = 1; // offset in list of first middle step int ofEndMiddle = matchStepsCount - 2; // offset in list of last middle step object first = list[0]; if (first != ANY) { m_stepFront = (MatchStep)first; ++ofStartMiddle; } object last = list[matchStepsCount - 1]; bool isLastStepAny = (last == ANY); if (!isLastStepAny) { m_stepBack = (MatchStep)last; --ofEndMiddle; } m_isTrailingTextAllowed = isLastStepAny; int matches = (ofEndMiddle - ofStartMiddle) / 2 + 1; var matchesArray = new MatchStep[matches]; int match = 0; for (int of = ofStartMiddle; of <= ofEndMiddle; of += 2) { matchesArray[match++] = (MatchStep)list[of]; } m_stepsMiddle = matchesArray; } break; } }
/// <summary> /// Check the passed string value to see if it matches the pattern /// that this filter was constructed with. /// </summary> /// <param name="value"> /// The <b>string</b> value to match against this filter's pattern. /// </param> /// <returns> /// <b>true</b> if the passed <b>string</b> value is LIKE this /// filter's pattern. /// </returns> protected internal virtual bool IsMatch(string value) { if (value == null) { // null is not like anything return(false); } int length = value.Length; switch (m_plan) { case OptimizationPlan.StartsWithChar: return(length >= 1 && value[0] == m_partChar); case OptimizationPlan.StartsWithString: return(value.StartsWith(m_part)); case OptimizationPlan.StartsWithInsens: { string prefix = m_part; int prefixLength = prefix.Length; if (prefixLength > length) { return(false); } return(String.Compare(value, 0, prefix, 0, prefixLength, true) == 0); } case OptimizationPlan.EndsWithChar: return(length >= 1 && value[length - 1] == m_partChar); case OptimizationPlan.EndsWithString: return(value.EndsWith(m_part)); case OptimizationPlan.EndsWithInsens: { string suffix = m_part; int suffixLength = suffix.Length; if (suffixLength > length) { return(false); } return(String.Compare(value, length - suffixLength, suffix, 0, suffixLength, true) == 0); } case OptimizationPlan.ContainsChar: return(value.IndexOf(m_partChar) >= 0); case OptimizationPlan.ContainsString: return(value.IndexOf(m_part) >= 0); case OptimizationPlan.AlwaysTrue: return(true); case OptimizationPlan.AlwaysFalse: return(false); case OptimizationPlan.ExactMatch: return(m_part.Equals(value)); case OptimizationPlan.InsensMatch: return(m_part.ToUpper().Equals(value.ToUpper())); } // get the character data and iteratively process the LIKE char[] chars = value.ToCharArray(); int charsLength = chars.Length; int ofBegin = 0; int ofEnd = charsLength; // start by checking the front MatchStep matchStep = m_stepFront; if (matchStep != null) { int stepLength = matchStep.Length; if (stepLength > charsLength || matchStep.IndexOf(chars, ofBegin, stepLength) < 0) { return(false); } ofBegin = stepLength; } // next check the back matchStep = m_stepBack; if (matchStep != null) { int stepLength = matchStep.Length; int ofStep = charsLength - stepLength; if (ofStep < ofBegin || matchStep.IndexOf(chars, ofStep, ofEnd) < 0) { return(false); } ofEnd = ofStep; } // check the middle MatchStep[] matchStepMiddle = m_stepsMiddle; if (matchStepMiddle != null) { for (int i = 0, c = matchStepMiddle.Length; i < c; ++i) { matchStep = matchStepMiddle[i]; int of = matchStep.IndexOf(chars, ofBegin, ofEnd); if (of < 0) { return(false); } ofBegin = of + matchStep.Length; } } // this is the "is there anything left" check, which solves an // ambiguity in the "iterative step" design that did not correctly // differentiate between "%a_%" and "%a_", for example if (m_stepBack == null && !m_isTrailingTextAllowed) { if (ofBegin != charsLength) { return(false); } } return(true); }
private async Task AnimateMatchStepAsync(MatchStep step) { await AnimateMatchedTilesAsync(step.MatchedTiles); await AnimateFallingTilesAsync(step.MovedTiles); }
private int ScoreMatchStep(MatchStep matchStep, int level) { return(matchStep.MatchedTiles.Count * baseScore * matchStep.ChainLength * (level + 1)); }
public List <Tool> RewardMatches(MatchStep step) { var previousToolUses = new Dictionary <Tool, int>(availableToolUses); foreach (Vector2Int match in step.LeftEndsOfHorizontalMatches) { RewardShape(step.ChainLength, MatchShape.Row3); if (step.LeftEndsOfHorizontalMatches.Contains(match + new Vector2Int(1, 0))) { RewardShape(step.ChainLength, MatchShape.Row4); } if (step.LeftEndsOfHorizontalMatches.Contains(match + new Vector2Int(2, 0))) { RewardShape(step.ChainLength, MatchShape.Row5); } if (step.BottomEndsOfVerticalMatches.Contains(match)) { RewardShape(step.ChainLength, MatchShape.L); if (step.LeftEndsOfHorizontalMatches.Contains(match + new Vector2Int(0, 2))) { RewardShape(step.ChainLength, MatchShape.U); if (step.BottomEndsOfVerticalMatches.Contains(match + new Vector2Int(2, 0))) { RewardShape(step.ChainLength, MatchShape.O); } } if (step.BottomEndsOfVerticalMatches.Contains(match + new Vector2Int(2, 0))) { RewardShape(step.ChainLength, MatchShape.U); } } if (step.BottomEndsOfVerticalMatches.Contains(match + new Vector2Int(2, 0))) { RewardShape(step.ChainLength, MatchShape.L); if (step.LeftEndsOfHorizontalMatches.Contains(match + new Vector2Int(0, 2))) { RewardShape(step.ChainLength, MatchShape.U); } } if (step.BottomEndsOfVerticalMatches.Contains(match + new Vector2Int(0, -2))) { RewardShape(step.ChainLength, MatchShape.L); if (step.BottomEndsOfVerticalMatches.Contains(match + new Vector2Int(2, -2))) { RewardShape(step.ChainLength, MatchShape.U); } } if (step.BottomEndsOfVerticalMatches.Contains(match + new Vector2Int(2, -2))) { RewardShape(step.ChainLength, MatchShape.L); } if (step.BottomEndsOfVerticalMatches.Contains(match + new Vector2Int(1, 0))) { RewardShape(step.ChainLength, MatchShape.T); if (step.LeftEndsOfHorizontalMatches.Contains(match + new Vector2Int(0, 2))) { RewardShape(step.ChainLength, MatchShape.H); } } if (step.BottomEndsOfVerticalMatches.Contains(match + new Vector2Int(1, -2))) { RewardShape(step.ChainLength, MatchShape.T); } if (step.BottomEndsOfVerticalMatches.Contains(match + new Vector2Int(0, -1))) { RewardShape(step.ChainLength, MatchShape.T); if (step.BottomEndsOfVerticalMatches.Contains(match + new Vector2Int(2, -1))) { RewardShape(step.ChainLength, MatchShape.H); } } if (step.BottomEndsOfVerticalMatches.Contains(match + new Vector2Int(2, -1))) { RewardShape(step.ChainLength, MatchShape.T); } if (step.BottomEndsOfVerticalMatches.Contains(match + new Vector2Int(1, -1))) { RewardShape(step.ChainLength, MatchShape.Plus); } } foreach (Vector2Int match in step.BottomEndsOfVerticalMatches) { RewardShape(step.ChainLength, MatchShape.Row3); if (step.BottomEndsOfVerticalMatches.Contains(match + new Vector2Int(0, 1))) { RewardShape(step.ChainLength, MatchShape.Row4); } if (step.BottomEndsOfVerticalMatches.Contains(match + new Vector2Int(0, 2))) { RewardShape(step.ChainLength, MatchShape.Row5); } } var newTools = new List <Tool>(); foreach ((Tool tool, int uses) in availableToolUses) { for (int i = previousToolUses[tool]; i < uses; ++i) { newTools.Add(tool); } } return(newTools); }