From 6a3d57145d4c5ab6cc915defd08f4f9f45c3ef16 Mon Sep 17 00:00:00 2001
From: Sehyeon <ksh1837@naver.com>
Date: Fri, 14 Mar 2025 14:37:10 +0900
Subject: [PATCH 1/7] DO-4 [Feat] Create AI-Minimax Algorithm Code

---
 Assets/Scenes/AITestScene.unity              | 1641 ++++++++++++++++++
 Assets/Scenes/AITestScene.unity.meta         |    7 +
 Assets/Script/AI.meta                        |    8 +
 Assets/Script/AI/MiniMaxAIController.cs      |  331 ++++
 Assets/Script/AI/MiniMaxAIController.cs.meta |   11 +
 Assets/Script/AI/TestGameManager.cs          |   82 +
 Assets/Script/AI/TestGameManager.cs.meta     |   11 +
 7 files changed, 2091 insertions(+)
 create mode 100644 Assets/Scenes/AITestScene.unity
 create mode 100644 Assets/Scenes/AITestScene.unity.meta
 create mode 100644 Assets/Script/AI.meta
 create mode 100644 Assets/Script/AI/MiniMaxAIController.cs
 create mode 100644 Assets/Script/AI/MiniMaxAIController.cs.meta
 create mode 100644 Assets/Script/AI/TestGameManager.cs
 create mode 100644 Assets/Script/AI/TestGameManager.cs.meta

