File size: 8,015 Bytes
b7e9b58 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using UnityEngine;
// Shared by PPO, DT and BC evaluation. No Unity editor APIs required.
public static class EvaluationProtocol
{
public const string Version = "aligned-evaluation-v2.1";
public static int EpisodeSeed(int masterSeed, int zeroBasedEpisode)
{
if (zeroBasedEpisode < 0) throw new ArgumentOutOfRangeException(nameof(zeroBasedEpisode));
return checked(masterSeed + zeroBasedEpisode);
}
[Serializable]
public class InitialState
{
public int episode;
public int seed;
public Vector3 agent_position;
public Quaternion agent_rotation;
public Vector3 agent_scale;
public Vector3 barrel_position;
public Vector3 barrel_scale;
public Quaternion barrel_rotation;
public List<EnemyDummy.EvaluationState> targets = new List<EnemyDummy.EvaluationState>();
}
[Serializable]
public class InputTrace
{
public int episode, timestep, valid_length;
public float[] observations, actions, returns_to_go, selected_action;
public int[] timesteps;
}
[Serializable]
public class Metadata
{
public string protocol_version = Version;
public string run_id;
public string model_name;
public string policy_type;
public int target_count;
public int master_seed;
public int episodes_requested;
public int max_action_steps;
public float fixed_delta_time;
public float rotation_speed;
public float shoot_distance;
public float step_penalty;
public int enemy_layer_mask;
public float initial_rtg;
public bool bc_zero_rtg;
public string sequence_protocol = "dataset-shifted-action-right-pad32-v2";
public string accuracy_protocol = "hits-per-fire-request-percent-v1";
public string action_history_protocol = "raw-continuous-and-actual-fire-v1";
public string inference_backend;
public int[] ppo_discrete_branch_sizes;
public bool ppo_deterministic_inference;
public bool queries_hit_triggers;
public bool rtg_sensitivity;
public string evaluated_utc = DateTime.UtcNow.ToString("o");
public List<float> episode_terminal_rtg = new List<float>();
public List<InputTrace> input_traces = new List<InputTrace>();
public string reward_protocol;
public string unity_version;
public List<int> episode_seeds = new List<int>();
public List<string> initial_state_sha256 = new List<string>();
public List<InitialState> initial_states = new List<InitialState>();
public List<float> max_abs_rtg_input = new List<float>();
public List<int> episode_action_steps = new List<int>();
public List<int> episode_shots_fired = new List<int>();
public List<int> episode_shots_hit = new List<int>();
public List<int> episode_raycasts = new List<int>();
}
public static void ValidateConfiguration(float rotationSpeed, float shootDistance,
float stepPenalty, float fixedDeltaTime)
{
if (!Finite(rotationSpeed) || rotationSpeed <= 0 || !Finite(shootDistance) || shootDistance <= 0 ||
!Finite(stepPenalty) || !Finite(fixedDeltaTime) || fixedDeltaTime <= 0)
throw new InvalidOperationException("Non-finite/invalid movement, reward or timestep settings.");
}
public static bool Finite(float value) { return !float.IsNaN(value) && !float.IsInfinity(value); }
public static InitialState ResetEnvironment(Transform root, Transform barrel,
List<EnemyDummy> enemies, int count, int masterSeed, int episode)
{
if (root == null || barrel == null || enemies == null || count <= 0 || count > enemies.Count)
throw new InvalidOperationException("Check root, barrel and EnemyCount/Enemies references.");
var unique = new HashSet<EnemyDummy>();
foreach (var e in enemies)
if (e == null || !unique.Add(e))
throw new InvalidOperationException("Enemies must contain unique, non-null entries in a fixed order.");
int seed = EpisodeSeed(masterSeed, episode);
// Lifecycle callbacks on deactivation must not consume our seeded initialization stream.
foreach (var e in enemies) e.gameObject.SetActive(false);
var saved = UnityEngine.Random.state;
var state = new InitialState {
episode = episode + 1, seed = seed, agent_position = root.position,
agent_rotation = root.rotation, barrel_rotation = barrel.localRotation,
agent_scale = root.lossyScale, barrel_position = barrel.position, barrel_scale = barrel.lossyScale
};
try
{
UnityEngine.Random.InitState(seed);
// Activate only after every target's random parameters have been drawn.
for (int i = 0; i < count; i++)
{
var e = enemies[i];
Vector3 pos = root.position + new Vector3(UnityEngine.Random.Range(-7f, 7f),
UnityEngine.Random.Range(2f, 3.5f), UnityEngine.Random.Range(-7f, 7f));
e.ResetForEvaluation(i, root, pos);
}
for (int i = 0; i < count; i++) enemies[i].gameObject.SetActive(true);
for (int i = 0; i < count; i++) state.targets.Add(enemies[i].CaptureEvaluationState());
}
finally { UnityEngine.Random.state = saved; }
Physics.SyncTransforms();
return state;
}
public static void RecordInitialState(Metadata metadata, InitialState state)
{
metadata.episode_seeds.Add(state.seed);
metadata.initial_states.Add(state);
// Episode/seed intentionally included; compare corresponding episodes only.
using (var sha = SHA256.Create())
{
byte[] bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(JsonUtility.ToJson(state)));
metadata.initial_state_sha256.Add(BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant());
}
}
public static void AdvanceTargets(List<EnemyDummy> enemies, int completedActionSteps)
{
foreach (var e in enemies)
if (e != null && e.gameObject.activeSelf)
e.AdvanceEvaluation(completedActionSteps, Time.fixedDeltaTime);
Physics.SyncTransforms();
}
public static void StopTargets(List<EnemyDummy> enemies)
{
if (enemies == null) return;
foreach (var e in enemies) if (e != null) e.gameObject.SetActive(false);
}
public static string NewRunId()
{
return DateTime.UtcNow.ToString("yyyyMMdd_HHmmss_fff") + "_" + Guid.NewGuid().ToString("N").Substring(0, 8);
}
public static string OutputDirectory(string runId, int targetCount, bool rtgTest)
{
string outputRoot = Path.Combine(Application.persistentDataPath, "RevisionEvaluation");
// Explicit batch jobs can keep all artifacts inside their isolated workspace.
var arguments = Environment.GetCommandLineArgs();
for (int i = 0; i + 1 < arguments.Length; i++)
if (arguments[i] == "-evaluationOutput") outputRoot = Path.GetFullPath(arguments[i + 1]);
string path = Path.Combine(outputRoot, runId,
rtgTest ? "RTG_Test" : "EnemyCount" + targetCount);
Directory.CreateDirectory(path);
return path;
}
public static string SafeName(string name)
{
foreach (char c in Path.GetInvalidFileNameChars()) name = name.Replace(c, '_');
return name.Replace('/', '_').Replace('\\', '_');
}
public static void WriteNew(string path, string json)
{
using (var stream = new FileStream(path, FileMode.CreateNew, FileAccess.Write))
using (var writer = new StreamWriter(stream, new UTF8Encoding(false))) writer.Write(json);
}
}
|