[C#] 틱택토 만들기 + 기본 문법

고현규·2023년 11월 6일

오늘 학습 내용

1. C언어와 다른 C# 표기법
2. C# 틱택토 만들기

1. C언어와 다른 C# 표기법

입출력 방법

Console.WriteLine() // printf 한줄 출력 후 개행.
Console.Write() // 개행 없이 한 줄 출력



Console.WriteLine("이름과 나이를 입력하세요: "); // Scanf 입력 받기
string[] info = profile.Split(' '); // 입력받은 값 여러개로 나누기 
Console.WriteLine("이름 : {0}, 나이 : {1}", info[0], info[1]); // %d 대신 {i}\


int num = int.Parse(Console.ReadLine());
  • Printf 대신 Console.WriteLine()을 사용해서 출력한다.

  • %d 대신 {i]를 사용해서 뒤에 오는 변수를 순서대로 불러들여온다.

  • int.Parse(Console.ReadLine()); 로 입력 받은 동시에 자료형을 바꿀 수 있다.

컬렉션 List

List<int> numbers = new List<int>(); // 빈 리스트 생성
numbers.Add(1); // 리스트에 데이터 추가
numbers.Add(2);
numbers.Add(3);
numbers.Remove(2); // 리스트에서 데이터 삭제

foreach(int number in numbers) // 리스트 데이터 출력
{
    Console.WriteLine(number);
}

가변형 배열로 Remove 를 사용하면 배열의 크기또한 바뀐다.
foreach를 이용하여 배열의 각 요소마다 한 번씩 접근할 수 있다.

2. 틱택토 게임 제작

namespace ConsoleApp3
{
    internal class Program
    {

        // 맵을 보여주는 메서드
        static void MapView(int[] map)
        {
            for (int i = 0; i < 9; i++)
            {
                if (map[i] == 0)
                {
                    Console.Write("■ ");
                }
                else if (map[i] == 1)
                {
                    Console.Write("◎ ");
                }
                else if (map[i] == -1)
                {
                    Console.Write("X ");
                }

                if ((i + 1) % 3 == 0)
                {
                    Console.WriteLine();
                }
            }
        }

        // 모든 게임 승리와 패배를 판단하는 메서드
        static bool IsGameOver(int[] map)
        {
            if (map[0] + map[1] + map[2] == 3 || map[0] + map[1] + map[2] == -3)
            {
                MapView(map);
                Console.WriteLine("게임이 끝났습니다.");
                return true;
            }
            else if (map[3] + map[4] + map[5] == 3 || map[3] + map[4] + map[5] == -3)
            {
                MapView(map);
                Console.WriteLine("게임이 끝났습니다.");
                return true;
            }
            else if (map[6] + map[7] + map[8] == 3 || map[6] + map[7] + map[8] == -3)
            {
                MapView(map);
                Console.WriteLine("게임이 끝났습니다.");
                return true;
            }
            else if (map[0] + map[3] + map[6] == 3 || map[0] + map[3] + map[6] == -3)
            {
                MapView(map);
                Console.WriteLine("게임이 끝났습니다.");
                return true;
            }
            else if (map[1] + map[4] + map[7] == 3 || map[1] + map[4] + map[7] == -3)
            {
                MapView(map);
                Console.WriteLine("게임이 끝났습니다.");
                return true;
            }
            else if (map[2] + map[5] + map[8] == 3 || map[2] + map[5] + map[8] == -3)
            {
                MapView(map);
                Console.WriteLine("게임이 끝났습니다.");
                return true;
            }
            else if (map[0] + map[4] + map[8] == 3 || map[0] + map[4] + map[8] == -3)
            {
                MapView(map);
                Console.WriteLine("게임이 끝났습니다.");
                return true;
            }
            else if (map[2] + map[4] + map[6] == 3 || map[2] + map[4] + map[6] == -3)
            {
                MapView(map);
                Console.WriteLine("게임이 끝났습니다.");
                return true;
            }
            else
            {
                return false;
            }
        }

        static void Main(string[] args)
        {
            // 맵 만들기
            int[] map = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };

            Random random = new Random();

            int computer = 10;
            int count;
            bool result = false;


            while (true)
            {                
                MapView(map);

                // 어느 위치에 원을 그릴지 입력
                Console.WriteLine("표시를 할 위치를 골라주세요 (1~9): ");
                int choice = int.Parse(Console.ReadLine());


                // 입력 예외처리 
                if (choice < 1 || choice > 9)
                {
                    Console.WriteLine("해당되지 않는 숫자입니다. 다시 입력해주세요.");
                    continue;
                }
                else if(map[choice - 1] != 0)
                {
                    Console.WriteLine("이미 쓰인 자리입니다. 다시 골라주세요");
                    continue;
                }
                // 입력한 자리에 1 값을 넣음
                map[choice-1] = 1;


                // 승패 결과를 확인
                result = IsGameOver(map);
                if (result == true)
                {
                    break;
                }


                // 비겼는지? 값 초기화 및 확인
                count = 0;
                for(int i=0; i<map.Length; i++)
                {
                    if (map[i] == 0)
                    {
                        count++;
                    }
                }

                // 만약 모든 배열의 0의 값이 0개이면 비김
                if (count == 0)
                {
                    Console.WriteLine("비겼습니다!");
                    break;
                }
                
                
                // 컴퓨터가 랜덤한 위치에 X를 놓는다. 있는 곳이라면 다시 한다.
                do
                {
                    computer = random.Next(1, 10);
                } while (map[computer - 1] != 0);
                map[computer - 1] = -1;

                Console.WriteLine("컴퓨터가 놓은 위치 : {0}", computer);


                // 컴퓨터가 놓은 뒤에 패배했는지 다시 한번 확인.
                result = IsGameOver(map);
                if (result == true)
                {
                    break;
                }
            }
        }
    }
}