diff --git a/Assets/Scenes/AITestScene.unity b/Assets/Scenes/AITestScene.unity
new file mode 100644
index 0000000..1ba087f
--- /dev/null
+++ b/Assets/Scenes/AITestScene.unity
@@ -0,0 +1,1641 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_OcclusionBakeSettings:
+    smallestOccluder: 5
+    smallestHole: 0.25
+    backfaceThreshold: 100
+  m_SceneGUID: 00000000000000000000000000000000
+  m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 9
+  m_Fog: 0
+  m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+  m_FogMode: 3
+  m_FogDensity: 0.01
+  m_LinearFogStart: 0
+  m_LinearFogEnd: 300
+  m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
+  m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
+  m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
+  m_AmbientIntensity: 1
+  m_AmbientMode: 3
+  m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+  m_SkyboxMaterial: {fileID: 0}
+  m_HaloStrength: 0.5
+  m_FlareStrength: 1
+  m_FlareFadeSpeed: 3
+  m_HaloTexture: {fileID: 0}
+  m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+  m_DefaultReflectionMode: 0
+  m_DefaultReflectionResolution: 128
+  m_ReflectionBounces: 1
+  m_ReflectionIntensity: 1
+  m_CustomReflection: {fileID: 0}
+  m_Sun: {fileID: 0}
+  m_UseRadianceAmbientProbe: 0
+--- !u!157 &3
+LightmapSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 12
+  m_GIWorkflowMode: 1
+  m_GISettings:
+    serializedVersion: 2
+    m_BounceScale: 1
+    m_IndirectOutputScale: 1
+    m_AlbedoBoost: 1
+    m_EnvironmentLightingMode: 0
+    m_EnableBakedLightmaps: 0
+    m_EnableRealtimeLightmaps: 0
+  m_LightmapEditorSettings:
+    serializedVersion: 12
+    m_Resolution: 2
+    m_BakeResolution: 40
+    m_AtlasSize: 1024
+    m_AO: 0
+    m_AOMaxDistance: 1
+    m_CompAOExponent: 1
+    m_CompAOExponentDirect: 0
+    m_ExtractAmbientOcclusion: 0
+    m_Padding: 2
+    m_LightmapParameters: {fileID: 0}
+    m_LightmapsBakeMode: 1
+    m_TextureCompression: 1
+    m_FinalGather: 0
+    m_FinalGatherFiltering: 1
+    m_FinalGatherRayCount: 256
+    m_ReflectionCompression: 2
+    m_MixedBakeMode: 2
+    m_BakeBackend: 0
+    m_PVRSampling: 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_PVRFilterTypeDirect: 0
+    m_PVRFilterTypeIndirect: 0
+    m_PVRFilterTypeAO: 0
+    m_PVREnvironmentMIS: 0
+    m_PVRCulling: 1
+    m_PVRFilteringGaussRadiusDirect: 1
+    m_PVRFilteringGaussRadiusIndirect: 5
+    m_PVRFilteringGaussRadiusAO: 2
+    m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+    m_PVRFilteringAtrousPositionSigmaIndirect: 2
+    m_PVRFilteringAtrousPositionSigmaAO: 1
+    m_ExportTrainingData: 0
+    m_TrainingDataDestination: TrainingData
+    m_LightProbeSampleCountMultiplier: 4
+  m_LightingDataAsset: {fileID: 0}
+  m_LightingSettings: {fileID: 0}
+--- !u!196 &4
+NavMeshSettings:
+  serializedVersion: 2
+  m_ObjectHideFlags: 0
+  m_BuildSettings:
+    serializedVersion: 3
+    agentTypeID: 0
+    agentRadius: 0.5
+    agentHeight: 2
+    agentSlope: 45
+    agentClimb: 0.4
+    ledgeDropHeight: 0
+    maxJumpAcrossDistance: 0
+    minRegionArea: 2
+    manualCellSize: 0
+    cellSize: 0.16666667
+    manualTileSize: 0
+    tileSize: 256
+    buildHeightMesh: 0
+    maxJobWorkers: 0
+    preserveTilesOutsideBounds: 0
+    debug:
+      m_Flags: 0
+  m_NavMeshData: {fileID: 0}
+--- !u!1 &492692667
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 492692668}
+  - component: {fileID: 492692670}
+  - component: {fileID: 492692669}
+  m_Layer: 5
+  m_Name: Panel
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &492692668
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 492692667}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 852683208}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 0, y: 0}
+  m_AnchorMax: {x: 1, y: 1}
+  m_AnchoredPosition: {x: 0, y: 0}
+  m_SizeDelta: {x: 0, y: 0}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &492692669
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 492692667}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Material: {fileID: 0}
+  m_Color: {r: 1, g: 1, b: 1, a: 0.392}
+  m_RaycastTarget: 1
+  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+  m_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0}
+  m_Type: 1
+  m_PreserveAspect: 0
+  m_FillCenter: 1
+  m_FillMethod: 4
+  m_FillAmount: 1
+  m_FillClockwise: 1
+  m_FillOrigin: 0
+  m_UseSpriteMesh: 0
+  m_PixelsPerUnitMultiplier: 1
+--- !u!222 &492692670
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 492692667}
+  m_CullTransparentMesh: 1
+--- !u!1 &493380229
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 493380230}
+  - component: {fileID: 493380233}
+  - component: {fileID: 493380232}
+  - component: {fileID: 493380231}
+  m_Layer: 5
+  m_Name: col
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &493380230
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 493380229}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children:
+  - {fileID: 1976308225}
+  m_Father: {fileID: 852683208}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 0.5, y: 0}
+  m_AnchorMax: {x: 0.5, y: 0}
+  m_AnchoredPosition: {x: 0, y: 30}
+  m_SizeDelta: {x: 300, y: 200}
+  m_Pivot: {x: 0.5, y: 0}
+--- !u!114 &493380231
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 493380229}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 2da0c512f12947e489f739169773d7ca, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Navigation:
+    m_Mode: 3
+    m_WrapAround: 0
+    m_SelectOnUp: {fileID: 0}
+    m_SelectOnDown: {fileID: 0}
+    m_SelectOnLeft: {fileID: 0}
+    m_SelectOnRight: {fileID: 0}
+  m_Transition: 1
+  m_Colors:
+    m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
+    m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
+    m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
+    m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
+    m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
+    m_ColorMultiplier: 1
+    m_FadeDuration: 0.1
+  m_SpriteState:
+    m_HighlightedSprite: {fileID: 0}
+    m_PressedSprite: {fileID: 0}
+    m_SelectedSprite: {fileID: 0}
+    m_DisabledSprite: {fileID: 0}
+  m_AnimationTriggers:
+    m_NormalTrigger: Normal
+    m_HighlightedTrigger: Highlighted
+    m_PressedTrigger: Pressed
+    m_SelectedTrigger: Selected
+    m_DisabledTrigger: Disabled
+  m_Interactable: 1
+  m_TargetGraphic: {fileID: 493380232}
+  m_TextViewport: {fileID: 1976308225}
+  m_TextComponent: {fileID: 1339168105}
+  m_Placeholder: {fileID: 0}
+  m_VerticalScrollbar: {fileID: 0}
+  m_VerticalScrollbarEventHandler: {fileID: 0}
+  m_LayoutGroup: {fileID: 0}
+  m_ScrollSensitivity: 1
+  m_ContentType: 0
+  m_InputType: 0
+  m_AsteriskChar: 42
+  m_KeyboardType: 0
+  m_LineType: 0
+  m_HideMobileInput: 0
+  m_HideSoftKeyboard: 0
+  m_CharacterValidation: 0
+  m_RegexValue: 
+  m_GlobalPointSize: 14
+  m_CharacterLimit: 0
+  m_OnEndEdit:
+    m_PersistentCalls:
+      m_Calls: []
+  m_OnSubmit:
+    m_PersistentCalls:
+      m_Calls: []
+  m_OnSelect:
+    m_PersistentCalls:
+      m_Calls: []
+  m_OnDeselect:
+    m_PersistentCalls:
+      m_Calls: []
+  m_OnTextSelection:
+    m_PersistentCalls:
+      m_Calls: []
+  m_OnEndTextSelection:
+    m_PersistentCalls:
+      m_Calls: []
+  m_OnValueChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_OnTouchScreenKeyboardStatusChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_CaretColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
+  m_CustomCaretColor: 0
+  m_SelectionColor: {r: 0.65882355, g: 0.80784315, b: 1, a: 0.7529412}
+  m_Text: 
+  m_CaretBlinkRate: 0.85
+  m_CaretWidth: 1
+  m_ReadOnly: 0
+  m_RichText: 1
+  m_GlobalFontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
+  m_OnFocusSelectAll: 1
+  m_ResetOnDeActivation: 1
+  m_RestoreOriginalTextOnEscape: 1
+  m_isRichTextEditingAllowed: 0
+  m_LineLimit: 0
+  m_InputValidator: {fileID: 0}
+--- !u!114 &493380232
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 493380229}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Material: {fileID: 0}
+  m_Color: {r: 1, g: 1, b: 1, a: 1}
+  m_RaycastTarget: 1
+  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+  m_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_Sprite: {fileID: 10911, guid: 0000000000000000f000000000000000, type: 0}
+  m_Type: 1
+  m_PreserveAspect: 0
+  m_FillCenter: 1
+  m_FillMethod: 4
+  m_FillAmount: 1
+  m_FillClockwise: 1
+  m_FillOrigin: 0
+  m_UseSpriteMesh: 0
+  m_PixelsPerUnitMultiplier: 1
+--- !u!222 &493380233
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 493380229}
+  m_CullTransparentMesh: 1
+--- !u!1 &519420028
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 519420032}
+  - component: {fileID: 519420031}
+  - component: {fileID: 519420029}
+  m_Layer: 0
+  m_Name: Main Camera
+  m_TagString: MainCamera
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!81 &519420029
+AudioListener:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 519420028}
+  m_Enabled: 1
+--- !u!20 &519420031
+Camera:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 519420028}
+  m_Enabled: 1
+  serializedVersion: 2
+  m_ClearFlags: 2
+  m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+  m_projectionMatrixMode: 1
+  m_GateFitMode: 2
+  m_FOVAxisMode: 0
+  m_Iso: 200
+  m_ShutterSpeed: 0.005
+  m_Aperture: 16
+  m_FocusDistance: 10
+  m_FocalLength: 50
+  m_BladeCount: 5
+  m_Curvature: {x: 2, y: 11}
+  m_BarrelClipping: 0.25
+  m_Anamorphism: 0
+  m_SensorSize: {x: 36, y: 24}
+  m_LensShift: {x: 0, y: 0}
+  m_NormalizedViewPortRect:
+    serializedVersion: 2
+    x: 0
+    y: 0
+    width: 1
+    height: 1
+  near clip plane: 0.3
+  far clip plane: 1000
+  field of view: 60
+  orthographic: 1
+  orthographic size: 5
+  m_Depth: -1
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingPath: -1
+  m_TargetTexture: {fileID: 0}
+  m_TargetDisplay: 0
+  m_TargetEye: 0
+  m_HDR: 1
+  m_AllowMSAA: 0
+  m_AllowDynamicResolution: 0
+  m_ForceIntoRT: 0
+  m_OcclusionCulling: 0
+  m_StereoConvergence: 10
+  m_StereoSeparation: 0.022
+--- !u!4 &519420032
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 519420028}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: -10}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &748424465
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 748424466}
+  - component: {fileID: 748424468}
+  - component: {fileID: 748424467}
+  m_Layer: 5
+  m_Name: board
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &748424466
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 748424465}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 852683208}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 0, y: 1}
+  m_AnchorMax: {x: 1, y: 1}
+  m_AnchoredPosition: {x: 0, y: -20}
+  m_SizeDelta: {x: 0, y: 1500}
+  m_Pivot: {x: 0.5, y: 1}
+--- !u!114 &748424467
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 748424465}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Material: {fileID: 0}
+  m_Color: {r: 1, g: 1, b: 1, a: 1}
+  m_RaycastTarget: 1
+  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+  m_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_text: New Text
+  m_isRightToLeft: 0
+  m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
+  m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
+  m_fontSharedMaterials: []
+  m_fontMaterial: {fileID: 0}
+  m_fontMaterials: []
+  m_fontColor32:
+    serializedVersion: 2
+    rgba: 4294967295
+  m_fontColor: {r: 1, g: 1, b: 1, a: 1}
+  m_enableVertexGradient: 0
+  m_colorMode: 3
+  m_fontColorGradient:
+    topLeft: {r: 1, g: 1, b: 1, a: 1}
+    topRight: {r: 1, g: 1, b: 1, a: 1}
+    bottomLeft: {r: 1, g: 1, b: 1, a: 1}
+    bottomRight: {r: 1, g: 1, b: 1, a: 1}
+  m_fontColorGradientPreset: {fileID: 0}
+  m_spriteAsset: {fileID: 0}
+  m_tintAllSprites: 0
+  m_StyleSheet: {fileID: 0}
+  m_TextStyleHashCode: -1183493901
+  m_overrideHtmlColors: 0
+  m_faceColor:
+    serializedVersion: 2
+    rgba: 4294967295
+  m_fontSize: 72
+  m_fontSizeBase: 72
+  m_fontWeight: 400
+  m_enableAutoSizing: 0
+  m_fontSizeMin: 18
+  m_fontSizeMax: 72
+  m_fontStyle: 0
+  m_HorizontalAlignment: 2
+  m_VerticalAlignment: 512
+  m_textAlignment: 65535
+  m_characterSpacing: 0
+  m_wordSpacing: 0
+  m_lineSpacing: 0
+  m_lineSpacingMax: 0
+  m_paragraphSpacing: 0
+  m_charWidthMaxAdj: 0
+  m_enableWordWrapping: 1
+  m_wordWrappingRatios: 0.4
+  m_overflowMode: 0
+  m_linkedTextComponent: {fileID: 0}
+  parentLinkedComponent: {fileID: 0}
+  m_enableKerning: 1
+  m_enableExtraPadding: 0
+  checkPaddingRequired: 0
+  m_isRichText: 1
+  m_parseCtrlCharacters: 1
+  m_isOrthographic: 1
+  m_isCullingEnabled: 0
+  m_horizontalMapping: 0
+  m_verticalMapping: 0
+  m_uvLineOffset: 0
+  m_geometrySortingOrder: 0
+  m_IsTextObjectScaleStatic: 0
+  m_VertexBufferAutoSizeReduction: 0
+  m_useMaxVisibleDescender: 1
+  m_pageToDisplay: 1
+  m_margin: {x: 0, y: 0, z: 0, w: 0}
+  m_isUsingLegacyAnimationComponent: 0
+  m_isVolumetricText: 0
+  m_hasFontAssetChanged: 0
+  m_baseMaterial: {fileID: 0}
+  m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
+--- !u!222 &748424468
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 748424465}
+  m_CullTransparentMesh: 1
+--- !u!1 &770473330
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 770473331}
+  - component: {fileID: 770473332}
+  m_Layer: 0
+  m_Name: TestGameManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &770473331
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 770473330}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 156.5, y: 250.75, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &770473332
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 770473330}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: facc79abb6042e846bb0a2b099b58e9c, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  rowText: {fileID: 1925068171}
+  colText: {fileID: 493380231}
+  boardText: {fileID: 748424467}
+--- !u!1 &785957518
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 785957521}
+  - component: {fileID: 785957520}
+  - component: {fileID: 785957519}
+  m_Layer: 0
+  m_Name: EventSystem
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &785957519
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 785957518}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_SendPointerHoverToParent: 1
+  m_HorizontalAxis: Horizontal
+  m_VerticalAxis: Vertical
+  m_SubmitButton: Submit
+  m_CancelButton: Cancel
+  m_InputActionsPerSecond: 10
+  m_RepeatDelay: 0.5
+  m_ForceModuleActive: 0
+--- !u!114 &785957520
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 785957518}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_FirstSelected: {fileID: 0}
+  m_sendNavigationEvents: 1
+  m_DragThreshold: 10
+--- !u!4 &785957521
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 785957518}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &852683204
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 852683208}
+  - component: {fileID: 852683207}
+  - component: {fileID: 852683206}
+  - component: {fileID: 852683205}
+  m_Layer: 5
+  m_Name: Canvas
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &852683205
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 852683204}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_IgnoreReversedGraphics: 1
+  m_BlockingObjects: 0
+  m_BlockingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+--- !u!114 &852683206
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 852683204}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_UiScaleMode: 0
+  m_ReferencePixelsPerUnit: 100
+  m_ScaleFactor: 1
+  m_ReferenceResolution: {x: 800, y: 600}
+  m_ScreenMatchMode: 0
+  m_MatchWidthOrHeight: 0
+  m_PhysicalUnit: 3
+  m_FallbackScreenDPI: 96
+  m_DefaultSpriteDPI: 96
+  m_DynamicPixelsPerUnit: 1
+  m_PresetInfoIsWorld: 0
+--- !u!223 &852683207
+Canvas:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 852683204}
+  m_Enabled: 1
+  serializedVersion: 3
+  m_RenderMode: 0
+  m_Camera: {fileID: 0}
+  m_PlaneDistance: 100
+  m_PixelPerfect: 0
+  m_ReceivesEvents: 1
+  m_OverrideSorting: 0
+  m_OverridePixelPerfect: 0
+  m_SortingBucketNormalizedSize: 0
+  m_VertexColorAlwaysGammaSpace: 0
+  m_AdditionalShaderChannelsFlag: 25
+  m_UpdateRectTransformForStandalone: 0
+  m_SortingLayerID: 0
+  m_SortingOrder: 0
+  m_TargetDisplay: 0
+--- !u!224 &852683208
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 852683204}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 0, y: 0, z: 0}
+  m_ConstrainProportionsScale: 0
+  m_Children:
+  - {fileID: 492692668}
+  - {fileID: 748424466}
+  - {fileID: 1925068170}
+  - {fileID: 493380230}
+  - {fileID: 1854815565}
+  m_Father: {fileID: 0}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 0, y: 0}
+  m_AnchorMax: {x: 0, y: 0}
+  m_AnchoredPosition: {x: 0, y: 0}
+  m_SizeDelta: {x: 0, y: 0}
+  m_Pivot: {x: 0, y: 0}
+--- !u!1 &944845861
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 944845862}
+  - component: {fileID: 944845864}
+  - component: {fileID: 944845863}
+  m_Layer: 5
+  m_Name: Text
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &944845862
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 944845861}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 1744271206}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 0, y: 0}
+  m_AnchorMax: {x: 1, y: 1}
+  m_AnchoredPosition: {x: 0, y: 0}
+  m_SizeDelta: {x: 0, y: 0}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &944845863
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 944845861}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Material: {fileID: 0}
+  m_Color: {r: 1, g: 1, b: 1, a: 1}
+  m_RaycastTarget: 1
+  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+  m_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_text: "\u200B"
+  m_isRightToLeft: 0
+  m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
+  m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
+  m_fontSharedMaterials: []
+  m_fontMaterial: {fileID: 0}
+  m_fontMaterials: []
+  m_fontColor32:
+    serializedVersion: 2
+    rgba: 4281479730
+  m_fontColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
+  m_enableVertexGradient: 0
+  m_colorMode: 3
+  m_fontColorGradient:
+    topLeft: {r: 1, g: 1, b: 1, a: 1}
+    topRight: {r: 1, g: 1, b: 1, a: 1}
+    bottomLeft: {r: 1, g: 1, b: 1, a: 1}
+    bottomRight: {r: 1, g: 1, b: 1, a: 1}
+  m_fontColorGradientPreset: {fileID: 0}
+  m_spriteAsset: {fileID: 0}
+  m_tintAllSprites: 0
+  m_StyleSheet: {fileID: 0}
+  m_TextStyleHashCode: -1183493901
+  m_overrideHtmlColors: 0
+  m_faceColor:
+    serializedVersion: 2
+    rgba: 4294967295
+  m_fontSize: 55
+  m_fontSizeBase: 55
+  m_fontWeight: 400
+  m_enableAutoSizing: 0
+  m_fontSizeMin: 18
+  m_fontSizeMax: 72
+  m_fontStyle: 0
+  m_HorizontalAlignment: 2
+  m_VerticalAlignment: 512
+  m_textAlignment: 65535
+  m_characterSpacing: 0
+  m_wordSpacing: 0
+  m_lineSpacing: 0
+  m_lineSpacingMax: 0
+  m_paragraphSpacing: 0
+  m_charWidthMaxAdj: 0
+  m_enableWordWrapping: 0
+  m_wordWrappingRatios: 0.4
+  m_overflowMode: 0
+  m_linkedTextComponent: {fileID: 0}
+  parentLinkedComponent: {fileID: 0}
+  m_enableKerning: 1
+  m_enableExtraPadding: 1
+  checkPaddingRequired: 0
+  m_isRichText: 1
+  m_parseCtrlCharacters: 1
+  m_isOrthographic: 1
+  m_isCullingEnabled: 0
+  m_horizontalMapping: 0
+  m_verticalMapping: 0
+  m_uvLineOffset: 0
+  m_geometrySortingOrder: 0
+  m_IsTextObjectScaleStatic: 0
+  m_VertexBufferAutoSizeReduction: 0
+  m_useMaxVisibleDescender: 1
+  m_pageToDisplay: 1
+  m_margin: {x: 0, y: 0, z: 0, w: 0}
+  m_isUsingLegacyAnimationComponent: 0
+  m_isVolumetricText: 0
+  m_hasFontAssetChanged: 0
+  m_baseMaterial: {fileID: 0}
+  m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
+--- !u!222 &944845864
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 944845861}
+  m_CullTransparentMesh: 1
+--- !u!1 &1144231521
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1144231522}
+  - component: {fileID: 1144231524}
+  - component: {fileID: 1144231523}
+  m_Layer: 5
+  m_Name: Text (TMP)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &1144231522
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1144231521}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 1854815565}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 0, y: 0}
+  m_AnchorMax: {x: 1, y: 1}
+  m_AnchoredPosition: {x: 0, y: 0}
+  m_SizeDelta: {x: 0, y: 0}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &1144231523
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1144231521}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Material: {fileID: 0}
+  m_Color: {r: 1, g: 1, b: 1, a: 1}
+  m_RaycastTarget: 1
+  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+  m_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_text: Go
+  m_isRightToLeft: 0
+  m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
+  m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
+  m_fontSharedMaterials: []
+  m_fontMaterial: {fileID: 0}
+  m_fontMaterials: []
+  m_fontColor32:
+    serializedVersion: 2
+    rgba: 4281479730
+  m_fontColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
+  m_enableVertexGradient: 0
+  m_colorMode: 3
+  m_fontColorGradient:
+    topLeft: {r: 1, g: 1, b: 1, a: 1}
+    topRight: {r: 1, g: 1, b: 1, a: 1}
+    bottomLeft: {r: 1, g: 1, b: 1, a: 1}
+    bottomRight: {r: 1, g: 1, b: 1, a: 1}
+  m_fontColorGradientPreset: {fileID: 0}
+  m_spriteAsset: {fileID: 0}
+  m_tintAllSprites: 0
+  m_StyleSheet: {fileID: 0}
+  m_TextStyleHashCode: -1183493901
+  m_overrideHtmlColors: 0
+  m_faceColor:
+    serializedVersion: 2
+    rgba: 4294967295
+  m_fontSize: 50
+  m_fontSizeBase: 50
+  m_fontWeight: 400
+  m_enableAutoSizing: 0
+  m_fontSizeMin: 18
+  m_fontSizeMax: 72
+  m_fontStyle: 0
+  m_HorizontalAlignment: 2
+  m_VerticalAlignment: 512
+  m_textAlignment: 65535
+  m_characterSpacing: 0
+  m_wordSpacing: 0
+  m_lineSpacing: 0
+  m_lineSpacingMax: 0
+  m_paragraphSpacing: 0
+  m_charWidthMaxAdj: 0
+  m_enableWordWrapping: 1
+  m_wordWrappingRatios: 0.4
+  m_overflowMode: 0
+  m_linkedTextComponent: {fileID: 0}
+  parentLinkedComponent: {fileID: 0}
+  m_enableKerning: 1
+  m_enableExtraPadding: 0
+  checkPaddingRequired: 0
+  m_isRichText: 1
+  m_parseCtrlCharacters: 1
+  m_isOrthographic: 1
+  m_isCullingEnabled: 0
+  m_horizontalMapping: 0
+  m_verticalMapping: 0
+  m_uvLineOffset: 0
+  m_geometrySortingOrder: 0
+  m_IsTextObjectScaleStatic: 0
+  m_VertexBufferAutoSizeReduction: 0
+  m_useMaxVisibleDescender: 1
+  m_pageToDisplay: 1
+  m_margin: {x: 0, y: 0, z: 0, w: 0}
+  m_isUsingLegacyAnimationComponent: 0
+  m_isVolumetricText: 0
+  m_hasFontAssetChanged: 0
+  m_baseMaterial: {fileID: 0}
+  m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
+--- !u!222 &1144231524
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1144231521}
+  m_CullTransparentMesh: 1
+--- !u!1 &1339168103
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1339168104}
+  - component: {fileID: 1339168106}
+  - component: {fileID: 1339168105}
+  m_Layer: 5
+  m_Name: Text
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &1339168104
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1339168103}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 1976308225}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 0, y: 0}
+  m_AnchorMax: {x: 1, y: 1}
+  m_AnchoredPosition: {x: 0, y: 0}
+  m_SizeDelta: {x: 0, y: 0}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &1339168105
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1339168103}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Material: {fileID: 0}
+  m_Color: {r: 1, g: 1, b: 1, a: 1}
+  m_RaycastTarget: 1
+  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+  m_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_text: "\u200B"
+  m_isRightToLeft: 0
+  m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
+  m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
+  m_fontSharedMaterials: []
+  m_fontMaterial: {fileID: 0}
+  m_fontMaterials: []
+  m_fontColor32:
+    serializedVersion: 2
+    rgba: 4281479730
+  m_fontColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
+  m_enableVertexGradient: 0
+  m_colorMode: 3
+  m_fontColorGradient:
+    topLeft: {r: 1, g: 1, b: 1, a: 1}
+    topRight: {r: 1, g: 1, b: 1, a: 1}
+    bottomLeft: {r: 1, g: 1, b: 1, a: 1}
+    bottomRight: {r: 1, g: 1, b: 1, a: 1}
+  m_fontColorGradientPreset: {fileID: 0}
+  m_spriteAsset: {fileID: 0}
+  m_tintAllSprites: 0
+  m_StyleSheet: {fileID: 0}
+  m_TextStyleHashCode: -1183493901
+  m_overrideHtmlColors: 0
+  m_faceColor:
+    serializedVersion: 2
+    rgba: 4294967295
+  m_fontSize: 55
+  m_fontSizeBase: 55
+  m_fontWeight: 400
+  m_enableAutoSizing: 0
+  m_fontSizeMin: 18
+  m_fontSizeMax: 72
+  m_fontStyle: 0
+  m_HorizontalAlignment: 2
+  m_VerticalAlignment: 512
+  m_textAlignment: 65535
+  m_characterSpacing: 0
+  m_wordSpacing: 0
+  m_lineSpacing: 0
+  m_lineSpacingMax: 0
+  m_paragraphSpacing: 0
+  m_charWidthMaxAdj: 0
+  m_enableWordWrapping: 0
+  m_wordWrappingRatios: 0.4
+  m_overflowMode: 0
+  m_linkedTextComponent: {fileID: 0}
+  parentLinkedComponent: {fileID: 0}
+  m_enableKerning: 1
+  m_enableExtraPadding: 1
+  checkPaddingRequired: 0
+  m_isRichText: 1
+  m_parseCtrlCharacters: 1
+  m_isOrthographic: 1
+  m_isCullingEnabled: 0
+  m_horizontalMapping: 0
+  m_verticalMapping: 0
+  m_uvLineOffset: 0
+  m_geometrySortingOrder: 0
+  m_IsTextObjectScaleStatic: 0
+  m_VertexBufferAutoSizeReduction: 0
+  m_useMaxVisibleDescender: 1
+  m_pageToDisplay: 1
+  m_margin: {x: 0, y: 0, z: 0, w: 0}
+  m_isUsingLegacyAnimationComponent: 0
+  m_isVolumetricText: 0
+  m_hasFontAssetChanged: 0
+  m_baseMaterial: {fileID: 0}
+  m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
+--- !u!222 &1339168106
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1339168103}
+  m_CullTransparentMesh: 1
+--- !u!1 &1744271205
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1744271206}
+  - component: {fileID: 1744271207}
+  m_Layer: 5
+  m_Name: Text Area
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &1744271206
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1744271205}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children:
+  - {fileID: 944845862}
+  m_Father: {fileID: 1925068170}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 0, y: 0}
+  m_AnchorMax: {x: 1, y: 1}
+  m_AnchoredPosition: {x: 0, y: -0.5}
+  m_SizeDelta: {x: -20, y: -13}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &1744271207
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1744271205}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3312d7739989d2b4e91e6319e9a96d76, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Padding: {x: -8, y: -5, z: -8, w: -5}
+  m_Softness: {x: 0, y: 0}
+--- !u!1 &1854815564
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1854815565}
+  - component: {fileID: 1854815568}
+  - component: {fileID: 1854815567}
+  - component: {fileID: 1854815566}
+  m_Layer: 5
+  m_Name: Button
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &1854815565
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1854815564}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children:
+  - {fileID: 1144231522}
+  m_Father: {fileID: 852683208}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 1, y: 0}
+  m_AnchorMax: {x: 1, y: 0}
+  m_AnchoredPosition: {x: -100, y: 100}
+  m_SizeDelta: {x: 250, y: 250}
+  m_Pivot: {x: 1, y: 0}
+--- !u!114 &1854815566
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1854815564}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Navigation:
+    m_Mode: 3
+    m_WrapAround: 0
+    m_SelectOnUp: {fileID: 0}
+    m_SelectOnDown: {fileID: 0}
+    m_SelectOnLeft: {fileID: 0}
+    m_SelectOnRight: {fileID: 0}
+  m_Transition: 1
+  m_Colors:
+    m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
+    m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
+    m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
+    m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
+    m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
+    m_ColorMultiplier: 1
+    m_FadeDuration: 0.1
+  m_SpriteState:
+    m_HighlightedSprite: {fileID: 0}
+    m_PressedSprite: {fileID: 0}
+    m_SelectedSprite: {fileID: 0}
+    m_DisabledSprite: {fileID: 0}
+  m_AnimationTriggers:
+    m_NormalTrigger: Normal
+    m_HighlightedTrigger: Highlighted
+    m_PressedTrigger: Pressed
+    m_SelectedTrigger: Selected
+    m_DisabledTrigger: Disabled
+  m_Interactable: 1
+  m_TargetGraphic: {fileID: 1854815567}
+  m_OnClick:
+    m_PersistentCalls:
+      m_Calls:
+      - m_Target: {fileID: 770473332}
+        m_TargetAssemblyTypeName: TestGameManager, Assembly-CSharp
+        m_MethodName: OnClickGoButton
+        m_Mode: 1
+        m_Arguments:
+          m_ObjectArgument: {fileID: 0}
+          m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
+          m_IntArgument: 0
+          m_FloatArgument: 0
+          m_StringArgument: 
+          m_BoolArgument: 0
+        m_CallState: 2
+--- !u!114 &1854815567
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1854815564}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Material: {fileID: 0}
+  m_Color: {r: 1, g: 1, b: 1, a: 1}
+  m_RaycastTarget: 1
+  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+  m_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}
+  m_Type: 1
+  m_PreserveAspect: 0
+  m_FillCenter: 1
+  m_FillMethod: 4
+  m_FillAmount: 1
+  m_FillClockwise: 1
+  m_FillOrigin: 0
+  m_UseSpriteMesh: 0
+  m_PixelsPerUnitMultiplier: 1
+--- !u!222 &1854815568
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1854815564}
+  m_CullTransparentMesh: 1
+--- !u!1 &1925068169
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1925068170}
+  - component: {fileID: 1925068173}
+  - component: {fileID: 1925068172}
+  - component: {fileID: 1925068171}
+  m_Layer: 5
+  m_Name: row
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &1925068170
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1925068169}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children:
+  - {fileID: 1744271206}
+  m_Father: {fileID: 852683208}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 0.5, y: 0}
+  m_AnchorMax: {x: 0.5, y: 0}
+  m_AnchoredPosition: {x: 0, y: 245}
+  m_SizeDelta: {x: 300, y: 200}
+  m_Pivot: {x: 0.5, y: 0}
+--- !u!114 &1925068171
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1925068169}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 2da0c512f12947e489f739169773d7ca, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Navigation:
+    m_Mode: 3
+    m_WrapAround: 0
+    m_SelectOnUp: {fileID: 0}
+    m_SelectOnDown: {fileID: 0}
+    m_SelectOnLeft: {fileID: 0}
+    m_SelectOnRight: {fileID: 0}
+  m_Transition: 1
+  m_Colors:
+    m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
+    m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
+    m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
+    m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
+    m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
+    m_ColorMultiplier: 1
+    m_FadeDuration: 0.1
+  m_SpriteState:
+    m_HighlightedSprite: {fileID: 0}
+    m_PressedSprite: {fileID: 0}
+    m_SelectedSprite: {fileID: 0}
+    m_DisabledSprite: {fileID: 0}
+  m_AnimationTriggers:
+    m_NormalTrigger: Normal
+    m_HighlightedTrigger: Highlighted
+    m_PressedTrigger: Pressed
+    m_SelectedTrigger: Selected
+    m_DisabledTrigger: Disabled
+  m_Interactable: 1
+  m_TargetGraphic: {fileID: 1925068172}
+  m_TextViewport: {fileID: 1744271206}
+  m_TextComponent: {fileID: 944845863}
+  m_Placeholder: {fileID: 0}
+  m_VerticalScrollbar: {fileID: 0}
+  m_VerticalScrollbarEventHandler: {fileID: 0}
+  m_LayoutGroup: {fileID: 0}
+  m_ScrollSensitivity: 1
+  m_ContentType: 0
+  m_InputType: 0
+  m_AsteriskChar: 42
+  m_KeyboardType: 0
+  m_LineType: 0
+  m_HideMobileInput: 0
+  m_HideSoftKeyboard: 0
+  m_CharacterValidation: 0
+  m_RegexValue: 
+  m_GlobalPointSize: 14
+  m_CharacterLimit: 0
+  m_OnEndEdit:
+    m_PersistentCalls:
+      m_Calls: []
+  m_OnSubmit:
+    m_PersistentCalls:
+      m_Calls: []
+  m_OnSelect:
+    m_PersistentCalls:
+      m_Calls: []
+  m_OnDeselect:
+    m_PersistentCalls:
+      m_Calls: []
+  m_OnTextSelection:
+    m_PersistentCalls:
+      m_Calls: []
+  m_OnEndTextSelection:
+    m_PersistentCalls:
+      m_Calls: []
+  m_OnValueChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_OnTouchScreenKeyboardStatusChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_CaretColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
+  m_CustomCaretColor: 0
+  m_SelectionColor: {r: 0.65882355, g: 0.80784315, b: 1, a: 0.7529412}
+  m_Text: 
+  m_CaretBlinkRate: 0.85
+  m_CaretWidth: 1
+  m_ReadOnly: 0
+  m_RichText: 1
+  m_GlobalFontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
+  m_OnFocusSelectAll: 1
+  m_ResetOnDeActivation: 1
+  m_RestoreOriginalTextOnEscape: 1
+  m_isRichTextEditingAllowed: 0
+  m_LineLimit: 0
+  m_InputValidator: {fileID: 0}
+--- !u!114 &1925068172
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1925068169}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Material: {fileID: 0}
+  m_Color: {r: 1, g: 1, b: 1, a: 1}
+  m_RaycastTarget: 1
+  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+  m_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_Sprite: {fileID: 10911, guid: 0000000000000000f000000000000000, type: 0}
+  m_Type: 1
+  m_PreserveAspect: 0
+  m_FillCenter: 1
+  m_FillMethod: 4
+  m_FillAmount: 1
+  m_FillClockwise: 1
+  m_FillOrigin: 0
+  m_UseSpriteMesh: 0
+  m_PixelsPerUnitMultiplier: 1
+--- !u!222 &1925068173
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1925068169}
+  m_CullTransparentMesh: 1
+--- !u!1 &1976308224
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1976308225}
+  - component: {fileID: 1976308226}
+  m_Layer: 5
+  m_Name: Text Area
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &1976308225
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1976308224}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children:
+  - {fileID: 1339168104}
+  m_Father: {fileID: 493380230}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 0, y: 0}
+  m_AnchorMax: {x: 1, y: 1}
+  m_AnchoredPosition: {x: 0, y: -0.5}
+  m_SizeDelta: {x: -20, y: -13}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &1976308226
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1976308224}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3312d7739989d2b4e91e6319e9a96d76, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Padding: {x: -8, y: -5, z: -8, w: -5}
+  m_Softness: {x: 0, y: 0}
+--- !u!1660057539 &9223372036854775807
+SceneRoots:
+  m_ObjectHideFlags: 0
+  m_Roots:
+  - {fileID: 519420032}
+  - {fileID: 852683208}
+  - {fileID: 785957521}
+  - {fileID: 770473331}
diff --git a/Assets/Scenes/AITestScene.unity.meta b/Assets/Scenes/AITestScene.unity.meta
new file mode 100644
index 0000000..f8991b5
--- /dev/null
+++ b/Assets/Scenes/AITestScene.unity.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: e200b684d5479a643aa06e6361c430c9
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Script/AI.meta b/Assets/Script/AI.meta
new file mode 100644
index 0000000..30d134e
--- /dev/null
+++ b/Assets/Script/AI.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2b0c3d8290ac86441b7db8c07a6d21a6
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Script/AI/MiniMaxAIController.cs b/Assets/Script/AI/MiniMaxAIController.cs
new file mode 100644
index 0000000..2e1424c
--- /dev/null
+++ b/Assets/Script/AI/MiniMaxAIController.cs
@@ -0,0 +1,331 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+
+public static class MiniMaxAIController
+{
+    // To-Do List
+    // 랜덤 실수 (랜덤하게 덜 좋은 수 리턴)
+    // 탐색 시간 개선
+    // 방어적인 플레이라 AI 자신이 5연승할 자리에 안 둠 -> 해결
+    
+    private const int SEARCH_DEPTH = 3; // 탐색 깊이 제한 (3 = 빠른 응답, 4 = 좀 더 강한 AI 그러나 느린)
+    private const int WIN_COUNT = 5;
+
+    private static int _playerLevel; // 급수 설정
+
+    // 급수 설정 -> 실수 넣을 때 계산
+    public static void SetLevel(int level)
+    {
+        _playerLevel = level;
+    }
+    
+    public static (int row, int col)? GetBestMove(Enums.PlayerType[,] board)
+    {
+        float bestScore = -1000;
+        (int row, int col)? bestMove = null;
+        List<(int row, int col)> validMoves = GetValidMoves(board);
+        
+        if (validMoves.Count == 0) // 놓을 수 있는 칸 없음 == 칸 꽉 참
+        {
+            Debug.Log("칸이 없습니다...");
+            return null;
+        }
+        
+        // To-Do : bestMove는 null로 유지하고 맨 마지막 리턴 문에서 삼항 연산자로 날리기(Second용)
+        bestMove = validMoves[0]; // 기본 값, null 반환 방지. 
+        
+        // 5연승 가능한 자리를 먼저 찾아서 우선적으로 설정
+        List<(int row, int col)> fiveInARowMoves = GetFiveInARowCandidateMoves(board);
+        if (fiveInARowMoves.Count > 0)
+        {
+            bestMove = fiveInARowMoves[0]; 
+            Debug.Log($"5 wins move {bestMove.Value.row}, {bestMove.Value.col}");
+            return bestMove;
+        }
+        
+        foreach (var (row, col) in validMoves)
+        {
+            board[row, col] = Enums.PlayerType.PlayerB;
+            float score = DoMinimax(board, SEARCH_DEPTH, false, -1000, 1000, row, col);
+            board[row, col] = Enums.PlayerType.None;
+
+            if (score > bestScore)
+            {
+                bestScore = score;
+                bestMove = (row, col); // 초반에는 bestMove가 잘 안바뀌어서 (돌 한 2~3개 둬야 여러 개 나옴) 2라운드까지는 그대로 출력 필요
+                // To-Do : 실수용으로 secondBestMove 추가
+            }
+        }
+
+        return bestMove;
+    }
+
+    private static float DoMinimax(Enums.PlayerType[,] board, int depth, bool isMaximizing, float alpha, float beta,
+        int recentRow, int recentCol)
+    {
+        if (CheckGameWin(Enums.PlayerType.PlayerA, board, recentRow, recentCol)) return -100 + depth;
+        if (CheckGameWin(Enums.PlayerType.PlayerB, board, recentRow, recentCol)) return 100 - depth;
+        if (depth == 0) return EvaluateBoard(board);
+
+        float bestScore = isMaximizing ? float.MinValue : float.MaxValue;
+        List<(int row, int col)> validMoves = GetValidMoves(board); // 현재 놓을 수 있는 자리 리스트
+
+        foreach (var (row, col) in validMoves)
+        {
+            board[row, col] = isMaximizing ? Enums.PlayerType.PlayerB : Enums.PlayerType.PlayerA;
+            float score = DoMinimax(board, depth - 1, !isMaximizing, alpha, beta, row, col);
+            board[row, col] = Enums.PlayerType.None;
+
+            if (isMaximizing)
+            {
+                bestScore = Math.Max(bestScore, score);
+                alpha = Math.Max(alpha, bestScore);
+            }
+            else
+            {
+                bestScore = Math.Min(bestScore, score);
+                beta = Math.Min(beta, bestScore);
+            }
+            
+            if (beta <= alpha) break;
+        }
+        return bestScore;
+    }
+
+    /// <summary>
+    /// 이동 가능 + 주변에 돌 있는 위치 탐색
+    /// </summary>
+    /// <returns></returns>
+    private static List<(int row, int col)> GetValidMoves(Enums.PlayerType[,] board)
+    {
+        List<(int, int)> validMoves = new List<(int, int)>();
+        int size = board.GetLength(0);
+
+        for (int row = 0; row < size; row++)
+        {
+            for (int col = 0; col < size; col++)
+            {
+                if (board[row, col] == Enums.PlayerType.None && HasNearbyStones(board, row, col))
+                {
+                    validMoves.Add((row, col));
+                }
+            }
+        }
+        return validMoves;
+    }
+
+    /// <summary>
+    /// 주변 8칸에 놓인 돌이 있는 지 확인
+    /// </summary>
+    /// <param name="row">현재 탐색하는 위치의 row값</param>
+    /// <param name="col">현재 탐색하는 위치의 col값</param>
+    /// <returns>true: 돌 있음, fasle: 돌 없음</returns>
+    private static bool HasNearbyStones(Enums.PlayerType[,] board, int row, int col)
+    {
+        // 9칸 기준으로 현재 위치를 중앙으로 상정한 후 나머지 8방향
+        int[] dr = { -1, -1, -1, 0, 0, 1, 1, 1 };
+        int[] dc = { -1, 0, 1, -1, 1, -1, 0, 1 };
+        int size = board.GetLength(0);
+
+        for(int i = 0; i < dr.Length; i++)
+        {
+            int nr = row + dr[i], nc = col + dc[i];
+            if (nr >= 0 && nr < size && nc >= 0 && nc < size && board[nr, nc] != Enums.PlayerType.None)
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /// <summary>
+    /// 최근에 둔 돌 위치 기반으로 게임 승리를 판별하는 함수.
+    /// </summary>
+    /// <param name="player"> 어떤 플레이어 기준으로 승리 판별 </param>
+    /// <param name="board"> 게임 보드 </param>
+    /// <param name="row"> 최근에 둔 돌의 위치 중 row 값 </param>
+    /// <param name="col">최근에 둔 돌의 위치 중 col 값 </param>
+    /// <returns> true: 승리, false: 승리 아님 </returns>
+    public static bool CheckGameWin(Enums.PlayerType player, Enums.PlayerType[,] board, int row, int col)
+    {
+        int size = board.GetLength(0);
+        
+        int[][] directions = new int[][]
+        {
+            new int[] {1, 0}, // 수직
+            new int[] {0, 1}, // 수평
+            new int[] {1, 1}, // 대각선 ↘ ↖
+            new int[] {1, -1} // 대각선 ↙ ↗
+        };
+        
+        // 각 방향별로 판단
+        foreach (var dir in directions)
+        {
+            // 자기 자신 포함해서 카운트 시작
+            int stoneCount = 1;
+
+            // 정방향 탐색
+            int r = row + dir[0], c = col + dir[1];
+            while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == player) // 0~15내에서 플레이어 타입 판단
+            {
+                // 동일 플레이어 타입인 경우
+                stoneCount++;
+                r += dir[0]; // row값 옮기기 
+                c += dir[1]; // col값 옮기기
+            }
+            
+            // 역방향 탐색 전에 정방향에서 Win했는지 확인 (이미 Win한 상태에서 역방향 검사 방지)
+            if (stoneCount >= WIN_COUNT) return true; 
+
+            // 역방향 탐색
+            r = row - dir[0]; 
+            c = col - dir[1];
+            while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == player)
+            {
+                stoneCount++;
+                r -= dir[0];
+                c -= dir[1];
+            }
+
+            if (stoneCount >= WIN_COUNT) return true; 
+        }
+        
+        return false;
+    }
+    
+    private static List<(int row, int col)> GetFiveInARowCandidateMoves(Enums.PlayerType[,] board)
+    {
+        List<(int row, int col)> fiveInARowMoves = new List<(int, int)>();
+        int size = board.GetLength(0);
+
+        // 방향 설정: 가로, 세로, 대각선
+        int[][] directions = new int[][]
+        {
+            new int[] {1, 0}, // 가로
+            new int[] {0, 1}, // 세로
+            new int[] {1, 1}, // 대각선 (\)
+            new int[] {1, -1} // 대각선 (/)
+        };
+
+        // 각 칸에 대해 5연승이 될 수 있는 위치 찾기
+        for (int row = 0; row < size; row++)
+        {
+            for (int col = 0; col < size; col++)
+            {
+                if (board[row, col] != Enums.PlayerType.None)
+                    continue; // 이미 돌이 놓인 곳
+
+                foreach (var dir in directions)
+                {
+                    int count = 0;
+                    int openEnds = 0;
+                    // 왼쪽 방향부터 확인
+                    int r = row + dir[0], c = col + dir[1];
+                    while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.PlayerB)
+                    {
+                        count++;
+                        r += dir[0];
+                        c += dir[1];
+                    }
+                    if (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.None)
+                        openEnds++;
+
+                    // 오른쪽 방향 확인
+                    r = row - dir[0];
+                    c = col - dir[1];
+                    while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.PlayerB)
+                    {
+                        count++;
+                        r -= dir[0];
+                        c -= dir[1];
+                    }
+                    if (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.None)
+                        openEnds++;
+
+                    // 5연승이 가능하면 그 자리를 리스트에 추가
+                    if (count == 4 && openEnds > 0)
+                    {
+                        fiveInARowMoves.Add((row, col));
+                    }
+                }
+            }
+        }
+
+        return fiveInARowMoves;
+    }
+    
+    /// <summary>
+    /// 현재 보드의 상태 평가
+    /// </summary>
+    /// <returns>평가 후 점수</returns>
+    private static float EvaluateBoard(Enums.PlayerType[,] board)
+    {
+        float score = 0;
+        int size = board.GetLength(0);
+        int[][] directions = new int[][] 
+        {
+            new int[] {1, 0},  // 수직
+            new int[] {0, 1},  // 수평
+            new int[] {1, 1},  // 대각선 ↘
+            new int[] {1, -1}  // 대각선 ↙
+        };
+
+        for (int row = 0; row < size; row++)
+        {
+            for (int col = 0; col < size; col++)
+            {
+                if (board[row, col] == Enums.PlayerType.None) continue;
+
+                Enums.PlayerType player = board[row, col];
+                int playerScore = (player == Enums.PlayerType.PlayerB) ? 1 : -1; // AI는 양수, 상대는 음수
+
+                foreach (var dir in directions)
+                {
+                    int count = 1;
+                    int openEnds = 0;
+                    int r = row + dir[0], c = col + dir[1];
+
+                    // 같은 돌 개수 세기
+                    while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == player)
+                    {
+                        count++;
+                        r += dir[0];
+                        c += dir[1];
+                    }
+
+                    // 열린 방향 확인
+                    if (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.None)
+                        openEnds++;
+
+                    // 반대 방향 검사
+                    r = row - dir[0]; 
+                    c = col - dir[1];
+
+                    while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == player)
+                    {
+                        count++;
+                        r -= dir[0];
+                        c -= dir[1];
+                    }
+
+                    if (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.None)
+                        openEnds++;
+
+                    // 점수 계산
+                    if (count >= 5) 
+                        score += playerScore * 1000000; // 실제로 호출되는 일이 없음 왜지??
+                    else if (count == 4)
+                        score += playerScore * (openEnds == 2 ? 10000 : 1000);
+                    else if (count == 3)
+                        score += playerScore * (openEnds == 2 ? 1000 : 100);
+                    else if (count == 2)
+                        score += playerScore * (openEnds == 2 ? 100 : 10);
+                }
+            }
+        }
+
+        return score;
+    }
+}
diff --git a/Assets/Script/AI/MiniMaxAIController.cs.meta b/Assets/Script/AI/MiniMaxAIController.cs.meta
new file mode 100644
index 0000000..1b56bd4
--- /dev/null
+++ b/Assets/Script/AI/MiniMaxAIController.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0cabba9cae3792747bd277ecdc12196d
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Script/AI/TestGameManager.cs b/Assets/Script/AI/TestGameManager.cs
new file mode 100644
index 0000000..050d5c1
--- /dev/null
+++ b/Assets/Script/AI/TestGameManager.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using TMPro;
+using UnityEngine;
+using UnityEngine.Serialization;
+using UnityEngine.UI;
+
+public class TestGameManager : MonoBehaviour
+{
+    [SerializeField] private TMP_InputField rowText;
+    [SerializeField] private TMP_InputField colText;
+    [SerializeField] private TMP_Text boardText;
+
+    private Enums.PlayerType[,] _board;
+
+    private void Start()
+    {
+        _board = new Enums.PlayerType[15, 15];
+        ResultBoard();
+    }
+
+    public void OnClickGoButton()
+    {
+        int row = int.Parse(rowText.text);
+        int col = int.Parse(colText.text);
+
+        if (_board[row, col] != Enums.PlayerType.None)
+        {
+            Debug.Log("중복 위치");
+            return;
+        }
+        
+        _board[row, col] = Enums.PlayerType.PlayerA;
+        
+        // var isEnded = MiniMaxAIController.CheckGameWin(Enums.PlayerType.PlayerA, _board, row, col);
+        // Debug.Log("PlayerA is Win: " + isEnded);
+        
+        // 인공지능 호출
+        var result = MiniMaxAIController.GetBestMove(_board);
+        
+        if (result.HasValue)
+        {
+            Debug.Log($"AI's row: {result.Value.row} col: {result.Value.col}");
+            _board[result.Value.row, result.Value.col] = Enums.PlayerType.PlayerB;
+            
+            // isEnded = MiniMaxAIController.CheckGameWin(Enums.PlayerType.PlayerB, _board, result.Value.row, result.Value.col);
+            // Debug.Log("PlayerB is Win: " + isEnded);
+        }
+        
+        ResultBoard();
+    }
+    
+    private void ResultBoard()
+    {
+        boardText.text = "";
+        
+        // player 타입에 따라 입력받는 건 무조건 A로 해서 A, AI는 B로 나타내고 None은 _
+        for (int i = 0; i < 15; i++)
+        {
+            for (int j = 0; j < 15; j++)
+            {
+                if (_board[i, j] == Enums.PlayerType.PlayerA)
+                {
+                    boardText.text += 'A';
+                }
+                else if (_board[i, j] == Enums.PlayerType.PlayerB)
+                {
+                    boardText.text += 'B';
+                } 
+                else if (_board[i, j] == Enums.PlayerType.None)
+                {
+                    boardText.text += '_';
+                }
+
+                boardText.text += ' ';
+            }
+
+            boardText.text += '\n';
+        }
+    }
+}
diff --git a/Assets/Script/AI/TestGameManager.cs.meta b/Assets/Script/AI/TestGameManager.cs.meta
new file mode 100644
index 0000000..50ae6fe
--- /dev/null
+++ b/Assets/Script/AI/TestGameManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: facc79abb6042e846bb0a2b099b58e9c
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

