diff --git a/Assets/Script/Renju/ForbiddenDetectorBase.cs b/Assets/Script/Renju/ForbiddenDetectorBase.cs index 24741c8..7c342c6 100644 --- a/Assets/Script/Renju/ForbiddenDetectorBase.cs +++ b/Assets/Script/Renju/ForbiddenDetectorBase.cs @@ -2,6 +2,7 @@ { private protected Enums.PlayerType Black = Enums.PlayerType.PlayerA; private protected Enums.PlayerType Space = Enums.PlayerType.None; + private protected Enums.PlayerType White = Enums.PlayerType.PlayerB; // 8방향을 나타내는 델타 배열 (가로, 세로, 대각선 방향) private protected readonly int[,] Directions = new int[8, 2] diff --git a/Assets/Script/Renju/RenjuDoubleThreeDetector.cs b/Assets/Script/Renju/RenjuDoubleThreeDetector.cs index 943063e..03eab22 100644 --- a/Assets/Script/Renju/RenjuDoubleThreeDetector.cs +++ b/Assets/Script/Renju/RenjuDoubleThreeDetector.cs @@ -16,11 +16,278 @@ public class RenjuDoubleThreeDetector: ForbiddenDetectorBase board[row, col] = Black; // 쌍삼 검사 - // bool isThreeThree = CheckThreeThree(board, row, col); + bool isDoubleThree = CheckDoubleThree(board, row, col); // 원래 상태로 되돌림 board[row, col] = Space; return false; } + + /// + /// 쌍삼(3-3) 여부를 검사합니다. + /// + private bool CheckDoubleThree(Enums.PlayerType[,] board, int row, int col) + { + int openThreeCount = 0; + + // 4개의 방향 쌍에 대해 검사 (대각선 및 직선 포함) + for (int i = 0; i < 4; i++) + { + int dir1 = DirectionPairs[i, 0]; + int dir2 = DirectionPairs[i, 1]; + + // 해당 방향에서 열린 3의 개수 계산 + if (CheckOpenThreeInDirection(board, row, col, dir1, dir2)) + { + openThreeCount++; + + // 이미 열린 3이 2개 이상 발견되면 쌍삼으로 판정 + if (openThreeCount >= 2) + { + return true; + } + } + } + + return false; + } + + /// + /// 특정 방향에서 열린 3이 형성되는지 확인합니다. + /// + private bool CheckOpenThreeInDirection(Enums.PlayerType[,] board, int row, int col, int dir1, int dir2) + { + // 각 방향으로 최대 5칸까지의 범위를 검사 (현재 위치 포함 총 11칸) + Enums.PlayerType[] linePattern = new Enums.PlayerType[11]; + int centerIndex = 5; // 중앙 인덱스 (현재 위치) + + // 현재 위치 설정 + linePattern[centerIndex] = Black; + + // dir1 방향으로 패턴 채우기 + for (int i = 1; i <= 5; i++) + { + int newRow = row + Directions[dir1, 0] * i; + int newCol = col + Directions[dir1, 1] * i; + + if (IsInBounds(newRow, newCol)) + { + linePattern[centerIndex + i] = board[newRow, newCol]; + } + else + { + linePattern[centerIndex + i] = White; // 범위 밖은 막힌 것으로 처리 + } + } + + // dir2 방향으로 패턴 채우기 + for (int i = 1; i <= 5; i++) + { + int newRow = row + Directions[dir2, 0] * i; + int newCol = col + Directions[dir2, 1] * i; + + if (IsInBounds(newRow, newCol)) + { + linePattern[centerIndex - i] = board[newRow, newCol]; + } + else + { + linePattern[centerIndex - i] = White; // 범위 밖은 막힌 것으로 처리 + } + } + + // 열린 3 패턴 확인 + return CheckForOpenThree(linePattern, centerIndex); + } + + /// + /// 라인 패턴에서 열린 3 패턴을 확인합니다. + /// + private bool CheckForOpenThree(Enums.PlayerType[] linePattern, int centerIndex) + { + // 연속된 열린 3 확인 + if (CheckConsecutiveOpenThree(linePattern, centerIndex)) + { + return true; + } + + // 한 칸 떨어진 열린 3 확인 + if (CheckGappedOpenThree(linePattern, centerIndex)) + { + return true; + } + + return false; + } + + /// + /// 연속된 열린 3 패턴을 확인합니다. + /// + private bool CheckConsecutiveOpenThree(Enums.PlayerType[] linePattern, int centerIndex) + { + // 연속된 3개의 돌 패턴 + // 시작 인덱스를 조정하여 센터가 패턴 내에 있는지 확인 + for (int start = centerIndex - 2; start <= centerIndex; start++) + { + // 범위 체크 + if (start < 0 || start + 2 >= linePattern.Length) + { + continue; + } + + // 3개의 연속된 돌 확인 + bool isConsecutiveThree = true; + for (int i = 0; i < 3; i++) + { + if (linePattern[start + i] != Black) + { + isConsecutiveThree = false; + break; + } + } + + if (isConsecutiveThree) + { + // 양쪽이 모두 열려있는지 확인 + bool isLeftOpen = (start - 1 >= 0) && (linePattern[start - 1] == Space); + bool isRightOpen = (start + 3 < linePattern.Length) && (linePattern[start + 3] == Space); + + // 양쪽이 모두 열려있으면 열린 3 + if (isLeftOpen && isRightOpen) + { + // 추가 검증: 더 확장해서 열려있는지 확인 + bool isExtendedLeftOpen = IsExtendedOpen(linePattern, start - 1, -1); + bool isExtendedRightOpen = IsExtendedOpen(linePattern, start + 3, 1); + + if (isExtendedLeftOpen && isExtendedRightOpen) + { + return true; + } + } + } + } + + return false; + } + + /// + /// 한 칸 떨어진 열린 3 패턴을 확인합니다. + /// + private bool CheckGappedOpenThree(Enums.PlayerType[] linePattern, int centerIndex) + { + // 한 칸 떨어진 패턴 확인 + // 패턴 1: ●●○● (centerIndex가 어느 위치든 가능) + // 패턴 2: ●○●● (centerIndex가 어느 위치든 가능) + + // 윈도우 크기 4로 슬라이딩하면서 검사 + for (int start = Mathf.Max(0, centerIndex - 3); start <= Mathf.Min(linePattern.Length - 4, centerIndex); start++) + { + // 패턴 내에 돌과 빈칸 개수 확인 + int stoneCount = 0; + int gapCount = 0; + int gapPosition = -1; + + for (int i = 0; i < 4; i++) + { + if (linePattern[start + i] == Black) + { + stoneCount++; + } + else if (linePattern[start + i] == Space) + { + gapCount++; + gapPosition = start + i; + } + else + { + // 상대 돌이나 벽이 있으면 패턴이 깨짐 + stoneCount = 0; + break; + } + } + + // 3개의 돌과 1개의 빈칸으로 구성된 패턴 + if (stoneCount == 3 && gapCount == 1) + { + // 빈칸에 돌을 놓았을 때 열린 4가 되는지 확인 + if (CheckIfCreatesOpenFour(linePattern, gapPosition)) + { + return true; + } + } + } + + return false; + } + + /// + /// 빈칸에 돌을 놓았을 때 열린 4가 되는지 확인합니다. + /// + private bool CheckIfCreatesOpenFour(Enums.PlayerType[] linePattern, int position) + { + // 시뮬레이션: 빈칸에 돌을 놓아봄 + Enums.PlayerType[] testPattern = new Enums.PlayerType[linePattern.Length]; + System.Array.Copy(linePattern, testPattern, linePattern.Length); + testPattern[position] = Black; + + // 놓은 위치를 포함해 연속된 4가 있는지 확인 + for (int start = Mathf.Max(0, position - 3); start <= position; start++) + { + // 범위 체크 + if (start + 3 >= testPattern.Length) + { + continue; + } + + // 4개의 연속된 돌 확인 + bool isConsecutiveFour = true; + for (int i = 0; i < 4; i++) + { + if (testPattern[start + i] != Black) + { + isConsecutiveFour = false; + break; + } + } + + if (isConsecutiveFour) + { + // 양쪽이 모두 열려있는지 확인 (열린 4) + bool isLeftOpen = (start - 1 >= 0) && (testPattern[start - 1] == Space); + bool isRightOpen = (start + 4 < testPattern.Length) && (testPattern[start + 4] == Space); + + if (isLeftOpen && isRightOpen) + { + return true; // 열린 4가 됨 + } + } + } + + return false; + } + + /// + /// 특정 방향으로 추가로 열려있는지 확인합니다. + /// + private bool IsExtendedOpen(Enums.PlayerType[] linePattern, int startPos, int direction) + { + // 한 칸 더 확장해서 확인 + int nextPos = startPos + direction; + if (nextPos >= 0 && nextPos < linePattern.Length) + { + // 다음 칸이 상대 돌이나 벽이면 확장 불가 + if (linePattern[nextPos] == White) + { + return false; + } + } + else + { + // 범위를 벗어나면 확장 불가 + return false; + } + + return true; + } } \ No newline at end of file diff --git a/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs b/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs index 52ddf34..35446af 100644 --- a/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs +++ b/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs @@ -15,6 +15,6 @@ public class RenjuForbiddenMoveDetector public List RenjuForbiddenMove(Enums.PlayerType[,] board) { var tempBoard = (Enums.PlayerType[,])board.Clone(); - return _ruleChecker.GetForbiddenMoves(board); + return _ruleChecker.GetForbiddenMoves(tempBoard); } } \ No newline at end of file