diff --git a/Assets/LYC/GameCopyLYC.unity b/Assets/LYC/GameCopyLYC.unity index 6f0d1e8..406f480 100644 --- a/Assets/LYC/GameCopyLYC.unity +++ b/Assets/LYC/GameCopyLYC.unity @@ -6020,6 +6020,50 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 9726acf6f82a3644ba31eda5ef496991, type: 3} m_Name: m_EditorClassIdentifier: +--- !u!1 &820078853 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 820078855} + - component: {fileID: 820078854} + m_Layer: 0 + m_Name: OmokAI + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &820078854 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 820078853} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 576baa0fe98d40608bf48109ba5ed788, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!4 &820078855 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 820078853} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 540, y: 1760, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1001 &831992430 PrefabInstance: m_ObjectHideFlags: 0 @@ -8175,6 +8219,8 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 1118625358} + - component: {fileID: 1118625360} + - component: {fileID: 1118625359} m_Layer: 0 m_Name: GameManager m_TagString: Untagged @@ -8197,6 +8243,116 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1118625359 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1118625357} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 35a4c6d5d3a97b444b968e68ec8bb9f7, type: 3} + m_Name: + m_EditorClassIdentifier: + panelManagerPrefab: {fileID: 3475740041361426276, guid: 085ca07ca90c92545b2594bd13412701, type: 3} + audioManagerPrefab: {fileID: 2946408323859178723, guid: e829818dce39a5d4383e061111bed871, type: 3} +--- !u!82 &1118625360 +AudioSource: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1118625357} + m_Enabled: 1 + serializedVersion: 4 + OutputAudioMixerGroup: {fileID: 0} + m_audioClip: {fileID: 0} + m_PlayOnAwake: 1 + m_Volume: 1 + m_Pitch: 1 + Loop: 0 + Mute: 0 + Spatialize: 0 + SpatializePostEffects: 0 + Priority: 128 + DopplerLevel: 1 + MinDistance: 1 + MaxDistance: 500 + Pan2D: 0 + rolloffMode: 0 + BypassEffects: 0 + BypassListenerEffects: 0 + BypassReverbZones: 0 + rolloffCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + panLevelCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + spreadCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + reverbZoneMixCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 --- !u!1001 &1121996003 PrefabInstance: m_ObjectHideFlags: 0 @@ -17421,7 +17577,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 7155909258766834886, guid: b1a31da84076a534cae7cb20a7913a93, type: 3} propertyPath: m_Name - value: '[Canvas] Game UI' + value: Canvas objectReference: {fileID: 0} m_RemovedComponents: [] m_RemovedGameObjects: [] @@ -17438,3 +17594,4 @@ SceneRoots: - {fileID: 307507522} - {fileID: 1632066875} - {fileID: 1118625358} + - {fileID: 820078855} diff --git a/Assets/Script/Common/Constants.cs b/Assets/Script/Common/Constants.cs index 9cd5c76..8fae832 100644 --- a/Assets/Script/Common/Constants.cs +++ b/Assets/Script/Common/Constants.cs @@ -4,4 +4,7 @@ public const string GameServerURL = "ws://localhost:3000"; public const int BoardSize = 15; public const int ReplayMaxRecordSize = 10; + public const int WIN_COUNT = 5; + //무승부 확인을 위한 최소 착수 수 + public const int MinCountForDrawCheck = 150; } \ No newline at end of file diff --git a/Assets/Script/Game/GameLogic.cs b/Assets/Script/Game/GameLogic.cs index 37aac6e..206c4b7 100644 --- a/Assets/Script/Game/GameLogic.cs +++ b/Assets/Script/Game/GameLogic.cs @@ -12,10 +12,10 @@ public abstract class BasePlayerState public void ProcessMove(GameLogic gameLogic, Enums.PlayerType playerType, int row, int col) { - gameLogic.fioTimer.PauseTimer(); gameLogic.SetNewBoardValue(playerType, row, col); + gameLogic.CountStoneCounter(); if (gameLogic.CheckGameWin(playerType, row, col)) { @@ -24,10 +24,23 @@ public abstract class BasePlayerState } else { - //TODO: 무승부 확인 - HandleNextTurn(gameLogic); + if (gameLogic.TotalStoneCounter >= Constants.MinCountForDrawCheck) + { + if (gameLogic.CheckGameDraw()) + { + GameManager.Instance.panelManager.OpenConfirmPanel($"Game Over: Draw",() =>{}); + gameLogic.EndGame(); + } + else + { + HandleNextTurn(gameLogic); + } + } + else + { + HandleNextTurn(gameLogic); + } } - } } @@ -85,7 +98,6 @@ public class AIState: BasePlayerState public override void OnEnter(GameLogic gameLogic) { gameLogic.fioTimer.StartTimer(); - //TODO: AI이식 OmokAI.Instance.StartBestMoveSearch(gameLogic.GetBoard(), (bestMove) => { if(bestMove.HasValue) @@ -137,17 +149,23 @@ public class GameLogic : MonoBehaviour public StoneController stoneController; public Enums.PlayerType currentTurn; public Enums.GameType gameType; + //총 착수된 돌 카운터 + public int _totalStoneCounter; + public int TotalStoneCounter{get{return _totalStoneCounter;}} + public BasePlayerState firstPlayerState; public BasePlayerState secondPlayerState; private BasePlayerState _currentPlayerState; + //타이머 public FioTimer fioTimer; - private const int WIN_COUNT = 5; //선택된 좌표 public int selectedRow; public int selectedCol; //마지막 배치된 좌표 - + private int _lastRow; + private int _lastCol; + #region Renju Members // 렌주룰 금수 검사기 private RenjuForbiddenMoveDetector _forbiddenDetector; @@ -156,9 +174,6 @@ public class GameLogic : MonoBehaviour private List _forbiddenMoves = new List(); #endregion - private int _lastRow; - private int _lastCol; - private static int[][] _directions = new int[][] { new int[] {1, 0}, // 수직 @@ -173,6 +188,7 @@ public class GameLogic : MonoBehaviour _board = new Enums.PlayerType[15, 15]; this.stoneController = stoneController; this.gameType = gameType; + _totalStoneCounter = 0; selectedRow = -1; selectedCol = -1; @@ -209,7 +225,6 @@ public class GameLogic : MonoBehaviour //TODO: 기보 매니저에게 플레이어 닉네임 넘겨주기 ReplayManager.Instance.InitReplayData("PlayerA","nicknameB"); - switch (gameType) { @@ -226,10 +241,10 @@ public class GameLogic : MonoBehaviour break; } } - - public Enums.PlayerType[,] GetBoard() + //돌 카운터 증가 함수 + public void CountStoneCounter() { - return _board; + _totalStoneCounter++; } //착수 버튼 클릭시 호출되는 함수 @@ -331,7 +346,7 @@ public class GameLogic : MonoBehaviour public void EndGame() { SetState(null); - + //TODO: 게임 종료 후 행동 구현 } //승리 확인 함수 @@ -342,7 +357,7 @@ public class GameLogic : MonoBehaviour var (count, _) = CountStones(_board, row, col, dir, player); // 자기 자신 포함하여 5개 이상일 시 true 반환 - if (count + 1 >= WIN_COUNT) + if (count + 1 >= Constants.WIN_COUNT) return true; } @@ -388,7 +403,56 @@ public class GameLogic : MonoBehaviour return (count, openEnds); } -#region Renju Rule Detector + public Enums.PlayerType[,] GetBoard() + { + return _board; + } + //무승부 확인 + public bool CheckGameDraw() + { + if (CheckIsFull(_board)) return true; // 빈 칸이 없으면 무승부 + bool playerAHasChance = CheckFiveChance(_board, Enums.PlayerType.PlayerA); + bool playerBHasChance = CheckFiveChance(_board, Enums.PlayerType.PlayerB); + return !(playerAHasChance || playerBHasChance); // 둘 다 기회가 없으면 무승부 + } + + //연속되는 5개가 만들어질 기회가 있는지 판단 + private bool CheckFiveChance(Enums.PlayerType[,] board, Enums.PlayerType player) + { + var tempBoard = (Enums.PlayerType[,])board.Clone(); + int size = board.GetLength(0); + for (int row = 0; row < size; row++) + { + for (int col = 0; col < size; col++) + { + if (tempBoard[row, col] != Enums.PlayerType.None) continue; + tempBoard[row, col] = player; + foreach (var dir in _directions) + { + var (count, _) = CountStones(tempBoard, row, col, dir, player); + + // 자기 자신 포함하여 5개 이상일 시 true 반환 + if (count + 1 >= Constants.WIN_COUNT) return true; + } + } + } + return false; + } + //보드가 꽉 찼는지 확인 + private static bool CheckIsFull(Enums.PlayerType[,] board) + { + int size = board.GetLength(0); + for (int row = 0; row < size; row++) + { + for (int col = 0; col < size; col++) + { + if (board[row, col] == Enums.PlayerType.None) return false; + } + } + return true; + } + + #region Renju Rule Detector // 금수 위치 업데이트 및 표시 private void UpdateForbiddenMoves() { diff --git a/Assets/Script/Game/GameManager.cs b/Assets/Script/Game/GameManager.cs index 0ec77d4..b2b3d65 100644 --- a/Assets/Script/Game/GameManager.cs +++ b/Assets/Script/Game/GameManager.cs @@ -31,10 +31,10 @@ public class GameManager : Singleton //게임 씬에서 확인하기 위한 임시 코드 // _canvas = canvas.GetComponent(); - // _stoneController = GameObject.FindObjectOfType(); - // _stoneController.InitStones(); - // var fioTimer = FindObjectOfType(); - // _gameLogic = new GameLogic(_stoneController, _gameType, fioTimer); + _stoneController = GameObject.FindObjectOfType(); + _stoneController.InitStones(); + var fioTimer = FindObjectOfType(); + _gameLogic = new GameLogic(_stoneController, _gameType, fioTimer); } private void InitPanels() @@ -51,7 +51,6 @@ public class GameManager : Singleton } else { - Debug.Log("착수 위치를 선택 해주세요"); //TODO: 착수할 위치를 선택하라는 동작 } }