From 8e54a46b3d54051d2d9df515a7f28310e0a39ced Mon Sep 17 00:00:00 2001
From: Sehyeon <ksh1837@naver.com>
Date: Fri, 14 Mar 2025 15:25:59 +0900
Subject: [PATCH 2/7] =?UTF-8?q?DO-4=20[Fix]=20Fix=20bestMove=20null=20?=
 =?UTF-8?q?=EA=B0=92=20=EB=A6=AC=ED=84=B4=20=EC=A0=9C=EA=B1=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 Assets/Script/AI/MiniMaxAIController.cs | 90 +++++++++----------------
 1 file changed, 30 insertions(+), 60 deletions(-)

diff --git a/Assets/Script/AI/MiniMaxAIController.cs b/Assets/Script/AI/MiniMaxAIController.cs
index 2e1424c..d89d9ce 100644
--- a/Assets/Script/AI/MiniMaxAIController.cs
+++ b/Assets/Script/AI/MiniMaxAIController.cs
@@ -8,10 +8,19 @@ public static class MiniMaxAIController
     // To-Do List
     // 랜덤 실수 (랜덤하게 덜 좋은 수 리턴)
     // 탐색 시간 개선
-    // 방어적인 플레이라 AI 자신이 5연승할 자리에 안 둠 -> 해결
+    // 코드 중복 제거
+    // AI 난이도 개선
     
     private const int SEARCH_DEPTH = 3; // 탐색 깊이 제한 (3 = 빠른 응답, 4 = 좀 더 강한 AI 그러나 느린)
     private const int WIN_COUNT = 5;
