From 577e204cfab9e9a8a5f6a20707db4e81e041f3c7 Mon Sep 17 00:00:00 2001
From: Sehyeon <ksh1837@naver.com>
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<Vector2Int> emptyPositions; // 빈 좌표들 (5를 만들 수 있는 위치)
+
+        public OpenFourInfo(int dir)
+        {
+            direction = dir;
+            emptyPositions = new List<Vector2Int>();
+        }
+    }
+    
+    // 쌍사(4-4) 여부를 검사합니다.
+    // <returns>쌍사이면 true, 아니면 false</returns>
+    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개 연속 불가능
+    }
+    
+    /// <summary>
+    /// 라인 패턴을 추출합니다.
+    /// </summary>
+    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<Vector2Int> emptyPositions; // 빈 좌표들 (4를 만들 수 있는 위치)
+
+        public OpenThreeInfo(int dir)
+        {
+            direction = dir;
+            emptyPositions = new List<Vector2Int>();
+        }
+    }
+    
+    // 쌍삼(3-3) 여부를 검사합니다.
+    // <returns>쌍삼이면 true, 아니면 false</returns>
+    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개 연속 불가능
+    }
+    
+    /// <summary>
+    /// 라인 패턴을 추출합니다.
+    /// </summary>
+    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
 
     /// <summary>
     /// 렌주 룰로 금수 리스트를 반환하는 함수
@@ -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 <ksh1837@naver.com>
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;
     }
-
+/*
     /// <summary>
     /// 보드 상태를 시각적으로 출력하는 디버깅 함수
     /// </summary>
     /// <param name="board">현재 보드 상태</param>
     /// <returns>보드의 시각적 표현 문자열</returns>
-    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 <ksh1837@naver.com>
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<int> openFourDirections = new List<int>();
         
         // 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<int> openThreeDirections = new List<int>(); // 열린 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 <ksh1837@naver.com>
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 <ksh1837@naver.com>
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) 여부를 검사합니다.
     // <returns>쌍사이면 true, 아니면 false</returns>
-    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<Vector2Int> resultMoves = CheckHasFiveStones(board, forbiddenMoves);
+        
+        return resultMoves;
+    }
+
+    // 금수 위치에서 5목이 가능할 경우 해당 위치는 금수 표기 X
+    private List<Vector2Int> CheckHasFiveStones(Enums.PlayerType[,] board, List<Vector2Int> 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;
     }