// expand an ambiguous direction throughout a rule // error if more than one in pattern IList <AtomicRule> ExpandDirectionMulti(AtomicRule oldrule, Direction olddir) { var pcells = oldrule.Patterns.SelectMany(p => p.Cells .SelectMany(c => c.Where(a => a.Direction == olddir))); if (pcells.Count() != 1) { _parser.CompileError("'{0}' in action cannot be matched", olddir); } // expand one rule per single direction var newrules = new List <AtomicRule>(); foreach (var newdir in _expanddirlookup[olddir]) { var newrule = oldrule.Clone(); for (var riter = new RuleIterator { Rule = oldrule }; !riter.Done; riter.Step()) { newrule.Patterns[riter.SubRuleIndex].Cells[riter.CellIndex] = ReplaceDirection(riter.PatternAtoms, olddir, newdir); newrule.Actions[riter.SubRuleIndex].Cells[riter.CellIndex] = ReplaceDirection(riter.ActionAtoms, olddir, newdir); } newrules.AddRange(ExpandRule(newrule)); } return(newrules); }
//========================================================================== // // compile a rule internal CompiledRule CompileRule(AtomicRule rule) { _parser.DebugLog("{0}", rule); if (rule.Directions.Count != 1) { throw Error.Assert("direction count"); } for (int i = 0; i < rule.Patterns.Count; i++) { // TODO: check singleton rules here } var crule = new CompiledRule { RuleId = rule.RuleId, RuleDirection = rule.Directions.First(), PatternCode = CompilePattern(rule.Directions.First(), rule.Patterns), ActionCode = (rule.HasAction) ? CompileActions(rule.Directions.First(), rule.Patterns, rule.Actions) : RuleCode.Empty, CommandCode = (rule.HasCommand) ? CompileCommand(rule.Commands) : RuleCode.Empty, }; return(crule); }
// expand an OR symbol throughout a rule // error if more than one in pattern IList <AtomicRule> ExpandSymbolMulti(AtomicRule oldrule, ObjectSymbol oldsym) { var pcells = oldrule.Patterns.SelectMany(p => p.Cells .SelectMany(c => c.Where(a => a.Symbol == oldsym && !a.IsNegated))); if (pcells.Count() != 1) { return(null); } // expand one rule per object id var newrules = new List <AtomicRule>(); foreach (var newobj in oldsym.ObjectIds) { var newrule = oldrule.Clone(); for (var riter = new RuleIterator { Rule = oldrule }; !riter.Done; riter.Step()) { newrule.Patterns[riter.SubRuleIndex].Cells[riter.CellIndex] = ReplaceObject(riter.PatternAtoms, oldsym, newobj); newrule.Actions[riter.SubRuleIndex].Cells[riter.CellIndex] = ReplaceObject(riter.ActionAtoms, oldsym, newobj); } newrules.AddRange(ExpandRule(newrule)); } return(newrules); }
// Convert every relative direction into absolute AtomicRule MakeAbsolute(AtomicRule rule, Direction ruledir) { var newrule = rule.Clone(); newrule.Directions = new HashSet <Direction>() { ruledir }; MakeAbsolute(newrule.Patterns, ruledir); if (newrule.HasAction) { MakeAbsolute(newrule.Actions, ruledir); } return(newrule); }
// expand ambiguous directions in rule pattern known to have no matching action // replacing by individuals (absolute or relative) // abs could be fixed by more powerful vm code IList <AtomicRule> ExpandDirectionSimple(AtomicRule oldrule) { for (var riter = new RuleIterator { Rule = oldrule }; !riter.Done; riter.Step()) { // look for any ambiguous unmatched pattern directions, just one each time var patom = riter.PatternAtoms.FirstOrDefault(a => _expanddirlookup.ContainsKey(a.Direction)); if (patom != null) { return(ExpandDirectionSingle(riter, patom.Direction)); } } return(new List <AtomicRule> { oldrule }); }
// expand a rule where an occurrence on an action does not match its pattern // replacing combination directions and symbols by individuals (absolute or relative) // could be fixed by more powerful vm code IList <AtomicRule> ExpandRule(AtomicRule oldrule) { if (!oldrule.HasAction) { return(ExpandDirectionSimple(oldrule)); } // check each cell of each subrule for (var riter = new RuleIterator { Rule = oldrule }; !riter.Done; riter.Step()) { // Do no expansion on random actions if (riter.ActionAtoms.Any(a => a.IsRandom)) { oldrule.Prefixes.Add(RulePrefix.Final); } else { // check for action atom with ambiguous direction and no matching pattern atom var datom = riter.ActionAtoms.FirstOrDefault(a => _expanddirlookup.ContainsKey(a.Direction) && !riter.PatternAtoms.Any(p => p.Matches(a) && p.Direction == a.Direction)); if (datom != null) { return(ExpandDirectionMulti(oldrule, datom.Direction)); } // check for action atom with property symbol and no matching pattern atom var satom = riter.ActionAtoms.FirstOrDefault(a => a.Symbol.Kind == SymbolKind.Property && !a.IsNegated && !a.IsRandom && !riter.PatternAtoms.Any(p => p.Matches(a))); if (satom != null) { var newrules = ExpandSymbolMulti(oldrule, satom.Symbol) ?? ExpandPropertyMatcher(riter); if (newrules == null) { _parser.CompileError("'{0}' in action cannot be matched", satom.Symbol.Name); } else { return(newrules); } } } } return(ExpandDirectionSimple(oldrule)); }
// Expand until rules are atomic, make absolute, return list of atomic rules internal IList <AtomicRule> BuildRuleList(AtomicRule arule, int groupno) { Logger.WriteLine(2, "Build group {0} rule {1}", groupno, arule.RuleId); // how many directions do we need? var singles = arule.Patterns.All(p => p.Cells.Count == 1) && arule.Patterns.All(p => p.Cells[0].All(a => _ruledirlookup.ContainsKey(a.Direction))); var ruledirs = (arule.Directions.Count == 0) ? _ruledirlookup[singles ? Direction.None : Direction.Orthogonal] : arule.Directions.SelectMany(d => _ruledirlookup[d]).Distinct(); // expand ambiguous directions and symbols into absolute/relative var exrules = ExpandRule(arule); // convert relative directions into absolute clones var absrules = ruledirs .SelectMany(d => exrules .Select(r => MakeAbsolute(r, d))).ToList(); return(absrules); }
// expand rule and add compiled rule to game data internal void AddRule(int ruleid, IList <RulePrefix> prefixes, IList <Direction> directions, IList <SubRule> pattern, IList <SubRule> action, IList <CommandCall> commands) { // create the base rule with unexpanded directions var arule = new AtomicRule { RuleId = ruleid, Prefixes = new HashSet <RulePrefix>(prefixes), Directions = new HashSet <Direction>(directions), Patterns = pattern, Actions = action, Commands = commands, }; // expand rules, all going in the same group, then compile them var rulegroup = _gamedef.GetRuleGroup(arule.Prefixes, _inloop ? _loopcounter : 0); foreach (var prule in _rulebuilder.BuildRuleList(arule, rulegroup.Id)) { _atomicrules.Add(prule); rulegroup.Rules.Add(_rulebuilder.CompileRule(prule)); rulegroup.IsFinal |= prule.IsFinal; // propagate retry suppression to group } }