merge main

This commit is contained in:
Sehyeon 2025-03-28 13:25:03 +09:00
commit 74442fd498
8 changed files with 90 additions and 31 deletions

View File

@ -40,35 +40,59 @@ public class GameUIController : MonoBehaviour
public void OnClickSurrenderButton() public void OnClickSurrenderButton()
{ {
GameManager.Instance.panelManager.OpenConfirmPanel("항복 하시겠습니까?", () => if (GameManager.Instance.CheckIsSinglePlay())
{ {
_multiplayManager.RequestSurrender(); GameManager.Instance.SurrenderSinglePlay();
}); }
else
{
GameManager.Instance.panelManager.OpenConfirmPanel("항복 하시겠습니까?", () =>
{
_multiplayManager.RequestSurrender();
});
}
} }
public void OnClickDrawRequestButton() public void OnClickDrawRequestButton()
{ {
if (GameManager.Instance.GetRequestDrawChance()) if (GameManager.Instance.CheckIsSinglePlay())
{ {
GameManager.Instance.panelManager.OpenConfirmPanel("무승부 신청을 하시겠습니까?", () => GameManager.Instance.DrawSinglePlay();
{
_multiplayManager.RequestDraw();
});
GameManager.Instance.SetRequestDrawChanceFalse();
} }
else else
{ {
GameManager.Instance.panelManager.OpenConfirmPanel("무승부 요청이 제한돼있습니다.",()=>{}); if (GameManager.Instance.GetRequestDrawChance())
{
GameManager.Instance.panelManager.OpenConfirmPanel("무승부 신청을 하시겠습니까?", () =>
{
_multiplayManager.RequestDraw();
});
GameManager.Instance.SetRequestDrawChanceFalse();
}
else
{
GameManager.Instance.panelManager.OpenConfirmPanel("무승부 요청이 제한돼있습니다.",()=>{});
}
} }
} }
public void OnClickRevengeRequestButton() public void OnClickRevengeRequestButton()
{ {
if (GameManager.Instance.CheckIsSinglePlay())
{
GameManager.Instance.panelManager.OpenConfirmPanel("상대방이 방을 나갔습니다.",() =>
{
});
}
else
{
GameManager.Instance.panelManager.OpenConfirmPanel("재대결 신청을\n하시겠습니까?", () => GameManager.Instance.panelManager.OpenConfirmPanel("재대결 신청을\n하시겠습니까?", () =>
{ {
GameManager.Instance.panelManager.OpenLoadingPanel(true, true, false, false); GameManager.Instance.panelManager.OpenLoadingPanel(true, true, false, false);
_multiplayManager.RequestRevengeRequest(); _multiplayManager.RequestRevengeRequest();
}); });
}
} }
public void OnClickSettingsButton() public void OnClickSettingsButton()

View File

@ -784,7 +784,7 @@ GameObject:
- component: {fileID: 4247753812167816150} - component: {fileID: 4247753812167816150}
- component: {fileID: 1548693011522092544} - component: {fileID: 1548693011522092544}
m_Layer: 5 m_Layer: 5
m_Name: Retry Button m_Name: Draw Button
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
m_NavMeshLayer: 0 m_NavMeshLayer: 0

View File

