From eedc964ea931b33efa7ade86eb70a18f48b4a83e Mon Sep 17 00:00:00 2001 From: fiore Date: Tue, 18 Mar 2025 11:03:22 +0900 Subject: [PATCH] =?UTF-8?q?DO-31=20feat=20=EB=A0=8C=EC=A3=BC=EB=A3=B0=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 일부 엣지 케이스 미적용 --- Assets/Script/Common/Constants.cs | 1 + Assets/Script/Game/GameLogic.cs | 35 +- Assets/Script/Renju/DoubleFourDetector.cs | 96 --- .../Script/Renju/DoubleFourDetector.cs.meta | 3 - Assets/Script/Renju/DoubleThreeDetector.cs | 201 ----- .../Script/Renju/DoubleThreeDetector.cs.meta | 3 - ...tectorBase.cs => ForbiddenDetectorBase.cs} | 8 +- ....cs.meta => ForbiddenDetectorBase.cs.meta} | 0 Assets/Script/Renju/OpenFourDetector.cs | 149 ---- Assets/Script/Renju/OpenFourDetector.cs.meta | 3 - Assets/Script/Renju/OverlineDetector.cs | 88 --- Assets/Script/Renju/OverlineDetector.cs.meta | 3 - .../Renju/RenjuForbiddenMoveDetector.cs | 104 +-- Assets/Script/Renju/RenjuRuleChecker.cs | 690 ++++++++++++++++++ Assets/Script/Renju/RenjuRuleChecker.cs.meta | 3 + 15 files changed, 722 insertions(+), 665 deletions(-) delete mode 100644 Assets/Script/Renju/DoubleFourDetector.cs delete mode 100644 Assets/Script/Renju/DoubleFourDetector.cs.meta delete mode 100644 Assets/Script/Renju/DoubleThreeDetector.cs delete mode 100644 Assets/Script/Renju/DoubleThreeDetector.cs.meta rename Assets/Script/Renju/{RenjuDetectorBase.cs => ForbiddenDetectorBase.cs} (81%) rename Assets/Script/Renju/{RenjuDetectorBase.cs.meta => ForbiddenDetectorBase.cs.meta} (100%) delete mode 100644 Assets/Script/Renju/OpenFourDetector.cs delete mode 100644 Assets/Script/Renju/OpenFourDetector.cs.meta delete mode 100644 Assets/Script/Renju/OverlineDetector.cs delete mode 100644 Assets/Script/Renju/OverlineDetector.cs.meta create mode 100644 Assets/Script/Renju/RenjuRuleChecker.cs create mode 100644 Assets/Script/Renju/RenjuRuleChecker.cs.meta diff --git a/Assets/Script/Common/Constants.cs b/Assets/Script/Common/Constants.cs index f847b15..e09e345 100644 --- a/Assets/Script/Common/Constants.cs +++ b/Assets/Script/Common/Constants.cs @@ -2,4 +2,5 @@ { public const string ServerURL = "http://localhost:3000"; public const string GameServerURL = "ws://localhost:3000"; + public const int BoardSize = 15; } \ No newline at end of file diff --git a/Assets/Script/Game/GameLogic.cs b/Assets/Script/Game/GameLogic.cs index cc3b28c..7e74ab2 100644 --- a/Assets/Script/Game/GameLogic.cs +++ b/Assets/Script/Game/GameLogic.cs @@ -14,14 +14,13 @@ public class GameLogic : MonoBehaviour public int lastRow; public int lastCol; - #region Renju Members +#region Renju Members // 렌주룰 금수 검사기 private RenjuForbiddenMoveDetector _forbiddenDetector; // 현재 금수 위치 목록 - private List<(Vector2Int position, ForbiddenType type)> _forbiddenMoves = new List<(Vector2Int, ForbiddenType)>(); - - #endregion + private List _forbiddenMoves = new List(); +#endregion public GameLogic(StoneController stoneController, Enums.GameType gameType) { @@ -33,8 +32,10 @@ public class GameLogic : MonoBehaviour lastRow = -1; lastCol = -1; +#region Renju Init // 금수 감지기 초기화 _forbiddenDetector = new RenjuForbiddenMoveDetector(); +#endregion currentTurn = Enums.PlayerType.PlayerA; @@ -45,8 +46,10 @@ public class GameLogic : MonoBehaviour { currentTurn = player; +#region Renju Turn Set // 턴이 변경될 때마다 금수 위치 업데이트 UpdateForbiddenMoves(); +#endregion _stoneController.OnStoneClickedDelegate = (row, col) => { @@ -110,31 +113,22 @@ public class GameLogic : MonoBehaviour _stoneController.SetStoneState(state, row, col); } - - #region Renju Rule Detector - +#region Renju Rule Detector // 금수 위치 업데이트 및 표시 private void UpdateForbiddenMoves() { - // TODO: 이전 금수 표시 제거 ClearForbiddenMarks(); - // 흑돌 차례에만 금수 규칙 적용 if (currentTurn == Enums.PlayerType.PlayerA) { - // 모든 금수의 위치 찾기 - _forbiddenMoves = _forbiddenDetector.FindAllForbiddenMoves(_board); - foreach (var forbiddenMove in _forbiddenMoves) + _forbiddenMoves = _forbiddenDetector.RenjuForbiddenMove(_board); + + foreach (var pos in _forbiddenMoves) { - Vector2Int pos = forbiddenMove.position; SetStoneNewState(Enums.StoneState.Blocked, pos.x, pos.y); } } - else - { - // 백돌 차례면 금수 목록 비우기 - _forbiddenMoves.Clear(); - } + } // 이전에 표시된 금수 마크 제거 @@ -142,13 +136,12 @@ public class GameLogic : MonoBehaviour { foreach (var forbiddenMove in _forbiddenMoves) { - Vector2Int pos = forbiddenMove.position; + Vector2Int pos = forbiddenMove; if (_board[pos.x, pos.y] == Enums.PlayerType.None) { SetStoneNewState(Enums.StoneState.None, pos.x, pos.y); } } } - - #endregion +#endregion } diff --git a/Assets/Script/Renju/DoubleFourDetector.cs b/Assets/Script/Renju/DoubleFourDetector.cs deleted file mode 100644 index 45b560c..0000000 --- a/Assets/Script/Renju/DoubleFourDetector.cs +++ /dev/null @@ -1,96 +0,0 @@ -using UnityEngine; - -// 렌주 룰에서 사사 금수 위치를 감지하는 클래스 -public class DoubleFourDetector : RenjuDetectorBase -{ - /// - /// 특정 위치에 흑돌을 놓았을 때 4-4 금수가 생기는지 검사 - /// - /// 현재 보드 상태 - /// 행 좌표 - /// 열 좌표 - /// 4-4 금수 여부 (true: 금수, false: 금수 아님) - public bool IsDoubleFour(Enums.PlayerType[,] board, int row, int col) - { - // 빈 위치가 아니면 검사할 필요 없음 - if (!IsEmptyPosition(board, row, col)) - { - return false; - } - - // 임시 보드 생성 - Enums.PlayerType[,] tempBoard = (Enums.PlayerType[,])board.Clone(); - - // 해당 위치에 흑돌 놓기 - tempBoard[row, col] = Enums.PlayerType.PlayerA; - - // 4목 개수 카운트 - int fourCount = 0; - - // 각 방향으로 4목 검사 - foreach (Vector2Int dir in directions) - { - int count = CountConsecutiveStones(tempBoard, row, col, dir); - - // 정확히 4개의 연속된 돌인 경우 - if (count == 4) - { - fourCount++; - - // 디버깅 정보 - Debug.Log($"4목 발견: ({row}, {col}) 방향: ({dir.x}, {dir.y})"); - - // 이미 2개의 4목을 찾았으면 4-4 금수임 - if (fourCount >= 2) - { - return true; - } - } - } - - return false; - } - - private int CountConsecutiveStones(Enums.PlayerType[,] board, int row, int col, Vector2Int dir) - { - Enums.PlayerType stone = board[row, col]; - if (stone != Enums.PlayerType.PlayerA) - { - return 0; - } - - int count = 1; // 현재 돌 포함 - int dRow = dir.x; - int dCol = dir.y; - - // 정방향 검사 - for (int i = 1; i <= 5; i++) // 최대 5칸까지만 검사 - { - int r = row + i * dRow; - int c = col + i * dCol; - - if (!IsValidPosition(r, c) || board[r, c] != stone) - { - break; - } - - count++; - } - - // 역방향 검사 - for (int i = 1; i <= 5; i++) // 최대 5칸까지만 검사 - { - int r = row - i * dRow; - int c = col - i * dCol; - - if (!IsValidPosition(r, c) || board[r, c] != stone) - { - break; - } - - count++; - } - - return count; - } -} diff --git a/Assets/Script/Renju/DoubleFourDetector.cs.meta b/Assets/Script/Renju/DoubleFourDetector.cs.meta deleted file mode 100644 index ef7bdce..0000000 --- a/Assets/Script/Renju/DoubleFourDetector.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 62718ad321de4ed0b6569d09b8e78716 -timeCreated: 1741961153 \ No newline at end of file diff --git a/Assets/Script/Renju/DoubleThreeDetector.cs b/Assets/Script/Renju/DoubleThreeDetector.cs deleted file mode 100644 index 426919a..0000000 --- a/Assets/Script/Renju/DoubleThreeDetector.cs +++ /dev/null @@ -1,201 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using UnityEngine; - -/// -/// 렌주 룰에서 삼삼 금수 위치를 감지하는 클래스 -/// -public class DoubleThreeDetector :RenjuDetectorBase -{ - // 열린 4 감지기 - private OpenFourDetector _openFourDetector = new(); - - // 디버깅용 방향 이름 - private readonly string[] _directionNames = new string[] - { - "가로", "세로", "우하향 대각선", "우상향 대각선" - }; - - private readonly string[] _rowNames = new[] - { - "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O" - }; - - /// - /// 해당 위치가 금수인지 반환하는 함수 - /// - /// 현재 보드 상태 - /// row 좌표 - /// col 좌표 - /// 금수 여부 - public bool IsDoubleThree(Enums.PlayerType[,] board, int row, int col) - { - // 빈 위치가 아니면 검사할 필요 없음 - if (!IsEmptyPosition(board, row, col)) - { - return false; - } - - // 임시 보드 생성 - Enums.PlayerType[,] tempBoard = (Enums.PlayerType[,])board.Clone(); - - // 해당 위치에 흑돌 놓기 - tempBoard[row, col] = Enums.PlayerType.PlayerA; - - // 열린 3의 방향 목록 - HashSet openThreeDirectionIndices = new HashSet(); - - // 각 빈 위치에 대해 가상으로 돌을 놓아보고 열린 4가 되는지 확인 - for (int r = 0; r < _boardSize; r++) - { - for (int c = 0; c < _boardSize; c++) - { - // 빈 위치만 검사 (원래 위치 제외) - if (tempBoard[r, c] != Enums.PlayerType.None || (r == row && c == col)) - { - continue; - } - - // 이 위치에 가상으로 돌을 놓았을 때 열린 4가 되는지 확인 - var (isOpenFour, openFourDirs) = _openFourDetector.DetectOpenFour(tempBoard, r, c, Enums.PlayerType.PlayerA); - - if (isOpenFour) - { - // 각 방향에 대해 원래 위치와 관련된 열린 3인지 확인 - foreach (Vector2Int dir in openFourDirs) - { - // 이 방향에서 원래 위치가 포함된 라인에 열린 4가 만들어지는지 확인 - if (IsPositionInSameLine(row, col, r, c, dir)) - { - // 방향 인덱스 찾기 - int dirIndex = GetDirectionIndex(dir); - if (dirIndex >= 0) - { - openThreeDirectionIndices.Add(dirIndex); - Debug.Log($"위치 ({_rowNames[row]}{col})에서 {_directionNames[dirIndex]} 방향으로 열린 3 발견 - ({_rowNames[r]}{c})에 놓으면 열린 4 형성"); - } - } - } - } - } - } - - // 서로 다른 방향에서 열린 3이 2개 이상 발견되면 3-3 금수 후보 - bool isDoubleThreeCandidate = openThreeDirectionIndices.Count >= 2; - - if (isDoubleThreeCandidate) - { - // 각 방향별로 진짜 열린 3인지 확인 - foreach (int dirIndex in new List(openThreeDirectionIndices)) - { - Vector2Int dir = directions[dirIndex]; - - // 이 방향에서 실제로 열린 4가 만들어질 수 있는지 확인 - bool canFormOpenFour = false; - - // 보드의 모든 빈 위치를 검사 - for (int r = 0; r < _boardSize; r++) - { - for (int c = 0; c < _boardSize; c++) - { - // 빈 위치만 검사 (원래 위치 제외) - if (tempBoard[r, c] != Enums.PlayerType.None || (r == row && c == col)) - { - continue; - } - - // 이 위치가 동일한 방향에 있는지 확인 - if (IsPositionInSameLine(row, col, r, c, dir)) - { - // 이 위치에 흑돌을 놓고 열린 4가 되는지 확인 - Enums.PlayerType[,] testBoard = (Enums.PlayerType[,])tempBoard.Clone(); - testBoard[r, c] = Enums.PlayerType.PlayerA; - - var (isOpenFour, _) = _openFourDetector.DetectOpenFour(testBoard, r, c, Enums.PlayerType.PlayerA); - - if (isOpenFour) - { - canFormOpenFour = true; - break; - } - } - } - - if (canFormOpenFour) - { - break; - } - } - - // 이 방향에서 열린 4를 만들 수 없다면 거짓 열린 3 - if (!canFormOpenFour) - { - openThreeDirectionIndices.Remove(dirIndex); - Debug.Log($"위치 ({row},{col})에서 {_directionNames[dirIndex]} 방향은 거짓 열린 3"); - } - } - - // 실제 열린 3이 2개 이상인 경우만 3-3 금수 - bool isDoubleThree = openThreeDirectionIndices.Count >= 2; - - if (isDoubleThree) - { - string directions = string.Join(", ", openThreeDirectionIndices.Select(i => _directionNames[i])); - Debug.Log($"위치 ({row},{col})에서 3-3 금수 감지! 진짜 열린 3 방향: {directions}"); - } - - return isDoubleThree; - } - - return false; - } - - /// - /// 두 위치가 같은 라인(직선) 상에 있는지 확인 - /// - private bool IsPositionInSameLine(int row1, int col1, int row2, int col2, Vector2Int dir) - { - int dRow = dir.x; - int dCol = dir.y; - - // 행 차이와 열 차이가 방향 벡터의 배수인지 확인 - int rowDiff = row2 - row1; - int colDiff = col2 - col1; - - // 방향 벡터가 0인 경우 처리 - if (dRow == 0) - return colDiff % dCol == 0 && rowDiff == 0; - if (dCol == 0) - return rowDiff % dRow == 0 && colDiff == 0; - - // 두 위치의 차이가 방향 벡터의 배수인지 확인 - return (rowDiff * dCol == colDiff * dRow); - } - - /// - /// 방향 벡터의 인덱스 찾기 - /// - private int GetDirectionIndex(Vector2Int dir) - { - // 방향 벡터 정규화 - Vector2Int normalizedDir = NormalizeDirection(dir); - - for (int i = 0; i < directions.Length; i++) - { - if (directions[i].Equals(normalizedDir)) - return i; - } - - return -1; - } - - /// - /// 방향 벡터 정규화 (항상 x가 양수이거나, x가 0일 때 y가 양수) - /// - private Vector2Int NormalizeDirection(Vector2Int dir) - { - if (dir.x < 0 || (dir.x == 0 && dir.y < 0)) - return new Vector2Int(-dir.x, -dir.y); - return dir; - } -} \ No newline at end of file diff --git a/Assets/Script/Renju/DoubleThreeDetector.cs.meta b/Assets/Script/Renju/DoubleThreeDetector.cs.meta deleted file mode 100644 index 1ad31a5..0000000 --- a/Assets/Script/Renju/DoubleThreeDetector.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: cb7f46b4a369421aa9fbcb2d1b53c68a -timeCreated: 1741960686 \ No newline at end of file diff --git a/Assets/Script/Renju/RenjuDetectorBase.cs b/Assets/Script/Renju/ForbiddenDetectorBase.cs similarity index 81% rename from Assets/Script/Renju/RenjuDetectorBase.cs rename to Assets/Script/Renju/ForbiddenDetectorBase.cs index ace6e2c..f770527 100644 --- a/Assets/Script/Renju/RenjuDetectorBase.cs +++ b/Assets/Script/Renju/ForbiddenDetectorBase.cs @@ -1,6 +1,6 @@ using UnityEngine; -public class RenjuDetectorBase +public class ForbiddenDetectorBase { // 방향 배열 (가로, 세로, 대각선) private protected Vector2Int[] directions = @@ -12,12 +12,12 @@ public class RenjuDetectorBase }; // 15*15 보드 사이즈 - private protected int _boardSize = 15; + private protected int _boardSize = Constants.BoardSize; /// /// 좌표가 보드 범위 내에 있는지 확인 /// - private protected bool IsValidPosition(int row, int col) + private protected bool IsInBounds(int row, int col) { var inBoardSizeRow = row >= 0 && row < _boardSize; var inBoardSizeCol = col >= 0 && col < _boardSize; @@ -30,7 +30,7 @@ public class RenjuDetectorBase /// private protected bool IsEmptyPosition(Enums.PlayerType[,] board, int row, int col) { - if (!IsValidPosition(row, col)) return false; + if (!IsInBounds(row, col)) return false; return board[row, col] == Enums.PlayerType.None; } diff --git a/Assets/Script/Renju/RenjuDetectorBase.cs.meta b/Assets/Script/Renju/ForbiddenDetectorBase.cs.meta similarity index 100% rename from Assets/Script/Renju/RenjuDetectorBase.cs.meta rename to Assets/Script/Renju/ForbiddenDetectorBase.cs.meta diff --git a/Assets/Script/Renju/OpenFourDetector.cs b/Assets/Script/Renju/OpenFourDetector.cs deleted file mode 100644 index 950dd9f..0000000 --- a/Assets/Script/Renju/OpenFourDetector.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; - -/// -/// 렌주에서 '열린 4'를 감지하는 클래스 -/// 열린 4: 한 수를 놓으면 5목을 완성할 수 있는 4개의 연속된 돌 -/// -public class OpenFourDetector : RenjuDetectorBase -{ - /// - /// 특정 위치에 돌을 놓았을 때 열린 4가 형성되는지 확인 - /// - /// 현재 보드 상태 - /// 행 좌표 - /// 열 좌표 - /// 검사할 플레이어 타입 (PlayerA 또는 PlayerB) - /// 열린 4 형성 여부 및 방향 목록 - public (bool isOpenFour, List directions) DetectOpenFour(Enums.PlayerType[,] board, int row, int col, - Enums.PlayerType playerType) - { - if (!IsValidPosition(row, col)) - { - return (false, new List()); - } - - // 임시 보드 생성 - Enums.PlayerType[,] tempBoard = (Enums.PlayerType[,])board.Clone(); - - // 원래 값 저장 - Enums.PlayerType originalValue = tempBoard[row, col]; - - // 해당 위치에 검사할 플레이어의 돌 놓기 - tempBoard[row, col] = playerType; - - List openFourDirections = new List(); - - // 각 방향으로 열린 4 검사 - foreach (Vector2Int dir in directions) - { - if (HasOpenFourInDirection(tempBoard, row, col, dir, playerType)) - { - openFourDirections.Add(dir); - } - } - - // 원래 값 복원 - tempBoard[row, col] = originalValue; - - return (openFourDirections.Count > 0, openFourDirections); - } - - /// - /// 특정 방향으로 열린 4가 있는지 검사 - /// - private bool HasOpenFourInDirection(Enums.PlayerType[,] board, int row, int col, Vector2Int dir, Enums.PlayerType playerType) - { - // 해당 방향으로 연속된 돌 개수 세기 - int consecutiveCount = CountConsecutiveStones(board, row, col, dir, playerType); - - // 정확히 4개의 연속된 돌이 있는지 확인 - if (consecutiveCount != 4) - { - return false; - } - - // 양쪽이 열려있는지 확인 - bool frontOpen = IsSideOpen(board, row, col, dir, true, playerType); - bool backOpen = IsSideOpen(board, row, col, dir, false, playerType); - - // 적어도 한쪽은 열려있어야 '열린 4' - return frontOpen || backOpen; - } - - /// - /// 특정 방향으로 연속된 같은 색 돌의 개수를 세는 함수 - /// - private int CountConsecutiveStones(Enums.PlayerType[,] board, int row, int col, Vector2Int dir, Enums.PlayerType playerType) - { - int count = 1; // 현재 위치 포함 - - // 정방향으로 연속된 돌 세기 - for (int i = 1; i <= 4; i++) // 최대 4칸까지 검사 (5목 이상이면 별도 처리) - { - int r = row + i * dir.x; - int c = col + i * dir.y; - - if (!IsValidPosition(r, c) || board[r, c] != playerType) - { - break; - } - - count++; - } - - // 역방향으로 연속된 돌 세기 - for (int i = 1; i <= 4; i++) - { - int r = row - i * dir.x; - int c = col - i * dir.y; - - if (!IsValidPosition(r, c) || board[r, c] != playerType) - { - break; - } - - count++; - } - - return count; - } - - /// - /// 특정 방향의 한쪽이 열려있는지 확인 (빈 칸인지, 상대방 돌이나 경계로 막혀있지 않은지) - /// - private bool IsSideOpen(Enums.PlayerType[,] board, int row, int col, Vector2Int dir, bool isFrontSide, Enums.PlayerType playerType) - { - // 확인할 방향 설정 - int dirMultiplier = isFrontSide ? 1 : -1; - - // 연속된 돌의 끝에서 한 칸 더 간 위치 확인 - int consecutiveCount = 0; - int currentRow = row; - int currentCol = col; - - // 해당 방향으로 연속된 돌 찾기 - while (true) - { - int r = currentRow + dirMultiplier * dir.x; - int c = currentCol + dirMultiplier * dir.y; - - if (!IsValidPosition(r, c) || board[r, c] != playerType) - { - break; - } - - consecutiveCount++; - currentRow = r; - currentCol = c; - } - - // 연속된 돌의 끝에서 한 칸 더 간 위치 - int nextRow = currentRow + dirMultiplier * dir.x; - int nextCol = currentCol + dirMultiplier * dir.y; - - // 해당 위치가 보드 내부이고 빈 칸이면 '열린' 상태 - return IsValidPosition(nextRow, nextCol) && board[nextRow, nextCol] == Enums.PlayerType.None; - } - -} diff --git a/Assets/Script/Renju/OpenFourDetector.cs.meta b/Assets/Script/Renju/OpenFourDetector.cs.meta deleted file mode 100644 index 79c51ed..0000000 --- a/Assets/Script/Renju/OpenFourDetector.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 9e99fad9f03a4a7794edd7194f6c5e5b -timeCreated: 1742178319 \ No newline at end of file diff --git a/Assets/Script/Renju/OverlineDetector.cs b/Assets/Script/Renju/OverlineDetector.cs deleted file mode 100644 index 75c6531..0000000 --- a/Assets/Script/Renju/OverlineDetector.cs +++ /dev/null @@ -1,88 +0,0 @@ -using UnityEngine; - -public class OverlineDetector:RenjuDetectorBase -{ - /// - /// 특정 위치에 흑돌을 놓았을 때 장목(6목 이상)이 생기는지 검사 - /// - /// 현재 보드 상태 - /// 행 좌표 - /// 열 좌표 - /// 장목 금수 여부 (true: 금수, false: 금수 아님) - public bool IsOverline(Enums.PlayerType[,] board, int row, int col) - { - // 빈 위치가 아니면 검사할 필요 없음 - if (!IsEmptyPosition(board, row, col)) - { - return false; - } - - // 임시 보드 생성 - Enums.PlayerType[,] tempBoard = (Enums.PlayerType[,])board.Clone(); - - // 해당 위치에 흑돌 놓기 - tempBoard[row, col] = Enums.PlayerType.PlayerA; - - // 각 방향으로 연속된 돌 개수 검사 - foreach (Vector2Int dir in directions) - { - int count = CountConsecutiveStones(tempBoard, row, col, dir); - - // 6목 이상인 경우 장목 금수 - if (count >= 6) - { - Debug.Log($"장목 발견: ({row}, {col}) 방향: ({dir.x}, {dir.y}), 연속 돌 개수: {count}"); - return true; - } - } - - return false; - } - - /// - /// 특정 방향으로 연속된 같은 색 돌의 개수를 세는 함수 - /// - private int CountConsecutiveStones(Enums.PlayerType[,] board, int row, int col, Vector2Int dir) - { - Enums.PlayerType stone = board[row, col]; - if (stone != Enums.PlayerType.PlayerA) - { - return 0; - } - - int count = 1; // 현재 돌 포함 - int dRow = dir.x; - int dCol = dir.y; - - // 정방향 검사 - for (int i = 1; i <= 5; i++) // 최대 5칸까지만 검사 (5칸 이후로는 6목 이상이 확정) - { - int r = row + i * dRow; - int c = col + i * dCol; - - if (!IsValidPosition(r, c) || board[r, c] != stone) - { - break; - } - - count++; - } - - // 역방향 검사 - for (int i = 1; i <= 5; i++) // 최대 5칸까지만 검사 - { - int r = row - i * dRow; - int c = col - i * dCol; - - if (!IsValidPosition(r, c) || board[r, c] != stone) - { - break; - } - - count++; - } - - return count; - } - -} diff --git a/Assets/Script/Renju/OverlineDetector.cs.meta b/Assets/Script/Renju/OverlineDetector.cs.meta deleted file mode 100644 index 84997fc..0000000 --- a/Assets/Script/Renju/OverlineDetector.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 0727203eebbc4c3e823fd4ee045cb713 -timeCreated: 1741966933 \ No newline at end of file diff --git a/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs b/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs index a764aa4..7833607 100644 --- a/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs +++ b/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs @@ -6,12 +6,7 @@ using UnityEngine; /// public class RenjuForbiddenMoveDetector { - // 3-3, 4-4, 장목 감지기 - private DoubleThreeDetector _doubleThreeDetector; - private DoubleFourDetector _doubleFourDetector; - private OverlineDetector _overlineDetector; - - private int _boardSize = 15; + private RenjuRuleChecker _ruleChecker; /// /// 렌주 금수 감지기 생성자 @@ -19,102 +14,23 @@ public class RenjuForbiddenMoveDetector public RenjuForbiddenMoveDetector() { // 각 감지기 초기화 - _doubleThreeDetector = new DoubleThreeDetector(); - _doubleFourDetector = new DoubleFourDetector(); - _overlineDetector = new OverlineDetector(); + _ruleChecker = new RenjuRuleChecker(); } /// - /// 모든 금수 위치를 찾아 반환하는 함수 + /// 렌주 룰로 금수 리스트를 반환하는 함수 /// - /// 현재 보드 상태 - /// 금수 위치 목록 (각 위치별 금수 타입 정보 포함) - public List<(Vector2Int position, ForbiddenType type)> FindAllForbiddenMoves(Enums.PlayerType[,] board) + /// 현재 보드의 상태 + /// 금수 좌표를 담은 리스트 + public List RenjuForbiddenMove(Enums.PlayerType[,] board) { - List<(Vector2Int, ForbiddenType)> forbiddenMoves = new List<(Vector2Int, ForbiddenType)>(); + var doubleThreeList = _ruleChecker.GetForbiddenMoves(board); - // 흑돌 차례에만 금수 규칙 적용 - for (int row = 0; row < _boardSize; row++) + foreach (var doubleThreePos in doubleThreeList) { - for (int col = 0; col < _boardSize; col++) - { - // 빈 위치만 검사 - if (board[row, col] != Enums.PlayerType.None) - { - continue; - } - - // 금수 타입 확인 - ForbiddenType type = CheckForbiddenType(board, row, col); - - // 금수인 경우 목록에 추가 - if (type != ForbiddenType.None) - { - forbiddenMoves.Add((new Vector2Int(row, col), type)); - } - } + Debug.Log("삼삼 금수 좌표 X축 : " + doubleThreePos.x + ", Y축 : " + doubleThreePos.y); } - - return forbiddenMoves; + return doubleThreeList; } - /// - /// 특정 위치의 금수 타입 확인 - /// - /// 현재 보드 상태 - /// 행 좌표 - /// 열 좌표 - /// 금수 타입 (None, DoubleThree, DoubleFour, Overline) - public ForbiddenType CheckForbiddenType(Enums.PlayerType[,] board, int row, int col) - { - // 빈 위치가 아니면 금수가 아님 - if (board[row, col] != Enums.PlayerType.None) - { - return ForbiddenType.None; - } - - // 장목 검사 (장목이 가장 우선) - if (_overlineDetector.IsOverline(board, row, col)) - { - return ForbiddenType.Overline; - } - - // 4-4 검사 - if (_doubleFourDetector.IsDoubleFour(board, row, col)) - { - return ForbiddenType.DoubleFour; - } - - // 3-3 검사 - if (_doubleThreeDetector.IsDoubleThree(board, row, col)) - { - return ForbiddenType.DoubleThree; - } - - // 금수 아님 - return ForbiddenType.None; - } - - /// - /// 특정 위치가 금수인지 간단히 확인 - /// - /// 현재 보드 상태 - /// 행 좌표 - /// 열 좌표 - /// 금수 여부 (true: 금수, false: 금수 아님) - public bool IsForbiddenMove(Enums.PlayerType[,] board, int row, int col) - { - return CheckForbiddenType(board, row, col) != ForbiddenType.None; - } -} - -/// -/// 금수 타입 열거형 -/// -public enum ForbiddenType -{ - None, // 금수 아님 - DoubleThree, // 3-3 금수 - DoubleFour, // 4-4 금수 - Overline // 장목 금수 } \ No newline at end of file diff --git a/Assets/Script/Renju/RenjuRuleChecker.cs b/Assets/Script/Renju/RenjuRuleChecker.cs new file mode 100644 index 0000000..3071293 --- /dev/null +++ b/Assets/Script/Renju/RenjuRuleChecker.cs @@ -0,0 +1,690 @@ +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 } // 오른쪽 위 + }; + + // 기본 삼(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++) + { + for (int y = 0; y < height; y++) + { + // 빈 칸만 검사 + if (board[x, y] != Enums.PlayerType.None) + continue; + + // 해당 위치에 흑돌을 임시로 놓았다고 가정 + Enums.PlayerType[,] simulatedBoard = (Enums.PlayerType[,])board.Clone(); + simulatedBoard[x, y] = Enums.PlayerType.PlayerA; + + // 장목(6목 이상) 확인 + if (IsOverline(simulatedBoard, x, y)) + { + forbiddenMoves.Add(new Vector2Int(x, y)); + continue; + } + + // 모든 방향별 패턴 정보 수집 + PatternInfo patternInfo = CollectAllPatterns(simulatedBoard, x, y); + + // 4-3 패턴 확인 (이 위치에 놓았을 때 열린 4가 1개, 열린 3이 1개 이상 생성되면 4-3 패턴) + bool isFourThree = IsFourThreeAdvanced(patternInfo); + if (isFourThree) + { + // 4-3 패턴은 금수가 아님 + continue; + } + + // 쌍삼(3-3) 또는 사사(4-4) 확인 + bool isThreeThree = IsThreeThreeAdvanced(patternInfo); + bool isFourFour = IsFourFourAdvanced(patternInfo); + + // 금수 여부 판정 + if (isThreeThree || isFourFour) + { + forbiddenMoves.Add(new Vector2Int(x, y)); + } + } + } + + 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>(); + } +} + diff --git a/Assets/Script/Renju/RenjuRuleChecker.cs.meta b/Assets/Script/Renju/RenjuRuleChecker.cs.meta new file mode 100644 index 0000000..a982a5d --- /dev/null +++ b/Assets/Script/Renju/RenjuRuleChecker.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 083e8b9070ed407b8744d4cacd0d53dc +timeCreated: 1742256498 \ No newline at end of file