코드 요약

메서드 내용

  • 맵을 보는 코드 - 배열에 들어간 숫자를 확인하고 도형으로 변환한다.
  • 게임이 끝나는 조건 - 각 승리조건과 패배조건을 확인해서 bool값으로 넘겨준다.

게임 엔딩 조건이 충족될 때 까지 while 무한반복문을 실행한다.
플레이어가 입력할 위치를 입력받고, 예외처리를 한다.
1~9 숫자 외의 예외와, 이미 입력된 장소인지 확인하고 Countinue로 반복한다.

O가 입력됐을 때 승리가 되었는지 첫 번째로 확인한다.
그 다음 플레이어가 고르지 않은 위치가 나올 때 까지 랜덤하게 컴퓨터가 값을 뽑아낸다.
해당 위치에 X값을 넣고 다시 한번 패배가 되었는지 확인한다.

이를 반복한다.
마지막에 모든 칸이 채워졌을 때 비겼다는 판정을 위해
모든 값을 확인하여 0이 있으면 Count를 올린다. 0으로 된 공간이 없을 때
모두 입력된 것으로 인지하여 비겼다는 판정을 낸다.

작성 후기

생각보다 고려할 조건이 많아서 코드가 길어지고 지저분하게 되었다.
예외 처리는 물로닝고, 코드의 순서 때문에 패배하냐 승리하냐 혹은 비기냐 계속 오류를 발생했다.
오랫만에 코딩에 머리를 쓰다보니 깔끔한 느낌은 아니었다.

최대한 코드를 깔끔하게 쓰려다보니 생각하고, 메서드도 쓰면서 생각보다 시간이 오래 걸렸다.
이제 보통 실행했을 때 오류가 뜨지 않아 완성시킨 듯 하지만
모든 오류를 검사한 것 같진 않아 살짝 불안한 감도 있다.
그래도 시간안에 완성하여 아직 실력이 녹슬지 않은 것 같아 뿌듯하다.

아래는 틱택토 문제의 풀이다

