2주차 과제에 text로 틱택토게임을 구현하라는 과제였는데 일반적인 배열로 판을 만드는게 아닌, 오늘 처음 배운 2차원 배열로 판을 만들고 이로 틱택토 게임을 하는것을 구현해 보겠다.
연습용으로 만드는 과제이기 때문에 잘 몰랐던 2차원 배열과 잘 못쓰던 do While을 사용하여 코드를 구현해 보았다.
1. 2차원 배열
2차원 배열은 행과 열로 이루어진 데이터 구조를 다루기에 적합한 배열의 한 종류다. 기본적인 코드는 아래와 같다.
int[,] map = new int[5, 5];
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
map[i, j] = i + j;
}
}
5, 5면 행이 5 열도 5칸인 표가 하나 만들어진다.
이를 이용해 3 x 3 틱택토 판을 만들어 구현하도록 하자.
2. 틱택토 구현
2-1. board와 플레이어 선언
플레이어는 X 를 놓는 플레이어와 O를 놓는 플레이어 둘 로 나뉜다. X 말을 가진 사람이 선으로 정한다.
보드는 3 X 3 보드이다.
static char[,] board = new char[3, 3];
static char currentPlayer = 'X';
// 보드 초기화
static void InitializeBoard()
{
for (int row = 0; row < 3; row++)
{
for (int col = 0; col < 3; col++)
{
board[row, col] = ' ';
}
}
}
위는 선언 및 보드판 초기화 코드이다. 게임이 끝나고 나면 항상 초기 보드판으로 돌려놔야 하기 때문에, 반복을 두번 돌려 3 X 3으로 만들고, 값을 ' ' 빈칸으로 줘서 초기화를 시킨다.
// 보드판 생성
static void DisplayBoard()
{
Console.Clear();
// 좌표 써놓기
Console.WriteLine(" 0 1 2");
for (int row = 0; row < 3; row++)
{
Console.Write(row + " ");
for (int col = 0; col < 3; col++)
{
// 이 좌표에 현재 currentPlayer의 좌표를 들고와서 찍어줌
Console.Write(board[row, col]);
if (col < 2)
{
Console.Write("|");
}
}
Console.WriteLine();
if (row < 2)
{
Console.WriteLine(" -+-+-");
}
}
}
보드판은 위와 같다. 사용자가 가로와 세로를 각각 입력하여 말을 두게 하는 방식인데, 가로 세로의 숫자가 헷갈릴 수 있기 때문에 가로 세로열에 맞춰 숫자로 좌표를 적어 놓기위해 가로 좌표를 먼저 쓴 후 반복문을 돌리는 식이다.
Console.Write(row + " "); 는 우측에 숫자를 쓰고 한칸 띄어서 숫자 옆에 "ㅣ" 표시가 들어갈 수 있도록 하기 위해 써넣었고, "ㅣ" 표시는 2개만 필요하기 때문에 if문으로 두 번만 출력했다.
-+-+-+ 로 가운데 판을 십자모양으로 만들었으며 위를 출력하면 아래 사진과 같은 판이 완성된다.
2-2. 게임 실행
플레이어가 행과 열 순으로 좌표를 입력해야 하므로 먼저 가로 세로값을 선언한 후 플레이어가 입력할 수 있게 Console.ReadLine().Split(' ')으로 한칸 띄우고 입력할 수 있게 한 후 두 값을 각각 담아준다. 여기서 do내에 줘서 무조건 실행 및 무한반복을 걸어둔 후 while 에서 0, 1 ,2 가 아닌 다른 수를 쓰면 탈출 못하게 끔 코드를 짰다.
//게임시 플레이어가 좌표
static void PlayerInputNum()
{
int row, col;
do
{
Console.Write($"플레이어 {currentPlayer}, 행과 열을 입력하세요 (예: 0 0, 주의!: 0 부터 2까지만 입력해주세요): ");
string[] input = Console.ReadLine().Split(' ');
row = int.Parse(input[0]);
col = int.Parse(input[1]);
} while (row < 0 || row >= 3 || col < 0 || col >= 3 || board[row, col] != ' ');
board[row, col] = currentPlayer;
}
그리고 만약 입력한 수가 0, 1, 2 에 맞다면, currenPlayer(X 또는 O)가 어느 좌표에 썼는지 보내준다.
2-3. 플레이어 교대
// 플레이어 교대
static void SwitchPlayer()
{
if (currentPlayer == 'X')
{
currentPlayer = 'O';
}
else
{
currentPlayer = 'X';
}
}
현재 플레이어가 X 면 O로 아니면 X로 바꾸는 식으로 플레이어를 교대해주는 코드이다. 그래야 X한번 O한번 찍힌다.
2-3. 승리 및 패배
// 승자가 생기거나, 무승부시 게임 종료
static bool IsGameOver()
{
return IsWinner() || IsBoardFull();
}
static bool IsWinner()
{
for (int i = 0; i < board.GetLength(0); i++)
{
//가로 또는 세로 승리
if (board[i, 0] == currentPlayer && board[i, 1] == currentPlayer && board[i, 2] == currentPlayer)
return true;
if (board[0, i] == currentPlayer && board[1, i] == currentPlayer && board[2, i] == currentPlayer)
return true;
}
// 대각선 승리
if (board[0, 0] == currentPlayer && board[1, 1] == currentPlayer && board[2, 2] == currentPlayer)
return true;
if (board[0, 2] == currentPlayer && board[1, 1] == currentPlayer && board[2, 0] == currentPlayer)
return true;
return false;
}
return이 true면 게임이 종료되게끔 코드를 짰다. 위 if문은 가로 또는 세로에 한 플레이어가 다 먹었을 시 true를 반환하고, 아래 if문은 대각선을 다 먹었을 시에 true를 반환해준다. 해당하지 않으면 false를 반환하여 isGameOver함수에 전달한다.
// 무승부
static bool IsBoardFull()
{
for (int row = 0; row < 3; row++)
{
for (int col = 0; col < 3; col++)
{
if (board[row, col] == ' ')
return false;
}
}
return true;
}
위는 무승부 함수이다. board[row, col]에 빈칸이 있다면 false고 아니라면 true다. 즉, 꽉 차면 무승부이기 때문에 판이 꽉 찼을때 false를 반환하는 코드이다.
3. 마무리 및 전체코드
이런식으로 간단한 방식으로 틱택토를 만들어봤다. 아래는 전체 코드이고, 게임 승리시 화면이다.
namespace _2_Homework2
{
internal class Program
{
static char[,] board = new char[3, 3];
static char currentPlayer = 'X';
static void Main(string[] args)
{
InitializeBoard();
while (true)
{
DisplayBoard();
PlayerInputNum();
if (IsGameOver())
{
DisplayBoard();
Console.WriteLine("게임 종료!");
break;
}
SwitchPlayer();
}
}
// 보드 초기화
static void InitializeBoard()
{
for (int row = 0; row < 3; row++)
{
for (int col = 0; col < 3; col++)
{
board[row, col] = ' ';
}
}
}
// 보드판 생성
static void DisplayBoard()
{
Console.Clear();
// 좌표 써놓기
Console.WriteLine(" 0 1 2");
for (int row = 0; row < 3; row++)
{
Console.Write(row + " ");
for (int col = 0; col < 3; col++)
{
Console.Write(board[row, col]);
if (col < 2)
{
Console.Write("|");
}
}
Console.WriteLine();
if (row < 2)
{
Console.WriteLine(" -+-+-");
}
}
}
//게임시 플레이어가 좌표
static void PlayerInputNum()
{
int row, col;
do
{
Console.Write($"플레이어 {currentPlayer}, 행과 열을 입력하세요 (예: 0 0, 주의!: 0 부터 2까지만 입력해주세요): ");
string[] input = Console.ReadLine().Split(' ');
row = int.Parse(input[0]);
col = int.Parse(input[1]);
} while (row < 0 || row >= 3 || col < 0 || col >= 3 || board[row, col] != ' ');
board[row, col] = currentPlayer;
}
// 승자가 생기거나, 무승부시 게임 종료
static bool IsGameOver()
{
return IsWinner() || IsBoardFull();
}
// 승자 함수
static bool IsWinner()
{
for (int i = 0; i < board.GetLength(0); i++)
{
//가로 또는 세로 승리
if (board[i, 0] == currentPlayer && board[i, 1] == currentPlayer && board[i, 2] == currentPlayer)
return true;
if (board[0, i] == currentPlayer && board[1, i] == currentPlayer && board[2, i] == currentPlayer)
return true;
}
// 대각선 승리
if (board[0, 0] == currentPlayer && board[1, 1] == currentPlayer && board[2, 2] == currentPlayer)
return true;
if (board[0, 2] == currentPlayer && board[1, 1] == currentPlayer && board[2, 0] == currentPlayer)
return true;
return false;
}
// 무승부
static bool IsBoardFull()
{
for (int row = 0; row < 3; row++)
{
for (int col = 0; col < 3; col++)
{
if (board[row, col] == ' ')
return false;
}
}
return true;
}
// 플레이어 선공 교체
static void SwitchPlayer()
{
if (currentPlayer == 'X')
{
currentPlayer = 'O';
}
else
{
currentPlayer = 'X';
}
}
}
}
4. 마지막 한 마디
막상 만들고나니 누가 이겼는지, 다른 키를 눌렀을 때 경고 등 아쉬운 부분이 있었다. 사용자 입장에서 생각하며 코드를 짜는 연습을 많이 해야겠다고 느꼈고, 제대로된 설계와 함께 만들어야 역시 더 퀄리티있는 게임이 나올 것 같다.
'Unity' 카테고리의 다른 글
C# 블랙잭 만들기 (4) | 2023.11.09 |
---|---|
SelectMany 를 이용한 다중 배열 (0) | 2023.11.08 |
[C#] 재귀 호출과 구조체(struct) 및 구조체와 클래스의 차이 (0) | 2023.11.06 |
[Unity] 카드 매칭 게임 프로젝트 마무리 (0) | 2023.11.03 |
[Unity] TimeScale 사용시 유의할 점 (2) | 2023.11.02 |