Merge pull request #71 from Degulleo/DO-74-game-logic-코드-정리

DO-74 [refactor] 게임 로직 스크립트 정리
This commit is contained in:
Fiore 2025-03-28 10:01:59 +09:00 committed by GitHub
commit b3f13d98fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 762 additions and 879 deletions

File diff suppressed because it is too large Load Diff

View File

@ -30,7 +30,7 @@ public class GameManager : Singleton<GameManager>
public MultiplayManager GetMultiplayManager() public MultiplayManager GetMultiplayManager()
{ {
_multiplayManager = _gameLogic._multiplayManager; _multiplayManager = _gameLogic.MultiPlayManager;
if (_multiplayManager == null) Debug.Log("MultiplayManager가 null입니다"); if (_multiplayManager == null) Debug.Log("MultiplayManager가 null입니다");
return _multiplayManager; return _multiplayManager;
} }
@ -49,7 +49,7 @@ public class GameManager : Singleton<GameManager>
public void OnClickConfirmButton() public void OnClickConfirmButton()
{ {
if (_gameLogic.selectedRow != -1 && _gameLogic.selectedCol != -1) if (_gameLogic.SelectedRow != -1 && _gameLogic.SelectedCol != -1)
{ {
_gameLogic.OnConfirm(); _gameLogic.OnConfirm();
} }
@ -99,7 +99,7 @@ public class GameManager : Singleton<GameManager>
if (_gameLogic == null) return; if (_gameLogic == null) return;
_gameLogic.ResetBoard(); _gameLogic.ResetBoard();
_stoneController.InitStones(); _stoneController.InitStones();
_gameLogic.SetState(_gameLogic.firstPlayerState); _gameLogic.SetState(_gameLogic.FirstPlayerState);
} }
//유저 이름 Game UI에 초기화 //유저 이름 Game UI에 초기화
public void InitPlayersName(string playerNameA, string playerNameB) public void InitPlayersName(string playerNameA, string playerNameB)

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b15e90e5f55e4d338705bfffd558d056
timeCreated: 1743068734

View File

