private void EnsureThrustKeysBuilt_Finish(KeyThrustRequest[] requests) { // Remember the mappings between key and desired thrust (this is used to drive the impulse engine) _keyThrustRequests = requests; if (this.Thrusters == null || this.Thrusters.Length == 0) { _isThrustMapDirty = false; return; } if (_cancelCurrentBalancer != null) { _cancelCurrentBalancer.Cancel(); _cancelCurrentBalancer = null; } // Remember the current solutions, so they can help get a good start on the new solver var previous = _thrustLines.Values.ToArray(); _thrustLines.Clear(); _cancelCurrentBalancer = new CancellationTokenSource(); ThrustContributionModel model = new ThrustContributionModel(this.Thrusters, this.PhysicsBody.CenterOfMass); MassMatrix inertia = this.PhysicsBody.MassMatrix; double mass = inertia.Mass; // Several key combos may request the same direction, so group them var grouped = requests. ToLookup(KeyThrustRequestComparer); foreach (var set in grouped) { // Create wrappers for this set ThrusterSolution[] solutionWrappers = set. Select(o => new ThrusterSolution(o, model, inertia, mass)). ToArray(); // Store the wrappers foreach (var wrapper in solutionWrappers) { _thrustLines.Add(Tuple.Create(wrapper.Request.Key, wrapper.Request.Shift), wrapper); } // This delegate gets called when a better solution is found. Distribute the map to the solution wrappers var newBestFound = new Action <ThrusterMap>(o => { ThrusterSolutionMap solutionMap = GetThrusterSolutionMap(o, model, inertia, mass); foreach (ThrusterSolution wrapper in solutionWrappers) { wrapper.Map = solutionMap; } }); var options = new DiscoverSolutionOptions2 <Tuple <int, int, double> >() { //MaxIterations = 2000, //TODO: Find a reasonable stop condition ThreadShare = _thrustWorkerThread, }; // Find the previous solution for this request var prevMatch = previous.FirstOrDefault(o => KeyThrustRequestComparer(set.Key, o.Request)); if (prevMatch != null && prevMatch.Map != null) { options.Predefined = new[] { prevMatch.Map.Map.Flattened }; } // Find the combination of thrusters that push in the requested direction //ThrustControlUtil.DiscoverSolutionAsync(this, solutionWrappers[0].Request.Linear, solutionWrappers[0].Request.Rotate, _cancelCurrentBalancer.Token, model, newBestFound, null, options); ThrustControlUtil.DiscoverSolutionAsync2(this, solutionWrappers[0].Request.Linear, solutionWrappers[0].Request.Rotate, _cancelCurrentBalancer.Token, model, newBestFound, options: options); } _isThrustMapDirty = false; }
private void EnsureThrustKeysBuilt_Finish(KeyThrustRequest[] requests) { if (_cancelCurrentBalancer != null) { _cancelCurrentBalancer.Cancel(); _cancelCurrentBalancer = null; } // Remember the current solutions, so they can help get a good start on the new solver var previous = _thrustLines.Values.ToArray(); _thrustLines.Clear(); _cancelCurrentBalancer = new CancellationTokenSource(); ThrustContributionModel model = new ThrustContributionModel(this.Thrusters, this.PhysicsBody.CenterOfMass); MassMatrix inertia = this.PhysicsBody.MassMatrix; double mass = inertia.Mass; // Several key combos may request the same direction, so group them var grouped = requests. ToLookup(KeyThrustRequestComparer); foreach (var set in grouped) { // Create wrappers for this set ThrusterSolution[] solutionWrappers = set. Select(o => new ThrusterSolution(o, model, inertia, mass)). ToArray(); // Store the wrappers foreach (var wrapper in solutionWrappers) { _thrustLines.Add(Tuple.Create(wrapper.Request.Key, wrapper.Request.Shift), wrapper); } // This delegate gets called when a better solution is found. Distribute the map to the solution wrappers var newBestFound = new Action<ThrusterMap>(o => { ThrusterSolutionMap solutionMap = GetThrusterSolutionMap(o, model, inertia, mass); foreach (ThrusterSolution wrapper in solutionWrappers) { wrapper.Map = solutionMap; } }); var options = new DiscoverSolutionOptions2<Tuple<int, int, double>>() { //MaxIterations = 2000, //TODO: Find a reasonable stop condition ThreadShare = _thrustWorkerThread, }; // Find the previous solution for this request var prevMatch = previous.FirstOrDefault(o => KeyThrustRequestComparer(set.Key, o.Request)); if (prevMatch != null && prevMatch.Map != null) { options.Predefined = new[] { prevMatch.Map.Map.Flattened }; } // Find the combination of thrusters that push in the requested direction //ThrustControlUtil.DiscoverSolutionAsync(this, solutionWrappers[0].Request.Linear, solutionWrappers[0].Request.Rotate, _cancelCurrentBalancer.Token, model, newBestFound, null, options); ThrustControlUtil.DiscoverSolutionAsync2(this, solutionWrappers[0].Request.Linear, solutionWrappers[0].Request.Rotate, _cancelCurrentBalancer.Token, model, newBestFound, null, options); } _isThrustMapDirty = false; }
public static void DiscoverSolutionAsync2(Bot bot, Vector3D? idealLinear, Vector3D? idealRotation, CancellationToken? cancel = null, ThrustContributionModel model = null, Action<ThrusterMap> newBestFound = null, Action<ThrusterMap> finalFound = null, DiscoverSolutionOptions2<Tuple<int, int, double>> options = null) { long token = bot.Token; // Cache Thrusters Thruster[] allThrusters = bot.Thrusters; if (allThrusters == null || allThrusters.Length == 0) { throw new ArgumentException("This bot has no thrusters"); } // Ensure model is created (if the caller wants to find solutions for several directions at the same time, it would be // more efficient to calculate the model once, and pass to all the solution finders) model = model ?? new ThrustContributionModel(bot.Thrusters, bot.PhysicsBody.CenterOfMass); // Figure out how much force can be generated in the ideal directions double maxForceLinear = idealLinear == null ? 0d : ThrustControlUtil.GetMaximumPossible_Linear(model, idealLinear.Value); double maxForceRotate = idealRotation == null ? 0d : ThrustControlUtil.GetMaximumPossible_Rotation(model, idealRotation.Value); // Mutate 2% of the elements, with a 10% value drift MutateUtility.MuateArgs mutateArgs = new MutateUtility.MuateArgs(false, .02, new MutateUtility.MuateFactorArgs(MutateUtility.FactorType.Distance, .1)); #region delegates //NOTE: While breeding/mutating, they don't get normalized. But error calculation and returned maps need them normalized var delegates = new DiscoverSolutionDelegates2<Tuple<int, int, double>>() { GetNewSample = new Func<Tuple<int, int, double>[]>(() => { ThrusterMap map = ThrustControlUtil.GenerateRandomMap(allThrusters, token); return map.Flattened; }), GetScore = new Func<Tuple<int, int, double>[], double[]>(o => { ThrusterMap map = new ThrusterMap(ThrustControlUtil.Normalize(o), allThrusters, token); return ThrustControlUtil.GetThrustMapScore3(map, model, idealLinear, idealRotation, maxForceLinear, maxForceRotate); }), Mutate = new Func<Tuple<int, int, double>[], Tuple<int, int, double>[]>(o => { ThrusterMap map = new ThrusterMap(o, allThrusters, token); return ThrustControlUtil.Mutate(map, mutateArgs, false).Flattened; }), }; if (cancel != null) { delegates.Cancel = cancel.Value; } if (newBestFound != null) { delegates.NewBestFound = new Action<SolutionResult2<Tuple<int, int, double>>>(o => { ThrusterMap map = new ThrusterMap(ThrustControlUtil.Normalize(o.Item), allThrusters, token); newBestFound(map); }); } if (finalFound != null) { delegates.FinalFound = new Action<SolutionResult2<Tuple<int, int, double>>>(o => { ThrusterMap map = new ThrusterMap(ThrustControlUtil.Normalize(o.Item), allThrusters, token); finalFound(map); }); } #endregion options = options ?? new DiscoverSolutionOptions2<Tuple<int, int, double>>(); options.ScoreAscendDescend = new bool[] { false, false }; // Do it //NOTE: If options.ThreadShare is set, then there's no reason to do this async, but there's no harm either Task.Run(() => UtilityAI.DiscoverSolution2(delegates, options)); }