+    
+    private static int[][] _directions = new int[][]
+    {
+        new int[] {1, 0}, // 수직
+        new int[] {0, 1}, // 수평
+        new int[] {1, 1}, // 대각선 ↘ ↖
+        new int[] {1, -1} // 대각선 ↙ ↗
+    };
 
     private static int _playerLevel; // 급수 설정
 
@@ -21,27 +30,25 @@ public static class MiniMaxAIController
         _playerLevel = level;
     }
     
+    // return 값이 null 일 경우 == 보드에 칸 꽉 참
     public static (int row, int col)? GetBestMove(Enums.PlayerType[,] board)
     {
-        float bestScore = -1000;
+        float bestScore = float.MinValue;
         (int row, int col)? bestMove = null;
+        (int row, int col)? secondBestMove = null;
         List<(int row, int col)> validMoves = GetValidMoves(board);
         
-        if (validMoves.Count == 0) // 놓을 수 있는 칸 없음 == 칸 꽉 참
+        if (validMoves.Count == 0)
         {
             Debug.Log("칸이 없습니다...");
             return null;
         }
         
-        // To-Do : bestMove는 null로 유지하고 맨 마지막 리턴 문에서 삼항 연산자로 날리기(Second용)
-        bestMove = validMoves[0]; // 기본 값, null 반환 방지. 
-        
         // 5연승 가능한 자리를 먼저 찾아서 우선적으로 설정
         List<(int row, int col)> fiveInARowMoves = GetFiveInARowCandidateMoves(board);
         if (fiveInARowMoves.Count > 0)
         {
             bestMove = fiveInARowMoves[0]; 
-            Debug.Log($"5 wins move {bestMove.Value.row}, {bestMove.Value.col}");
             return bestMove;
         }
         
@@ -54,8 +61,13 @@ public static class MiniMaxAIController
             if (score > bestScore)
             {
                 bestScore = score;
-                bestMove = (row, col); // 초반에는 bestMove가 잘 안바뀌어서 (돌 한 2~3개 둬야 여러 개 나옴) 2라운드까지는 그대로 출력 필요
-                // To-Do : 실수용으로 secondBestMove 추가
+
+                if (bestMove != null)
+                {
+                    secondBestMove = bestMove;
+                }
+                
+                bestMove = (row, col);
             }
         }
 
