using System; using System.Linq; using UnityEngine; namespace Unity.MLAgents.Actuators { /// /// A structure that wraps the s for a particular and is /// used when is called. /// public readonly struct ActionBuffers { /// /// An empty action buffer. /// public static ActionBuffers Empty = new ActionBuffers(ActionSegment.Empty, ActionSegment.Empty); /// /// Holds the Continuous to be used by an . /// public ActionSegment ContinuousActions { get; } /// /// Holds the Discrete to be used by an . /// public ActionSegment DiscreteActions { get; } /// /// Create an instance with discrete actions stored as a float array. This exists /// to achieve backward compatibility with the former Agent methods which used a float array for both continuous /// and discrete actions. /// /// The float array of discrete actions. /// An instance initialized with a /// initialized from a float array. public static ActionBuffers FromDiscreteActions(float[] discreteActions) { return new ActionBuffers(ActionSegment.Empty, discreteActions == null ? ActionSegment.Empty : new ActionSegment(Array.ConvertAll(discreteActions, x => (int)x))); } public ActionBuffers(float[] continuousActions, int[] discreteActions) : this(new ActionSegment(continuousActions), new ActionSegment(discreteActions)) { } /// /// Construct an instance with the continuous and discrete actions that will /// be used. /// /// The continuous actions to send to an . /// The discrete actions to send to an . public ActionBuffers(ActionSegment continuousActions, ActionSegment discreteActions) { ContinuousActions = continuousActions; DiscreteActions = discreteActions; } /// /// Clear the and segments to be all zeros. /// public void Clear() { ContinuousActions.Clear(); DiscreteActions.Clear(); } /// public override bool Equals(object obj) { if (!(obj is ActionBuffers)) { return false; } var ab = (ActionBuffers)obj; return ab.ContinuousActions.SequenceEqual(ContinuousActions) && ab.DiscreteActions.SequenceEqual(DiscreteActions); } /// public override int GetHashCode() { unchecked { return (ContinuousActions.GetHashCode() * 397) ^ DiscreteActions.GetHashCode(); } } /// /// Packs the continuous and discrete actions into one float array. The array passed into this method /// must have a Length that is greater than or equal to the sum of the Lengths of /// and . /// /// A float array to pack actions into whose length is greater than or /// equal to the addition of the Lengths of this objects and /// segments. public void PackActions(in float[] destination) { Debug.Assert(destination.Length >= ContinuousActions.Length + DiscreteActions.Length, $"argument '{nameof(destination)}' is not large enough to pack the actions into.\n" + $"{nameof(destination)}.Length: {destination.Length}\n" + $"{nameof(ContinuousActions)}.Length + {nameof(DiscreteActions)}.Length: {ContinuousActions.Length + DiscreteActions.Length}"); var start = 0; if (ContinuousActions.Length > 0) { Array.Copy(ContinuousActions.Array, ContinuousActions.Offset, destination, start, ContinuousActions.Length); start = ContinuousActions.Length; } if (start >= destination.Length) { return; } if (DiscreteActions.Length > 0) { Array.Copy(DiscreteActions.Array, DiscreteActions.Offset, destination, start, DiscreteActions.Length); } } } /// /// An interface that describes an object that can receive actions from a Reinforcement Learning network. /// public interface IActionReceiver { /// /// The specification of the Action space for this IActionReceiver. /// /// ActionSpec ActionSpec { get; } /// /// Method called in order too allow object to execute actions based on the /// contents. The structure of the contents in the /// are defined by the . /// /// The data structure containing the action buffers for this object. void OnActionReceived(ActionBuffers actionBuffers); /// /// Implement `WriteDiscreteActionMask()` to modify the masks for discrete /// actions. When using discrete actions, the agent will not perform the masked /// action. /// /// /// The action mask for the agent. /// /// /// When using Discrete Control, you can prevent the Agent from using a certain /// action by masking it with . /// /// See [Agents - Actions] for more information on masking actions. /// /// [Agents - Actions]: https://github.com/Unity-Technologies/ml-agents/blob/release_4_docs/docs/Learning-Environment-Design-Agents.md#actions /// /// void WriteDiscreteActionMask(IDiscreteActionMask actionMask); } /// /// Helper methods to be shared by all classes that implement . /// public static class ActionReceiverExtensions { /// /// Returns the number of discrete branches + the number of continuous actions. /// /// /// public static int TotalNumberOfActions(this IActionReceiver actionReceiver) { return actionReceiver.ActionSpec.NumContinuousActions + actionReceiver.ActionSpec.NumDiscreteActions; } } }