DO-4 [Fix] 캐시 - 그리드 해시맵으로 변경

This commit is contained in:
Sehyeon 2025-03-19 11:26:06 +09:00
parent 4cd42862bf
commit 51f9299c61
3 changed files with 40 additions and 44 deletions

View File

@ -4817,11 +4817,11 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 35a4c6d5d3a97b444b968e68ec8bb9f7, type: 3} m_Script: {fileID: 11500000, guid: 35a4c6d5d3a97b444b968e68ec8bb9f7, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
mainPanel: {fileID: 0} mainPanel: {fileID: 8564394481744056992, guid: e1835a90a4d722a4b99be61179de8789, type: 3}
signinPanel: {fileID: 0} signinPanel: {fileID: 925522282249935710, guid: e14c9b2925f0ddb4192af743e5cc166a, type: 3}
signupPanel: {fileID: 0} signupPanel: {fileID: 3181524094944658765, guid: 8827fe7caa3145e40b1369cc42f8697d, type: 3}
settingsPanel: {fileID: 0} settingsPanel: {fileID: 2861881646994438329, guid: ea820246b5075c54d9f614291ca41c5d, type: 3}
confirmPanel: {fileID: 0} confirmPanel: {fileID: 8145365568262946399, guid: 76f1fe6b5243faf4f9b8caee7312d336, type: 3}
rankingPanel: {fileID: 0} rankingPanel: {fileID: 0}
shopPanel: {fileID: 0} shopPanel: {fileID: 0}
giboPanel: {fileID: 0} giboPanel: {fileID: 0}

View File

@ -5,10 +5,6 @@ using UnityEngine;
public static class MiniMaxAIController public static class MiniMaxAIController
{ {
// To-Do List
// 탐색 시간 개선: 캐싱(_stoneInfoCache), 좋은 수부터 탐색(Move Ordering)
// AI 난이도 개선
private const int SEARCH_DEPTH = 3; // 탐색 깊이 제한 (3 = 빠른 응답, 4 = 좀 더 강한 AI 그러나 느린) private const int SEARCH_DEPTH = 3; // 탐색 깊이 제한 (3 = 빠른 응답, 4 = 좀 더 강한 AI 그러나 느린)
private const int WIN_COUNT = 5; private const int WIN_COUNT = 5;
@ -24,9 +20,12 @@ public static class MiniMaxAIController
private static float _mistakeMove; private static float _mistakeMove;
private static Enums.PlayerType _AIPlayerType = Enums.PlayerType.PlayerB; private static Enums.PlayerType _AIPlayerType = Enums.PlayerType.PlayerB;
// 중복 계산을 방지하기 위한 캐싱 데이터. 위치(row, col) 와 방향(dirX, dirY) 중복 계산 방지 private static System.Random _random = new System.Random();
private static Dictionary<(int, int, int, int), (int count, int openEnds)> _stoneCountCache
= new Dictionary<(int, int, int, int), (int count, int openEnds)>(); // 중복 계산을 방지하기 위한 캐싱 데이터. 위치 기반 (그리드 기반 해시맵)
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) 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"); Debug.Log("AI Mistake");
return secondBestMove; return secondBestMove;
}*/ }
return bestMove; 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) Enums.PlayerType[,] board, int row, int col, int[] direction, Enums.PlayerType player, bool isSaveInCache = true)
{ {
int dirX = direction[0], dirY = direction[1]; 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; return cachedResult;
} }
@ -224,7 +227,12 @@ public static class MiniMaxAIController
var resultValue = (count, openEnds); var resultValue = (count, openEnds);
if(isSaveInCache) // 결과 저장 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; return resultValue;
@ -235,37 +243,26 @@ public static class MiniMaxAIController
// 캐시 초기화, 새로운 돌이 놓일 시 실행 // 캐시 초기화, 새로운 돌이 놓일 시 실행
private static void ClearCache() private static void ClearCache()
{ {
_stoneCountCache.Clear(); _spatialStoneCache.Clear();
} }
// 캐시 부분 초기화 (현재 변경된 위치 N에서 반경 5칸만 초기화) // 캐시 부분 초기화 (현재 변경된 위치 N에서 반경 5칸만 초기화)
private static void ClearCachePartial(int centerRow, int centerCol, int radius = 5) private static void ClearCachePartial(int centerRow, int centerCol, int radius = 5)
{ {
// 캐시가 비어있으면 아무 작업도 하지 않음 // 캐시가 비어있으면 아무 작업도 하지 않음
if (_stoneCountCache.Count == 0) return; if (_spatialStoneCache.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));
// 지정된 반경 내에 있는 캐시 항목을 삭제 목록에 추가 for (int r = centerRow - radius; r <= centerRow + radius; r++)
if (distance <= radius)
{
keysToRemove.Add(key);
}
}
// 반경 내의 키 제거
foreach (var key in keysToRemove)
{ {
_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));
}
}
} }
} }

View File

@ -261,7 +261,7 @@ public class GameLogic : MonoBehaviour
ReplayManager.Instance.RecordStonePlaced(Enums.StoneType.Black, row, col); //기보 데이터 저장 ReplayManager.Instance.RecordStonePlaced(Enums.StoneType.Black, row, col); //기보 데이터 저장
break; break;
case Enums.PlayerType.PlayerB: case Enums.PlayerType.PlayerB:
/*
// AI 테스트 시작 // AI 테스트 시작
OmokAI.Instance.StartBestMoveSearch(_board, (bestMove) => OmokAI.Instance.StartBestMoveSearch(_board, (bestMove) =>
{ {
@ -277,14 +277,13 @@ public class GameLogic : MonoBehaviour
} }
}); });
// AI 테스트 끝 // AI 테스트 끝
*/
stoneController.SetStoneType(Enums.StoneType.White, row, col); /*stoneController.SetStoneType(Enums.StoneType.White, row, col);
stoneController.SetStoneState(Enums.StoneState.LastPositioned, row, col); stoneController.SetStoneState(Enums.StoneState.LastPositioned, row, col);
_board[row, col] = Enums.PlayerType.PlayerB; _board[row, col] = Enums.PlayerType.PlayerB;
LastNSelectedSetting(row, col); LastNSelectedSetting(row, col);
ReplayManager.Instance.RecordStonePlaced(Enums.StoneType.White, row, col); ReplayManager.Instance.RecordStonePlaced(Enums.StoneType.White, row, col);*/
break; break;
} }
} }