diff --git a/Assets/Script/Renju/ForbiddenDetectorBase.cs b/Assets/Script/Renju/ForbiddenDetectorBase.cs
index f770527..12c35a9 100644
--- a/Assets/Script/Renju/ForbiddenDetectorBase.cs
+++ b/Assets/Script/Renju/ForbiddenDetectorBase.cs
@@ -1,16 +1,28 @@
-using UnityEngine;
-
-public class ForbiddenDetectorBase
+public class ForbiddenDetectorBase
{
- // 방향 배열 (가로, 세로, 대각선)
- private protected Vector2Int[] directions =
+ private protected Enums.PlayerType Black = Enums.PlayerType.PlayerA;
+ private protected Enums.PlayerType Space = Enums.PlayerType.None;
+
+ // 8방향을 나타내는 델타 배열 (가로, 세로, 대각선 방향)
+ private protected readonly int[,] Directions = new int[8, 2]
{
- new Vector2Int(1, 0), // 가로
- new Vector2Int(0, 1), // 세로
- new Vector2Int(1, 1), // 대각선 (우하향)
- new Vector2Int(1, -1) // 대각선 (우상향)
+ { 1, 0 }, // 오른쪽
+ { 1, 1 }, // 오른쪽 아래
+ { 0, 1 }, // 아래
+ { -1, 1 }, // 왼쪽 아래
+ { -1, 0 }, // 왼쪽
+ { -1, -1 }, // 왼쪽 위
+ { 0, -1 }, // 위
+ { 1, -1 } // 오른쪽 위
};
+ // 방향 쌍을 정의 (반대 방향끼리 쌍을 이룸)
+ // 0-4: 가로 방향 쌍 (동-서)
+ // 1-5: 대각선 방향 쌍 (남동-북서)
+ // 2-6: 세로 방향 쌍 (남-북)
+ // 3-7: 대각선 방향 쌍 (남서-북동)
+ private protected readonly int[,] DirectionPairs = { { 0, 4 }, { 1, 5 }, { 2, 6 }, { 3, 7 } };
+
// 15*15 보드 사이즈
private protected int _boardSize = Constants.BoardSize;
@@ -34,4 +46,4 @@ public class ForbiddenDetectorBase
return board[row, col] == Enums.PlayerType.None;
}
-}
+}
\ No newline at end of file
diff --git a/Assets/Script/Renju/RenjuDoubleFourDetector.cs b/Assets/Script/Renju/RenjuDoubleFourDetector.cs
new file mode 100644
index 0000000..8bc8b65
--- /dev/null
+++ b/Assets/Script/Renju/RenjuDoubleFourDetector.cs
@@ -0,0 +1,194 @@
+using System;
+using System.Text;
+using UnityEngine;
+
+public class RenjuDoubleFourDetector: ForbiddenDetectorBase
+{
+ ///
+ /// 쌍사 여부를 검사합니다.
+ ///
+ /// 현재 보드 상태
+ /// 행 (y 좌표)
+ /// 열 (x 좌표)
+ /// 쌍사면 true, 아니면 false
+ public bool IsDoubleFour(Enums.PlayerType[,] board, int row, int col)
+ {
+ // 임시로 돌 배치
+ board[row, col] = Black;
+
+ // 쌍사 검사
+ bool isDoubleFour = CheckDoubleFour(board, row, col);
+
+ // 원래 상태로 되돌림
+ board[row, col] = Space;
+
+ return isDoubleFour;
+ }
+
+ ///
+ /// 쌍사(4-4) 여부를 검사합니다.
+ ///
+ private bool CheckDoubleFour(Enums.PlayerType[,] board, int row, int col)
+ {
+ // 각각 두개의 라인에서 쌍사를 형성하는 경우
+ if (FindDoubleLineFour(board, row, col)) return true;
+
+ // true : 일직선으로 쌍사가 만들어지는 특수 패턴
+ // false : 모든 경우에도 쌍사가 만들어지지 않음
+ return FindSingleLineDoubleFour(board, row, col);
+ }
+
+ private bool FindDoubleLineFour(Enums.PlayerType[,] board, int row, int col)
+ {
+ int fourCount = 0;
+
+ // 4개의 방향 쌍에 대해 검사 (대각선 및 직선 포함)
+ for (int i = 0; i < 4; i++)
+ {
+ int dir1 = DirectionPairs[i, 0];
+ int dir2 = DirectionPairs[i, 1];
+
+ // 4가 몇 개 만들어지는지 확인
+ if (CheckFourInDirection(board, row, col, dir1, dir2))
+ {
+ fourCount++;
+
+ // 이미 4가 2개 이상 발견되면 쌍사로 판정
+ if (fourCount >= 2)
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private bool FindSingleLineDoubleFour(Enums.PlayerType[,] board, int row, int col)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ int dir1 = DirectionPairs[i, 0];
+ int dir2 = DirectionPairs[i, 1];
+
+ // 각 방향 라인 패턴
+ Enums.PlayerType[] linePattern = ExtractLinePattern(board, row, col, dir1, dir2);
+
+ // 패턴을 문자열로 변환
+ StringBuilder temp = new StringBuilder();
+ foreach (var cell in linePattern)
+ {
+ temp.Append(cell switch
+ {
+ Enums.PlayerType.None => "□",
+ Enums.PlayerType.PlayerA => "●",
+ Enums.PlayerType.PlayerB => "○",
+ _ => "?"
+ });
+ }
+
+ string patternStr = temp.ToString();
+
+ // 한줄로 발생하는 쌍사 패턴 검사
+ if (patternStr.Contains("●●□●●□●●") || patternStr.Contains("●□●●●□●"))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// 특정 방향에서 4가 형성되는지 확인합니다.
+ /// 떨어져 있는 돌도 고려합니다 (한 칸 또는 두 칸 떨어진 패턴 포함).
+ ///
+ private bool CheckFourInDirection(Enums.PlayerType[,] board, int row, int col, int dir1, int dir2)
+ {
+ // 각 방향으로 최대 5칸까지의 범위를 검사 (현재 위치 포함 총 11칸)
+ Enums.PlayerType[] linePattern = ExtractLinePattern(board, row, col, dir1, dir2);
+ int centerIndex = 5; // 중앙 인덱스 (현재 위치)
+
+ // 모든 가능한 패턴 확인
+ return CheckFourOneGap(linePattern, centerIndex);
+ }
+
+ ///
+ /// 라인 패턴을 추출합니다. (중복 코드를 방지하기 위한 헬퍼 메서드)
+ ///
+ private Enums.PlayerType[] ExtractLinePattern(Enums.PlayerType[,] board, int row, int col, int dir1, int dir2)
+ {
+ 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] = Space; // 범위 밖은 빈칸으로 처리
+ }
+ }
+
+ // 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] = Space; // 범위 밖은 빈칸으로 처리
+ }
+ }
+
+ return linePattern;
+ }
+
+ ///
+ /// 한 칸 떨어진 4 패턴을 확인합니다.
+ ///
+ private bool CheckFourOneGap(Enums.PlayerType[] linePattern, int centerIndex)
+ {
+ // 윈도우 슬라이딩으로 연속된 5칸을 검사 (한 칸 떨어진 패턴을 위해)
+ for (int start = 0; start <= 5; start++)
+ {
+ // 현재 위치가 이 윈도우에 포함되는지 확인
+ bool currentPositionInWindow = (start <= centerIndex && centerIndex < start + 5);
+ if (!currentPositionInWindow) continue;
+
+ // 윈도우 내의 돌 개수 세기
+ int stoneCount = 0;
+ for (int i = 0; i < 5; i++)
+ {
+ if (linePattern[start + i] == Black)
+ {
+ stoneCount++;
+ }
+ }
+
+ // 정확히 4개의 돌이 있고, 1개의 빈칸이 있으면 4로 판정
+ // (현재 위치는 흑으로 이미 설정되어 있음)
+ if (stoneCount == 4)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/Assets/Script/Renju/RenjuDoubleFourDetector.cs.meta b/Assets/Script/Renju/RenjuDoubleFourDetector.cs.meta
new file mode 100644
index 0000000..abc7cef
--- /dev/null
+++ b/Assets/Script/Renju/RenjuDoubleFourDetector.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 0daf1f2b8cbe4b19adc0e42db7a15991
+timeCreated: 1742270734
\ No newline at end of file
diff --git a/Assets/Script/Renju/RenjuDoubleThreeDetector.cs b/Assets/Script/Renju/RenjuDoubleThreeDetector.cs
new file mode 100644
index 0000000..943063e
--- /dev/null
+++ b/Assets/Script/Renju/RenjuDoubleThreeDetector.cs
@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class RenjuDoubleThreeDetector: ForbiddenDetectorBase
+{
+ ///
+ /// 쌍삼(3-3) 여부를 검사합니다.
+ ///
+ /// 현재 보드 상태
+ /// 행 좌표
+ /// 열 좌표
+ /// 쌍삼이면 true, 아니면 false
+ public bool IsDoubleThree(Enums.PlayerType[,] board, int row, int col)
+ {
+ // 임시로 돌 배치
+ board[row, col] = Black;
+
+ // 쌍삼 검사
+ // bool isThreeThree = CheckThreeThree(board, row, col);
+
+ // 원래 상태로 되돌림
+ board[row, col] = Space;
+
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/Assets/Script/Renju/RenjuDoubleThreeDetector.cs.meta b/Assets/Script/Renju/RenjuDoubleThreeDetector.cs.meta
new file mode 100644
index 0000000..3ab4d04
--- /dev/null
+++ b/Assets/Script/Renju/RenjuDoubleThreeDetector.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 1c3d6cdb2557431f82e7997bc5ad685c
+timeCreated: 1742283793
\ No newline at end of file
diff --git a/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs b/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs
index 7833607..b6d6789 100644
--- a/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs
+++ b/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs
@@ -13,7 +13,7 @@ public class RenjuForbiddenMoveDetector
///
public RenjuForbiddenMoveDetector()
{
- // 각 감지기 초기화
+ // 감지기 초기화
_ruleChecker = new RenjuRuleChecker();
}
@@ -26,10 +26,6 @@ public class RenjuForbiddenMoveDetector
{
var doubleThreeList = _ruleChecker.GetForbiddenMoves(board);
- foreach (var doubleThreePos in doubleThreeList)
- {
- Debug.Log("삼삼 금수 좌표 X축 : " + doubleThreePos.x + ", Y축 : " + doubleThreePos.y);
- }
return doubleThreeList;
}
diff --git a/Assets/Script/Renju/RenjuOverlineDetector.cs b/Assets/Script/Renju/RenjuOverlineDetector.cs
new file mode 100644
index 0000000..f9646a6
--- /dev/null
+++ b/Assets/Script/Renju/RenjuOverlineDetector.cs
@@ -0,0 +1,102 @@
+public class RenjuOverlineDetector : ForbiddenDetectorBase
+{
+ ///
+ /// 장목 여부를 검사합니다.
+ ///
+ /// 현재 보드 상태
+ /// 행 (y 좌표)
+ /// 열 (x 좌표)
+ /// 장목이면 true, 아니면 false
+ public bool IsOverline(Enums.PlayerType[,] board, int row, int col)
+ {
+ // 임시로 돌 놓기
+ board[row, col] = Black;
+
+ // 장목 검사
+ bool isOverline = CheckOverline(board, row, col);
+
+
+ board[row, col] = Space;
+
+ return isOverline;
+ }
+
+ ///
+ /// 장목(6목 이상) 여부를 검사합니다.
+ ///
+ private bool CheckOverline(Enums.PlayerType[,] board, int row, int col)
+ {
+ // 4개의 방향 쌍에 대해 검사
+ for (int i = 0; i < 4; i++)
+ {
+ int dir1 = DirectionPairs[i, 0];
+ int dir2 = DirectionPairs[i, 1];
+
+ // 해당 방향 쌍에서 연속된 돌의 총 개수 계산
+ int count = CountConsecutiveStones(board, row, col, dir1, dir2);
+
+ // 6목 이상이면 장목
+ if (count >= 6)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// 특정 방향 쌍에서 연속된 돌의 개수를 계산합니다.
+ ///
+ /// 현재 보드 상태
+ /// 시작 행
+ /// 시작 열
+ /// 첫 번째 방향 인덱스
+ /// 두 번째(반대) 방향 인덱스
+ /// 연속된 돌의 총 개수
+ private int CountConsecutiveStones(Enums.PlayerType[,] board, int row, int col, int dir1, int dir2)
+ {
+ int count = 1; // 현재 위치의 돌 포함
+
+ // 첫 번째 방향으로 연속된 돌 확인
+ count += CountInDirection(board, row, col, dir1);
+
+ // 두 번째(반대) 방향으로 연속된 돌 확인
+ count += CountInDirection(board, row, col, dir2);
+
+ return count;
+ }
+
+ ///
+ /// 특정 방향으로의 연속된 돌 개수를 계산합니다.
+ ///
+ /// 현재 보드 상태
+ /// 시작 행
+ /// 시작 열
+ /// 방향 인덱스
+ /// 검사할 플레이어 타입
+ /// 해당 방향의 연속된 돌 개수
+ private int CountInDirection(Enums.PlayerType[,] board, int startRow, int startCol, int dirIndex)
+ {
+ int count = 0;
+ int dRow = Directions[dirIndex, 0];
+ int dCol = Directions[dirIndex, 1];
+
+ for (int i = 1; i < _boardSize; i++)
+ {
+ int newRow = startRow + dRow * i;
+ int newCol = startCol + dCol * i;
+
+ if (IsInBounds(newRow, newCol) && board[newRow, newCol] == Black)
+ {
+ count++;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ return count;
+ }
+}
diff --git a/Assets/Script/Renju/RenjuOverlineDetector.cs.meta b/Assets/Script/Renju/RenjuOverlineDetector.cs.meta
new file mode 100644
index 0000000..680b6a5
--- /dev/null
+++ b/Assets/Script/Renju/RenjuOverlineDetector.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: bbb7c61178964d4b8aeb7fb8375bf285
+timeCreated: 1742265525
\ No newline at end of file
diff --git a/Assets/Script/Renju/RenjuRuleChecker.cs b/Assets/Script/Renju/RenjuRuleChecker.cs
index 3071293..7beab19 100644
--- a/Assets/Script/Renju/RenjuRuleChecker.cs
+++ b/Assets/Script/Renju/RenjuRuleChecker.cs
@@ -1,690 +1,48 @@
using System.Collections.Generic;
-using System.Linq;
using UnityEngine;
public class RenjuRuleChecker: ForbiddenDetectorBase
{
- // 8방향을 나타내는 델타 배열 (가로, 세로, 대각선 방향)
- private static readonly int[,] directions = new int[8, 2]
- {
- { 1, 0 }, // 오른쪽
- { 1, 1 }, // 오른쪽 아래
- { 0, 1 }, // 아래
- { -1, 1 }, // 왼쪽 아래
- { -1, 0 }, // 왼쪽
- { -1, -1 }, // 왼쪽 위
- { 0, -1 }, // 위
- { 1, -1 } // 오른쪽 위
- };
+ private RenjuOverlineDetector _overlineDetactor = new();
+ private RenjuDoubleFourDetector _doubleFourDetactor = new();
+ private RenjuDoubleThreeDetector _doubleThreeDetector = new();
- // 기본 삼(3) 패턴 (흑돌 = 1, 빈칸 = 0)
- private static readonly int[][] threePatterns = new int[][]
- {
- new int[] { 0, 1, 1, 1, 0 }, // _OOO_
- new int[] { 0, 1, 1, 0, 1, 0 }, // _OO_O_
- new int[] { 0, 1, 0, 1, 1, 0 } // _O_OO_
- };
-
- // 기본 사(4) 패턴 (흑돌 = 1, 빈칸 = 0)
- private static readonly int[][] fourPatterns = new int[][]
- {
- new int[] { 0, 1, 1, 1, 1, 0 }, // _OOOO_
- new int[] { 0, 1, 1, 1, 0, 1, 0 }, // _OOO_O_
- new int[] { 0, 1, 1, 0, 1, 1, 0 }, // _OO_OO_
- new int[] { 0, 1, 0, 1, 1, 1, 0 } // _O_OOO_
- };
-
- // 디버그 모드 활성화 (문제 해결 시 사용)
- private bool debugMode = false;
-
- ///
- /// 현재 보드 상태에서 흑돌(PlayerA)이 둘 수 없는 금수 위치 목록을 반환합니다.
- ///
- /// 현재 보드 상태
- /// 금수 위치 목록
public List GetForbiddenMoves(Enums.PlayerType[,] board)
{
- int width = board.GetLength(0);
- int height = board.GetLength(1);
- List forbiddenMoves = new List();
-
- // 빈 칸마다 금수인지 확인
- for (int x = 0; x < width; x++)
+ List forbiddenMoves = new();
+ for (int row = 0; row < _boardSize; row++)
{
- for (int y = 0; y < height; y++)
+ for (int col = 0; col < _boardSize; col++)
{
- // 빈 칸만 검사
- if (board[x, y] != Enums.PlayerType.None)
- continue;
+ // ** 비어 있지 않으면 검사할 필요 없음 **
+ if (!IsEmptyPosition(board, row, col)) continue;
- // 해당 위치에 흑돌을 임시로 놓았다고 가정
- Enums.PlayerType[,] simulatedBoard = (Enums.PlayerType[,])board.Clone();
- simulatedBoard[x, y] = Enums.PlayerType.PlayerA;
-
- // 장목(6목 이상) 확인
- if (IsOverline(simulatedBoard, x, y))
+ // 장목 검사
+ if (_overlineDetactor.IsOverline(board, row, col))
{
- forbiddenMoves.Add(new Vector2Int(x, y));
+ Debug.Log("장목 금수 좌표 X축 : " + row + ", Y축 : " + col);
+ forbiddenMoves.Add(new Vector2Int(row, col));
continue;
}
- // 모든 방향별 패턴 정보 수집
- PatternInfo patternInfo = CollectAllPatterns(simulatedBoard, x, y);
-
- // 4-3 패턴 확인 (이 위치에 놓았을 때 열린 4가 1개, 열린 3이 1개 이상 생성되면 4-3 패턴)
- bool isFourThree = IsFourThreeAdvanced(patternInfo);
- if (isFourThree)
+ // 4-4 검사
+ if (_doubleFourDetactor.IsDoubleFour(board, row, col))
{
- // 4-3 패턴은 금수가 아님
+ Debug.Log("사사 금수 좌표 X축 : " + row + ", Y축 : " + col);
+ forbiddenMoves.Add(new Vector2Int(row, col));
continue;
}
- // 쌍삼(3-3) 또는 사사(4-4) 확인
- bool isThreeThree = IsThreeThreeAdvanced(patternInfo);
- bool isFourFour = IsFourFourAdvanced(patternInfo);
-
- // 금수 여부 판정
- if (isThreeThree || isFourFour)
+ // 3-3 검사
+ if (_doubleThreeDetector.IsDoubleThree(board, row, col))
{
- forbiddenMoves.Add(new Vector2Int(x, y));
+ Debug.Log("삼삼 금수 좌표 X축 : " + row + ", Y축 : " + col);
+ forbiddenMoves.Add(new Vector2Int(row, col));
}
}
}
return forbiddenMoves;
}
-
- ///
- /// 한 위치에서 모든 방향의 패턴 정보를 수집
- ///
- private PatternInfo CollectAllPatterns(Enums.PlayerType[,] board, int x, int y)
- {
- int width = board.GetLength(0);
- int height = board.GetLength(1);
- PatternInfo patternInfo = new PatternInfo();
-
- // 각 방향별로 패턴 수집
- for (int dir = 0; dir < 4; dir++)
- {
- // 해당 방향에서 연속된 돌의 배열 얻기
- List line = GetLineOfStones(board, x, y, dir, width, height, 8);
-
- // 열린 3 패턴 찾기
- List threePatterns = FindOpenThreePatterns(line, x, y, dir);
- patternInfo.OpenThreePatterns.AddRange(threePatterns);
-
- // 열린 4 패턴 찾기
- List fourPatterns = FindOpenFourPatterns(line, x, y, dir);
- patternInfo.OpenFourPatterns.AddRange(fourPatterns);
-
- // 방향별 돌 배열 저장 (디버깅용)
- if (debugMode)
- {
- patternInfo.LinesByDirection[dir] = line;
- }
- }
-
- return patternInfo;
- }
-
- ///
- /// 고도화된 4-3 패턴 확인 로직
- ///
- private bool IsFourThreeAdvanced(PatternInfo patternInfo)
- {
- // 열린 4와 열린 3 패턴 확인
- List fourPatterns = patternInfo.OpenFourPatterns;
- List threePatterns = patternInfo.OpenThreePatterns;
-
- if (fourPatterns.Count == 0 || threePatterns.Count == 0)
- {
- // 열린 4 또는 열린 3이 없으면 4-3 패턴이 아님
- return false;
- }
-
- // 방향별로 패턴 그룹화
- var fourPatternsByDir = fourPatterns.GroupBy(p => p.Direction).ToList();
- var threePatternsByDir = threePatterns.GroupBy(p => p.Direction).ToList();
-
- // 열린 4가 있는 각 방향에 대해
- foreach (var fourGroup in fourPatternsByDir)
- {
- int fourDir = fourGroup.Key;
-
- // 열린 3이 있는 각 방향에 대해
- foreach (var threeGroup in threePatternsByDir)
- {
- int threeDir = threeGroup.Key;
-
- // 서로 다른 방향이면 (열린 4와 열린 3이 다른 방향에 있으면)
- if (fourDir != threeDir)
- {
- // 4와 3 패턴이 서로 중첩되지 않는지 확인
- bool patternsOverlap = false;
-
- foreach (var fourPattern in fourGroup)
- {
- foreach (var threePattern in threeGroup)
- {
- // 중심 돌 외에 다른 공유 돌이 있는지 확인
- if (PatternsShareStones(fourPattern, threePattern))
- {
- patternsOverlap = true;
- break;
- }
- }
- if (patternsOverlap) break;
- }
-
- // 중첩되지 않는 4-3 패턴 발견됨
- if (!patternsOverlap)
- {
- return true;
- }
- }
- }
- }
-
- return false;
- }
-
- ///
- /// 두 패턴이 중심 돌 외에 다른 돌을 공유하는지 확인
- ///
- private bool PatternsShareStones(OpenFourPattern fourPattern, OpenThreePattern threePattern)
- {
- // 중심 돌 외에 공유하는 돌이 있는지 확인
- foreach (Vector2Int pos4 in fourPattern.StonePositions)
- {
- foreach (Vector2Int pos3 in threePattern.StonePositions)
- {
- // 두 패턴이 같은 좌표의 돌을 공유하고, 그것이 중심 돌이 아니면
- if (pos4.x == pos3.x && pos4.y == pos3.y &&
- !(pos4.x == fourPattern.CenterX && pos4.y == fourPattern.CenterY))
- {
- return true;
- }
- }
- }
-
- return false;
- }
-
- ///
- /// 고도화된 삼삼 확인 로직
- ///
- private bool IsThreeThreeAdvanced(PatternInfo patternInfo)
- {
- List threePatterns = patternInfo.OpenThreePatterns;
-
- // 열린 3 패턴이 2개 미만이면 삼삼이 아님
- if (threePatterns.Count < 2)
- return false;
-
- // 방향별로 패턴 그룹화
- var patternsByDir = threePatterns.GroupBy(p => p.Direction).ToList();
-
- // 서로 다른 방향의 패턴 쌍을 검사
- for (int i = 0; i < patternsByDir.Count; i++)
- {
- for (int j = i + 1; j < patternsByDir.Count; j++)
- {
- // 첫 번째 방향의 모든 패턴
- var patternsDir1 = patternsByDir[i].ToList();
- // 두 번째 방향의 모든 패턴
- var patternsDir2 = patternsByDir[j].ToList();
-
- // 두 방향의 패턴들이 서로 중첩되지 않는지 확인
- bool foundNonOverlapping = false;
-
- foreach (var pattern1 in patternsDir1)
- {
- if (foundNonOverlapping) break;
-
- foreach (var pattern2 in patternsDir2)
- {
- // 중심 돌 외에 다른 공유 돌이 없으면 비중첩 삼삼
- if (!PatternsOverlap(pattern1, pattern2))
- {
- foundNonOverlapping = true;
- break;
- }
- }
- }
-
- // 비중첩 삼삼 발견
- if (foundNonOverlapping)
- {
- return true;
- }
- }
- }
-
- return false;
- }
-
- ///
- /// 두 열린 3 패턴이 중첩되는지 확인
- ///
- private bool PatternsOverlap(OpenThreePattern pattern1, OpenThreePattern pattern2)
- {
- // 중심 돌 외에 공유하는 돌이 있는지 확인
- foreach (Vector2Int pos1 in pattern1.StonePositions)
- {
- foreach (Vector2Int pos2 in pattern2.StonePositions)
- {
- // 두 패턴이 같은 좌표의 돌을 공유하고, 그것이 중심 돌이 아니면 중첩
- if (pos1.x == pos2.x && pos1.y == pos2.y &&
- !(pos1.x == pattern1.CenterX && pos1.y == pattern1.CenterY))
- {
- return true;
- }
- }
- }
-
- return false;
- }
-
- ///
- /// 고도화된 사사 확인 로직
- ///
- private bool IsFourFourAdvanced(PatternInfo patternInfo)
- {
- List fourPatterns = patternInfo.OpenFourPatterns;
-
- // 열린 4 패턴이 2개 미만이면 사사가 아님
- if (fourPatterns.Count < 2)
- return false;
-
- // 방향별로 패턴 그룹화
- var patternsByDir = fourPatterns.GroupBy(p => p.Direction).ToList();
-
- // 서로 다른 방향의 패턴이 2개 이상이면 사사
- if (patternsByDir.Count >= 2)
- return true;
-
- // 같은 방향에서도 중첩되지 않는 패턴이 있는지 확인
- foreach (var group in patternsByDir)
- {
- var patterns = group.ToList();
-
- for (int i = 0; i < patterns.Count - 1; i++)
- {
- for (int j = i + 1; j < patterns.Count; j++)
- {
- // 중심 돌 외에 다른 공유 돌이 없으면 비중첩 사사
- if (!FourPatternsOverlap(patterns[i], patterns[j]))
- {
- return true;
- }
- }
- }
- }
-
- return false;
- }
-
- ///
- /// 두 열린 4 패턴이 중첩되는지 확인
- ///
- private bool FourPatternsOverlap(OpenFourPattern pattern1, OpenFourPattern pattern2)
- {
- // 중심 돌 외에 공유하는 돌이 있는지 확인
- foreach (Vector2Int pos1 in pattern1.StonePositions)
- {
- foreach (Vector2Int pos2 in pattern2.StonePositions)
- {
- // 두 패턴이 같은 좌표의 돌을 공유하고, 그것이 중심 돌이 아니면 중첩
- if (pos1.x == pos2.x && pos1.y == pos2.y &&
- !(pos1.x == pattern1.CenterX && pos1.y == pattern1.CenterY))
- {
- return true;
- }
- }
- }
-
- return false;
- }
-
- ///
- /// 한 방향의 돌 배열 얻기 (지정된 범위만큼 확장)
- ///
- private List GetLineOfStones(Enums.PlayerType[,] board, int x, int y, int dirIndex, int width, int height, int extensionRange = 5)
- {
- List line = new List();
-
- // 중앙에 놓인 현재 돌 추가
- line.Add(new Stone { Type = board[x, y], X = x, Y = y, RelativePosition = 0 });
-
- // 양방향으로 지정된 칸수만큼 확장
- for (int extendDir = 0; extendDir < 2; extendDir++)
- {
- int actualDirIndex = extendDir == 0 ? dirIndex : dirIndex + 4; // 반대 방향으로도 확장
- int dx = directions[actualDirIndex, 0];
- int dy = directions[actualDirIndex, 1];
-
- // 지정된 칸수만큼 확장
- for (int i = 1; i <= extensionRange; i++)
- {
- int nx = x + (dx * i);
- int ny = y + (dy * i);
-
- if (!IsInBounds(nx, ny))
- break;
-
- Stone stone = new Stone
- {
- Type = board[nx, ny],
- X = nx,
- Y = ny,
- RelativePosition = extendDir == 0 ? i : -i
- };
-
- if (extendDir == 0)
- line.Add(stone); // 정방향은 뒤에 추가
- else
- line.Insert(0, stone); // 역방향은 앞에 추가
- }
- }
-
- return line;
- }
-
- ///
- /// 주어진 라인에서 열린 3 패턴 찾기 (백돌로 막혀있는 경우 고려)
- ///
- private List FindOpenThreePatterns(List line, int centerX, int centerY, int dirIndex)
- {
- List result = new List();
- int lineLength = line.Count;
-
- // 각 열린 3 패턴에 대해 검사
- foreach (int[] pattern in threePatterns)
- {
- int patternLength = pattern.Length;
-
- // 라인을 슬라이딩 윈도우로 검사
- for (int i = 0; i <= lineLength - patternLength; i++)
- {
- bool patternMatches = true;
- List stonePositions = new List();
- List stoneRelativePositions = new List();
-
- // 패턴과 라인이 일치하는지 확인
- for (int j = 0; j < patternLength; j++)
- {
- Stone stone = line[i + j];
-
- // 패턴의 1은 흑돌(PlayerA)과 일치해야 함
- if (pattern[j] == 1)
- {
- if (stone.Type != Enums.PlayerType.PlayerA)
- {
- patternMatches = false;
- break;
- }
- stonePositions.Add(new Vector2Int(stone.X, stone.Y));
- stoneRelativePositions.Add(stone.RelativePosition);
- }
- // 패턴의 0은 빈 칸(None)과 일치해야 함
- else if (pattern[j] == 0 && stone.Type != Enums.PlayerType.None)
- {
- patternMatches = false;
- break;
- }
- }
-
- // 패턴이 일치하고, 중심 돌이 패턴에 포함되는 경우에만 열린 3으로 인정
- if (patternMatches && stonePositions.Any(pos => pos.x == centerX && pos.y == centerY))
- {
- // 패턴 양쪽의 확장성 검사 (백돌로 막혀있는지 확인)
- bool isReallyOpen = IsPatternReallyOpen(line, i, i + patternLength - 1);
-
- // 진짜 열린 3인 경우에만 추가
- if (isReallyOpen)
- {
- result.Add(new OpenThreePattern
- {
- Direction = dirIndex,
- StonePositions = stonePositions,
- RelativePositions = stoneRelativePositions,
- CenterX = centerX,
- CenterY = centerY
- });
- }
- }
- }
- }
-
- return result;
- }
-
- ///
- /// 주어진 라인에서 열린 4 패턴 찾기 (백돌로 막혀있는 경우 고려)
- ///
- private List FindOpenFourPatterns(List line, int centerX, int centerY, int dirIndex)
- {
- List result = new List();
- int lineLength = line.Count;
-
- // 각 열린 4 패턴에 대해 검사
- foreach (int[] pattern in fourPatterns)
- {
- int patternLength = pattern.Length;
-
- // 라인을 슬라이딩 윈도우로 검사
- for (int i = 0; i <= lineLength - patternLength; i++)
- {
- bool patternMatches = true;
- List stonePositions = new List();
-
- // 패턴과 라인이 일치하는지 확인
- for (int j = 0; j < patternLength; j++)
- {
- Stone stone = line[i + j];
-
- // 패턴의 1은 흑돌(PlayerA)과 일치해야 함
- if (pattern[j] == 1)
- {
- if (stone.Type != Enums.PlayerType.PlayerA)
- {
- patternMatches = false;
- break;
- }
- stonePositions.Add(new Vector2Int(stone.X, stone.Y));
- }
- // 패턴의 0은 빈 칸(None)과 일치해야 함
- else if (pattern[j] == 0 && stone.Type != Enums.PlayerType.None)
- {
- patternMatches = false;
- break;
- }
- }
-
- // 패턴이 일치하고, 중심 돌이 패턴에 포함되는 경우에만 열린 4로 인정
- if (patternMatches && stonePositions.Any(pos => pos.x == centerX && pos.y == centerY))
- {
- // 패턴 양쪽의 확장성 검사 (백돌로 막혀있는지 확인)
- bool isReallyOpen = IsPatternReallyOpen(line, i, i + patternLength - 1);
-
- // 진짜 열린 4인 경우에만 추가
- if (isReallyOpen)
- {
- result.Add(new OpenFourPattern
- {
- Direction = dirIndex,
- StonePositions = stonePositions,
- CenterX = centerX,
- CenterY = centerY
- });
- }
- }
- }
- }
-
- return result;
- }
-
- ///
- /// 패턴이 정말로 열려있는지 확인 (백돌로 막혀있지 않은지)
- ///
- private bool IsPatternReallyOpen(List line, int startIdx, int endIdx)
- {
- int lineLength = line.Count;
- bool leftSideOpen = false;
- bool rightSideOpen = false;
-
- // 좌측 확장성 검사
- int leftCheckIdx = startIdx - 1;
- int leftExtendableSpaces = 0;
-
- while (leftCheckIdx >= 0)
- {
- if (line[leftCheckIdx].Type == Enums.PlayerType.None)
- {
- leftExtendableSpaces++;
- leftCheckIdx--;
- }
- else if (line[leftCheckIdx].Type == Enums.PlayerType.PlayerB)
- {
- // 백돌로 막혀있음
- break;
- }
- else
- {
- // 흑돌이 있음 (이미 다른 패턴의 일부일 수 있음)
- break;
- }
- }
-
- // 우측 확장성 검사
- int rightCheckIdx = endIdx + 1;
- int rightExtendableSpaces = 0;
-
- while (rightCheckIdx < lineLength)
- {
- if (line[rightCheckIdx].Type == Enums.PlayerType.None)
- {
- rightExtendableSpaces++;
- rightCheckIdx++;
- }
- else if (line[rightCheckIdx].Type == Enums.PlayerType.PlayerB)
- {
- // 백돌로 막혀있음
- break;
- }
- else
- {
- // 흑돌이 있음 (이미 다른 패턴의 일부일 수 있음)
- break;
- }
- }
-
- // 양쪽이 모두 백돌로 막혀있다면 진짜 열린 3이 아님
- if (leftCheckIdx >= 0 && rightCheckIdx < lineLength &&
- line[leftCheckIdx].Type == Enums.PlayerType.PlayerB &&
- line[rightCheckIdx].Type == Enums.PlayerType.PlayerB)
- {
- return false;
- }
-
- // 적어도 한쪽에 빈 공간이 있으면 열린 패턴으로 간주
- leftSideOpen = leftCheckIdx < 0 || line[leftCheckIdx].Type != Enums.PlayerType.PlayerB;
- rightSideOpen = rightCheckIdx >= lineLength || line[rightCheckIdx].Type != Enums.PlayerType.PlayerB;
-
- // 한쪽이라도 열려있어야 열린 패턴으로 인정
- return leftSideOpen || rightSideOpen;
- }
-
- ///
- /// 장목(6목 이상) 금수 검사
- ///
- private bool IsOverline(Enums.PlayerType[,] board, int x, int y)
- {
- int width = board.GetLength(0);
- int height = board.GetLength(1);
-
- // 4방향(가로, 세로, 대각선1, 대각선2)에 대해 6목 이상 검사
- for (int dir = 0; dir < 4; dir++)
- {
- int count = 1; // 현재 위치 포함
-
- // 정방향 탐색
- int dx = directions[dir, 0];
- int dy = directions[dir, 1];
- int nx = x + dx;
- int ny = y + dy;
-
- while (IsInBounds(nx, ny) && board[nx, ny] == Enums.PlayerType.PlayerA)
- {
- count++;
- nx += dx;
- ny += dy;
- }
-
- // 역방향 탐색
- dx = directions[dir + 4, 0];
- dy = directions[dir + 4, 1];
- nx = x + dx;
- ny = y + dy;
-
- while (IsInBounds(nx, ny) && board[nx, ny] == Enums.PlayerType.PlayerA)
- {
- count++;
- nx += dx;
- ny += dy;
- }
-
- // 6목 이상이면 장목(금수)
- if (count >= 6)
- return true;
- }
-
- return false;
- }
-
- ///
- /// 돌 정보를 저장하는 구조체
- ///
- private struct Stone
- {
- public Enums.PlayerType Type;
- public int X;
- public int Y;
- public int RelativePosition; // 중심 돌로부터의 상대적 위치
- }
-
- ///
- /// 열린 3 패턴 정보를 저장하는 클래스
- ///
- private class OpenThreePattern
- {
- public int Direction;
- public List StonePositions;
- public List RelativePositions; // 중심 돌로부터의 상대적 위치
- public int CenterX;
- public int CenterY;
- }
-
- ///
- /// 열린 4 패턴 정보를 저장하는 클래스
- ///
- private class OpenFourPattern
- {
- public int Direction;
- public List StonePositions;
- public int CenterX;
- public int CenterY;
- }
-
- ///
- /// 한 위치의 모든 패턴 정보를 저장하는 클래스
- ///
- private class PatternInfo
- {
- public List OpenThreePatterns = new List();
- public List OpenFourPatterns = new List();
- public Dictionary> LinesByDirection = new Dictionary>();
- }
}