@ -11,9 +11,9 @@ public static class AIEvaluator
// AI 패턴 점수 // AI 패턴 점수
public const float FIVE_IN_A_ROW = 100000f; public const float FIVE_IN_A_ROW = 100000f;
public const float OPEN_FOUR = 15000f; public const float OPEN_FOUR = 15000f;
public const float HALF_OPEN_FOUR = 5000f; public const float HALF_OPEN_FOUR = 6000f;
public const float CLOSED_FOUR = 500f; public const float CLOSED_FOUR = 500f;
public const float OPEN_THREE = 3000f; public const float OPEN_THREE = 3500f;
public const float HALF_OPEN_THREE = 500f; public const float HALF_OPEN_THREE = 500f;
public const float CLOSED_THREE = 50f; public const float CLOSED_THREE = 50f;
public const float OPEN_TWO = 100f; public const float OPEN_TWO = 100f;
@ -23,13 +23,13 @@ public static class AIEvaluator
public const float CLOSED_ONE = 1f; public const float CLOSED_ONE = 1f;
// 복합 패턴 점수 // 복합 패턴 점수
public const float DOUBLE_THREE = 8000f; public const float DOUBLE_THREE = 9000f;
public const float DOUBLE_FOUR = 12000f; public const float DOUBLE_FOUR = 12000f;
public const float FOUR_THREE = 10000f; public const float FOUR_THREE = 11000f;
// 위치 가중치 기본값 // 위치 가중치 기본값
public const float CENTER_WEIGHT = 1.2f; public const float CENTER_WEIGHT = 1.3f;
public const float EDGE_WEIGHT = 0.8f; public const float EDGE_WEIGHT = 0.85f;
} }
private static readonly int[][] Directions = AIConstants.Directions; private static readonly int[][] Directions = AIConstants.Directions;
@ -51,7 +51,7 @@ public static class AIEvaluator
ai4Positions, player4Positions); ai4Positions, player4Positions);
// 2. 복합 패턴 평가 // 2. 복합 패턴 평가
score += EvaluateComplexPatterns(aiOpen3Positions, playerOpen3Positions, ai4Positions, player4Positions, aiPlayer); score += EvaluateComplexPatterns(aiOpen3Positions, playerOpen3Positions, ai4Positions, player4Positions);
return score; return score;
} }
@ -153,8 +153,7 @@ public static class AIEvaluator
List<(int row, int col, int[] dir)> aiOpen3Positions, List<(int row, int col, int[] dir)> aiOpen3Positions,
List<(int row, int col, int[] dir)> playerOpen3Positions, List<(int row, int col, int[] dir)> playerOpen3Positions,
List<(int row, int col, int[] dir)> ai4Positions, List<(int row, int col, int[] dir)> ai4Positions,
List<(int row, int col, int[] dir)> player4Positions, List<(int row, int col, int[] dir)> player4Positions)
Enums.PlayerType aiPlayer)
{ {
float score = 0; float score = 0;
@ -494,12 +493,12 @@ public static class AIEvaluator
if (count >= 4) if (count >= 4)
{ {
normalScore = PatternScore.FIVE_IN_A_ROW / 8.5f; normalScore = PatternScore.FIVE_IN_A_ROW / 7.5f;
} }
else if (count == 3) else if (count == 3)
{ {
// 일관된 분모 사용 (방어 가중치는 유지) // 일관된 분모 사용 (방어 가중치는 유지)
normalScore = (openEnds == 2) ? PatternScore.OPEN_THREE / 1.3f : normalScore = (openEnds == 2) ? PatternScore.OPEN_THREE / 1.25f :
(openEnds == 1) ? PatternScore.HALF_OPEN_THREE / 3.2f : (openEnds == 1) ? PatternScore.HALF_OPEN_THREE / 3.2f :
PatternScore.CLOSED_THREE / 4.2f; PatternScore.CLOSED_THREE / 4.2f;
} }
@ -562,7 +561,7 @@ public static class AIEvaluator
if (!AreParallelDirections(openThrees[i].dir, openThrees[j].dir)) if (!AreParallelDirections(openThrees[i].dir, openThrees[j].dir))
{ {
float threeThreeScore = PatternScore.DOUBLE_THREE / 4; // 복합 패턴 가중치 float threeThreeScore = PatternScore.DOUBLE_THREE / 4; // 복합 패턴 가중치
score += isAI ? threeThreeScore * 1.1f : threeThreeScore * 1.3f; score += isAI ? threeThreeScore * 1.2f : threeThreeScore * 1.3f;
break; break;
} }
} }
@ -579,7 +578,7 @@ public static class AIEvaluator
if (!AreParallelDirections(fours[i].dir, fours[j].dir)) if (!AreParallelDirections(fours[i].dir, fours[j].dir))
{ {
float fourFourScore = PatternScore.DOUBLE_FOUR / 4; float fourFourScore = PatternScore.DOUBLE_FOUR / 4;
score += isAI ? fourFourScore * 1.2f : fourFourScore * 1.5f; score += isAI ? fourFourScore * 1.3f : fourFourScore * 1.7f;
break; break;
} }
} }
@ -590,7 +589,7 @@ public static class AIEvaluator
if (fours.Count > 0 && openThrees.Count > 0) if (fours.Count > 0 && openThrees.Count > 0)
{ {
float fourThreeScore = PatternScore.FOUR_THREE / 4; float fourThreeScore = PatternScore.FOUR_THREE / 4;
score += isAI ? fourThreeScore * 1.1f : fourThreeScore * 1.4f; score += isAI ? fourThreeScore * 1.1f : fourThreeScore * 1.6f;
} }
return score; return score;