@@ -94,10 +106,7 @@ public static class MiniMaxAIController
         return bestScore;
     }
 
-    /// <summary>
-    /// 이동 가능 + 주변에 돌 있는 위치 탐색
-    /// </summary>
-    /// <returns></returns>
+    // 이동 가능 + 주변에 돌 있는 위치 탐색
     private static List<(int row, int col)> GetValidMoves(Enums.PlayerType[,] board)
     {
         List<(int, int)> validMoves = new List<(int, int)>();
@@ -115,13 +124,7 @@ public static class MiniMaxAIController
         }
         return validMoves;
     }
-
-    /// <summary>
-    /// 주변 8칸에 놓인 돌이 있는 지 확인
-    /// </summary>
-    /// <param name="row">현재 탐색하는 위치의 row값</param>
-    /// <param name="col">현재 탐색하는 위치의 col값</param>
-    /// <returns>true: 돌 있음, fasle: 돌 없음</returns>
+    
     private static bool HasNearbyStones(Enums.PlayerType[,] board, int row, int col)
     {
         // 9칸 기준으로 현재 위치를 중앙으로 상정한 후 나머지 8방향
@@ -140,28 +143,13 @@ public static class MiniMaxAIController
         return false;
     }
 
-    /// <summary>
-    /// 최근에 둔 돌 위치 기반으로 게임 승리를 판별하는 함수.
-    /// </summary>
-    /// <param name="player"> 어떤 플레이어 기준으로 승리 판별 </param>
-    /// <param name="board"> 게임 보드 </param>
-    /// <param name="row"> 최근에 둔 돌의 위치 중 row 값 </param>
-    /// <param name="col">최근에 둔 돌의 위치 중 col 값 </param>
-    /// <returns> true: 승리, false: 승리 아님 </returns>
+    // 최근에 둔 돌 위치 기반으로 게임 승리를 판별하는 함수
     public static bool CheckGameWin(Enums.PlayerType player, Enums.PlayerType[,] board, int row, int col)
     {
         int size = board.GetLength(0);
         
-        int[][] directions = new int[][]
-        {
-            new int[] {1, 0}, // 수직
-            new int[] {0, 1}, // 수평
-            new int[] {1, 1}, // 대각선 ↘ ↖
-            new int[] {1, -1} // 대각선 ↙ ↗
-        };
-        
         // 각 방향별로 판단
-        foreach (var dir in directions)
+        foreach (var dir in _directions)
         {
             // 자기 자신 포함해서 카운트 시작
             int stoneCount = 1;
@@ -200,15 +188,6 @@ public static class MiniMaxAIController
         List<(int row, int col)> fiveInARowMoves = new List<(int, int)>();
         int size = board.GetLength(0);
 
-        // 방향 설정: 가로, 세로, 대각선
-        int[][] directions = new int[][]
-        {
-            new int[] {1, 0}, // 가로
-            new int[] {0, 1}, // 세로
-            new int[] {1, 1}, // 대각선 (\)
-            new int[] {1, -1} // 대각선 (/)
-        };
-
         // 각 칸에 대해 5연승이 될 수 있는 위치 찾기
         for (int row = 0; row < size; row++)
         {
@@ -217,11 +196,12 @@ public static class MiniMaxAIController
                 if (board[row, col] != Enums.PlayerType.None)
                     continue; // 이미 돌이 놓인 곳
 
-                foreach (var dir in directions)
+                foreach (var dir in _directions)
                 {
                     int count = 0;
                     int openEnds = 0;
-                    // 왼쪽 방향부터 확인
+                    
+                    // 왼쪽 방향 확인
                     int r = row + dir[0], c = col + dir[1];
                     while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.PlayerB)
                     {
@@ -256,21 +236,11 @@ public static class MiniMaxAIController
         return fiveInARowMoves;
     }
     
-    /// <summary>
-    /// 현재 보드의 상태 평가
-    /// </summary>
-    /// <returns>평가 후 점수</returns>
+    // 현재 보드의 상태 평가
     private static float EvaluateBoard(Enums.PlayerType[,] board)
     {
         float score = 0;
         int size = board.GetLength(0);
-        int[][] directions = new int[][] 
-        {
-            new int[] {1, 0},  // 수직
-            new int[] {0, 1},  // 수평
-            new int[] {1, 1},  // 대각선 ↘
-            new int[] {1, -1}  // 대각선 ↙
-        };
 
         for (int row = 0; row < size; row++)
         {
@@ -281,7 +251,7 @@ public static class MiniMaxAIController
                 Enums.PlayerType player = board[row, col];
                 int playerScore = (player == Enums.PlayerType.PlayerB) ? 1 : -1; // AI는 양수, 상대는 음수
 
-                foreach (var dir in directions)
+                foreach (var dir in _directions)
                 {
                     int count = 1;
                     int openEnds = 0;

From b105c1ff606f47ea15ac2574a037a491312588a8 Mon Sep 17 00:00:00 2001
From: Sehyeon <ksh1837@naver.com>
Date: Fri, 14 Mar 2025 16:44:47 +0900
Subject: [PATCH 3/7] DO-4 [Fix] AI makes Random Mistake

---
 Assets/Script/AI/MiniMaxAIController.cs | 37 +++++++++++++++++--------
 1 file changed, 25 insertions(+), 12 deletions(-)

diff --git a/Assets/Script/AI/MiniMaxAIController.cs b/Assets/Script/AI/MiniMaxAIController.cs
index d89d9ce..33bfc84 100644
--- a/Assets/Script/AI/MiniMaxAIController.cs
+++ b/Assets/Script/AI/MiniMaxAIController.cs
@@ -6,7 +6,6 @@ using UnityEngine;
 public static class MiniMaxAIController
 {
     // To-Do List
-    // 랜덤 실수 (랜덤하게 덜 좋은 수 리턴)
     // 탐색 시간 개선
     // 코드 중복 제거
     // AI 난이도 개선
@@ -22,12 +21,23 @@ public static class MiniMaxAIController
         new int[] {1, -1} // 대각선 ↙ ↗
     };
 
-    private static int _playerLevel; // 급수 설정
+    private static int _playerLevel = 1; // 급수 설정
+    private static float _mistakeMove;
+    private static Enums.PlayerType _AIPlayerType = Enums.PlayerType.PlayerB;
 
     // 급수 설정 -> 실수 넣을 때 계산
     public static void SetLevel(int level)
     {
         _playerLevel = level;
+        
+        _mistakeMove = GetMistakeProbability(_playerLevel);
+    }
+    
+    // 실수 확률 계산 함수
+    private static float GetMistakeProbability(int level)
+    {
+        // 레벨이 1일 때 실수 확률 0%, 레벨이 18일 때 실수 확률 50%
+        return (level - 1) / 17f * 0.5f;
     }
     
     // return 값이 null 일 경우 == 보드에 칸 꽉 참
@@ -54,7 +64,7 @@ public static class MiniMaxAIController
         
         foreach (var (row, col) in validMoves)
         {
-            board[row, col] = Enums.PlayerType.PlayerB;
+            board[row, col] = _AIPlayerType;
             float score = DoMinimax(board, SEARCH_DEPTH, false, -1000, 1000, row, col);
             board[row, col] = Enums.PlayerType.None;
 
@@ -71,6 +81,13 @@ public static class MiniMaxAIController
             }
         }
 
+        // 랜덤 실수
+        if (secondBestMove != null && UnityEngine.Random.value < _mistakeMove) // UnityEngine.Random.value == 0~1 사이 반환
+        {
+            Debug.Log("AI Mistake");
+            return secondBestMove;
+        }
+        
         return bestMove;
     }
 
@@ -203,7 +220,7 @@ public static class MiniMaxAIController
                     
                     // 왼쪽 방향 확인
                     int r = row + dir[0], c = col + dir[1];
-                    while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.PlayerB)
+                    while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == _AIPlayerType)
                     {
                         count++;
                         r += dir[0];
@@ -215,7 +232,7 @@ public static class MiniMaxAIController
                     // 오른쪽 방향 확인
                     r = row - dir[0];
                     c = col - dir[1];
-                    while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.PlayerB)
+                    while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == _AIPlayerType)
                     {
                         count++;
                         r -= dir[0];
@@ -223,8 +240,7 @@ public static class MiniMaxAIController
                     }
                     if (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.None)
                         openEnds++;
-
-                    // 5연승이 가능하면 그 자리를 리스트에 추가
+                    
                     if (count == 4 && openEnds > 0)
                     {
                         fiveInARowMoves.Add((row, col));
@@ -249,7 +265,7 @@ public static class MiniMaxAIController
                 if (board[row, col] == Enums.PlayerType.None) continue;
 
                 Enums.PlayerType player = board[row, col];
-                int playerScore = (player == Enums.PlayerType.PlayerB) ? 1 : -1; // AI는 양수, 상대는 음수
+                int playerScore = (player == _AIPlayerType) ? 1 : -1; // AI는 양수, 상대는 음수
 
                 foreach (var dir in _directions)
                 {
@@ -275,7 +291,6 @@ public static class MiniMaxAIController
 
                     while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == player)
                     {
-                        count++;
                         r -= dir[0];
                         c -= dir[1];
                     }
@@ -284,9 +299,7 @@ public static class MiniMaxAIController
                         openEnds++;
 
                     // 점수 계산
-                    if (count >= 5) 
-                        score += playerScore * 1000000; // 실제로 호출되는 일이 없음 왜지??
-                    else if (count == 4)
+                    if (count == 4)
                         score += playerScore * (openEnds == 2 ? 10000 : 1000);
                     else if (count == 3)
                         score += playerScore * (openEnds == 2 ? 1000 : 100);

From c3c670bbdca2be4dd2e326f72327c6dffb88fc6e Mon Sep 17 00:00:00 2001
From: Sehyeon <ksh1837@naver.com>
Date: Sun, 16 Mar 2025 21:27:46 +0900
Subject: [PATCH 4/7] =?UTF-8?q?DO-4=20[Style]=20=EC=A4=91=EB=B3=B5=20?=
 =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B5=9C=EC=86=8C=ED=99=94?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 Assets/Script/AI/MiniMaxAIController.cs | 158 +++++++++++++++++-------
 1 file changed, 116 insertions(+), 42 deletions(-)

diff --git a/Assets/Script/AI/MiniMaxAIController.cs b/Assets/Script/AI/MiniMaxAIController.cs
index 33bfc84..9cda8d7 100644
--- a/Assets/Script/AI/MiniMaxAIController.cs
+++ b/Assets/Script/AI/MiniMaxAIController.cs
@@ -50,7 +50,6 @@ public static class MiniMaxAIController
         
         if (validMoves.Count == 0)
         {
-            Debug.Log("칸이 없습니다...");
             return null;
         }
         
@@ -159,48 +158,8 @@ public static class MiniMaxAIController
         }
         return false;
     }
