浏览代码

[MLA-1584] Match3 variable board size (#5189)

/check-for-ModelOverriders
GitHub 4 年前
当前提交
443f1cba
共有 42 个文件被更改,包括 1943 次插入547 次删除
  1. 6
      Project/Assets/ML-Agents/Examples/Match3/Prefabs/Match3Heuristic.prefab
  2. 6
      Project/Assets/ML-Agents/Examples/Match3/Prefabs/Match3VectorObs.prefab
  3. 6
      Project/Assets/ML-Agents/Examples/Match3/Prefabs/Match3VisualObs.prefab
  4. 430
      Project/Assets/ML-Agents/Examples/Match3/Scenes/Match3.unity
  5. 1
      Project/Assets/ML-Agents/Examples/Match3/Scripts/Match3Agent.cs
  6. 107
      Project/Assets/ML-Agents/Examples/Match3/Scripts/Match3Board.cs
  7. 37
      Project/Assets/ML-Agents/Examples/Match3/Scripts/Match3Drawer.cs
  8. 40
      Project/Assets/ML-Agents/Examples/Match3/Scripts/Match3ExampleActuator.cs
  9. 2
      Project/Assets/ML-Agents/Examples/Match3/Scripts/Match3ExampleActuatorComponent.cs
  10. 72
      com.unity.ml-agents.extensions/Documentation~/Match3.md
  11. 129
      com.unity.ml-agents.extensions/Runtime/Match3/AbstractBoard.cs
  12. 61
      com.unity.ml-agents.extensions/Runtime/Match3/Match3Actuator.cs
  13. 5
      com.unity.ml-agents.extensions/Runtime/Match3/Match3ActuatorComponent.cs
  14. 213
      com.unity.ml-agents.extensions/Runtime/Match3/Match3Sensor.cs
  15. 13
      com.unity.ml-agents.extensions/Runtime/Match3/Match3SensorComponent.cs
  16. 64
      com.unity.ml-agents.extensions/Runtime/Match3/Move.cs
  17. 115
      com.unity.ml-agents.extensions/Tests/Editor/Match3/AbstractBoardTests.cs
  18. 83
      com.unity.ml-agents.extensions/Tests/Editor/Match3/Match3ActuatorTests.cs
  19. 162
      com.unity.ml-agents.extensions/Tests/Editor/Match3/Match3SensorTests.cs
  20. 26
      com.unity.ml-agents.extensions/Tests/Editor/Match3/MoveTests.cs
  21. 2
      com.unity.ml-agents.extensions/Tests/Editor/Match3/match3obs_0.png
  22. 2
      com.unity.ml-agents.extensions/Tests/Editor/Match3/match3obs_0.png.meta
  23. 2
      com.unity.ml-agents.extensions/Tests/Editor/Match3/match3obs_2x2_0.png
  24. 2
      com.unity.ml-agents.extensions/Tests/Editor/Match3/match3obs_1.png.meta
  25. 1
      com.unity.ml-agents/Runtime/InplaceArray.cs
  26. 332
      com.unity.ml-agents.extensions/Documentation~/images/match3-moves.png
  27. 3
      com.unity.ml-agents.extensions/Tests/Editor/Match3/match3obs_1.png
  28. 92
      com.unity.ml-agents.extensions/Tests/Editor/Match3/match3obs_2x2_0.png.meta
  29. 3
      com.unity.ml-agents.extensions/Tests/Editor/Match3/match3obs_2x2_1.png
  30. 92
      com.unity.ml-agents.extensions/Tests/Editor/Match3/match3obs_2x2_1.png.meta
  31. 3
      com.unity.ml-agents.extensions/Tests/Editor/Match3/match3obs_special_0.png
  32. 92
      com.unity.ml-agents.extensions/Tests/Editor/Match3/match3obs_special_0.png.meta
  33. 3
      com.unity.ml-agents.extensions/Tests/Editor/Match3/match3obs_special_1.png
  34. 92
      com.unity.ml-agents.extensions/Tests/Editor/Match3/match3obs_special_1.png.meta
  35. 4
      com.unity.ml-agents.extensions/Tests/Editor/Match3/match3obs_special_2x2_0.png
  36. 92
      com.unity.ml-agents.extensions/Tests/Editor/Match3/match3obs_special_2x2_0.png.meta
  37. 3
      com.unity.ml-agents.extensions/Tests/Editor/Match3/match3obs_special_2x2_1.png
  38. 92
      com.unity.ml-agents.extensions/Tests/Editor/Match3/match3obs_special_2x2_1.png.meta
  39. 0
      /com.unity.ml-agents.extensions/Tests/Editor/Match3/match3obs_0.png
  40. 0
      /com.unity.ml-agents.extensions/Tests/Editor/Match3/match3obs_0.png.meta
  41. 0
      /com.unity.ml-agents.extensions/Tests/Editor/Match3/match3obs_2x2_0.png
  42. 0
      /com.unity.ml-agents.extensions/Tests/Editor/Match3/match3obs_1.png.meta

6
Project/Assets/ML-Agents/Examples/Match3/Prefabs/Match3Heuristic.prefab


m_Script: {fileID: 11500000, guid: 6d852a063770348b68caa91b8e7642a5, type: 3}
m_Name:
m_EditorClassIdentifier:
Rows: 9
Columns: 8
MinRows: 6
MaxRows: 9
MinColumns: 6
MaxColumns: 8
NumCellTypes: 6
NumSpecialTypes: 2
BasicCellPoints: 1

6
Project/Assets/ML-Agents/Examples/Match3/Prefabs/Match3VectorObs.prefab


m_Script: {fileID: 11500000, guid: 6d852a063770348b68caa91b8e7642a5, type: 3}
m_Name:
m_EditorClassIdentifier:
Rows: 9
Columns: 8
MinRows: 6
MaxRows: 9
MinColumns: 6
MaxColumns: 8
NumCellTypes: 6
NumSpecialTypes: 2
BasicCellPoints: 1

6
Project/Assets/ML-Agents/Examples/Match3/Prefabs/Match3VisualObs.prefab


m_Script: {fileID: 11500000, guid: 6d852a063770348b68caa91b8e7642a5, type: 3}
m_Name:
m_EditorClassIdentifier:
Rows: 9
Columns: 8
MinRows: 6
MaxRows: 9
MinColumns: 6
MaxColumns: 8
NumCellTypes: 6
NumSpecialTypes: 2
BasicCellPoints: 1

430
Project/Assets/ML-Agents/Examples/Match3/Scenes/Match3.unity


m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_IndirectSpecularColor: {r: 0.43632728, g: 0.4747097, b: 0.51471573, a: 1}
m_IndirectSpecularColor: {r: 0.43632758, g: 0.47471005, b: 0.5147158, a: 1}
m_UseRadianceAmbientProbe: 0
--- !u!157 &3
LightmapSettings:

m_EnableBakedLightmaps: 1
m_EnableRealtimeLightmaps: 1
m_LightmapEditorSettings:
serializedVersion: 10
serializedVersion: 12
m_Resolution: 2
m_BakeResolution: 40
m_AtlasSize: 1024

m_CompAOExponentDirect: 0
m_ExtractAmbientOcclusion: 0
m_Padding: 2
m_LightmapParameters: {fileID: 0}
m_LightmapsBakeMode: 1

m_PVRDirectSampleCount: 32
m_PVRSampleCount: 500
m_PVRBounces: 2
m_PVREnvironmentSampleCount: 500
m_PVREnvironmentReferencePointCount: 2048
m_PVRFilteringMode: 2
m_PVRDenoiserTypeDirect: 0
m_PVRDenoiserTypeIndirect: 0
m_PVRDenoiserTypeAO: 0
m_PVRFilteringMode: 1
m_PVREnvironmentMIS: 0
m_PVRCulling: 1
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 5

m_PVRFilteringAtrousPositionSigmaAO: 1
m_ShowResolutionOverlay: 1
m_ExportTrainingData: 0
m_TrainingDataDestination: TrainingData
m_LightProbeSampleCountMultiplier: 4
m_LightingDataAsset: {fileID: 0}
m_UseShadowmask: 1
--- !u!196 &4

m_Modifications:
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_RootOrder
value: 11
objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_LocalPosition.x
value: 60
objectReference: {fileID: 0}

type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}

objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_RootOrder
value: 11
objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}

m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:

m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0

m_Component:
- component: {fileID: 327661545}
- component: {fileID: 327661544}
- component: {fileID: 327661543}
m_Layer: 0
m_Name: Main Camera
m_TagString: MainCamera

m_IsActive: 1
--- !u!81 &327661543
AudioListener:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 327661542}
m_Enabled: 1
--- !u!20 &327661544
Camera:
m_ObjectHideFlags: 0

m_ClearFlags: 2
m_BackGroundColor: {r: 0.58746636, g: 0.71687025, b: 0.78431374, a: 1}
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
m_GateFitMode: 2
m_FocalLength: 50
m_NormalizedViewPortRect:
serializedVersion: 2

value: 3
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 02d33201e78d54a67ac7b0734cd6d8aa, type: 3}
propertyPath: m_RootOrder
value: 1
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 02d33201e78d54a67ac7b0734cd6d8aa, type: 3}
propertyPath: m_LocalScale.x
value: 48.39589
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 02d33201e78d54a67ac7b0734cd6d8aa, type: 3}
propertyPath: m_LocalScale.y
value: 26.17337
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 02d33201e78d54a67ac7b0734cd6d8aa, type: 3}
propertyPath: m_LocalScale.z
value: 274.20087
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 02d33201e78d54a67ac7b0734cd6d8aa, type: 3}
propertyPath: m_LocalPosition.x
value: 0.31264985
objectReference: {fileID: 0}

value: 0.33805987
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 02d33201e78d54a67ac7b0734cd6d8aa, type: 3}
propertyPath: m_LocalRotation.w
value: 0.7071069
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 02d33201e78d54a67ac7b0734cd6d8aa, type: 3}
propertyPath: m_LocalRotation.x
value: 0.000000015454312
objectReference: {fileID: 0}

value: -0.70710677
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 02d33201e78d54a67ac7b0734cd6d8aa, type: 3}
propertyPath: m_LocalRotation.w
value: 0.7071069
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 02d33201e78d54a67ac7b0734cd6d8aa, type: 3}
propertyPath: m_RootOrder
value: 1
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 02d33201e78d54a67ac7b0734cd6d8aa, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}

propertyPath: m_LocalEulerAnglesHint.z
value: -90.00001
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 02d33201e78d54a67ac7b0734cd6d8aa, type: 3}
propertyPath: m_LocalScale.x
value: 48.39589
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 02d33201e78d54a67ac7b0734cd6d8aa, type: 3}
propertyPath: m_LocalScale.y
value: 26.17337
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 02d33201e78d54a67ac7b0734cd6d8aa, type: 3}
propertyPath: m_LocalScale.z
value: 274.20087
objectReference: {fileID: 0}
- target: {fileID: 2300000, guid: 02d33201e78d54a67ac7b0734cd6d8aa, type: 3}
propertyPath: m_Materials.Array.data[0]
value:

m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 448464282}
m_Enabled: 1
serializedVersion: 8
serializedVersion: 10
m_Shape: 0
m_InnerSpotAngle: 21.802082
m_CookieSize: 10
m_Shadows:
m_Type: 2

m_Bias: 0.05
m_NormalBias: 0.4
m_NearPlane: 0.2
m_CullingMatrixOverride:
e00: 1
e01: 0
e02: 0
e03: 0
e10: 0
e11: 1
e12: 0
e13: 0
e20: 0
e21: 0
e22: 1
e23: 0
e30: 0
e31: 0
e32: 0
e33: 1
m_UseCullingMatrixOverride: 0
m_Cookie: {fileID: 0}
m_DrawHalo: 0
m_Flare: {fileID: 0}

