From 577e204cfab9e9a8a5f6a20707db4e81e041f3c7 Mon Sep 17 00:00:00 2001 From: Sehyeon Date: Wed, 26 Mar 2025 16:44:02 +0900 Subject: [PATCH 1/5] =?UTF-8?q?DO-54=20[Feat]=20=EA=B1=B0=EC=A7=93=20?= =?UTF-8?q?=EA=B8=88=EC=88=98=20=EC=B2=B4=ED=81=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Script/Renju/KSH_Renju.meta | 3 + .../Script/Renju/KSH_Renju/DoubleFourCheck.cs | 214 +++++++++++++++++ .../Renju/KSH_Renju/DoubleFourCheck.cs.meta | 3 + .../Renju/KSH_Renju/DoubleThreeCheck.cs | 216 ++++++++++++++++++ .../Renju/KSH_Renju/DoubleThreeCheck.cs.meta | 3 + .../Renju/RenjuForbiddenMoveDetector.cs | 89 +++++++- 6 files changed, 522 insertions(+), 6 deletions(-) create mode 100644 Assets/Script/Renju/KSH_Renju.meta create mode 100644 Assets/Script/Renju/KSH_Renju/DoubleFourCheck.cs create mode 100644 Assets/Script/Renju/KSH_Renju/DoubleFourCheck.cs.meta create mode 100644 Assets/Script/Renju/KSH_Renju/DoubleThreeCheck.cs create mode 100644 Assets/Script/Renju/KSH_Renju/DoubleThreeCheck.cs.meta diff --git a/Assets/Script/Renju/KSH_Renju.meta b/Assets/Script/Renju/KSH_Renju.meta new file mode 100644 index 0000000..b00f897 --- /dev/null +++ b/Assets/Script/Renju/KSH_Renju.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 787a283493d7435ea606170d7442790a +timeCreated: 1742967205 \ No newline at end of file diff --git a/Assets/Script/Renju/KSH_Renju/DoubleFourCheck.cs b/Assets/Script/Renju/KSH_Renju/DoubleFourCheck.cs new file mode 100644 index 0000000..c4e23b5 --- /dev/null +++ b/Assets/Script/Renju/KSH_Renju/DoubleFourCheck.cs @@ -0,0 +1,214 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +// 오목 렌주룰 4-4 금수 판정. +public class DoubleFourCheck : ForbiddenDetectorBase +{ + // 열린 4 패턴 정보를 저장하는 구조체 + private struct OpenFourInfo + { + public int direction; // 방향 인덱스 + public List emptyPositions; // 빈 좌표들 (5를 만들 수 있는 위치) + + public OpenFourInfo(int dir) + { + direction = dir; + emptyPositions = new List(); + } + } + + // 쌍사(4-4) 여부를 검사합니다. + // 쌍사이면 true, 아니면 false + public bool IsDoubleFour(Enums.PlayerType[,] board, int row, int col) + { + // 임시로 돌 배치 + board[row, col] = Black; + + // 실제 열린 4 개수 카운트 + int realOpenFourCount = 0; + + // 4개의 방향 검사 + for (int i = 0; i < 4; i++) + { + int dir1 = DirectionPairs[i, 0]; + int dir2 = DirectionPairs[i, 1]; + + // 이 방향에서 실제 열린 4가 있는지 확인 + if (HasRealOpenFour(board, row, col, dir1, dir2)) + { + realOpenFourCount++; + if (realOpenFourCount >= 2) + { + // 원래 상태로 되돌리기 + board[row, col] = Space; + return true; // 실제 열린 4가 2개 이상이면 쌍사 + } + } + } + + // 원래 상태로 되돌림 + board[row, col] = Space; + return false; + } + + // 특정 방향에 대해 열린 4 검사 + private bool HasRealOpenFour(Enums.PlayerType[,] board, int row, int col, + int dir1, int dir2) + { + // 패턴 추출 + Enums.PlayerType[] linePattern = ExtractLinePattern(board, row, col, dir1, dir2); + int centerIndex = 5; // 중앙 인덱스 (현재 위치) + + // 연속된 4개 돌 패턴 검사 (●●●●) + for (int start = centerIndex - 3; start <= centerIndex; start++) + { + if (start < 0 || start + 3 >= linePattern.Length) continue; + + // 4개의 연속된 돌 확인 + bool isFour = true; + for (int i = 0; i < 4; i++) + { + if (linePattern[start + i] != Black) + { + isFour = false; + break; + } + } + + if (isFour) + { + // 양쪽이 모두 열려있는지 확인 + bool isLeftOpen = IsOpen(linePattern, start - 1); + bool isRightOpen = IsOpen(linePattern, start + 4); + + // 적어도 한쪽이 열려있으면 (한쪽이라도 5를 만들 수 있으면) 열린 4 + if ((isLeftOpen || isRightOpen) && CanFormFive(linePattern, start)) + { + return true; // 실제 열린 4 발견 + } + } + } + + // 한 칸 떨어진 패턴 검사 (●●●○● 또는 ●○●●● 등) + for (int start = Math.Max(0, centerIndex - 4); start <= centerIndex; start++) + { + if (start + 4 >= linePattern.Length) continue; + + // 5칸 내에 4개 돌과 1개 빈칸이 있는지 확인 + int stoneCount = 0; + int emptyCount = 0; + int emptyPos = -1; + + for (int i = 0; i < 5; i++) + { + if (linePattern[start + i] == Black) stoneCount++; + else if (linePattern[start + i] == Space) + { + emptyCount++; + emptyPos = start + i; + } + } + + // 4개 돌 + 1개 빈칸 패턴이면 + if (stoneCount == 4 && emptyCount == 1) + { + // 5개 돌을 만들 수 있는지 확인 + if (CanFormFive(linePattern, start)) + { + return true; // 실제 열린 4 발견 + } + } + } + + return false; // 열린 4 없음 + } + + // 해당 위치가 실제로 열려 있는지 확인 (백돌이나 벽으로 막히지 않은지) + private bool IsOpen(Enums.PlayerType[] pattern, int position) + { + // 범위 체크 + if (position < 0 || position >= pattern.Length) + { + return false; // 벽으로 막힘 + } + + // 빈 공간인지 확인 + return pattern[position] == Space; + } + + // 이 패턴으로 5개 돌을 만들 수 있는지 확인 + private bool CanFormFive(Enums.PlayerType[] pattern, int startPos) + { + // 모든 가능한 5칸 슬라이딩 윈도우 확인 + for (int slide = -4; slide <= 0; slide++) + { + bool possible = true; + + // 5개 윈도우 내에 상대 돌이나 벽이 없는지 확인 + for (int i = 0; i < 5; i++) + { + int pos = startPos + slide + i; + + if (pos < 0 || pos >= pattern.Length || pattern[pos] == White) + { + possible = false; + break; + } + } + + if (possible) + { + return true; // 5개 연속 가능 + } + } + + return false; // 어떤 위치에서도 5개 연속 불가능 + } + + /// + /// 라인 패턴을 추출합니다. + /// + 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] = 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; // 범위 밖은 벽으로 처리하여 일관성 유지 + } + } + + return linePattern; + } +} \ No newline at end of file diff --git a/Assets/Script/Renju/KSH_Renju/DoubleFourCheck.cs.meta b/Assets/Script/Renju/KSH_Renju/DoubleFourCheck.cs.meta new file mode 100644 index 0000000..5b8bf0b --- /dev/null +++ b/Assets/Script/Renju/KSH_Renju/DoubleFourCheck.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a0f3f36424a845a3bfa973db9ee6c627 +timeCreated: 1742967613 \ No newline at end of file diff --git a/Assets/Script/Renju/KSH_Renju/DoubleThreeCheck.cs b/Assets/Script/Renju/KSH_Renju/DoubleThreeCheck.cs new file mode 100644 index 0000000..e886f20 --- /dev/null +++ b/Assets/Script/Renju/KSH_Renju/DoubleThreeCheck.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +// 오목 렌주룰 3-3 금수 판정. 3-3 둘다 열린 상황만 금수로 판정합니다. +public class DoubleThreeCheck : ForbiddenDetectorBase +{ + // 열린 3 패턴 정보를 저장하는 구조체 + private struct OpenThreeInfo + { + public int direction; // 방향 인덱스 + public List emptyPositions; // 빈 좌표들 (4를 만들 수 있는 위치) + + public OpenThreeInfo(int dir) + { + direction = dir; + emptyPositions = new List(); + } + } + + // 쌍삼(3-3) 여부를 검사합니다. + // 쌍삼이면 true, 아니면 false + public bool IsDoubleThree(Enums.PlayerType[,] board, int row, int col) + { + // 임시로 돌 배치 + board[row, col] = Black; + + // 실제 열린 3 개수 카운트 + int realOpenThreeCount = 0; + + // 4개의 방향 검사 + for (int i = 0; i < 4; i++) + { + int dir1 = DirectionPairs[i, 0]; + int dir2 = DirectionPairs[i, 1]; + + // 이 방향에서 실제 열린 3이 있는지 확인 + if (HasRealOpenThree(board, row, col, dir1, dir2)) + { + realOpenThreeCount++; + if (realOpenThreeCount >= 2) + { + // 원래 상태로 되돌리기 + board[row, col] = Space; + return true; // 실제 열린 3이 2개 이상이면 삼삼 + } + } + } + + // 원래 상태로 되돌림 + board[row, col] = Space; + return false; + } + + // 특정 방향에 대해 열린 3 검사 + private bool HasRealOpenThree(Enums.PlayerType[,] board, int row, int col, + int dir1, int dir2) // ref OpenThreeInfo threeInfo + { + // 패턴 추출 + Enums.PlayerType[] linePattern = ExtractLinePattern(board, row, col, dir1, dir2); + int centerIndex = 5; // 중앙 인덱스 (현재 위치) + + // 연속된 3 또는 한 칸 떨어진 3 확인 + int threePatternCount = 0; + + // 연속된 3개 돌 패턴 검사 (●●●) + for (int start = centerIndex - 2; start <= centerIndex; start++) + { + if (start < 0 || start + 2 >= linePattern.Length) continue; + + // 3개의 연속된 돌 확인 + bool isThree = true; + for (int i = 0; i < 3; i++) + { + if (linePattern[start + i] != Black) + { + isThree = false; + break; + } + } + + if (isThree) + { + // 양쪽이 모두 열려있는지 확인 (진짜 열린 3) + bool isLeftOpen = IsOpen(linePattern, start - 1); + bool isRightOpen = IsOpen(linePattern, start + 3); + + // 양쪽이 모두 열려있고 5개 돌을 만들 수 있는지 확인 + if (isLeftOpen && isRightOpen && CanFormFive(linePattern, start)) + { + return true; // 실제 열린 3 발견 + } + } + } + + // 한 칸 떨어진 패턴 검사 (●●○● 또는 ●○●● 등) + for (int start = Math.Max(0, centerIndex - 3); start <= centerIndex; start++) + { + if (start + 3 >= linePattern.Length) continue; + + // 4칸 내에 3개 돌과 1개 빈칸이 있는지 확인 + int stoneCount = 0; + int emptyCount = 0; + + for (int i = 0; i < 4; i++) + { + if (linePattern[start + i] == Black) stoneCount++; + else if (linePattern[start + i] == Space) emptyCount++; + } + + // 3개 돌 + 1개 빈칸 패턴이면 + if (stoneCount == 3 && emptyCount == 1) + { + // 양쪽이 모두 열려있는지 확인 + bool isLeftOpen = start > 0 && linePattern[start - 1] == Space; + bool isRightOpen = start + 4 < linePattern.Length && linePattern[start + 4] == Space; + + // 양쪽이 모두 열려있고 5개 돌을 만들 수 있는지 확인 + if (isLeftOpen && isRightOpen && CanFormFive(linePattern, start)) + { + return true; // 실제 열린 3 발견 + } + } + } + + return false; // 열린 3 없음 + } + + // 추가: 해당 위치가 실제로 열려 있는지 확인 (백돌이나 벽으로 막히지 않은지) + private bool IsOpen(Enums.PlayerType[] pattern, int position) + { + // 범위 체크 + if (position < 0 || position >= pattern.Length) + { + return false; // 벽으로 막힘 + } + + // 빈 공간인지 확인 + return pattern[position] == Space; + } + + // 추가: 이 패턴으로 5개 돌을 만들 수 있는지 확인 + private bool CanFormFive(Enums.PlayerType[] pattern, int startPos) + { + // 모든 가능한 5칸 슬라이딩 윈도우 확인 + for (int slide = -4; slide <= 0; slide++) + { + bool possible = true; + + // 5개 윈도우 내에 상대 돌이나 벽이 없는지 확인 + for (int i = 0; i < 5; i++) + { + int pos = startPos + slide + i; + + if (pos < 0 || pos >= pattern.Length || pattern[pos] == White) + { + possible = false; + break; + } + } + + if (possible) + { + return true; // 5개 연속 가능 + } + } + + return false; // 어떤 위치에서도 5개 연속 불가능 + } + + /// + /// 라인 패턴을 추출합니다. + /// + 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] = 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; // 범위 밖은 벽으로 처리하여 일관성 유지 + } + } + + return linePattern; + } +} \ No newline at end of file diff --git a/Assets/Script/Renju/KSH_Renju/DoubleThreeCheck.cs.meta b/Assets/Script/Renju/KSH_Renju/DoubleThreeCheck.cs.meta new file mode 100644 index 0000000..a8aa313 --- /dev/null +++ b/Assets/Script/Renju/KSH_Renju/DoubleThreeCheck.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b85e35d8df4b40f0826732775c528c83 +timeCreated: 1742967219 \ No newline at end of file diff --git a/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs b/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs index b3f5e62..b25c277 100644 --- a/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs +++ b/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs @@ -6,9 +6,14 @@ using UnityEngine; public class RenjuForbiddenMoveDetector : ForbiddenDetectorBase { // 렌주 룰 금수 감지기 생성 - private RenjuOverlineDetector _overlineDetactor = new(); + /*private RenjuOverlineDetector _overlineDetactor = new(); private RenjuDoubleFourDetector _doubleFourDetactor = new(); - private RenjuDoubleThreeDetector _doubleThreeDetector = new(); + private RenjuDoubleThreeDetector _doubleThreeDetector = new();*/ + + // 임시 테스트 + private RenjuOverlineDetector _overlineDetactor = new(); + private DoubleFourCheck _doubleFourDetactor = new(); // DoubleFourCheck + private DoubleThreeCheck _doubleThreeDetector = new(); // DoubleThreeCheck /// /// 렌주 룰로 금수 리스트를 반환하는 함수 @@ -51,6 +56,11 @@ public class RenjuForbiddenMoveDetector : ForbiddenDetectorBase if (_doubleThreeDetector.IsDoubleThree(board, row, col)) { tempForbiddenMoves.Add(new Vector2Int(row, col)); + /*if (HasWinningPotential(board, row, col)) + { + tempForbiddenMoves.Add(new Vector2Int(row, col)); + }*/ + // if (!SimulateDoubleFour(tempBoard)) // { // Debug.Log("삼삼 금수 좌표 X축 : " + row + ", Y축 : " + col); @@ -63,23 +73,87 @@ public class RenjuForbiddenMoveDetector : ForbiddenDetectorBase foreach (var pos in tempForbiddenMoves) { board[pos.x, pos.y] = Black; - if (!SimulateDoubleFour(board)&& !SimulateOverline(board)) + if (!SimulateDoubleFour(board) && !SimulateOverline(board)) { - Debug.Log("X: "+pos.x + "Y: "+ pos.y); + Debug.Log("X: "+pos.x + "Y: "+ pos.y); forbiddenMoves.Add(new Vector2Int(pos.x, pos.y)); } + board[pos.x, pos.y] = Space; } + return forbiddenMoves; } + + private bool HasWinningPotential(Enums.PlayerType[,] board, int row, int col) + { + // 모든 방향에 대해 5개 연속 돌을 만들 수 있는지 확인 + for (int dirPair = 0; dirPair < 4; dirPair++) + { + int dir1 = DirectionPairs[dirPair, 0]; + int dir2 = DirectionPairs[dirPair, 1]; + + if (CanFormFiveInDirection(board, row, col, dir1, dir2)) + { + return true; + } + } + + return false; + } - - + private bool CanFormFiveInDirection(Enums.PlayerType[,] board, int row, int col, int dir1, int dir2) + { + // 해당 방향으로 5개 연속 돌을 놓을 가능성 확인 + // 현재 위치에 흑돌 임시 배치 + board[row, col] = Black; + + // 양방향으로 연속된 돌 또는 빈 공간 개수 세기 + int length = 1; // 현재 위치 포함 + + // 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) || board[newRow, newCol] == White) + { + break; + } + + length++; + } + + // 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) || board[newRow, newCol] == White) + { + break; + } + + length++; + } + + // 원래 상태로 복원 + board[row, col] = Space; + + // 5개 이상 연속 가능하면 승리 가능성 있음 + return length >= 5; + } + private bool SimulateDoubleFour(Enums.PlayerType[,] board) { for (int row = 0; row < BoardSize; row++) { for (int col = 0; col < BoardSize; col++) { + if (board[row, col] != Space) + continue; + if (_doubleFourDetactor.IsDoubleFour(board, row, col)) return true; } @@ -93,6 +167,9 @@ public class RenjuForbiddenMoveDetector : ForbiddenDetectorBase { for (int col = 0; col < BoardSize; col++) { + if (board[row, col] != Space) + continue; + if (_overlineDetactor.IsOverline(board, row, col)) { return true; From 80b050afe0e1db62a00c22725257b546a4c431d6 Mon Sep 17 00:00:00 2001 From: Sehyeon Date: Wed, 26 Mar 2025 17:26:26 +0900 Subject: [PATCH 2/5] =?UTF-8?q?DO-54=20[Style]=20Debug.Log=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Renju/KSH_Renju/DoubleThreeCheck.cs | 3 --- .../Renju/RenjuForbiddenMoveDetector.cs | 23 +++++++------------ 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/Assets/Script/Renju/KSH_Renju/DoubleThreeCheck.cs b/Assets/Script/Renju/KSH_Renju/DoubleThreeCheck.cs index e886f20..9ebf7c5 100644 --- a/Assets/Script/Renju/KSH_Renju/DoubleThreeCheck.cs +++ b/Assets/Script/Renju/KSH_Renju/DoubleThreeCheck.cs @@ -59,9 +59,6 @@ public class DoubleThreeCheck : ForbiddenDetectorBase // 패턴 추출 Enums.PlayerType[] linePattern = ExtractLinePattern(board, row, col, dir1, dir2); int centerIndex = 5; // 중앙 인덱스 (현재 위치) - - // 연속된 3 또는 한 칸 떨어진 3 확인 - int threePatternCount = 0; // 연속된 3개 돌 패턴 검사 (●●●) for (int start = centerIndex - 2; start <= centerIndex; start++) diff --git a/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs b/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs index b25c277..c9575f6 100644 --- a/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs +++ b/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs @@ -6,12 +6,9 @@ using UnityEngine; public class RenjuForbiddenMoveDetector : ForbiddenDetectorBase { // 렌주 룰 금수 감지기 생성 - /*private RenjuOverlineDetector _overlineDetactor = new(); - private RenjuDoubleFourDetector _doubleFourDetactor = new(); - private RenjuDoubleThreeDetector _doubleThreeDetector = new();*/ - - // 임시 테스트 private RenjuOverlineDetector _overlineDetactor = new(); + /*private RenjuDoubleFourDetector _doubleFourDetactor = new(); + private RenjuDoubleThreeDetector _doubleThreeDetector = new();*/ private DoubleFourCheck _doubleFourDetactor = new(); // DoubleFourCheck private DoubleThreeCheck _doubleThreeDetector = new(); // DoubleThreeCheck @@ -36,7 +33,7 @@ public class RenjuForbiddenMoveDetector : ForbiddenDetectorBase if (_overlineDetactor.IsOverline(board, row, col)) { forbiddenCount++; - Debug.Log("장목 금수 좌표 X축 : " + row + ", Y축 : " + col); + // Debug.Log("장목 금수 좌표 X축 : " + row + ", Y축 : " + col); forbiddenMoves.Add(new Vector2Int(row, col)); continue; } @@ -45,7 +42,7 @@ public class RenjuForbiddenMoveDetector : ForbiddenDetectorBase if (_doubleFourDetactor.IsDoubleFour(board, row, col)) { forbiddenCount++; - Debug.Log("사사 금수 좌표 X축 : " + row + ", Y축 : " + col); + // Debug.Log("사사 금수 좌표 X축 : " + row + ", Y축 : " + col); forbiddenMoves.Add(new Vector2Int(row, col)); continue; } @@ -56,10 +53,6 @@ public class RenjuForbiddenMoveDetector : ForbiddenDetectorBase if (_doubleThreeDetector.IsDoubleThree(board, row, col)) { tempForbiddenMoves.Add(new Vector2Int(row, col)); - /*if (HasWinningPotential(board, row, col)) - { - tempForbiddenMoves.Add(new Vector2Int(row, col)); - }*/ // if (!SimulateDoubleFour(tempBoard)) // { @@ -75,7 +68,7 @@ public class RenjuForbiddenMoveDetector : ForbiddenDetectorBase board[pos.x, pos.y] = Black; if (!SimulateDoubleFour(board) && !SimulateOverline(board)) { - Debug.Log("X: "+pos.x + "Y: "+ pos.y); + // Debug.Log("X: "+pos.x + "Y: "+ pos.y); forbiddenMoves.Add(new Vector2Int(pos.x, pos.y)); } board[pos.x, pos.y] = Space; @@ -178,13 +171,13 @@ public class RenjuForbiddenMoveDetector : ForbiddenDetectorBase } return false; } - +/* /// /// 보드 상태를 시각적으로 출력하는 디버깅 함수 /// /// 현재 보드 상태 /// 보드의 시각적 표현 문자열 - private string DebugBoard(Enums.PlayerType[,] board) + private string DebugBoard(Enums.PlayerType[,] board) { StringBuilder sb = new StringBuilder(); @@ -204,6 +197,6 @@ public class RenjuForbiddenMoveDetector : ForbiddenDetectorBase } return sb.ToString(); - } + }*/ } From 4a24d0f7c3659e91979941e151170b22a222b901 Mon Sep 17 00:00:00 2001 From: Sehyeon Date: Wed, 26 Mar 2025 22:09:20 +0900 Subject: [PATCH 3/5] =?UTF-8?q?DO-54=20[Fix]=203-3=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Script/Renju/KSH_Renju/DoubleFourCheck.cs | 12 ++++-------- .../Script/Renju/KSH_Renju/DoubleThreeCheck.cs | 18 ++++++------------ 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/Assets/Script/Renju/KSH_Renju/DoubleFourCheck.cs b/Assets/Script/Renju/KSH_Renju/DoubleFourCheck.cs index c4e23b5..499c28d 100644 --- a/Assets/Script/Renju/KSH_Renju/DoubleFourCheck.cs +++ b/Assets/Script/Renju/KSH_Renju/DoubleFourCheck.cs @@ -25,8 +25,7 @@ public class DoubleFourCheck : ForbiddenDetectorBase // 임시로 돌 배치 board[row, col] = Black; - // 실제 열린 4 개수 카운트 - int realOpenFourCount = 0; + List openFourDirections = new List(); // 4개의 방향 검사 for (int i = 0; i < 4; i++) @@ -37,19 +36,16 @@ public class DoubleFourCheck : ForbiddenDetectorBase // 이 방향에서 실제 열린 4가 있는지 확인 if (HasRealOpenFour(board, row, col, dir1, dir2)) { - realOpenFourCount++; - if (realOpenFourCount >= 2) + if (HasRealOpenFour(board, row, col, dir1, dir2)) { - // 원래 상태로 되돌리기 - board[row, col] = Space; - return true; // 실제 열린 4가 2개 이상이면 쌍사 + openFourDirections.Add(i); } } } // 원래 상태로 되돌림 board[row, col] = Space; - return false; + return openFourDirections.Count >= 2; } // 특정 방향에 대해 열린 4 검사 diff --git a/Assets/Script/Renju/KSH_Renju/DoubleThreeCheck.cs b/Assets/Script/Renju/KSH_Renju/DoubleThreeCheck.cs index 9ebf7c5..fcacf1a 100644 --- a/Assets/Script/Renju/KSH_Renju/DoubleThreeCheck.cs +++ b/Assets/Script/Renju/KSH_Renju/DoubleThreeCheck.cs @@ -25,36 +25,30 @@ public class DoubleThreeCheck : ForbiddenDetectorBase // 임시로 돌 배치 board[row, col] = Black; - // 실제 열린 3 개수 카운트 - int realOpenThreeCount = 0; + List openThreeDirections = new List(); // 열린 3 저장용 // 4개의 방향 검사 for (int i = 0; i < 4; i++) { int dir1 = DirectionPairs[i, 0]; int dir2 = DirectionPairs[i, 1]; - + // 이 방향에서 실제 열린 3이 있는지 확인 if (HasRealOpenThree(board, row, col, dir1, dir2)) { - realOpenThreeCount++; - if (realOpenThreeCount >= 2) - { - // 원래 상태로 되돌리기 - board[row, col] = Space; - return true; // 실제 열린 3이 2개 이상이면 삼삼 - } + openThreeDirections.Add(i); } } // 원래 상태로 되돌림 board[row, col] = Space; - return false; + + return openThreeDirections.Count >= 2; } // 특정 방향에 대해 열린 3 검사 private bool HasRealOpenThree(Enums.PlayerType[,] board, int row, int col, - int dir1, int dir2) // ref OpenThreeInfo threeInfo + int dir1, int dir2) { // 패턴 추출 Enums.PlayerType[] linePattern = ExtractLinePattern(board, row, col, dir1, dir2); From 62d4d70eeae0c8e93c4672db2c2cc6d73dd863bd Mon Sep 17 00:00:00 2001 From: Sehyeon Date: Wed, 26 Mar 2025 22:27:36 +0900 Subject: [PATCH 4/5] =?UTF-8?q?DO-54=20[Style]=20=EC=95=88=EC=93=B0?= =?UTF-8?q?=EB=8A=94=20=EC=BD=94=EB=93=9C=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Renju/RenjuForbiddenMoveDetector.cs | 65 +------------------ 1 file changed, 2 insertions(+), 63 deletions(-) diff --git a/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs b/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs index c9575f6..4810e48 100644 --- a/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs +++ b/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs @@ -77,74 +77,13 @@ public class RenjuForbiddenMoveDetector : ForbiddenDetectorBase return forbiddenMoves; } - private bool HasWinningPotential(Enums.PlayerType[,] board, int row, int col) - { - // 모든 방향에 대해 5개 연속 돌을 만들 수 있는지 확인 - for (int dirPair = 0; dirPair < 4; dirPair++) - { - int dir1 = DirectionPairs[dirPair, 0]; - int dir2 = DirectionPairs[dirPair, 1]; - - if (CanFormFiveInDirection(board, row, col, dir1, dir2)) - { - return true; - } - } - - return false; - } - - private bool CanFormFiveInDirection(Enums.PlayerType[,] board, int row, int col, int dir1, int dir2) - { - // 해당 방향으로 5개 연속 돌을 놓을 가능성 확인 - // 현재 위치에 흑돌 임시 배치 - board[row, col] = Black; - - // 양방향으로 연속된 돌 또는 빈 공간 개수 세기 - int length = 1; // 현재 위치 포함 - - // 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) || board[newRow, newCol] == White) - { - break; - } - - length++; - } - - // 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) || board[newRow, newCol] == White) - { - break; - } - - length++; - } - - // 원래 상태로 복원 - board[row, col] = Space; - - // 5개 이상 연속 가능하면 승리 가능성 있음 - return length >= 5; - } - private bool SimulateDoubleFour(Enums.PlayerType[,] board) { for (int row = 0; row < BoardSize; row++) { for (int col = 0; col < BoardSize; col++) { - if (board[row, col] != Space) + if (board[row, col] != Space) // 보드 초기화 방지용 continue; if (_doubleFourDetactor.IsDoubleFour(board, row, col)) @@ -160,7 +99,7 @@ public class RenjuForbiddenMoveDetector : ForbiddenDetectorBase { for (int col = 0; col < BoardSize; col++) { - if (board[row, col] != Space) + if (board[row, col] != Space) // 보드 초기화 방지용 continue; if (_overlineDetactor.IsOverline(board, row, col)) From ce940a4f1c4ad2e83058e5a6168e394da8d95767 Mon Sep 17 00:00:00 2001 From: Sehyeon Date: Thu, 27 Mar 2025 16:13:46 +0900 Subject: [PATCH 5/5] =?UTF-8?q?DO-54=20[Feat]=20=EC=9E=A5=EB=AA=A9=20?= =?UTF-8?q?=EA=B1=B0=EC=A7=93=20=EA=B8=88=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Script/AI/MiniMaxAIController.cs | 2 +- .../Script/Renju/KSH_Renju/DoubleFourCheck.cs | 66 +++++++++++++++++-- .../Renju/RenjuForbiddenMoveDetector.cs | 32 ++++++--- 3 files changed, 85 insertions(+), 15 deletions(-) diff --git a/Assets/Script/AI/MiniMaxAIController.cs b/Assets/Script/AI/MiniMaxAIController.cs index 5952707..062f181 100644 --- a/Assets/Script/AI/MiniMaxAIController.cs +++ b/Assets/Script/AI/MiniMaxAIController.cs @@ -360,7 +360,7 @@ public static class MiniMaxAIController var (count, _) = CountStones(board, row, col, dir, player, isSavedCache); // 자기 자신 포함하여 5개 이상일 시 true 반환 - if (count + 1 >= WIN_COUNT) + if (count + 1 == WIN_COUNT) return true; } diff --git a/Assets/Script/Renju/KSH_Renju/DoubleFourCheck.cs b/Assets/Script/Renju/KSH_Renju/DoubleFourCheck.cs index 499c28d..b9900f9 100644 --- a/Assets/Script/Renju/KSH_Renju/DoubleFourCheck.cs +++ b/Assets/Script/Renju/KSH_Renju/DoubleFourCheck.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Text; using UnityEngine; // 오목 렌주룰 4-4 금수 판정. @@ -18,9 +19,16 @@ public class DoubleFourCheck : ForbiddenDetectorBase } } + // 4-4 금수를 체크합니다. + public bool IsDoubleFour(Enums.PlayerType[,] board, int row, int col) + { + return FindDoubleLineFour(board, row, col) || // 각각 두개의 라인에서 쌍사를 형성하는 경우 + FindSingleLineDoubleFour(board, row, col); // 일직선으로 쌍사가 만들어지는 특수 패턴 + } + // 쌍사(4-4) 여부를 검사합니다. // 쌍사이면 true, 아니면 false - public bool IsDoubleFour(Enums.PlayerType[,] board, int row, int col) + public bool FindDoubleLineFour(Enums.PlayerType[,] board, int row, int col) { // 임시로 돌 배치 board[row, col] = Black; @@ -45,7 +53,44 @@ public class DoubleFourCheck : ForbiddenDetectorBase // 원래 상태로 되돌림 board[row, col] = Space; - return openFourDirections.Count >= 2; + + return openFourDirections.Count >= 2; + } + + 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("●□●●●□●")) + { + Debug.Log("patternStr: " + patternStr); + return true; + } + } + + return false; } // 특정 방향에 대해 열린 4 검사 @@ -173,7 +218,20 @@ public class DoubleFourCheck : ForbiddenDetectorBase // 현재 위치 설정 linePattern[centerIndex] = Black; - // dir1 방향으로 패턴 채우기 + for (int i = 1; i <= 5; i++) + { + for (int j = 0; j < 2; j++) // dir1과 dir2를 한 번에 처리 + { + int direction = (j == 0) ? dir1 : dir2; + int newRow = row + Directions[direction, 0] * i; + int newCol = col + Directions[direction, 1] * i; + int index = (j == 0) ? centerIndex + i : centerIndex - i; + + linePattern[index] = IsInBounds(newRow, newCol) ? board[newRow, newCol] : White; + } + } + + /*// dir1 방향으로 패턴 채우기 for (int i = 1; i <= 5; i++) { int newRow = row + Directions[dir1, 0] * i; @@ -203,7 +261,7 @@ public class DoubleFourCheck : ForbiddenDetectorBase { linePattern[centerIndex - i] = White; // 범위 밖은 벽으로 처리하여 일관성 유지 } - } + }*/ return linePattern; } diff --git a/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs b/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs index 4810e48..7e74980 100644 --- a/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs +++ b/Assets/Script/Renju/RenjuForbiddenMoveDetector.cs @@ -6,9 +6,9 @@ using UnityEngine; public class RenjuForbiddenMoveDetector : ForbiddenDetectorBase { // 렌주 룰 금수 감지기 생성 - private RenjuOverlineDetector _overlineDetactor = new(); /*private RenjuDoubleFourDetector _doubleFourDetactor = new(); private RenjuDoubleThreeDetector _doubleThreeDetector = new();*/ + private RenjuOverlineDetector _overlineDetactor = new(); private DoubleFourCheck _doubleFourDetactor = new(); // DoubleFourCheck private DoubleThreeCheck _doubleThreeDetector = new(); // DoubleThreeCheck @@ -33,7 +33,6 @@ public class RenjuForbiddenMoveDetector : ForbiddenDetectorBase if (_overlineDetactor.IsOverline(board, row, col)) { forbiddenCount++; - // Debug.Log("장목 금수 좌표 X축 : " + row + ", Y축 : " + col); forbiddenMoves.Add(new Vector2Int(row, col)); continue; } @@ -42,7 +41,6 @@ public class RenjuForbiddenMoveDetector : ForbiddenDetectorBase if (_doubleFourDetactor.IsDoubleFour(board, row, col)) { forbiddenCount++; - // Debug.Log("사사 금수 좌표 X축 : " + row + ", Y축 : " + col); forbiddenMoves.Add(new Vector2Int(row, col)); continue; } @@ -53,12 +51,6 @@ public class RenjuForbiddenMoveDetector : ForbiddenDetectorBase if (_doubleThreeDetector.IsDoubleThree(board, row, col)) { tempForbiddenMoves.Add(new Vector2Int(row, col)); - - // if (!SimulateDoubleFour(tempBoard)) - // { - // Debug.Log("삼삼 금수 좌표 X축 : " + row + ", Y축 : " + col); - // forbiddenMoves.Add(new Vector2Int(row, col)); - // } } } } @@ -68,12 +60,32 @@ public class RenjuForbiddenMoveDetector : ForbiddenDetectorBase board[pos.x, pos.y] = Black; if (!SimulateDoubleFour(board) && !SimulateOverline(board)) { - // Debug.Log("X: "+pos.x + "Y: "+ pos.y); forbiddenMoves.Add(new Vector2Int(pos.x, pos.y)); } board[pos.x, pos.y] = Space; } + List resultMoves = CheckHasFiveStones(board, forbiddenMoves); + + return resultMoves; + } + + // 금수 위치에서 5목이 가능할 경우 해당 위치는 금수 표기 X + private List CheckHasFiveStones(Enums.PlayerType[,] board, List forbiddenMoves) + { + // 리스트를 수정하는 동안 오류를 방지하기 위해 뒤에서부터 반복 + for (int i = forbiddenMoves.Count - 1; i >= 0; i--) + { + int row = forbiddenMoves[i].x; + int col = forbiddenMoves[i].y; + + // 해당 위치에서 승리(5목)이 가능하면 금수 표기 X + if (OmokAI.Instance.CheckGameWin(Enums.PlayerType.PlayerA, board, row, col)) + { + forbiddenMoves.RemoveAt(i); + } + } + return forbiddenMoves; }