View File

@ -360,7 +360,7 @@ public static class MiniMaxAIController
var (count, _) = CountStones(board, row, col, dir, player, isSavedCache); var (count, _) = CountStones(board, row, col, dir, player, isSavedCache);
// 자기 자신 포함하여 5개 이상일 시 true 반환 // 자기 자신 포함하여 5개 이상일 시 true 반환
if (count + 1 == WIN_COUNT) if (count + 1 >= WIN_COUNT)
return true; return true;
} }

View File

@ -145,6 +145,7 @@ public partial class GameLogic : IDisposable
break; break;
case Constants.MultiplayManagerState.ReceiveDrawRequest: case Constants.MultiplayManagerState.ReceiveDrawRequest:
Debug.Log("상대방의 무승부 요청 들어옴"); Debug.Log("상대방의 무승부 요청 들어옴");
TimerPause();
ExecuteOnMainThread(() => ExecuteOnMainThread(() =>
{ {
GameManager.Instance.panelManager.OpenDrawConfirmPanel("무승부 요청을 승낙하시겠습니까?", () => GameManager.Instance.panelManager.OpenDrawConfirmPanel("무승부 요청을 승낙하시겠습니까?", () =>
@ -160,6 +161,7 @@ public partial class GameLogic : IDisposable
break; break;
case Constants.MultiplayManagerState.DrawRequestSent: case Constants.MultiplayManagerState.DrawRequestSent:
Debug.Log("무승부 요청 전송 완료"); Debug.Log("무승부 요청 전송 완료");
TimerPause();
break; break;
case Constants.MultiplayManagerState.DrawAccepted: case Constants.MultiplayManagerState.DrawAccepted:
Debug.Log("무승부 요청이 승낙이 들어옴"); Debug.Log("무승부 요청이 승낙이 들어옴");
@ -174,6 +176,7 @@ public partial class GameLogic : IDisposable
break; break;
case Constants.MultiplayManagerState.DrawRejected: case Constants.MultiplayManagerState.DrawRejected:
Debug.Log("무승부 요청이 거부가 들어옴"); Debug.Log("무승부 요청이 거부가 들어옴");
TimerUnpause();
ExecuteOnMainThread(() => ExecuteOnMainThread(() =>
{ {
GameManager.Instance.panelManager.OpenConfirmPanel("무승부 요청을 거부하였습니다.", () => { }); GameManager.Instance.panelManager.OpenConfirmPanel("무승부 요청을 거부하였습니다.", () => { });
@ -181,7 +184,7 @@ public partial class GameLogic : IDisposable
break; break;
case Constants.MultiplayManagerState.DrawRejectionConfirmed: case Constants.MultiplayManagerState.DrawRejectionConfirmed:
Debug.Log("무승부 요청 거부 완료"); Debug.Log("무승부 요청 거부 완료");
TimerUnpause();
break; break;
case Constants.MultiplayManagerState.ReceiveTimeout: case Constants.MultiplayManagerState.ReceiveTimeout:
Debug.Log("상대방이 타임 아웃 됨"); Debug.Log("상대방이 타임 아웃 됨");
@ -484,6 +487,7 @@ public partial class GameLogic : IDisposable
// 기존 멀티플레이 상태 초기화 // 기존 멀티플레이 상태 초기화
MultiPlayManager = null; MultiPlayManager = null;
_roomId = null; _roomId = null;
GameType = Enums.GameType.SinglePlay;
// 싱글 플레이 상태로 변경 // 싱글 플레이 상태로 변경
InitializeSinglePlayMode(); InitializeSinglePlayMode();
@ -540,6 +544,12 @@ public partial class GameLogic : IDisposable
return AI_NAMIES[index]; return AI_NAMIES[index];
} }
// 타이머 일시정지
private void TimerPause() => FioTimer.PauseTimer();
// 타이머 일시정지 해제
private void TimerUnpause() => FioTimer.StartTimer();
#endregion #endregion
public void Dispose() public void Dispose()

View File

@ -132,6 +132,27 @@ public class GameManager : Singleton<GameManager>
_gameLogic.RequestDrawChance = false; _gameLogic.RequestDrawChance = false;
} }
public bool CheckIsSinglePlay()
{
if (_gameLogic == null) return false;
return _gameLogic.GameType == Enums.GameType.SinglePlay;
}
public void SurrenderSinglePlay()
{
if(_gameLogic == null) return;
panelManager.OpenEffectPanel(Enums.GameResult.Lose);
_gameLogic.EndGame(Enums.GameResult.Lose);
}
public void DrawSinglePlay()
{
if(_gameLogic == null) return;
panelManager.OpenEffectPanel(Enums.GameResult.Draw);
_gameLogic.EndGame(Enums.GameResult.Draw);
}
private void OnApplicationQuit() private void OnApplicationQuit()
{ {
Debug.Log("앱 종료 감지: 소켓 연결 정리 중..."); Debug.Log("앱 종료 감지: 소켓 연결 정리 중...");

View File

@ -7,7 +7,7 @@
} }
// 특정 방향으로 같은 돌 개수와 열린 끝 개수를 계산하는 함수 // 특정 방향으로 같은 돌 개수와 열린 끝 개수를 계산하는 함수
private (int count, int openEnds) CountStones( public static (int count, int openEnds) CountStones(
Enums.PlayerType[,] board, int row, int col, int[] direction, Enums.PlayerType player) Enums.PlayerType[,] board, int row, int col, int[] direction, Enums.PlayerType player)
{ {
int size = board.GetLength(0); int size = board.GetLength(0);

View File

@ -73,16 +73,21 @@ public class RenjuForbiddenMoveDetector : ForbiddenDetectorBase
// 금수 위치에서 5목이 가능할 경우 해당 위치는 금수 표기 X // 금수 위치에서 5목이 가능할 경우 해당 위치는 금수 표기 X
private List<Vector2Int> CheckHasFiveStones(Enums.PlayerType[,] board, List<Vector2Int> forbiddenMoves) private List<Vector2Int> CheckHasFiveStones(Enums.PlayerType[,] board, List<Vector2Int> forbiddenMoves)
{ {
int[][] directions = AIConstants.Directions;
// 리스트를 수정하는 동안 오류를 방지하기 위해 뒤에서부터 반복 // 리스트를 수정하는 동안 오류를 방지하기 위해 뒤에서부터 반복
for (int i = forbiddenMoves.Count - 1; i >= 0; i--) for (int i = forbiddenMoves.Count - 1; i >= 0; i--)
{ {
int row = forbiddenMoves[i].x; int row = forbiddenMoves[i].x;
int col = forbiddenMoves[i].y; int col = forbiddenMoves[i].y;
// 해당 위치에서 승리(5목)이 가능하면 금수 표기 X foreach (var dir in directions)
if (OmokAI.Instance.CheckGameWin(Enums.PlayerType.PlayerA, board, row, col))
{ {
forbiddenMoves.RemoveAt(i); var (count, _) = GameLogic.CountStones(board, row, col, dir, Enums.PlayerType.PlayerA);
// 해당 위치에서 승리(5목)이 가능하면 금수 표기 X
if (count + 1 == 5)
forbiddenMoves.RemoveAt(i);
} }
} }