m_Bits: 4294967295
m_RenderingLayerMask: 1
m_Lightmapping: 4
m_LightShadowCasterMode: 0
m_AreaSize: {x: 1, y: 1}

m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
m_UseBoundingSphereOverride: 0
m_ShadowRadius: 0
m_ShadowAngle: 0
--- !u!4 &448464284

objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_RootOrder
value: 15
objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_LocalPosition.x
value: 60
objectReference: {fileID: 0}

objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}

objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_RootOrder
value: 15
objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}

m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:

m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0

objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_RootOrder
value: 12
objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}

type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}

objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_RootOrder
value: 12
objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}

m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:

m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0

m_Modifications:
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_RootOrder
value: 6
objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_LocalPosition.x
value: 40
objectReference: {fileID: 0}

objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}

objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_RootOrder
value: 6
objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}

value: Match
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 6cb02a85514f94d7f8266348b5c021cd, type: 3}
propertyPath: m_RootOrder
value: 0
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 6cb02a85514f94d7f8266348b5c021cd, type: 3}
propertyPath: m_LocalScale.x
value: 37.38769
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 6cb02a85514f94d7f8266348b5c021cd, type: 3}
propertyPath: m_LocalScale.y
value: 20.219938
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 6cb02a85514f94d7f8266348b5c021cd, type: 3}
propertyPath: m_LocalScale.z
value: 211.83073
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 6cb02a85514f94d7f8266348b5c021cd, type: 3}
propertyPath: m_LocalPosition.x
value: 0.33568624
objectReference: {fileID: 0}

value: 0.33805987
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 6cb02a85514f94d7f8266348b5c021cd, type: 3}
propertyPath: m_LocalRotation.w
value: 0.7071069
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 6cb02a85514f94d7f8266348b5c021cd, type: 3}
propertyPath: m_LocalRotation.x
value: 0.000000015454312
objectReference: {fileID: 0}

value: -0.70710677
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 6cb02a85514f94d7f8266348b5c021cd, type: 3}
propertyPath: m_LocalRotation.w
value: 0.7071069
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 6cb02a85514f94d7f8266348b5c021cd, type: 3}
propertyPath: m_RootOrder
value: 0
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 6cb02a85514f94d7f8266348b5c021cd, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}

propertyPath: m_LocalEulerAnglesHint.z
value: -90.00001
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 6cb02a85514f94d7f8266348b5c021cd, type: 3}
propertyPath: m_LocalScale.x
value: 37.38769
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 6cb02a85514f94d7f8266348b5c021cd, type: 3}
propertyPath: m_LocalScale.y
value: 20.219938
objectReference: {fileID: 0}
- target: {fileID: 400000, guid: 6cb02a85514f94d7f8266348b5c021cd, type: 3}
propertyPath: m_LocalScale.z
value: 211.83073
objectReference: {fileID: 0}
- target: {fileID: 2300000, guid: 6cb02a85514f94d7f8266348b5c021cd, type: 3}
propertyPath: m_Materials.Array.data[0]
value:

objectReference: {fileID: 0}
- target: {fileID: 224194346362733190, guid: 3ce107b4a79bc4eef83afde434932a68,
type: 3}
propertyPath: m_LocalPosition.x
propertyPath: m_Pivot.x
propertyPath: m_LocalPosition.y
propertyPath: m_Pivot.y
propertyPath: m_LocalPosition.z
value: 0
propertyPath: m_RootOrder
value: 1
propertyPath: m_LocalRotation.x
propertyPath: m_AnchorMax.x
propertyPath: m_LocalRotation.y
propertyPath: m_AnchorMax.y
propertyPath: m_LocalRotation.z
propertyPath: m_AnchorMin.x
propertyPath: m_LocalRotation.w
value: 1
propertyPath: m_AnchorMin.y
value: 0
propertyPath: m_RootOrder
value: 1
propertyPath: m_SizeDelta.x
value: 0
propertyPath: m_LocalEulerAnglesHint.x
propertyPath: m_SizeDelta.y
propertyPath: m_LocalEulerAnglesHint.y
propertyPath: m_LocalPosition.x
propertyPath: m_LocalEulerAnglesHint.z
propertyPath: m_LocalPosition.y
propertyPath: m_AnchoredPosition.x
propertyPath: m_LocalPosition.z
propertyPath: m_AnchoredPosition.y
value: 0
propertyPath: m_LocalRotation.w
value: 1
propertyPath: m_SizeDelta.x
propertyPath: m_LocalRotation.x
propertyPath: m_SizeDelta.y
propertyPath: m_LocalRotation.y
propertyPath: m_AnchorMin.x
propertyPath: m_LocalRotation.z
propertyPath: m_AnchorMin.y
propertyPath: m_AnchoredPosition.x
propertyPath: m_AnchorMax.x
propertyPath: m_AnchoredPosition.y
propertyPath: m_AnchorMax.y
propertyPath: m_LocalEulerAnglesHint.x
propertyPath: m_Pivot.x
propertyPath: m_LocalEulerAnglesHint.y
propertyPath: m_Pivot.y
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []

m_Modifications:
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_RootOrder
value: 9
objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_LocalPosition.x
value: 20
objectReference: {fileID: 0}

objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}

objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_RootOrder
value: 9
objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}

m_Modifications:
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_RootOrder
value: 5
objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_LocalPosition.x
value: 20
objectReference: {fileID: 0}

objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}

type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_RootOrder
value: 5
objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}

objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_RootOrder
value: 14
objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_LocalPosition.x
value: 40
objectReference: {fileID: 0}

objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}

objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_RootOrder
value: 14
objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}

objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_RootOrder
value: 13
objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_LocalPosition.x
value: 20
objectReference: {fileID: 0}

type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}

type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}
propertyPath: m_RootOrder
value: 13
objectReference: {fileID: 0}
- target: {fileID: 3508723250774301920, guid: 2fafdcd0587684641b03b11f04454f1b,
type: 3}

