오늘은 2번 강의를 교육하며 C# 기초와 2번 과제를 해결하기로 했다.
3번 강의까지 듣고 싶었지만 과제에서 좀 헤매는 바람에 시간이 부족하였다.
처음엔 강의 교육 내용을 정리하고자 했지만 과제 내용이 많아 그것을 쓰고자 한다.
과제는 숫자 맞추기 게임과 틱택토 만들기 였는데 숫자 맞추기는 강의 내용을 참고하며 금방 작성할 수 있었기에 코드만 남겨 놓았다.
강의 과제 - 숫자 맞추기 게임
숫자 맞추기 게임 - 1부터 100 사이 숫자를 맞춰나가는 게임
ReadLine()을 받은 숫자가 랜덤 넘버보다 큰지 작은지 비교하여 출력해주는 것만 생각하면 됬다.
static void Main(string[] args)
{
//숫자 맞추기
int targetNumber = new Random().Next(1, 101);
int guess = 0;
int count = 0;
Console.WriteLine("1부터 100 사이의 숫자를 맞춰보세요");
while(guess != targetNumber)
{
Console.Write("추측한 숫자를 입력하세요: ");
guess = int.Parse(Console.ReadLine());
count++;
if(guess < targetNumber)
{
Console.WriteLine("좀 더 큰 숫자를 입력하세요.");
}
else if(guess > targetNumber)
{
Console.WriteLine("좀 더 작은 숫자를 입력하세요.");
}
else
{
Console.WriteLine("축하합니다! 숫자를 맞추셨습니다!");
Console.WriteLine("시도한 횟수 : " + count);
}
}
}
강의 과제 - 틱택토 만들기
틱택토 만들기는 어려운 점들이 있었다
1. 승리 조건을 체크하는 조건문 작성 - 가로, 세로, 대각선 승리 조건 / 무승부 출력
2. 9칸을 배치하고 배열로 저장한 뒤 이를 ReadLine() 방식 input으로 칸을 X 혹은 O로 바꾸는 방법
3. 플레이어의 턴을 배정하는 방법
이 세 개의 방법을 정하는데 고민을 많이 했다.
우선 2번의 9칸 배치는 본 2번 강의에서 설명해준 2차원 배열을 사용해보기로 했다.
static char[,] numberField = new char[3, 3]
{
{'1', '2', '3' },
{'4', '5', '6' },
{'7', '8', '9' }
};
이렇게 배열을 저장한 뒤 //다음에는 numberField처럼 긴 이름 말고 더 짧게 써보자... 나중에 길어져서 보기 불편했다.
static void Field()
{
Console.WriteLine("플레이어 1: X 와 플레이어 2: O ");
Console.WriteLine();
Console.WriteLine("-------------");
Console.WriteLine("| {0} | {1} | {2} |", numberField[0, 0], numberField[0, 1], numberField[0, 2]);
Console.WriteLine("I---I---I---I");
Console.WriteLine("I {0} | {1} | {2} |", numberField[1, 0], numberField[1, 1], numberField[1, 2]);
Console.WriteLine("I---I---I---I");
Console.WriteLine("| {0} | {1} | {2} |", numberField[2, 0], numberField[2, 1], numberField[2, 2]);
Console.WriteLine("-------------");
Console.WriteLine();
}
이렇게 필드를 WriteLine()을 통해 나타냈다.
그럼 이제는 어떻게 플레이어의 턴을 체크할지 고민해야했다.
그 방법으로 처음에는 Turn이라는 함수를 만들어 체크해보고자 했지만 더 좋은 방법이 떠올랐다.
후에 무승부를 체크하는 방법으로 복잡한 코드를 생각하기 보다
틱택토라는 게임의 특성상 턴의 횟수가 9번 이상을 넘을 수 없게 되어있는데
이를 체크하기 위해 턴 Count를 제기로 하였다.
int count = 1; 를 만들고 플레이어가 칸을 선택한 뒤 count++;를 통해 턴을 하나씩 늘려주고 이렇게 생겨난 턴 숫자를
2명의 플레이어 뿐이기에
if (count % 2 == 0)
{
Console.WriteLine("플레이어 2의 차례");
}
else
{
Console.WriteLine("플레이어 1의 차례");
}
홀수 짝수를 확인하던 코드를 이용해 count가 짝수라면 플레이어 2, 홀수라면 1로 나타내도록 했다.
이제 문제는 입력 받은 칸을 'O'와 'X'로 바꾸는 방법이었다.
이를 위해 우선 이해해야 했던 것이
2차원 배열에서 각 칸은 16진법을 통한다는 것이었다.
그렇기에 Input으로 적은 숫자로 각 해당하는 칸을 호출하기 위해서는
Input을 /3으로 나눈 값 x 와 %3을 한 나머지 값 y를 numberField[x, y]로 나타내야 했다.
이렇게 호출된 칸을 플레이어의 턴을 체크하여 'X' 혹은 'O'로 바꿔줘야 했다. 이 코드는
int row = userInput / 3;
int column = userInput % 3; //코드 단순화를 위한 함수
if (count % 2 == 0)
{
numberField[row, column] = 'O';
}
else
{
numberField[row, column] = 'X';
}
count++; //턴을 바꿈
그리고 또 한 가지 신경 써야 할 것은 이미 'O' 혹은 'X'로 점유된 칸을 다시 호출할 때로 이때는 바뀌거나 count가 높아져서는 안된다 그렇기에 이렇게 칸을 변경하는 코드 이전에 이미 변경된 칸인지 체크하는 코드를 작성하여 먼저 체크하도록 했다.
int row = userInput / 3;
int column = userInput % 3;
if (numberField[row, column] == 'X' || numberField[row, column] == 'O') //코드 추가
{
continue;
}
if (count % 2 == 0)
{
numberField[row, column] = 'O';
}
else
{
numberField[row, column] = 'X';
}
count++;
이렇게 해서 어려웠던 파트 중 하나를 해결했다.
이제 플레이가 가능해졌으니 해야 하는 것은 승리 조건을 구현해서 게임이 끝나도록 하는 것
이에는 여러가지 방식이 있었고 어떻게 구현해야 할지 어려움을 느껴 주변인에게 도움을 요청했고 복잡한 방법보다 보다 직관적인 방법으로 input 받은 칸을 인식한 뒤 그 주변의 칸을 체크해서 3칸이 채워졌는지 확인하는 코드를 작성하라는 조언을 받았다.
좋은 방법이라 생각하여 마침 2차원 배열을 이용했기에 각 row와 column 값을 고정하고 나머지 값을 확인하면 되어 조금 더 짧은 코드를 작성할 수 있을 거라고 생각했다.
승리하는 플레이어의 경우 count % 2는 똑같이 하되 이는 1, 0 값으로 나오기 때문에 + 1을 해주었다.
이를 고려해 다음과 같이 구현했다.
if (numberField[row, 0] == numberField[row, 1] && numberField[row, 1] == numberField[row, 2])
{
Console.Clear();
Console.WriteLine("플레이어 {0}의 승리입니다!", (count % 2) + 1);
break;
}
else if (numberField[0, column] == numberField[1, column] && numberField[1, column] == numberField[2, column])
{
Console.Clear();
Console.WriteLine("플레이어 {0}의 승리입니다!", (count % 2) + 1);
break;
}
(numberField가 너무 긴 이름이었다...)
이렇게 각각 가로 승리 조건 / 세로 승리 조건을 체크할 수 있게 되었다.
하지만 틱택토는 대각선을 채워 승리할 수도 있다.
그렇지만 대각선을 만드는 방법은 단 두 개 뿐으로 1~9 번호 중 1 / 5 / 9와 3 / 5 / 7 을 채워 3개를 만드는 방법 뿐이다.
이를 더 세련되게 체크할 수도 있겠지만 이미 여기까지 공부하는데 피로를 느껴 대각선은 단순하게 체크하기로 했다.
else if (numberField[0, 0] == numberField[1, 1] && numberField[1,1] == numberField[2, 2] || numberField[0, 2] == numberField[1, 1] && numberField[1, 1] == numberField[2, 0])
{
Console.Clear();
Console.WriteLine("플레이어 {0}의 승리입니다!", (count % 2) + 1);
break;
}
"작동하니 아무튼 된 것 아닐까?"
그리고 아까 말한 무승부 조건은 턴 수를 통해 체크했다. 이 체크를 할 때 칸의 변경 보다 먼저 체크하게 될 경우 칸 변경이 안되고 게임이 끝나는 어색함이 나타나기에 무승부 체크를 순서 상 모든 변경이 끝난 가장 마지막에 체크하도록 하여 해결하였다.
if (count > 9)
{
Console.Clear();
Console.WriteLine("무승부 입니다!");
Console.WriteLine();
break;
}
이렇게 해서 요구 조건대로 작동하는 틱택토가 완성되었다.
완성된 코드는 접은글에 남겨 놓도록 하겠다.
틱택토 게임 전체 코드
static char[,] numberField = new char[3, 3]
{
{'1', '2', '3' },
{'4', '5', '6' },
{'7', '8', '9' }
};
static void Field()
{
Console.WriteLine("플레이어 1: X 와 플레이어 2: O ");
Console.WriteLine();
Console.WriteLine("-------------");
Console.WriteLine("| {0} | {1} | {2} |", numberField[0, 0], numberField[0, 1], numberField[0, 2]);
Console.WriteLine("I---I---I---I");
Console.WriteLine("I {0} | {1} | {2} |", numberField[1, 0], numberField[1, 1], numberField[1, 2]);
Console.WriteLine("I---I---I---I");
Console.WriteLine("| {0} | {1} | {2} |", numberField[2, 0], numberField[2, 1], numberField[2, 2]);
Console.WriteLine("-------------");
Console.WriteLine();
}
static void Main(string[] args)
{
int userInput;
int count = 1;
while (true)
{
Console.Clear();
Field();
if (count % 2 == 0)
{
Console.WriteLine("플레이어 2의 차례");
}
else
{
Console.WriteLine("플레이어 1의 차례");
}
userInput = int.Parse(Console.ReadLine()) - 1;
int row = userInput / 3;
int column = userInput % 3;
if (numberField[row, column] == 'X' || numberField[row, column] == 'O')
{
continue;
}
if (count % 2 == 0)
{
numberField[row, column] = 'O';
}
else
{
numberField[row, column] = 'X';
}
count++;
if (numberField[row, 0] == numberField[row, 1] && numberField[row, 1] == numberField[row, 2])
{
Console.Clear();
Console.WriteLine("플레이어 {0}의 승리입니다!", (count % 2) + 1);
break;
}
else if (numberField[0, column] == numberField[1, column] && numberField[1, column] == numberField[2, column])
{
Console.Clear();
Console.WriteLine("플레이어 {0}의 승리입니다!", (count % 2) + 1);
break;
}
else if (numberField[0, 0] == numberField[1, 1] && numberField[1,1] == numberField[2, 2] || numberField[0, 2] == numberField[1, 1] && numberField[1, 1] == numberField[2, 0])
{
Console.Clear();
Console.WriteLine("플레이어 {0}의 승리입니다!", (count % 2) + 1);
break;
}
if (count > 9)
{
Console.Clear();
Console.WriteLine("무승부 입니다!");
Console.WriteLine();
break;
}
}
Field();
}
오늘은 어렵지만 무언가 배운 건 많았다. 하지만 3주차에 더 어려운 과제가 나오기 때문에 이 속도로 하게 된다면
진짜 과제인 개인 과제를 금요일까지 제출하는데 어려움이 클 것 같다.
3주차 과제는 도움을 많이 받아서라도 좀 더 빠르게 해결하고 4주차 강의를 내일까지 들을 수 있도록 해야 하겠다.
'TIL' 카테고리의 다른 글
[TIL]2024-1-4 / 9일차 - 개인과제 : 텍스트 게임 기본 기능 구현 (0) | 2024.01.04 |
---|---|
[TIL]2024-1-3 / 8일차 - '또' 어려웠던 스네이크 게임 만들기 (1) | 2024.01.03 |
[TIL]2023-12-29 / 6일차 - C# 기초 공부 복기 (1) | 2023.12.29 |
[TIL]2023-12-28 / 5일차 - 발표와 정리 (0) | 2023.12.28 |
[TIL]2023-12-27 / 4일차 - 디버깅과 다듬기, 발표 준비 (1) | 2023.12.27 |