From 51f9299c614c0309bf96292ceed4ca2c676fad1e Mon Sep 17 00:00:00 2001 From: Sehyeon Date: Wed, 19 Mar 2025 11:26:06 +0900 Subject: [PATCH] =?UTF-8?q?DO-4=20[Fix]=20=EC=BA=90=EC=8B=9C=20-=20?= =?UTF-8?q?=EA=B7=B8=EB=A6=AC=EB=93=9C=20=ED=95=B4=EC=8B=9C=EB=A7=B5?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Script/AI/GameCopy_AI.unity | 10 ++-- Assets/Script/AI/MiniMaxAIController.cs | 67 ++++++++++++------------- Assets/Script/Game/GameLogic.cs | 7 ++- 3 files changed, 40 insertions(+), 44 deletions(-) diff --git a/Assets/Script/AI/GameCopy_AI.unity b/Assets/Script/AI/GameCopy_AI.unity index 2831f39..dfb4e27 100644 --- a/Assets/Script/AI/GameCopy_AI.unity +++ b/Assets/Script/AI/GameCopy_AI.unity @@ -4817,11 +4817,11 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 35a4c6d5d3a97b444b968e68ec8bb9f7, type: 3} m_Name: m_EditorClassIdentifier: - mainPanel: {fileID: 0} - signinPanel: {fileID: 0} - signupPanel: {fileID: 0} - settingsPanel: {fileID: 0} - confirmPanel: {fileID: 0} + mainPanel: {fileID: 8564394481744056992, guid: e1835a90a4d722a4b99be61179de8789, type: 3} + signinPanel: {fileID: 925522282249935710, guid: e14c9b2925f0ddb4192af743e5cc166a, type: 3} + signupPanel: {fileID: 3181524094944658765, guid: 8827fe7caa3145e40b1369cc42f8697d, type: 3} + settingsPanel: {fileID: 2861881646994438329, guid: ea820246b5075c54d9f614291ca41c5d, type: 3} + confirmPanel: {fileID: 8145365568262946399, guid: 76f1fe6b5243faf4f9b8caee7312d336, type: 3} rankingPanel: {fileID: 0} shopPanel: {fileID: 0} giboPanel: {fileID: 0} diff --git a/Assets/Script/AI/MiniMaxAIController.cs b/Assets/Script/AI/MiniMaxAIController.cs index fa9318e..5cf111e 100644 --- a/Assets/Script/AI/MiniMaxAIController.cs +++ b/Assets/Script/AI/MiniMaxAIController.cs @@ -5,10 +5,6 @@ using UnityEngine; public static class MiniMaxAIController { - // To-Do List - // 탐색 시간 개선: 캐싱(_stoneInfoCache), 좋은 수부터 탐색(Move Ordering) - // AI 난이도 개선 - private const int SEARCH_DEPTH = 3; // 탐색 깊이 제한 (3 = 빠른 응답, 4 = 좀 더 강한 AI 그러나 느린) private const int WIN_COUNT = 5; @@ -24,9 +20,12 @@ public static class MiniMaxAIController 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)> _stoneCountCache - = new Dictionary<(int, int, int, int), (int count, int openEnds)>(); + private static System.Random _random = new System.Random(); + + // 중복 계산을 방지하기 위한 캐싱 데이터. 위치 기반 (그리드 기반 해시맵) + private static Dictionary<(int row, int col), Dictionary<(int dirX, int dirY), (int count, int openEnds)>> + _spatialStoneCache = new Dictionary<(int row, int col), Dictionary<(int dirX, int dirY), (int count, int openEnds)>>(); + // 급수 설정 -> 실수 넣을 때 계산 public static void SetLevel(int level) @@ -88,11 +87,11 @@ public static class MiniMaxAIController } // 랜덤 실수 - /*if (secondBestMove != null && UnityEngine.Random.value < _mistakeMove) // UnityEngine.Random.value == 0~1 사이 반환 + if (secondBestMove != null && _random.NextDouble() < _mistakeMove) { Debug.Log("AI Mistake"); return secondBestMove; - }*/ + } return bestMove; } @@ -180,10 +179,14 @@ public static class MiniMaxAIController Enums.PlayerType[,] board, int row, int col, int[] direction, Enums.PlayerType player, bool isSaveInCache = true) { int dirX = direction[0], dirY = direction[1]; - var key = (row, col, dirX, dirY); + // var key = (row, col, dirX, dirY); + + var posKey = (row, col); + var dirKey = (dirX, dirY); // 캐시에 존재하면 바로 반환 (탐색 시간 감소) - if (_stoneCountCache.TryGetValue(key, out var cachedResult)) + if (_spatialStoneCache.TryGetValue(posKey, out var dirCache) && + dirCache.TryGetValue(dirKey, out var cachedResult)) { return cachedResult; } @@ -224,7 +227,12 @@ public static class MiniMaxAIController var resultValue = (count, openEnds); if(isSaveInCache) // 결과 저장 { - _stoneCountCache[key] = resultValue; + if (!_spatialStoneCache.TryGetValue(posKey, out dirCache)) + { + dirCache = new Dictionary<(int, int), (int, int)>(); + _spatialStoneCache[posKey] = dirCache; + } + dirCache[dirKey] = (count, openEnds); } return resultValue; @@ -235,37 +243,26 @@ public static class MiniMaxAIController // 캐시 초기화, 새로운 돌이 놓일 시 실행 private static void ClearCache() { - _stoneCountCache.Clear(); + _spatialStoneCache.Clear(); } // 캐시 부분 초기화 (현재 변경된 위치 N에서 반경 5칸만 초기화) private static void ClearCachePartial(int centerRow, int centerCol, int radius = 5) { // 캐시가 비어있으면 아무 작업도 하지 않음 - if (_stoneCountCache.Count == 0) return; - - // 제거할 키 목록 - List<(int, int, int, int)> keysToRemove = new List<(int, int, int, int)>(); - - // 모든 캐시 항목을 검사 - foreach (var key in _stoneCountCache.Keys) - { - var (row, col, _, _) = key; - - // 거리 계산 - int distance = Math.Max(Math.Abs(row - centerRow), Math.Abs(col - centerCol)); + if (_spatialStoneCache.Count == 0) return; - // 지정된 반경 내에 있는 캐시 항목을 삭제 목록에 추가 - if (distance <= radius) - { - keysToRemove.Add(key); - } - } - - // 반경 내의 키 제거 - foreach (var key in keysToRemove) + for (int r = centerRow - radius; r <= centerRow + radius; r++) { - _stoneCountCache.Remove(key); + for (int c = centerCol - radius; c <= centerCol + radius; c++) + { + // 반경 내 위치 확인 + if (Math.Max(Math.Abs(r - centerRow), Math.Abs(c - centerCol)) <= radius) + { + // 해당 위치의 캐시 항목 제거 + _spatialStoneCache.Remove((r, c)); + } + } } } diff --git a/Assets/Script/Game/GameLogic.cs b/Assets/Script/Game/GameLogic.cs index 688b390..c46f97f 100644 --- a/Assets/Script/Game/GameLogic.cs +++ b/Assets/Script/Game/GameLogic.cs @@ -261,7 +261,7 @@ public class GameLogic : MonoBehaviour ReplayManager.Instance.RecordStonePlaced(Enums.StoneType.Black, row, col); //기보 데이터 저장 break; case Enums.PlayerType.PlayerB: - /* + // AI 테스트 시작 OmokAI.Instance.StartBestMoveSearch(_board, (bestMove) => { @@ -277,14 +277,13 @@ public class GameLogic : MonoBehaviour } }); // AI 테스트 끝 - */ - stoneController.SetStoneType(Enums.StoneType.White, row, col); + /*stoneController.SetStoneType(Enums.StoneType.White, row, col); stoneController.SetStoneState(Enums.StoneState.LastPositioned, row, col); _board[row, col] = Enums.PlayerType.PlayerB; LastNSelectedSetting(row, col); - ReplayManager.Instance.RecordStonePlaced(Enums.StoneType.White, row, col); + ReplayManager.Instance.RecordStonePlaced(Enums.StoneType.White, row, col);*/ break; } }