-
-    // 최근에 둔 돌 위치 기반으로 게임 승리를 판별하는 함수
-    public static bool CheckGameWin(Enums.PlayerType player, Enums.PlayerType[,] board, int row, int col)
-    {
-        int size = board.GetLength(0);
-        
-        // 각 방향별로 판단
-        foreach (var dir in _directions)
-        {
-            // 자기 자신 포함해서 카운트 시작
-            int stoneCount = 1;
-
-            // 정방향 탐색
-            int r = row + dir[0], c = col + dir[1];
-            while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == player) // 0~15내에서 플레이어 타입 판단
-            {
-                // 동일 플레이어 타입인 경우
-                stoneCount++;
-                r += dir[0]; // row값 옮기기 
-                c += dir[1]; // col값 옮기기
-            }
-            
-            // 역방향 탐색 전에 정방향에서 Win했는지 확인 (이미 Win한 상태에서 역방향 검사 방지)
-            if (stoneCount >= WIN_COUNT) return true; 
-
-            // 역방향 탐색
-            r = row - dir[0]; 
-            c = col - dir[1];
-            while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == player)
-            {
-                stoneCount++;
-                r -= dir[0];
-                c -= dir[1];
-            }
-
-            if (stoneCount >= WIN_COUNT) return true; 
-        }
-        
-        return false;
-    }
     