m_Modifications:
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_RootOrder
value: 7
objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_LocalPosition.x
value: 60
objectReference: {fileID: 0}

objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}

objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_RootOrder
value: 7
objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}

m_Modifications:
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_RootOrder
value: 10
objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_LocalPosition.x
value: 40
objectReference: {fileID: 0}

objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}

type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_RootOrder
value: 10
objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}

m_Modifications:
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_RootOrder
value: 4
objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}

objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}

objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_RootOrder
value: 4
objectReference: {fileID: 0}
- target: {fileID: 2118285883905619878, guid: 6944ca02359f5427aa13c8551236a824,
type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}

m_Modifications:
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_RootOrder
value: 8
objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}

objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}

type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}
propertyPath: m_RootOrder
value: 8
objectReference: {fileID: 0}
- target: {fileID: 3019509691567202569, guid: aaa471bd5e2014848a66917476671aed,
type: 3}

1
Project/Assets/ML-Agents/Examples/Match3/Scripts/Match3Agent.cs


{
base.OnEpisodeBegin();
Board.UpdateCurrentBoardSize();
Board.InitSettled();
m_CurrentState = State.FindMatches;
m_TimeUntilMove = MoveTime;

107
Project/Assets/ML-Agents/Examples/Match3/Scripts/Match3Board.cs


using System;
using Unity.MLAgents.Extensions.Match3;
using UnityEngine;
using UnityEngine.Serialization;
namespace Unity.MLAgentsExamples
{

{
public int MinRows;
[FormerlySerializedAs("Rows")]
public int MaxRows;
public int MinColumns;
[FormerlySerializedAs("Columns")]
public int MaxColumns;
public int NumCellTypes;
public int NumSpecialTypes;
public const int k_EmptyCell = -1;
[Tooltip("Points earned for clearing a basic cell (cube)")]
public int BasicCellPoints = 1;

/// </summary>
public int RandomSeed;
(int, int)[,] m_Cells;
(int CellType, int SpecialType)[,] m_Cells;
private BoardSize m_CurrentBoardSize;
m_Cells = new (int, int)[Columns, Rows];
m_Matched = new bool[Columns, Rows];
m_Cells = new (int, int)[MaxColumns, MaxRows];
m_Matched = new bool[MaxColumns, MaxRows];
// Start using the max rows and columns, but we'll update the current size at the start of each episode.
m_CurrentBoardSize = new BoardSize
{
Rows = MaxRows,
Columns = MaxColumns,
NumCellTypes = NumCellTypes,
NumSpecialTypes = NumSpecialTypes
};
}
void Start()

}
public override BoardSize GetMaxBoardSize()
{
return new BoardSize
{
Rows = MaxRows,
Columns = MaxColumns,
NumCellTypes = NumCellTypes,
NumSpecialTypes = NumSpecialTypes
};
}
public override BoardSize GetCurrentBoardSize()
{
return m_CurrentBoardSize;
}
/// <summary>
/// Change the board size to a random size between the min and max rows and columns. This is
/// cached so that the size is consistent until it is updated.
/// This is just for an example; you can change your board size however you want.
/// </summary>
public void UpdateCurrentBoardSize()
{
var newRows = m_Random.Next(MinRows, MaxRows + 1);
var newCols = m_Random.Next(MinColumns, MaxColumns + 1);
m_CurrentBoardSize.Rows = newRows;
m_CurrentBoardSize.Columns = newCols;
}
public override bool MakeMove(Move move)
{
if (!IsMoveValid(move))

public override int GetCellType(int row, int col)
{
return m_Cells[col, row].Item1;
if (row >= m_CurrentBoardSize.Rows || col >= m_CurrentBoardSize.Columns)
{
throw new IndexOutOfRangeException();
}
return m_Cells[col, row].CellType;
return m_Cells[col, row].Item2;
if (row >= m_CurrentBoardSize.Rows || col >= m_CurrentBoardSize.Columns)
{
throw new IndexOutOfRangeException();
}
return m_Cells[col, row].SpecialType;
}
public override bool IsMoveValid(Move m)

{
ClearMarked();
bool madeMatch = false;
for (var i = 0; i < Rows; i++)
for (var i = 0; i < m_CurrentBoardSize.Rows; i++)
for (var j = 0; j < Columns; j++)
for (var j = 0; j < m_CurrentBoardSize.Columns; j++)
for (var iOffset = i; iOffset < Rows; iOffset++)
for (var iOffset = i; iOffset < m_CurrentBoardSize.Rows; iOffset++)
if (m_Cells[j, i].Item1 != m_Cells[j, iOffset].Item1)
if (m_Cells[j, i].CellType != m_Cells[j, iOffset].CellType)
{
break;
}

// Check vertically
var matchedCols = 0;
for (var jOffset = j; jOffset < Columns; jOffset++)
for (var jOffset = j; jOffset < m_CurrentBoardSize.Columns; jOffset++)
if (m_Cells[j, i].Item1 != m_Cells[jOffset, i].Item1)
if (m_Cells[j, i].CellType != m_Cells[jOffset, i].CellType)
{
break;
}

{
var pointsByType = new[] { BasicCellPoints, SpecialCell1Points, SpecialCell2Points };
int pointsEarned = 0;
for (var i = 0; i < Rows; i++)
for (var i = 0; i < m_CurrentBoardSize.Rows; i++)
for (var j = 0; j < Columns; j++)
for (var j = 0; j < m_CurrentBoardSize.Columns; j++)
{
if (m_Matched[j, i])
{

{
var madeChanges = false;
// Gravity is applied in the negative row direction
for (var j = 0; j < Columns; j++)
for (var j = 0; j < m_CurrentBoardSize.Columns; j++)
for (var readIndex = 0; readIndex < Rows; readIndex++)
for (var readIndex = 0; readIndex < m_CurrentBoardSize.Rows; readIndex++)
if (m_Cells[j, readIndex].Item1 != k_EmptyCell)
if (m_Cells[j, readIndex].CellType != k_EmptyCell)
{
writeIndex++;
}

for (; writeIndex < Rows; writeIndex++)
for (; writeIndex < m_CurrentBoardSize.Rows; writeIndex++)
{
madeChanges = true;
m_Cells[j, writeIndex] = (k_EmptyCell, 0);

public bool FillFromAbove()
{
bool madeChanges = false;
for (var i = 0; i < Rows; i++)
for (var i = 0; i < m_CurrentBoardSize.Rows; i++)
for (var j = 0; j < Columns; j++)
for (var j = 0; j < m_CurrentBoardSize.Columns; j++)
if (m_Cells[j, i].Item1 == k_EmptyCell)
if (m_Cells[j, i].CellType == k_EmptyCell)
{
madeChanges = true;
m_Cells[j, i] = (GetRandomCellType(), GetRandomSpecialType());

// Initialize the board to random values.
public void InitRandom()
{
for (var i = 0; i < Rows; i++)
for (var i = 0; i < MaxRows; i++)
for (var j = 0; j < Columns; j++)
for (var j = 0; j < MaxColumns; j++)
{
m_Cells[j, i] = (GetRandomCellType(), GetRandomSpecialType());
}

void ClearMarked()
{
for (var i = 0; i < Rows; i++)
for (var i = 0; i < MaxRows; i++)
for (var j = 0; j < Columns; j++)
for (var j = 0; j < MaxColumns; j++)
{
m_Matched[j, i] = false;
}

37
Project/Assets/ML-Agents/Examples/Match3/Scripts/Match3Drawer.cs


using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
namespace Unity.MLAgentsExamples
{

tilesDict.Clear();
for (var i = 0; i < m_Board.Rows; i++)
for (var i = 0; i < m_Board.MaxRows; i++)
for (var j = 0; j < m_Board.Columns; j++)
for (var j = 0; j < m_Board.MaxColumns; j++)
{
var go = Instantiate(TilePrefab, transform.position, Quaternion.identity, transform);
go.name = $"r{i}_c{j}";

InitializeDict();
}
for (var i = 0; i < m_Board.Rows; i++)
var currentSize = m_Board.GetCurrentBoardSize();
for (var i = 0; i < m_Board.MaxRows; i++)
for (var j = 0; j < m_Board.Columns; j++)
for (var j = 0; j < m_Board.MaxColumns; j++)
var value = m_Board.Cells != null ? m_Board.GetCellType(i, j) : Match3Board.k_EmptyCell;
int value = Match3Board.k_EmptyCell;
int specialType = 0;
if (m_Board.Cells != null && i < currentSize.Rows && j < currentSize.Columns)
{
value = m_Board.GetCellType(i, j);
specialType = m_Board.GetSpecialType(i, j);
}
var specialType = m_Board.Cells != null ? m_Board.GetSpecialType(i, j) : 0;
tilesDict[(i, j)].transform.position = transform.TransformPoint(pos);
tilesDict[(i, j)].SetActiveTile(specialType, value);
}

void OnDrawGizmos()
{
Profiler.BeginSample("Match3.OnDrawGizmos");
var cubeSize = .5f;
var matchedWireframeSize = .5f * (cubeSize + CubeSpacing);

}
for (var i = 0; i < m_Board.Rows; i++)
var currentSize = m_Board.GetCurrentBoardSize();
for (var i = 0; i < m_Board.MaxRows; i++)
for (var j = 0; j < m_Board.Columns; j++)
for (var j = 0; j < m_Board.MaxColumns; j++)
var value = m_Board.Cells != null ? m_Board.GetCellType(i, j) : Match3Board.k_EmptyCell;
int value = Match3Board.k_EmptyCell;
int specialType = 0;
if (m_Board.Cells != null && i < currentSize.Rows && j < currentSize.Columns)
{
value = m_Board.GetCellType(i, j);
specialType = m_Board.GetSpecialType(i, j);
}
if (value >= 0 && value < s_Colors.Length)
{
Gizmos.color = s_Colors[value];

var pos = new Vector3(j, i, 0);
pos *= CubeSpacing;
var specialType = m_Board.Cells != null ? m_Board.GetSpecialType(i, j) : 0;
if (specialType == 2)
{
Gizmos.DrawCube(transform.TransformPoint(pos), cubeSize * new Vector3(1f, .5f, .5f));

var threeQuarters = Vector3.Lerp(pos, otherPos, .75f);
Gizmos.DrawLine(transform.TransformPoint(oneQuarter), transform.TransformPoint(threeQuarters));
}
Profiler.EndSample();
}
}
}

40
Project/Assets/ML-Agents/Examples/Match3/Scripts/Match3ExampleActuator.cs


{
public class Match3ExampleActuator : Match3Actuator
{
Match3Board Board => (Match3Board)m_Board;
private Match3Board m_Board;
Match3Board Board => m_Board;
Agent agent,
)
: base(board, forceHeuristic, seed, agent, name) { }
)
: base(board, forceHeuristic, seed, name)
{
m_Board = board;
}
protected override int EvalMovePoints(Move move)

var moveVal = m_Board.GetCellType(move.Row, move.Column);
var moveSpecial = m_Board.GetSpecialType(move.Row, move.Column);
var moveVal = Board.GetCellType(move.Row, move.Column);
var moveSpecial = Board.GetSpecialType(move.Row, move.Column);
var oppositeVal = m_Board.GetCellType(otherRow, otherCol);
var oppositeSpecial = m_Board.GetSpecialType(otherRow, otherCol);
var oppositeVal = Board.GetCellType(otherRow, otherCol);
var oppositeSpecial = Board.GetSpecialType(otherRow, otherCol);
int movePoints = EvalHalfMove(

int EvalHalfMove(int newRow, int newCol, int newValue, int newSpecial, Direction incomingDirection, int[] pointsByType)
{
// This is a essentially a duplicate of AbstractBoard.CheckHalfMove but also counts the points for the move.
var currentBoardSize = Board.GetCurrentBoardSize();
int matchedLeft = 0, matchedRight = 0, matchedUp = 0, matchedDown = 0;
int scoreLeft = 0, scoreRight = 0, scoreUp = 0, scoreDown = 0;

{
if (m_Board.GetCellType(newRow, c) == newValue)
if (Board.GetCellType(newRow, c) == newValue)
scoreLeft += pointsByType[m_Board.GetSpecialType(newRow, c)];
scoreLeft += pointsByType[Board.GetSpecialType(newRow, c)];
}
else
break;

if (incomingDirection != Direction.Left)
{
for (var c = newCol + 1; c < m_Board.Columns; c++)
for (var c = newCol + 1; c < currentBoardSize.Columns; c++)
if (m_Board.GetCellType(newRow, c) == newValue)
if (Board.GetCellType(newRow, c) == newValue)
scoreRight += pointsByType[m_Board.GetSpecialType(newRow, c)];
scoreRight += pointsByType[Board.GetSpecialType(newRow, c)];
}
else
break;

if (incomingDirection != Direction.Down)
{
for (var r = newRow + 1; r < m_Board.Rows; r++)
for (var r = newRow + 1; r < currentBoardSize.Rows; r++)
if (m_Board.GetCellType(r, newCol) == newValue)
if (Board.GetCellType(r, newCol) == newValue)
scoreUp += pointsByType[m_Board.GetSpecialType(r, newCol)];
scoreUp += pointsByType[Board.GetSpecialType(r, newCol)];
}
else
break;

{
for (var r = newRow - 1; r >= 0; r--)
{
if (m_Board.GetCellType(r, newCol) == newValue)
if (Board.GetCellType(r, newCol) == newValue)
scoreDown += pointsByType[m_Board.GetSpecialType(r, newCol)];
scoreDown += pointsByType[Board.GetSpecialType(r, newCol)];
}
else
break;

2
Project/Assets/ML-Agents/Examples/Match3/Scripts/Match3ExampleActuatorComponent.cs


var board = GetComponent<Match3Board>();
var agent = GetComponentInParent<Agent>();
var seed = RandomSeed == -1 ? gameObject.GetInstanceID() : RandomSeed + 1;
return new IActuator[] { new Match3ExampleActuator(board, ForceHeuristic, agent, ActuatorName, seed) };
return new IActuator[] { new Match3ExampleActuator(board, ForceHeuristic, ActuatorName, seed) };
}
}
}

72
com.unity.ml-agents.extensions/Documentation~/Match3.md


<img src="images/match3.png" align="center" width="3000"/>
## Overview
One of the main feedback we get is to illustrate more real game examples using ML-Agents. We are excited to provide an example implementation of Match-3 using ML-Agents and additional utilities to integrate ML-Agents with Match-3 games.
One of the main feedback we get is to illustrate more real game examples using ML-Agents. We are excited to provide an
example implementation of Match-3 using ML-Agents and additional utilities to integrate ML-Agents with Match-3 games.
Our aim is to enable Match-3 teams to leverage ML-Agents to create player agents to learn and play different Match-3 levels. This implementation is intended as a starting point and guide for teams to get started (as there are many nuances with Match-3 for training ML-Agents) and for us to iterate both on the C#, hyperparameters, and trainers to improve ML-Agents for Match-3.
Our aim is to enable Match-3 teams to leverage ML-Agents to create player agents to learn and play different Match-3
levels. This implementation is intended as a starting point and guide for teams to get started (as there are many
nuances with Match-3 for training ML-Agents) and for us to iterate both on the C#, hyperparameters, and trainers to
improve ML-Agents for Match-3.
* C# implementation catered toward a Match-3 setup including concepts around encoding for moves based on [Human Like Playtesting with Deep Learning](https://www.researchgate.net/publication/328307928_Human-Like_Playtesting_with_Deep_Learning)
* An example Match-3 scene with ML-Agents implemented (located under /Project/Assets/ML-Agents/Examples/Match3). More information, on Match-3 example [here](https://github.com/Unity-Technologies/ml-agents/tree/release_15_docs/docs/docs/Learning-Environment-Examples.md#match-3).
* C# implementation catered toward a Match-3 setup including concepts around encoding for moves based on
[Human Like Playtesting with Deep Learning](https://www.researchgate.net/publication/328307928_Human-Like_Playtesting_with_Deep_Learning)
* An example Match-3 scene with ML-Agents implemented (located under /Project/Assets/ML-Agents/Examples/Match3).
More information on the Match-3 example is [here](https://github.com/Unity-Technologies/ml-agents/tree/release_15_docs/docs/docs/Learning-Environment-Examples.md#match-3).
If you are a Match-3 developer and are trying to leverage ML-Agents for this scenario, [we want to hear from you](https://forms.gle/TBsB9jc8WshgzViU9). Additionally, we are also looking for interested Match-3 teams to speak with us for 45 minutes. If you are interested, please indicate that in the [form](https://forms.gle/TBsB9jc8WshgzViU9). If selected, we will provide gift cards as a token of appreciation.
If you are a Match-3 developer and are trying to leverage ML-Agents for this scenario,
[we want to hear from you](https://forms.gle/TBsB9jc8WshgzViU9). Additionally, we are also looking for interested
Match-3 teams to speak with us for 45 minutes. If you are interested, please indicate that in the
[form](https://forms.gle/TBsB9jc8WshgzViU9). If selected, we will provide gift cards as a token of appreciation.
Do you have a type of game you are interested for ML-Agents? If so, please post a [forum issue](https://forum.unity.com/forums/ml-agents.453/) with [GAME TEMPLATE] in the title.
Do you have a type of game you are interested for ML-Agents? If so, please post a
[forum issue](https://forum.unity.com/forums/ml-agents.453/) with [GAME TEMPLATE] in the title.
The C# code for Match-3 exists inside of the extensions package (com.unity.ml-agents.extensions). A good first step would be to familiarize with the extensions package by reading the document [here](com.unity.ml-agents.extensions.md). The second step would be to take a look at how we have implemented the C# code in the example Match-3 scene (located under /Project/Assets/ML-Agents/Examples/match3). Once you have some familiarity, then the next step would be to implement the C# code for Match-3 from the extensions package.
The C# code for Match-3 exists inside of the extensions package (com.unity.ml-agents.extensions). A good first step
would be to familiarize with the extensions package by reading the document [here](com.unity.ml-agents.extensions.md).
The second step would be to take a look at how we have implemented the C# code in the example Match-3 scene (located
under /Project/Assets/ML-Agents/Examples/match3). Once you have some familiarity, then the next step would be to
implement the C# code for Match-3 from the extensions package.
Additionally, see below for additional technical specifications on the C# code for Match-3. Please note the Match-3 game isn't human playable as implemented and can be only played via training.
Additionally, see below for additional technical specifications on the C# code for Match-3. Please note the Match-3
game isn't human playable as implemented and can be only played via training.
* ask your game what the current and maximum sizes (rows, columns, and potential piece types) of the board are
These are handled by implementing the `GetCellType()`, `IsMoveValid()`, and `MakeMove()` abstract methods.
These are handled by implementing the abstract methods of `AbstractBoard`.
The AbstractBoard also tracks the number of rows, columns, and potential piece types that the board can have.
##### `public abstract BoardSize GetMaxBoardSize()`
Returns the largest `BoardSize` that the game can use. This is used to determine the sizes of observations and sensors,
so don't make it larger than necessary.
##### `public virtual BoardSize GetCurrentBoardSize()`
Returns the current size of the board. Each field on this BoardSize must be less than or equal to the corresponding
field returned by `GetMaxBoardSize()`. This method is optional; if your always use the same size board, you don't
need to override it.
If the current board size is smaller than the maximum board size, `GetCellType()` and `GetSpecialType()` will not be
called for cells outside the current board size, and `IsValidMove` won't be called for moves that would go outside of
the current board size.
##### `public abstract int GetCellType(int row, int col)`
Returns the "color" of piece at the given row and column.

Note that during training, a move that was marked as invalid may occasionally still be
requested. If this happens, it is safe to do nothing and request another move.
### Move struct
### `Move` struct
for a board of a given size with. `Move.NumPotentialMoves(NumRows, NumColumns)`. There are two helper
for a board of a given size with. `Move.NumPotentialMoves(maxBoardSize)`. There are two helper
* `public static Move FromMoveIndex(int moveIndex, int maxRows, int maxCols)` can be used to
* `public static Move FromMoveIndex(int moveIndex, BoardSize maxBoardSize)` can be used to
* `public static Move FromPositionAndDirection(int row, int col, Direction dir, int maxRows, int maxCols)` creates
* `public static Move FromPositionAndDirection(int row, int col, Direction dir, BoardSize maxBoardSize)` creates
### `BoardSize` struct
Describes the "size" of the board, including the number of potential piece types that the board can have.
This is returned by the AbstractBoard.GetMaxBoardSize() and GetCurrentBoardSize() methods.
#### `Match3Sensor` and `Match3SensorComponent` classes
The `Match3Sensor` generates observations about the state using the `AbstractBoard` interface. You can

A `Match3SensorComponent` generates a `Match3Sensor` at runtime, and should be added to the same GameObject
as your `Agent` implementation. You do not need to write any additional code to use them.
A `Match3SensorComponent` generates `Match3Sensor`s (the exact number of sensors depends on your configuration)
at runtime, and should be added to the same GameObject as your `Agent` implementation. You do not need to write any
additional code to use them.
#### `Match3Actuator` and `Match3ActuatorComponent` classes
The `Match3Actuator` converts actions from training or inference into a `Move` that is sent to` AbstractBoard.MakeMove()`

`GameObject`.
* Call `Agent.RequestDecision()` when you're ready for the `Agent` to make a move on the next `Academy` step. During
the next `Academy` step, the `MakeMove()` method on the board will be called.
## Implementation Details
### Action Space
The indexing for actions is the same as described in
[Human Like Playtesting with Deep Learning](https://www.researchgate.net/publication/328307928_Human-Like_Playtesting_with_Deep_Learning)
(for example, Figure 2b). The horizontal moves are enumerated first, then the vertical ones.
<img src="images/match3-moves.png" align="center"/>

129
com.unity.ml-agents.extensions/Runtime/Match3/AbstractBoard.cs


using System;
using System.Collections.Generic;
using System.Diagnostics;
using Debug = UnityEngine.Debug;
public abstract class AbstractBoard : MonoBehaviour
/// <summary>
/// Representation of the AbstractBoard size and number of cell and special types.
/// </summary>
public struct BoardSize
{
/// <summary>
/// Number of rows on the board

public int NumSpecialTypes;
/// <summary>
/// Check that all fields of the left-hand BoardSize are less than or equal to the field of the right-hand BoardSize
/// </summary>
/// <param name="lhs"></param>
/// <param name="rhs"></param>
/// <returns>True if all fields are less than or equal.</returns>
public static bool operator <=(BoardSize lhs, BoardSize rhs)
{
return lhs.Rows <= rhs.Rows && lhs.Columns <= rhs.Columns && lhs.NumCellTypes <= rhs.NumCellTypes &&
lhs.NumSpecialTypes <= rhs.NumSpecialTypes;
}
/// <summary>
/// Check that all fields of the left-hand BoardSize are greater than or equal to the field of the right-hand BoardSize
/// </summary>
/// <param name="lhs"></param>
/// <param name="rhs"></param>
/// <returns>True if all fields are greater than or equal.</returns>
public static bool operator >=(BoardSize lhs, BoardSize rhs)
{
return lhs.Rows >= rhs.Rows && lhs.Columns >= rhs.Columns && lhs.NumCellTypes >= rhs.NumCellTypes &&
lhs.NumSpecialTypes >= rhs.NumSpecialTypes;
}
/// <summary>
/// Return a string representation of the BoardSize.
/// </summary>
/// <returns></returns>
public override string ToString()
{
return
$"Rows: {Rows}, Columns: {Columns}, NumCellTypes: {NumCellTypes}, NumSpecialTypes: {NumSpecialTypes}";
}
}
/// <summary>
/// An adapter between ML Agents and a Match-3 game.
/// </summary>
public abstract class AbstractBoard : MonoBehaviour
{
/// <summary>
/// Return the maximum size of the board. This is used to determine the size of observations and actions,
/// so the returned values must not change.
/// </summary>
/// <returns></returns>
public abstract BoardSize GetMaxBoardSize();
/// <summary>
/// Return the current size of the board. The values must less than or equal to the values returned from
/// GetMaxBoardSize().
/// By default, this will return GetMaxBoardSize(); if your board doesn't change size, you don't need to
/// override it.
/// </summary>
/// <returns></returns>
public virtual BoardSize GetCurrentBoardSize()
{
return GetMaxBoardSize();
}
/// <summary>
/// Returns the "color" of the piece at the given row and column.
/// This should be between 0 and NumCellTypes-1 (inclusive).
/// The actual order of the values doesn't matter.

/// The actual results will depend on the rules of the game, but we provide SimpleIsMoveValid()
/// that handles basic match3 rules with no special or immovable pieces.
/// </summary>
/// <remarks>
/// Moves that would go outside of GetCurrentBoardSize() are filtered out before they are
/// passed to IsMoveValid().
/// </remarks>
/// <param name="m"></param>
/// <returns></returns>
public abstract bool IsMoveValid(Move m);

/// <returns></returns>
public int NumMoves()
{
return Move.NumPotentialMoves(Rows, Columns);
return Move.NumPotentialMoves(GetMaxBoardSize());
}
/// <summary>

/// <returns></returns>
public IEnumerable<Move> AllMoves()
{
var currentMove = Move.FromMoveIndex(0, Rows, Columns);
for (var i = 0; i < NumMoves(); i++)
{
yield return currentMove;
currentMove.Next(Rows, Columns);
}
}
var maxBoardSize = GetMaxBoardSize();
var currentBoardSize = GetCurrentBoardSize();
/// <summary>
/// Iterate through all valid Moves on the board.
/// </summary>
/// <returns></returns>
public IEnumerable<Move> ValidMoves()
{
var currentMove = Move.FromMoveIndex(0, Rows, Columns);
var currentMove = Move.FromMoveIndex(0, maxBoardSize);
if (IsMoveValid(currentMove))
if (currentMove.InRangeForBoard(currentBoardSize))
currentMove.Next(Rows, Columns);
currentMove.Next(maxBoardSize);
/// Iterate through all invalid Moves on the board.
/// Iterate through all valid Moves on the board.
public IEnumerable<Move> InvalidMoves()
public IEnumerable<Move> ValidMoves()
var currentMove = Move.FromMoveIndex(0, Rows, Columns);
var maxBoardSize = GetMaxBoardSize();
var currentBoardSize = GetCurrentBoardSize();
var currentMove = Move.FromMoveIndex(0, maxBoardSize);
if (!IsMoveValid(currentMove))
if (currentMove.InRangeForBoard(currentBoardSize) && IsMoveValid(currentMove))
currentMove.Next(Rows, Columns);
currentMove.Next(maxBoardSize);
}
}

/// <returns></returns>
bool CheckHalfMove(int newRow, int newCol, int newValue, Direction incomingDirection)
{
var currentBoardSize = GetCurrentBoardSize();
int matchedLeft = 0, matchedRight = 0, matchedUp = 0, matchedDown = 0;
if (incomingDirection != Direction.Right)

if (incomingDirection != Direction.Left)
{
for (var c = newCol + 1; c < Columns; c++)
for (var c = newCol + 1; c < currentBoardSize.Columns; c++)
{
if (GetCellType(newRow, c) == newValue)
matchedRight++;

if (incomingDirection != Direction.Down)
{
for (var r = newRow + 1; r < Rows; r++)
for (var r = newRow + 1; r < currentBoardSize.Rows; r++)
{
if (GetCellType(r, newCol) == newValue)
matchedUp++;

return false;
}
/// <summary>
/// Make sure that the current BoardSize isn't larger than the original value of GetMaxBoardSize().
/// If it is, log a warning.
/// </summary>
/// <param name="originalMaxBoardSize"></param>
[Conditional("DEBUG")]
internal void CheckBoardSizes(BoardSize originalMaxBoardSize)
{
var currentBoardSize = GetCurrentBoardSize();
if (!(currentBoardSize <= originalMaxBoardSize))
{
Debug.LogWarning(
"Current BoardSize is larger than maximum board size was on initialization. This may cause unexpected results.\n" +
$"Original GetMaxBoardSize() result: {originalMaxBoardSize}\n" +
$"GetCurrentBoardSize() result: {currentBoardSize}"
);
}
}
}
}

61
com.unity.ml-agents.extensions/Runtime/Match3/Match3Actuator.cs


using System.Collections.Generic;
using UnityEngine;
using Debug = UnityEngine.Debug;
namespace Unity.MLAgents.Extensions.Match3

/// in action masks, and applies the action to the board via AbstractBoard.MakeMove().
/// </summary>
public class Match3Actuator : IActuator, IHeuristicProvider, IBuiltInActuator
public class Match3Actuator : IActuator, IBuiltInActuator
protected AbstractBoard m_Board;
protected System.Random m_Random;
private ActionSpec m_ActionSpec;
private bool m_ForceHeuristic;
private Agent m_Agent;
private int m_Rows;
private int m_Columns;
private int m_NumCellTypes;
AbstractBoard m_Board;
System.Random m_Random;
ActionSpec m_ActionSpec;
bool m_ForceHeuristic;
BoardSize m_MaxBoardSize;
/// <summary>
/// Create a Match3Actuator.

public Match3Actuator(AbstractBoard board,
bool forceHeuristic,
int seed,
Agent agent,