internal class Program
    {
        static char[] arr = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
        static int player = 1;
        static int choice;
        static int flag = 0;

        static void Main(string[] args)
        {
            do
            {
                Console.Clear();
                Console.WriteLine("플레이어 1: X 와 플레이어 2: O");
                Console.WriteLine("\n");

                if (player % 2 == 0)
                {
                    Console.WriteLine("플레이어 2의 차례");
                }
                else
                {
                    Console.WriteLine("플레이어 1의 차례");
                }

                Console.WriteLine("\n");
                Board();

                string line = Console.ReadLine();
                bool res = int.TryParse(line, out choice);

                if(res == true)
                {
                    if (arr[choice] != 'X' && arr[choice] != 'O')
                    {
                        if(player % 2 == 0)
                        {
                            arr[choice] = 'O';
                        }
                        else
                        {
                            arr[choice] = 'X';
                        }

                        player++;
                    }
                    else
                    {
                        Console.WriteLine("죄송합니다. {0} 행은 이미 {1}로 표시되어 있습니다.", choice, arr[choice]);
                        Console.ReadLine();
                    }
                }
                else
                {
                    Console.WriteLine("숫자를 입려해주세요.");
                }

                flag = CheckWin();
            }
            while (flag != -1 && flag != 1);

            if(flag == 1)
            {
                Console.WriteLine("플레이어 {0}이(가) 이겼습니다.", (player % 2) + 1);
            }
            else
            {
                Console.WriteLine("무승부");
            }

            Console.ReadLine();
        }

        static void Board()
        {
            Console.WriteLine("     |     |     ");
            Console.WriteLine("  {0}  |  {1}  |  {2}  ", arr[1], arr[2], arr[3]);
            Console.WriteLine("_____|_____|_____");
            Console.WriteLine("     |     |     ");
            Console.WriteLine("  {0}  |  {1}  |  {2}  ", arr[4], arr[5], arr[6]);
            Console.WriteLine("_____|_____|_____");
            Console.WriteLine("     |     |     ");
            Console.WriteLine("  {0}  |  {1}  |  {2}  ", arr[7], arr[8], arr[9]);
            Console.WriteLine("     |     |     ");
        }

        static int CheckWin()
        {
            // 가로 승리 조건
            if (arr[1] == arr[2] && arr[2] == arr[3])
            {
                return 1;
            }
            else if (arr[4] == arr[5] && arr[5] == arr[6])
            {
                return 1;
            }
            else if (arr[7] == arr[8] && arr[8] == arr[9])
            {
                return 1;
            }

            // 세로 승리 조건
            else if (arr[1] == arr[4] && arr[4] == arr[7])
            {
                return 1;
            }
            else if (arr[2] == arr[5] && arr[5] == arr[8])
            {
                return 1;
            }
            else if (arr[3] == arr[6] && arr[6] == arr[9])
            {
                return 1;
            }

            // 대각선 승리조건
            else if (arr[1] == arr[5] && arr[5] == arr[9])
            {
                return 1;
            }
            else if (arr[3] == arr[5] && arr[5] == arr[7])
            {
                return 1;
            }
            
            // 무승부
            else if (arr[1] != '1' && arr[2] != '2' && arr[3] != '3' && arr[4] != '4' && arr[5] != '5' && arr[6] != '6' &&
                arr[7] != '7' && arr[8] != '8' && arr[9] != '9')
            {
                return -1;
            }
            else { return 0; }
            
        }
    }

나의 코드와 다른 점

  • 다른 플레이어의 입력이 자동으로 되지 않고 수동으로 입력해줘야 한다.
    컴퓨터와 싸우는 게 아니라 플레이어 vs 플레이어 형식이라 Random을 사용할 필요가 없다.

  • 게임이 끝날 때 마지막으로 선택한 플레이어가 승리한 것으로 판단한다.
    스이판단을 굉장히 편리하게 한 것 같다. 그래도 코드가 조금 길어져도 승리 패배 조건을 따로 정해줘도 될 것 같다.

  • 배열의 숫자를 0으로 하지않고 각 순번에 맞는 숫자를 넣어 알아보기 편하게 했다.
    나는 빈 칸을 네모칸으로 표기했지만, 순번은 직접 세야했다. 숫자로 써있는 틱택토를 플레이 해보니 확실히 가시성이 좋아 어느 장소에 넣어야 할지 보여 좋은 코드라 생각했다.

  • 무승부 판정을 모든 숫자가 원래 숫자가 아닐 때로 조건을 정했다.
    코드가 너무 길어질 것 같아서 count로 했는데 생각보다 길지도 않았고 오히려 반복문을 사용해서 비용을 더 잡아 먹는 것 같다.

profile
게임 개발과 기획

0개의 댓글