-    private static List<(int row, int col)> GetFiveInARowCandidateMoves(Enums.PlayerType[,] board)
+    /*private static List<(int row, int col)> GetFiveInARowCandidateMoves(Enums.PlayerType[,] board)
     {
         List<(int row, int col)> fiveInARowMoves = new List<(int, int)>();
         int size = board.GetLength(0);
@@ -309,6 +268,121 @@ public static class MiniMaxAIController
             }
         }
 
+        return score;
+    }*/
+    
+    // 특정 방향으로 같은 돌 개수와 열린 끝 개수를 계산하는 함수
+    private static (int count, int openEnds) CountStones(
+        Enums.PlayerType[,] board, int row, int col, int[] direction, Enums.PlayerType player)
+    {
+        int size = board.GetLength(0);
+        int count = 0;
+        int openEnds = 0;
+
+        // 정방향 탐색
+        int r = row + direction[0], c = col + direction[1];
+        while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == player)
+        {
+            count++;
+            r += direction[0]; // row값 옮기기
+            c += direction[1]; // col값 옮기기
+        }
+        
+        if (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.None)
+        {
+            openEnds++;
+        }
+
+        // 역방향 탐색
+        r = row - direction[0]; 
+        c = col - direction[1];
+        while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == player)
+        {
+            count++;
+            r -= direction[0];
+            c -= direction[1];
+        }
+        
+        if (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.None)
+        {
+            openEnds++;
+        }
+
+        return (count, openEnds);
+    }
+    
+    // 최근에 둔 돌 위치 기반으로 게임 승리를 판별하는 함수
+    public static bool CheckGameWin(Enums.PlayerType player, Enums.PlayerType[,] board, int row, int col)
+    {
+        foreach (var dir in _directions)
+        {
+            var (count, _) = CountStones(board, row, col, dir, player);
+
+            // 자기 자신 포함하여 5개 이상일 시 true 반환
+            if (count + 1 >= WIN_COUNT) 
+                return true;
+        }
+        
+        return false;
+    }
+
+    // 5목이 될 수 있는 위치 찾기
+    private static List<(int row, int col)> GetFiveInARowCandidateMoves(Enums.PlayerType[,] board)
+    {
+        List<(int row, int col)> fiveInARowMoves = new List<(int, int)>();
+        int size = board.GetLength(0);
+
+        for (int row = 0; row < size; row++)
+        {
+            for (int col = 0; col < size; col++)
+            {
+                if (board[row, col] != Enums.PlayerType.None) continue;
+
+                foreach (var dir in _directions)
+                {
+                    var (count, openEnds) = CountStones(board, row, col, dir, _AIPlayerType);
+
+                    if (count == 4 && openEnds > 0)
+                    {
+                        fiveInARowMoves.Add((row, col));
+                        break;  // 하나 나오면 바로 break (시간 단축)
+                    }
+                }
+            }
+        }
+
+        return fiveInARowMoves;
+    }
+
+    // 현재 보드 평가 함수
+    private static float EvaluateBoard(Enums.PlayerType[,] board)
+    {
+        float score = 0;
+        int size = board.GetLength(0);
+
+        for (int row = 0; row < size; row++)
+        {
+            for (int col = 0; col < size; col++)
+            {
+                if (board[row, col] == Enums.PlayerType.None) continue;
+
+                Enums.PlayerType player = board[row, col];
+                int playerScore = (player == _AIPlayerType) ? 1 : -1; // AI는 양수, 플레이어는 음수
+
+                foreach (var dir in _directions)
+                {
+                    var (count, openEnds) = CountStones(board, row, col, dir, player);
+
+                    // 점수 계산
+                    if (count == 4)
+                        score += playerScore * (openEnds == 2 ? 10000 : 1000);
+                    else if (count == 3)
+                        score += playerScore * (openEnds == 2 ? 1000 : 100);
+                    else if (count == 2)
+                        score += playerScore * (openEnds == 2 ? 100 : 10);
+                }
+            }
+        }
         return score;
     }
 }

From 9f94a8d0a59e41040979fdd3bd78621346051cf3 Mon Sep 17 00:00:00 2001
From: Sehyeon <ksh1837@naver.com>
Date: Mon, 17 Mar 2025 10:58:14 +0900
Subject: [PATCH 5/7] =?UTF-8?q?DO-4=20[Refactor]=20=EC=BA=90=EC=8B=B1?=
 =?UTF-8?q?=EC=9D=84=20=ED=86=B5=ED=95=B4=20=EC=A4=91=EB=B3=B5=20=EA=B3=84?=
 =?UTF-8?q?=EC=82=B0=20=EB=B0=A9=EC=A7=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 Assets/KSH.meta                               |   8 +
 Assets/{Scenes => KSH}/AITestScene.unity      |   0
 Assets/{Scenes => KSH}/AITestScene.unity.meta |   0
 Assets/Script/AI/MiniMaxAIController.cs       | 146 ++++--------------
 Assets/Script/AI/TestGameManager.cs           |   2 +
 5 files changed, 41 insertions(+), 115 deletions(-)
 create mode 100644 Assets/KSH.meta
 rename Assets/{Scenes => KSH}/AITestScene.unity (100%)
 rename Assets/{Scenes => KSH}/AITestScene.unity.meta (100%)