@ -0,0 +1,27 @@
public class AIState: BasePlayerState
{
public override void OnEnter(GameLogic gameLogic)
{
gameLogic.FioTimer.StartTimer();
OmokAI.Instance.StartBestMoveSearch(gameLogic.GetBoard(), (bestMove) =>
{
if(bestMove.HasValue)
HandleMove(gameLogic, bestMove.Value.Item1, bestMove.Value.Item2);
});
}
public override void OnExit(GameLogic gameLogic)
{
gameLogic.FioTimer.InitTimer();
}
public override void HandleMove(GameLogic gameLogic, int row, int col)
{
ProcessMove(gameLogic, Enums.PlayerType.PlayerB,row, col);
}
public override void HandleNextTurn(GameLogic gameLogic)
{
gameLogic.SetState(gameLogic.FirstPlayerState);
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fbd2216a641845bc9892444713d3497f
timeCreated: 1743068785

View File

@ -0,0 +1,58 @@
using UnityEngine;
public abstract class BasePlayerState
{
public abstract void OnEnter(GameLogic gameLogic);
public abstract void OnExit(GameLogic gameLogic);
public abstract void HandleMove(GameLogic gameLogic, int row, int col);
public abstract void HandleNextTurn(GameLogic gameLogic);
protected string _roomId;
protected bool _isMultiplay;
protected MultiplayManager _multiplayManager;
public void ProcessMove(GameLogic gameLogic, Enums.PlayerType playerType, int row, int col)
{
gameLogic.FioTimer.PauseTimer();
gameLogic.SetNewBoardValue(playerType, row, col);
gameLogic.CountStoneCounter();
if (_isMultiplay)
{
_multiplayManager.SendPlayerMove(_roomId, new Vector2Int(row, col));
}
if (gameLogic.CheckGameWin(playerType, row, col))
{
var gameResult = playerType == Enums.PlayerType.PlayerA? Enums.GameResult.Win:Enums.GameResult.Lose;
if (gameLogic.GameType == Enums.GameType.MultiPlay)
{
if (gameLogic.FirstPlayerState.GetType() != typeof(PlayerState))
{
gameResult = gameResult == Enums.GameResult.Win ? Enums.GameResult.Lose : Enums.GameResult.Win;
}
}
GameManager.Instance.panelManager.OpenEffectPanel(gameResult);
gameLogic.EndGame(gameResult);
}
else
{
if (gameLogic.TotalStoneCounter >= Constants.MinCountForDrawCheck)
{
if (gameLogic.CheckGameDraw())
{
GameManager.Instance.panelManager.OpenEffectPanel(Enums.GameResult.Draw);
gameLogic.EndGame(Enums.GameResult.Draw);
}
else
{
HandleNextTurn(gameLogic);
}
}
else
{
HandleNextTurn(gameLogic);
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 28db9c0415a04bd288598b502b43066f
timeCreated: 1743068714

View File

@ -0,0 +1,62 @@
public class MultiPlayerState: BasePlayerState
{
private Enums.PlayerType _playerType;
private bool _isFirstPlayer;
private MultiplayManager _multiplayManager;
public MultiPlayerState(bool isFirstPlayer, MultiplayManager multiplayManager)
{
_isFirstPlayer = isFirstPlayer;
_playerType = isFirstPlayer ? Enums.PlayerType.PlayerA : Enums.PlayerType.PlayerB;
_multiplayManager = multiplayManager;
}
public override void OnEnter(GameLogic gameLogic)
{
gameLogic.FioTimer.StartTimer();
//TODO: 첫번째 플레이어면 렌주 룰 확인
#region Renju Turn Set
// 턴이 변경될 때마다 금수 위치 업데이트
gameLogic.UpdateForbiddenMoves();
#endregion
gameLogic.CurrentTurn = _playerType;
// gameLogic.stoneController.OnStoneClickedDelegate = (row, col) =>
// {
// HandleMove(gameLogic, row, col);
// };
_multiplayManager.OnOpponentMove = moveData =>
{
var row = moveData.position.x;
var col = moveData.position.y;
UnityThread.executeInUpdate(() =>
{
HandleMove(gameLogic, row, col);
});
};
}
public override void OnExit(GameLogic gameLogic)
{
gameLogic.FioTimer.InitTimer();
_multiplayManager.OnOpponentMove = null;
}
public override void HandleMove(GameLogic gameLogic, int row, int col)
{
ProcessMove(gameLogic, _playerType, row, col);
}
public override void HandleNextTurn(GameLogic gameLogic)
{
if (_isFirstPlayer)
{
gameLogic.SetState(gameLogic.SecondPlayerState);
}
else
{
gameLogic.SetState(gameLogic.FirstPlayerState);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 20e292a1c2064962ab8eaaa5a80bc3b9
timeCreated: 1743068802

View File

@ -0,0 +1,63 @@
public class PlayerState : BasePlayerState
{
private Enums.PlayerType _playerType;
private bool _isFirstPlayer;
public PlayerState(bool isFirstPlayer)
{
_isFirstPlayer = isFirstPlayer;
_playerType = isFirstPlayer ? Enums.PlayerType.PlayerA : Enums.PlayerType.PlayerB;
_isMultiplay = false;
}
public PlayerState(bool isFirstPlayer, MultiplayManager multiplayManager, string roomId)
: this(isFirstPlayer)
{
_isFirstPlayer = isFirstPlayer;
_multiplayManager = multiplayManager;
_roomId = roomId;
_isMultiplay = true;
}
public override void OnEnter(GameLogic gameLogic)
{
gameLogic.FioTimer.StartTimer();
//TODO: 첫번째 플레이어면 렌주 룰 확인
#region Renju Turn Set
// 턴이 변경될 때마다 금수 위치 업데이트
gameLogic.UpdateForbiddenMoves();
#endregion
gameLogic.CurrentTurn = _playerType;
gameLogic.StoneController.OnStoneClickedDelegate = (row, col) =>
{
HandleMove(gameLogic, row, col);
};
}
public override void OnExit(GameLogic gameLogic)
{
//TODO: 렌주 룰 금수자리 초기화
gameLogic.FioTimer.InitTimer();
gameLogic.StoneController.OnStoneClickedDelegate = null;
}
public override void HandleMove(GameLogic gameLogic, int row, int col)
{
gameLogic.SetStoneSelectedState(row, col);
}
public override void HandleNextTurn(GameLogic gameLogic)
{
if (_isFirstPlayer)
{
gameLogic.SetState(gameLogic.SecondPlayerState);
}
else
{
gameLogic.SetState(gameLogic.FirstPlayerState);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 951fc71a9c154522b3562053350536b8
timeCreated: 1743068763

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 79bd83540d994967a0e3b7ee6d5bc835
timeCreated: 1743076291

View File

@ -0,0 +1,89 @@
public partial class GameLogic
{
// 돌 카운터 증가 함수
public void CountStoneCounter() => _totalStoneCounter++;
// 착수 버튼 클릭시 호출되는 함수
public void OnConfirm() => CurrentPlayerState.ProcessMove(this, CurrentTurn, SelectedRow, SelectedCol);
// 스톤의 상태변경 명령함수
private void SetStoneNewState(Enums.StoneState state, int row, int col)
{
StoneController.SetStoneState(state, row, col);
}
public void SetStoneSelectedState(int row, int col)
{
if (_board[row, col] != Enums.PlayerType.None) return;
if (StoneController.GetStoneState(row, col) != Enums.StoneState.None &&
CurrentTurn == Enums.PlayerType.PlayerA) return;
// 첫수 및 중복 확인
if ((SelectedRow != row || SelectedCol != col) && (SelectedRow != -1 && SelectedCol != -1))
{
StoneController.SetStoneState(Enums.StoneState.None, SelectedRow, SelectedCol);
}
(SelectedRow, SelectedCol) = (row, col);
StoneController.SetStoneState(Enums.StoneState.Selected, row, col);
}
// 보드에 돌추가 함수
public void SetNewBoardValue(Enums.PlayerType playerType, int row, int col)
{
if (_board[row, col] != Enums.PlayerType.None) return;
switch (playerType)
{
case Enums.PlayerType.PlayerA:
StoneController.SetStoneType(Enums.StoneType.Black, row, col);
StoneController.SetStoneState(Enums.StoneState.LastPositioned, row, col);
_board[row, col] = Enums.PlayerType.PlayerA;
LastNSelectedSetting(row, col);
ReplayManager.Instance.RecordStonePlaced(Enums.StoneType.Black, row, col); //기보 데이터 저장
break;
case Enums.PlayerType.PlayerB:
StoneController.SetStoneType(Enums.StoneType.White, row, col);
StoneController.SetStoneState(Enums.StoneState.LastPositioned, row, col);
_board[row, col] = Enums.PlayerType.PlayerB;
LastNSelectedSetting(row, col);
ReplayManager.Instance.RecordStonePlaced(Enums.StoneType.White, row, col);
break;
}
}
// 돌 지우는 함수
public void RemoveStone(int row, int col)
{
_board[row, col] = Enums.PlayerType.None;
StoneController.SetStoneType(Enums.StoneType.None, row, col);
StoneController.SetStoneState(Enums.StoneState.None, row, col);
}
// 마지막 좌표와 선택 좌표 세팅
private void LastNSelectedSetting(int row, int col)
{
//첫수 확인
if (_lastRow != -1 || _lastCol != -1)
{
StoneController.SetStoneState(Enums.StoneState.None, _lastRow, _lastCol);
}
//마지막 좌표 저장
(_lastRow, _lastCol) = (row, col);
//선택 좌표 초기화
(SelectedRow, SelectedCol) = (-1, -1);
}
// 게임 끝
public void EndGame(Enums.GameResult result)
{
SetState(null);
ReplayManager.Instance.SaveReplayDataResult(result);
//TODO: 게임 종료 후 행동 구현
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d911ca14696a433ebc112577b60de664
timeCreated: 1743076571

View File

@ -0,0 +1,96 @@
public partial class GameLogic
{
//승리 확인 함수
public bool CheckGameWin(Enums.PlayerType player, int row, int col)
{
return OmokAI.Instance.CheckGameWin(player, _board, row, col);
}
// 특정 방향으로 같은 돌 개수와 열린 끝 개수를 계산하는 함수
private (int count, int openEnds) CountStones(
Enums.PlayerType[,] board, int row, int col, int[] direction, Enums.PlayerType player)
{
int size = board.GetLength(0);
int count = 0;
int openEnds = 0;
// 정방향 탐색
int r = row + direction[0], c = col + direction[1];
while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == player)
{
count++;
r += direction[0]; // row값 옮기기
c += direction[1]; // col값 옮기기
}
if (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.None)
{
openEnds++;
}
// 역방향 탐색
r = row - direction[0];
c = col - direction[1];
while (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == player)
{
count++;
r -= direction[0];
c -= direction[1];
}
if (r >= 0 && r < size && c >= 0 && c < size && board[r, c] == Enums.PlayerType.None)
{
openEnds++;
}
return (count, openEnds);
}
//무승부 확인
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 AIConstants.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;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 367e9d42ab0f4a879d00bac722922970
timeCreated: 1743076291