Commit 6eb5d7ae authored by Mikko Lainio's avatar Mikko Lainio 🇺🇦
Browse files

Quantum Wheel Demo game

parent 9c76cd48
fileFormatVersion: 2
guid: b42e57c94b6314749a45798ebfa8c16b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
This diff is collapsed.
fileFormatVersion: 2
guid: 3a02248cc5ca6754faf2af226da10f5d
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: 7dcece53e9eb19a4d8cc8d37d37bc5fd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Analytics;
[RequireComponent(typeof(CanvasGroup))]
public class Blink : MonoBehaviour
{
public float blinkFadeDuration = 0.25f;
public float blinkDelay = 0.75f;
private CanvasGroup m_group;
void Awake()
{
m_group = GetComponent<CanvasGroup>();
}
void Start()
{
}
void Update()
{
}
private void OnEnable()
{
StartCoroutine(DoBlink());
}
private IEnumerator DoBlink()
{
float start, end;
start = 1f;
end = 0f;
while (enabled)
{
float t = 0f;
float delta = end - start;
float value;
do
{
t += Time.deltaTime / blinkFadeDuration;
if (delta < 0)
value = 1f + delta * t;
else
value = delta * t;
m_group.alpha = value;
yield return null;
} while (t < 1f);
yield return new WaitForSeconds(blinkDelay);
float temp = start;
start = end;
end = temp;
Debug.Log("Blink Cycle End. Start="+start+" End="+end);
}
}
}
fileFormatVersion: 2
guid: a261f04762c8c3b46b0ae8c28761aa24
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Experimental.UIElements;
public class CubeController : MonoBehaviour
{
public enum CubePosition
{
Left,
Right
}
public KeyCode MoveLeft = KeyCode.A;
public KeyCode MoveRight = KeyCode.D;
public CubePosition cPosition;
private Vector3 m_defaultPos;
public TMPro.TextMeshProUGUI textObj;
// Start is called before the first frame update
void Awake()
{
QuantumDemoManager.RegisterController(this, cPosition);
}
void Start()
{
m_defaultPos = transform.position;
}
// Update is called once per frame
void Update()
{
if (Input.GetKey(MoveLeft))
{
Move(-1f);
}
else if (Input.GetKey(MoveRight))
{
Move(1f);
}
if (textObj != null)
{
textObj.text = cPosition + $"\n{MoveLeft} <-> {MoveRight}\n" + CurrentInputPosition;
}
}
private void Move(float dir)
{
transform.position += Vector3.right * dir * Time.deltaTime;
float x = transform.position.x - m_defaultPos.x;
x = Mathf.Min(1f, Mathf.Max(x, -1f));
Vector3 v = transform.position;
v.x = x + m_defaultPos.x;
transform.position = v;
}
public float CurrentInputPosition => transform.position.x - m_defaultPos.x;
}
fileFormatVersion: 2
guid: df42b666f982e19419892d16aa75bdb2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Python.Runtime;
public class QuantumDemoManager : MonoBehaviour
{
private static QuantumDemoManager m_instance;
public static QuantumDemoManager Instance
{
get
{
if (m_instance == null)
{
m_instance = FindObjectOfType<QuantumDemoManager>();
}
return m_instance;
}
}
private CubeController m_left;
private CubeController m_right;
private StirapEnv m_env;
public float UpdateFrequency;
[SerializeField] private TMPro.TextMeshProUGUI m_timeStepText;
[SerializeField] private TMPro.TextMeshProUGUI m_scoreText;
[SerializeField] private TMPro.TextMeshProUGUI m_growthText;
[SerializeField] private TMPro.TextMeshProUGUI m_endScoreText;
[SerializeField] private UnityEngine.UI.Image m_rewardImage;
[SerializeField] private GameObject FinalScoreObject;
[SerializeField] private GameObject StartTextObject;
[SerializeField] private Gradient m_rewardGradient;
[SerializeField] private float MaxGradientThreshold = 10f;
public int TimeSteps = 400;
private float m_score;
public enum State
{
Initialized,
Started,
Stopped
}
private State m_state;
// Start is called before the first frame update
void Start()
{
InitEnv();
FinalScoreObject.SetActive(false);
StartTextObject.SetActive(true);
m_left.enabled = false;
m_right.enabled = false;
ResetUI();
}
private void ResetUI()
{
m_timeStepText.text = "";
m_scoreText.text = "";
m_growthText.text = "";
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space) && m_state != State.Started)
{
StartGame();
}
if (Input.GetKeyDown(KeyCode.Escape))
{
StopAllCoroutines();
Application.Quit();
}
}
public void StartGame()
{
m_state = State.Started;
FinalScoreObject.SetActive(false);
StartTextObject.SetActive(false);
StartCoroutine(GameCoroutine());
m_left.enabled = true;
m_right.enabled = true;
}
private void InitEnv()
{
using (Python.Runtime.Py.GIL())
{
m_env = new StirapEnv(false, TimeSteps+1);
}
}
private IEnumerator GameCoroutine()
{
float time, delta;
int step = 0;
do
{
time = Time.time;
yield return new WaitForSeconds(UpdateFrequency);
delta = Time.time - time;
if (m_timeStepText != null)
m_timeStepText.text = (TimeSteps - step).ToString();
step++;
} while (!RunStep(delta));
EndGame();
}
private void EndGame()
{
m_state = State.Stopped;
if (m_endScoreText == null)
return;
FinalScoreObject.SetActive(true);
m_endScoreText.text = "Final Score: <color=#fff>" + m_score.ToString("F0");
StartTextObject.SetActive(true);
m_env.Reset();
m_left.enabled = false;
m_right.enabled = false;
}
private bool RunStep(float deltaTime)
{
StirapEnv.StepResult result;
using (Py.GIL())
{
float left = m_left.CurrentInputPosition * deltaTime;
float right = m_right.CurrentInputPosition * deltaTime;
result = m_env.Step(left, right);
}
Vector3 leftScale = m_left.transform.localScale;
Vector3 rightScale = m_right.transform.localScale;
float lastRightScale = rightScale.y;
leftScale.y = result.Observation[0] * 3f;
rightScale.y = result.Observation[1] * 3f;
float growth = rightScale.y - lastRightScale;
m_left.transform.localScale = leftScale;
m_right.transform.localScale = rightScale;
float lPos = result.Observation[2];
float rPos = result.Observation[3];
//m_left.transform.position = new Vector3(lPos * 2, 0f, 0f) + Vector3.left * 2f;
//m_right.transform.position = new Vector3(rPos * 2, 0f, 0f) + Vector3.right * 2f;
SetScore(result.Reward, growth*100000f);
return result.Done;
}
private void SetScore(float score, float growth)
{
m_score += score * 10000;
if (m_scoreText != null)
{
m_scoreText.text = "SCORE: " + m_score.ToString("F0");
}
if (m_growthText != null)
{
m_growthText.text = (growth).ToString("F0");
}
m_rewardImage.color = m_rewardGradient.Evaluate(growth / MaxGradientThreshold);
}
public static void RegisterController(CubeController ctrl, CubeController.CubePosition cPosition)
{
switch (cPosition)
{
case CubeController.CubePosition.Left: Instance.m_left = ctrl;
break;
case CubeController.CubePosition.Right: Instance.m_right = ctrl;
break;
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: e7e6eeaf347f5d84592c063c68f5a535
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ExampleStirap : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ExampleStirap : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
UnityEngine.Random.InitState(9989);
using (Python.Runtime.Py.GIL())
{
......@@ -17,7 +17,7 @@ public class ExampleStirap : MonoBehaviour
{
float left = Random.Range(-.5f, .5f);
float right = Random.Range(-.5f, .5f);
StirapEnv.StepResult result = stirapEnv.Step(new float[] { left, right });
StirapEnv.StepResult result = stirapEnv.Step(left, right);
Rewards[i] = result.Reward;
if (result.Done)
......@@ -30,8 +30,8 @@ public class ExampleStirap : MonoBehaviour
Debug.Log("Score: " + Numpy.Sum(Rewards));
}
}
}
}
\ No newline at end of file
......@@ -5,7 +5,58 @@ using System.Text;
using System.Threading.Tasks;
using Python.Runtime;
//Class to access
//Class to access
/*
Description:
A potential with three wells is used to confine a quantum system:
v(x) = 0.5 * self.trap_strength * (x - xl)**2 * x ** 2 * (x - xr)**2
The dynamics is described by the 1D Schrodinger equation.
The system is initially in the ground state of the left well. The goal is to move as much of the
probability density to the right well by the end of the dynamics.
The agent can move the left and right wells left independently by an amount Delta at each timestep.
Source:
Observation:
If full_observation=False:
Type: Box(4)
Num Observation Min Max
0 Left population 0 1
1 Right population 0 1
2 Left well position -2 +2
3 Right well position -2 +2
If full_observation=True:
Type: Box(2 * n + 2)
where n is the number of space points.
(re(psi), im(psi), left_well_pos, right_well_pos)
Actions:
Type: Box(2)
Num Action Min Max
0 Move left well of amount -1 +1
1 Move right well of amount -1 +1
Reward:
Reward at each time step is proportional to the population in the right well times t
Reward -10 is given if the episode terminates before hand
Starting State:
The system is initially in the ground state of the left well.
Episode Termination:
* After the amount of time defined by env.totaltime (and the timesteps defined by env.timesteps)
* If the agent moves the traps out of range (or swaps them)
Solved Requirements:
Handmade solution scores 120. Try and beat it
*/
public class StirapEnv
{
......@@ -26,13 +77,16 @@ public class StirapEnv
private dynamic m_stirap;
public StirapEnv(bool FullObservation = true)
public StirapEnv(bool FullObservation = true, int timeSteps = 400)
{
m_stirap = PythonManager.Instance.StirapEnv.StirapEnv(full_observation: FullObservation);
m_stirap = PythonManager.Instance.StirapEnv.StirapEnv(FullObservation, false, new PyInt(timeSteps));
}
public StepResult Step(ICollection<float> action)
public StepResult Step(float left, float right)
{
left = Math.Min(1, Math.Max(right, 0));
right = Math.Min(1, Math.Max(right, 0));
ICollection<float> action = new float[] {left, right};
return new StepResult(m_stirap.step(Numpy.Array(action)));
}
......@@ -44,7 +98,7 @@ public class StirapEnv
public int TimeSteps
{
get { return new PyInt(m_stirap.timesteps).ToInt32(); }
set { m_stirap.timesteps = new PyInt(value); }
private set { m_stirap.timesteps = new PyInt(value); }
}
}
......
fileFormatVersion: 2
guid: df7d99ed8d3c7db4780c34a15a897244
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: e87c632d52b020c478ec6c78b8e71a59
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 7
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 3
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 4
spriteBorder: {x: 0, y: 0, z: 0, w: 0}