// apply a rule group, return true if there was any change // loop until no further changes bool ApplyRuleGroup(RuleGroup group) { Logger.WriteLine(4, "ApplyRuleGroup {0} {1}", _rulestate, group); // skip this group -- it failed rigid previously if (_disabledgrouplookup.Contains(group)) { return(false); } var groupchange = false; // Apply a single match chosen at random if (group.IsRandom) { var matches = _rulestate.FindMatches(group); if (matches.Count > 0) { var n = _rng.Next(matches.Count); var randmatch = new List <RuleMatch> { matches[n] }; _rulestate.DoActions(group, randmatch); _rulestate.DoCommands(group, randmatch); groupchange = true; } // Find all matches and do all actions for all the rules in the group // loop until no more changes, return true if there were any } else { for (var retry = 0; ; ++retry) { if (retry > 200) { throw Error.Fatal("too many rule group retries: <{0}>", group); } var matches = _rulestate.FindMatches(group); var rulechange = _rulestate.DoActions(group, matches); groupchange |= rulechange; if (retry == 0) // just the first time { _rulestate.DoCommands(group, matches); } if (!rulechange || group.IsFinal) { break; } } } Logger.WriteLine(4, "[ARG {0}]", groupchange); return(groupchange); }
// carry out commands for this rulegroup internal void DoCommands(RuleGroup rulegroup, IList <RuleMatch> matches) { var rules = matches.Select(m => m.rule).Distinct(); Logger.WriteLine(4, "Do commands {0} #{1}", rulegroup.Id, rules.Count()); foreach (var rule in rules) { if (!rule.CommandCode.IsEmpty) { _evaluator.Exec(rule.CommandCode, this); CommandCounter++; } } }
// find all the matches for the rules in a group internal IList <RuleMatch> FindMatches(RuleGroup rulegroup) { Logger.WriteLine(3, "Find matches group {0} #{1}", rulegroup.Id, rulegroup.Rules.Count); var matches = new List <RuleMatch>(); // find all possible matches for each rule foreach (var rule in rulegroup.Rules) { _vm_trail = new Trail(); _evaluator.Exec(rule.PatternCode, this); matches.AddRange(_vm_trail.GetMatches(rule)); } Logger.WriteLine(3, "[FM found {0}]", matches.Count); return(matches); }
// move an object at location in a direction // direction None used to cancel existing movement internal void MoveObject(int obj, int cellindex, Direction direction, RuleGroup rulegroup) { if (!(GameDef.MoveDirections.Contains(direction) || direction == Direction.Stationary)) { throw Error.Assert("move {0}", direction); } // only move an object if it's here, else add a new mover if (_level[cellindex, _gamedef.GetLayer(obj)] == obj) { var found = false; // first try to update move list // do not use rule group in comparison -- if rigid, this may cause a problem for (var movex = 0; movex < _movers.Count; ++movex) { var mover = _movers[movex]; if (mover.ObjectId == obj && mover.CellIndex == cellindex) { found = true; if (_movers[movex].Direction != direction) { Logger.WriteLine(2, "Mover now {0} to {1} at {2}", direction, _gamedef.ShowName(obj), cellindex); if (direction == Direction.Stationary) { _movers.RemoveAt(movex); } else { _movers[movex].Direction = direction; } //_vm_moved = true; // TODO: only for net change } break; } } // otherwise add a new mover if (!found && direction != Direction.Stationary) { Logger.WriteLine(2, "Mover set {0} to {1} at {2}", direction, _gamedef.ShowName(obj), cellindex); _movers.Add(Mover.Create(obj, cellindex, _gamedef.GetLayer(obj), direction, rulegroup)); //_vm_moved = true; // TODO: only for net change } } }
// carry out the actions for this set of matches // return true if anything actually was changed internal bool DoActions(RuleGroup rulegroup, IList <RuleMatch> matches) { Logger.WriteLine(4, "Do actions {0} #{1}", rulegroup.Id, matches.Count); _rulegroup = rulegroup; // needed to track rigid // track net changes made by this entire rule group // note: cannot use object map because of unstable order var objiter = matches.SelectMany(m => m.path.SelectMany(p => _levelstate.GetObjects(p))); var objinit = objiter.ToList(); var moviter = _levelstate.GetMovers(); var movinit = moviter.ToList(); //_vm_moved = false; foreach (var match in matches) { var rule = match.rule; // do action if any if (!rule.ActionCode.IsEmpty) { // check per individual rule to get good verbose logging // note the need to flatten in order to compare objects as ints _vm_matchpath = match.path; _refobjects = match.refs; _vm_pathindex = 0; _model.VerboseLog("Matched rule {0} {1}", rule.RuleId, rule.RuleDirection); _evaluator.Exec(rule.ActionCode, this); ActionCounter++; } } // could optimise this with _vm_moved? var changed = !objinit.SequenceEqual(objiter) || !movinit.SequenceEqual(moviter); Logger.WriteLine(4, "[DA {0}]", changed); return(changed); }
// create a mover, which may target an illegal location static internal Mover Create(int obj, int cellindex, int layer, Direction dir, RuleGroup group = null, bool rigid = false) { if (!GameDef.MoveDirections.Contains(dir)) { throw Error.Assert("create {0}", dir); } return(new Mover { ObjectId = obj, CellIndex = cellindex, Layer = layer, Direction = dir, RuleGroup = group, }); }