diff --git a/Assets/KSH.meta b/Assets/KSH.meta
new file mode 100644
index 0000000..b7a58f9
--- /dev/null
+++ b/Assets/KSH.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d652b2a2f29c0c541983b529f66a5169
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Scenes/AITestScene.unity b/Assets/KSH/AITestScene.unity
similarity index 100%
rename from Assets/Scenes/AITestScene.unity
rename to Assets/KSH/AITestScene.unity
diff --git a/Assets/Scenes/AITestScene.unity.meta b/Assets/KSH/AITestScene.unity.meta
similarity index 100%
rename from Assets/Scenes/AITestScene.unity.meta
rename to Assets/KSH/AITestScene.unity.meta
diff --git a/Assets/Script/AI/MiniMaxAIController.cs b/Assets/Script/AI/MiniMaxAIController.cs
index 9cda8d7..2ffa77e 100644
--- a/Assets/Script/AI/MiniMaxAIController.cs
+++ b/Assets/Script/AI/MiniMaxAIController.cs
@@ -7,7 +7,6 @@ public static class MiniMaxAIController
 {
     // To-Do List
     // 탐색 시간 개선
-    // 코드 중복 제거
     // AI 난이도 개선
     
     private const int SEARCH_DEPTH = 3; // 탐색 깊이 제한 (3 = 빠른 응답, 4 = 좀 더 강한 AI 그러나 느린)
@@ -24,7 +23,11 @@ public static class MiniMaxAIController
     private static int _playerLevel = 1; // 급수 설정
     private static float _mistakeMove;
     private static Enums.PlayerType _AIPlayerType = Enums.PlayerType.PlayerB;
-
+    
+    // 중복 계산을 방지하기 위한 캐싱 데이터. 위치(row, col) 와 방향(dirX, dirY) 중복 계산 방지
+    private static Dictionary<(int, int, int, int), (int count, int openEnds)> _stoneInfoCache 
+        = new Dictionary<(int, int, int, int), (int count, int openEnds)>();
+    
     // 급수 설정 -> 실수 넣을 때 계산
     public static void SetLevel(int level)
     {
@@ -43,11 +46,15 @@ public static class MiniMaxAIController
     // return 값이 null 일 경우 == 보드에 칸 꽉 참
     public static (int row, int col)? GetBestMove(Enums.PlayerType[,] board)
     {
+        // 캐시 초기화
+        ClearCache();
+        
         float bestScore = float.MinValue;
         (int row, int col)? bestMove = null;
         (int row, int col)? secondBestMove = null;
         List<(int row, int col)> validMoves = GetValidMoves(board);
         
+        // 보드에 놓을 수 있는 자리가 있는지 확인
         if (validMoves.Count == 0)
         {
             return null;
@@ -103,8 +110,12 @@ public static class MiniMaxAIController
         foreach (var (row, col) in validMoves)
         {
             board[row, col] = isMaximizing ? Enums.PlayerType.PlayerB : Enums.PlayerType.PlayerA;
+            ClearCache(); // 돌 
+            
             float score = DoMinimax(board, depth - 1, !isMaximizing, alpha, beta, row, col);
+            
             board[row, col] = Enums.PlayerType.None;
+            ClearCache();
 
             if (isMaximizing)
             {
@@ -159,122 +170,19 @@ public static class MiniMaxAIController
         return false;
     }
     
-    /*private static List<(int row, int col)> GetFiveInARowCandidateMoves(Enums.PlayerType[,] board)
-    {
-        List<(int row, int col)> fiveInARowMoves = new List<(int, int)>();
-        int size = board.GetLength(0);
-
-        // 각 칸에 대해 5연승이 될 수 있는 위치 찾기
-        for (int row = 0; row < size; row++)
-        {
-            for (int col = 0; col < size; col++)
-            {
-                if (board[row, col] != Enums.PlayerType.None)
-                    continue; // 이미 돌이 놓인 곳
-
-                foreach (var dir in _directions)
-                {
-                    int count = 0;
-                    int openEnds = 0;
-                    
-                    // 왼쪽 방향 확인
-                    int r = row + dir[0], c = col + dir[1];
-                    while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == _AIPlayerType)
-                    {
-                        count++;
-                        r += dir[0];
-                        c += dir[1];
-                    }
-                    if (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.None)
-                        openEnds++;
-
-                    // 오른쪽 방향 확인
-                    r = row - dir[0];
-                    c = col - dir[1];
-                    while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == _AIPlayerType)
-                    {
-                        count++;
-                        r -= dir[0];
-                        c -= dir[1];
-                    }
-                    if (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.None)
-                        openEnds++;
-                    
-                    if (count == 4 && openEnds > 0)
-                    {
-                        fiveInARowMoves.Add((row, col));
-                    }
-                }
-            }
-        }
-
-        return fiveInARowMoves;
-    }
-    
-    // 현재 보드의 상태 평가
-    private static float EvaluateBoard(Enums.PlayerType[,] board)
-    {
-        float score = 0;
-        int size = board.GetLength(0);
-
-        for (int row = 0; row < size; row++)
-        {
-            for (int col = 0; col < size; col++)
-            {
-                if (board[row, col] == Enums.PlayerType.None) continue;
-
-                Enums.PlayerType player = board[row, col];
-                int playerScore = (player == _AIPlayerType) ? 1 : -1; // AI는 양수, 상대는 음수
-
-                foreach (var dir in _directions)
-                {
-                    int count = 1;
-                    int openEnds = 0;
-                    int r = row + dir[0], c = col + dir[1];
-
-                    // 같은 돌 개수 세기
-                    while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == player)
-                    {
-                        count++;
-                        r += dir[0];
-                        c += dir[1];
-                    }
-
-                    // 열린 방향 확인
-                    if (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.None)
-                        openEnds++;
-
-                    // 반대 방향 검사
-                    r = row - dir[0]; 
-                    c = col - dir[1];
-
-                    while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == player)
-                    {
-                        r -= dir[0];
-                        c -= dir[1];
-                    }
-
-                    if (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.None)
-                        openEnds++;
-
-                    // 점수 계산
-                    if (count == 4)
-                        score += playerScore * (openEnds == 2 ? 10000 : 1000);
-                    else if (count == 3)
-                        score += playerScore * (openEnds == 2 ? 1000 : 100);
-                    else if (count == 2)
-                        score += playerScore * (openEnds == 2 ? 100 : 10);
-                }
-            }
-        }
-
-        return score;
-    }*/
-    
     // 특정 방향으로 같은 돌 개수와 열린 끝 개수를 계산하는 함수
     private static (int count, int openEnds) CountStones(
         Enums.PlayerType[,] board, int row, int col, int[] direction, Enums.PlayerType player)
     {
+        int dirX = direction[0], dirY = direction[1];
+        var key = (row, col, dirX, dirY);
+
+        // 캐시에 존재하면 바로 반환 (탐색 시간 감소)
+        if (_stoneInfoCache.TryGetValue(key, out var cachedResult))
+        {
+            return cachedResult;
+        }
+        
         int size = board.GetLength(0);
         int count = 0;
         int openEnds = 0;
@@ -308,7 +216,15 @@ public static class MiniMaxAIController
             openEnds++;
         }
 
-        return (count, openEnds);
+        var resultValue = (count, openEnds);
+        _stoneInfoCache[key] = resultValue; // 결과 저장
+        return resultValue;
+    }
+    
+    // 캐시 초기화, 새로운 돌이 놓일 시 실행
+    private static void ClearCache()
+    {
+        _stoneInfoCache.Clear();
     }
     
     // 최근에 둔 돌 위치 기반으로 게임 승리를 판별하는 함수
diff --git a/Assets/Script/AI/TestGameManager.cs b/Assets/Script/AI/TestGameManager.cs
index 050d5c1..9cd8b64 100644
--- a/Assets/Script/AI/TestGameManager.cs
+++ b/Assets/Script/AI/TestGameManager.cs
@@ -17,6 +17,7 @@ public class TestGameManager : MonoBehaviour
     private void Start()
     {
         _board = new Enums.PlayerType[15, 15];
+        MiniMaxAIController.SetLevel(1); // 급수 설정 테스트
         ResultBoard();
     }
 
@@ -32,6 +33,7 @@ public class TestGameManager : MonoBehaviour
         }
         
         _board[row, col] = Enums.PlayerType.PlayerA;
+        Debug.Log($"Player's row: {row} col: {col}");
         
         // var isEnded = MiniMaxAIController.CheckGameWin(Enums.PlayerType.PlayerA, _board, row, col);
         // Debug.Log("PlayerA is Win: " + isEnded);

From 23f114897ab78ecd5584570c988c2ac7be069fb7 Mon Sep 17 00:00:00 2001
From: Sehyeon <ksh1837@naver.com>
Date: Mon, 17 Mar 2025 16:17:25 +0900
Subject: [PATCH 6/7] =?UTF-8?q?DO-4=20[Refactor]=20=EC=8B=9C=EA=B0=84=20?=
 =?UTF-8?q?=EB=8B=A8=EC=B6=95=20-=20Move=20Ordering=20=EC=A0=81=EC=9A=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 Assets/Script/AI/MiniMaxAIController.cs | 26 ++++++++++++++-----------
 1 file changed, 15 insertions(+), 11 deletions(-)

diff --git a/Assets/Script/AI/MiniMaxAIController.cs b/Assets/Script/AI/MiniMaxAIController.cs
index 2ffa77e..c47073e 100644
--- a/Assets/Script/AI/MiniMaxAIController.cs
+++ b/Assets/Script/AI/MiniMaxAIController.cs
@@ -6,7 +6,7 @@ using UnityEngine;
 public static class MiniMaxAIController
 {
     // To-Do List
-    // 탐색 시간 개선
+    // 탐색 시간 개선: 캐싱(_stoneInfoCache), 좋은 수부터 탐색(Move Ordering) 
     // AI 난이도 개선
     
     private const int SEARCH_DEPTH = 3; // 탐색 깊이 제한 (3 = 빠른 응답, 4 = 좀 더 강한 AI 그러나 느린)
@@ -52,7 +52,7 @@ public static class MiniMaxAIController
         float bestScore = float.MinValue;
         (int row, int col)? bestMove = null;
         (int row, int col)? secondBestMove = null;
-        List<(int row, int col)> validMoves = GetValidMoves(board);
+        List<(int row, int col, float score)> validMoves = GetValidMoves(board);
         
         // 보드에 놓을 수 있는 자리가 있는지 확인
         if (validMoves.Count == 0)
@@ -68,7 +68,7 @@ public static class MiniMaxAIController
             return bestMove;
         }
         
-        foreach (var (row, col) in validMoves)
+        foreach (var (row, col, _) in validMoves)
         {
             board[row, col] = _AIPlayerType;
             float score = DoMinimax(board, SEARCH_DEPTH, false, -1000, 1000, row, col);
@@ -105,26 +105,26 @@ public static class MiniMaxAIController
         if (depth == 0) return EvaluateBoard(board);
 
         float bestScore = isMaximizing ? float.MinValue : float.MaxValue;
-        List<(int row, int col)> validMoves = GetValidMoves(board); // 현재 놓을 수 있는 자리 리스트
+        List<(int row, int col, float score)> validMoves = GetValidMoves(board); // 현재 놓을 수 있는 자리 리스트
 
-        foreach (var (row, col) in validMoves)
+        foreach (var (row, col, _) in validMoves)
         {
             board[row, col] = isMaximizing ? Enums.PlayerType.PlayerB : Enums.PlayerType.PlayerA;
             ClearCache(); // 돌 
             
-            float score = DoMinimax(board, depth - 1, !isMaximizing, alpha, beta, row, col);
+            float minimaxScore = DoMinimax(board, depth - 1, !isMaximizing, alpha, beta, row, col);
             
             board[row, col] = Enums.PlayerType.None;
             ClearCache();
 
             if (isMaximizing)
             {
-                bestScore = Math.Max(bestScore, score);
+                bestScore = Math.Max(bestScore, minimaxScore);
                 alpha = Math.Max(alpha, bestScore);
             }
             else
             {
-                bestScore = Math.Min(bestScore, score);
+                bestScore = Math.Min(bestScore, minimaxScore);
                 beta = Math.Min(beta, bestScore);
             }
             
@@ -134,9 +134,9 @@ public static class MiniMaxAIController
     }
 
     // 이동 가능 + 주변에 돌 있는 위치 탐색
-    private static List<(int row, int col)> GetValidMoves(Enums.PlayerType[,] board)
+    private static List<(int row, int col, float score)> GetValidMoves(Enums.PlayerType[,] board)
     {
-        List<(int, int)> validMoves = new List<(int, int)>();
+        List<(int, int, float)> validMoves = new List<(int, int, float)>();
         int size = board.GetLength(0);
 
         for (int row = 0; row < size; row++)
@@ -145,10 +145,13 @@ public static class MiniMaxAIController
             {
                 if (board[row, col] == Enums.PlayerType.None && HasNearbyStones(board, row, col))
                 {
-                    validMoves.Add((row, col));
+                    float score = EvaluateBoard(board);
+                    validMoves.Add((row, col, score));
                 }
             }
         }
+
+        validMoves.Sort((a, b) => b.Item3.CompareTo(a.Item3));  
         return validMoves;
     }
     
@@ -224,6 +227,7 @@ public static class MiniMaxAIController
     // 캐시 초기화, 새로운 돌이 놓일 시 실행
     private static void ClearCache()
     {
+        // 전체 초기화가 아닌 부분 초기화하기? (현재 변경된 위치 N에서 반경 5칸만 초기화)
         _stoneInfoCache.Clear();
     }
     

From f95a3881d4dc542f8017bccbbb9567b3d4cb2e24 Mon Sep 17 00:00:00 2001
From: Sehyeon <ksh1837@naver.com>
Date: Mon, 17 Mar 2025 17:15:16 +0900
Subject: [PATCH 7/7] =?UTF-8?q?DO-4=20[Refactor]=20=EC=8B=9C=EA=B0=84=20?=
 =?UTF-8?q?=EB=8B=A8=EC=B6=95=20-=20=EC=BA=90=EC=8B=9C=20=EB=B6=80?=
 =?UTF-8?q?=EB=B6=84=20=EC=B4=88=EA=B8=B0=ED=99=94?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 Assets/Script/AI/MiniMaxAIController.cs | 40 ++++++++++++++++++++++---
 1 file changed, 36 insertions(+), 4 deletions(-)

diff --git a/Assets/Script/AI/MiniMaxAIController.cs b/Assets/Script/AI/MiniMaxAIController.cs
index c47073e..b1f26e8 100644
--- a/Assets/Script/AI/MiniMaxAIController.cs
+++ b/Assets/Script/AI/MiniMaxAIController.cs
@@ -110,12 +110,14 @@ public static class MiniMaxAIController
         foreach (var (row, col, _) in validMoves)
         {
             board[row, col] = isMaximizing ? Enums.PlayerType.PlayerB : Enums.PlayerType.PlayerA;
-            ClearCache(); // 돌 
+            ClearCachePartial(row, col); // 부분 초기화
+            // ClearCache();
             
             float minimaxScore = DoMinimax(board, depth - 1, !isMaximizing, alpha, beta, row, col);
             
             board[row, col] = Enums.PlayerType.None;
-            ClearCache();
+            ClearCachePartial(row, col);
+            // ClearCache();
 
             if (isMaximizing)
             {
@@ -155,7 +157,7 @@ public static class MiniMaxAIController
         return validMoves;
     }
     
-    private static bool HasNearbyStones(Enums.PlayerType[,] board, int row, int col)
+    private static bool HasNearbyStones(Enums.PlayerType[,] board, int row, int col, int distance = 3)
     {
         // 9칸 기준으로 현재 위치를 중앙으로 상정한 후 나머지 8방향
         int[] dr = { -1, -1, -1, 0, 0, 1, 1, 1 };
@@ -227,10 +229,40 @@ public static class MiniMaxAIController
     // 캐시 초기화, 새로운 돌이 놓일 시 실행
     private static void ClearCache()
     {
-        // 전체 초기화가 아닌 부분 초기화하기? (현재 변경된 위치 N에서 반경 5칸만 초기화)
         _stoneInfoCache.Clear();
     }
     
+    // 캐시 부분 초기화 (현재 변경된 위치 N에서 반경 5칸만 초기화)
+    private static void ClearCachePartial(int centerRow, int centerCol, int radius = 5)
+    {
+        // 캐시가 비어있으면 아무 작업도 하지 않음
+        if (_stoneInfoCache.Count == 0) return;
+    
+        // 제거할 키 목록
+        List<(int, int, int, int)> keysToRemove = new List<(int, int, int, int)>();
+    
+        // 모든 캐시 항목을 검사
+        foreach (var key in _stoneInfoCache.Keys)
+        {
+            var (row, col, _, _) = key;
+            
+            // 거리 계산
+            int distance = Math.Max(Math.Abs(row - centerRow), Math.Abs(col - centerCol));
+        
+            // 지정된 반경 내에 있는 캐시 항목을 삭제 목록에 추가
+            if (distance <= radius)
+            {
+                keysToRemove.Add(key);
+            }
+        }
+    
+        // 반경 내의 키 제거
+        foreach (var key in keysToRemove)
+        {
+            _stoneInfoCache.Remove(key);
+        }
+    }
+    
     // 최근에 둔 돌 위치 기반으로 게임 승리를 판별하는 함수
     public static bool CheckGameWin(Enums.PlayerType player, Enums.PlayerType[,] board, int row, int col)
     {