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);
}
}