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))); } /// /// 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(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; } /// /// Construct an instance with . All values are initialized to zeros. /// /// /// The to send to an . public ActionBuffers(ActionSpec actionSpec) : this(new ActionSegment(new float[actionSpec.NumContinuousActions]), new ActionSegment(new int[actionSpec.NumDiscreteActions])) { } /// /// Create an instance with ActionSpec and all actions stored as a float array. /// /// of the /// The float array of all actions, including discrete and continuous actions. /// An instance initialized with a and a float array. internal static ActionBuffers FromActionSpec(ActionSpec actionSpec, float[] actions) { if (actions == null) { return ActionBuffers.Empty; } Debug.Assert(actions.Length == actionSpec.NumContinuousActions + actionSpec.NumDiscreteActions, $"The length of '{nameof(actions)}' does not match the total size of ActionSpec.\n" + $"{nameof(actions)}.Length: {actions.Length}\n" + $"{nameof(actionSpec)}: {actionSpec.NumContinuousActions + actionSpec.NumDiscreteActions}"); ActionSegment continuousActionSegment = ActionSegment.Empty; ActionSegment discreteActionSegment = ActionSegment.Empty; int offset = 0; if (actionSpec.NumContinuousActions > 0) { continuousActionSegment = new ActionSegment(actions, 0, actionSpec.NumContinuousActions); offset += actionSpec.NumContinuousActions; } if (actionSpec.NumDiscreteActions > 0) { int[] discreteActions = new int[actionSpec.NumDiscreteActions]; for (var i = 0; i < actionSpec.NumDiscreteActions; i++) { discreteActions[i] = (int)actions[i + offset]; } discreteActionSegment = new ActionSegment(discreteActions); } return new ActionBuffers(continuousActionSegment, discreteActionSegment); } /// /// Clear the and segments to be all zeros. /// public void Clear() { ContinuousActions.Clear(); DiscreteActions.Clear(); } /// /// Check if the is empty. /// /// Whether the buffers are empty. public bool IsEmpty() { return ContinuousActions.IsEmpty() && DiscreteActions.IsEmpty(); } /// /// Indicates whether the current ActionBuffers is equal to another ActionBuffers. /// /// An ActionBuffers to compare with this ActionBuffers. /// true if the current ActionBuffers is equal to the other parameter; otherwise, false. 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); } /// /// Computes the hash code of the ActionBuffers. /// /// A hash code for the current ActionBuffers. public override int GetHashCode() { unchecked { return (ContinuousActions.GetHashCode() * 397) ^ DiscreteActions.GetHashCode(); } } } /// /// An interface that describes an object that can receive actions from a Reinforcement Learning network. /// public interface IActionReceiver { /// /// 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_17_docs/docs/Learning-Environment-Design-Agents.md#actions /// /// void WriteDiscreteActionMask(IDiscreteActionMask actionMask); } }