내일배움캠프/강의 과제

C# 문법 종합반 - 2주차 : 콘솔 게임 만들기

서보훈 2024. 9. 20. 16:50

2주차 강의에서는 조건문과 반복문, 배열, 메서드 등에 대해서 배울 수 있었습니다.

 

2주차의 과제 내용을 정리해보겠습니다.

 


숫자 맞추기 게임

 

1~100 사이의 무작위 값이 주어지고, 숫자를 입력하면 무작위 값이 입력한 값보다 높다, 낮다의 힌트를 주어 숫자를 맞추는 게임입니다.

 

무작위 값은 C#에서 지원하는 Random 클래스를 통해 생성하고, 정답을 맞출때 까지 코드를 반복하여 이런 형태의 게임을 만들 수 있습니다.

더보기
//숫자맞추기
{
    // 1~100 사이의 무작위 숫자 생성
    int randomNum = new Random().Next(1, 101);
    //몇회 입력을 시도했는지 저장하는 변수
    int inputCounter = 0;

    Console.WriteLine("1에서 100까지의 숫자 중 하나를 맞춰보세요.");

    //무한반복
    while (true)
    {
        Console.Write("숫자를 입력하세요 : ");
        //입력값 저장
        string input = Console.ReadLine();
        //변환후 저장될 입력값
        int inputInt;

        //입력값이 int로 변환 가능한지 체크
        if (int.TryParse(input, out inputInt))
        {
            //1 ~ 100 사이의 값인지 체크
            if (inputInt >= 1 && inputInt <= 100)
            {
                if (inputInt == randomNum)
                {
                    //입력 시도 증가
                    //아래 break로 인해 즉시 반복이 종료되기 때문에 정답시 따로 시도를 증가시킴
                    inputCounter++;
                    //정답을 맞추면 무한반복문 종료
                    break;
                }
                else if (inputInt < randomNum)
                {
                    //입력값이 낮을떄 작동
                    Console.WriteLine("너무 작습니다!");
                }
                else
                {
                    //입력값이 클때 작동
                    Console.WriteLine("너무 큽니다!");
                }

                //입력 시도 증가
                inputCounter++;
            }
            else
            {
                Console.WriteLine("1 ~ 100 사이의 숫자가 아닙니다.");
            }
        }
        else
        {
            Console.WriteLine("잘못된 입력입니다.");
        }
    }

    //While문 종료후, 입력시도횟수 출력
    Console.WriteLine($"축하합니다! {inputCounter}번 만에 숫자를 맞추었습니다.");
}

먼저 변수를 선언해주었습니다.

랜덤 클래스의 경우, 무작위 값을 만들때 클래스 객체를 선언하는 방식으로 즉시 무작위 값을 만드는 방식을 선택하였습니다.

또한 시도한 횟수를 결과로써 알려주어야 하기 때문에 시도횟수를 저장할 int 변수를 만들어주었습니다.

// 1~100 사이의 무작위 숫자 생성
int randomNum = new Random().Next(1, 101);
//몇회 입력을 시도했는지 저장하는 변수
int inputCounter = 0;

Random().Next 함수의 경우 왼쪽값 이상, 오른쪽 값 미만의 무작위 정수값을 생성합니다.

따라서 1, 101 을 선언하여 1이상, 101 미만의 값을 생성하도록 만들어 1 ~ 100 사이의 정수를 목표값에 저장하게 됩니다.

 

무한반복을 시작합니다.

//무한반복
while (true)
{
    Console.Write("숫자를 입력하세요 : ");
    //입력값 저장
    string input = Console.ReadLine();
    //변환후 저장될 입력값
    int inputInt;
    //입력값이 int로 변환 가능한지 체크
    if (int.TryParse(input, out inputInt))
    {
    
    }
    else
    {
        Console.WriteLine("잘못된 입력입니다.");
    }
}

 

무한반복을 시작하면서, 숫자를 입력해달라는 텍스트를 출력하고 사용자로부터 숫자를 입력받습니다.

이후 입력값을 int 값으로 변환을 시도하고, 실패할경우 다시 입력을 유도하도록 조건문인 if 를 사용해주었습니다.

 

변환을 시도하는 if문 내부값 입니다.

//입력값이 int로 변환 가능한지 체크
if (int.TryParse(input, out inputInt))
{
    //1 ~ 100 사이의 값인지 체크
    if (inputInt >= 1 && inputInt <= 100)
    {
        //입력시도 증가
        inputCounter++;
        if (inputInt == randomNum)
        {
            //정답을 맞추면 무한반복문 종료
            break;
        }
        else if (inputInt < randomNum)
        {
            //입력값이 낮을떄 작동
            Console.WriteLine("너무 작습니다!");
        }
        else
        {
            //입력값이 클때 작동
            Console.WriteLine("너무 큽니다!");
        }
    }
    else
    {
        Console.WriteLine("1 ~ 100 사이의 숫자가 아닙니다.");
    }
}
else
{
    Console.WriteLine("잘못된 입력입니다.");
}

1부터 100 사이의 정수값을 입력하지 않을경우, 다시 입력을 시도하도록 유도하였습니다.

 

입력이 정확한경우 먼저 입력 횟수 카운터를 1증가시켜 결과를 확인할 때 입력한 횟수를 확인할 수 있도록 만들었습니다.

 

또한 while(true) 를 사용하여 무한루프를 만든 상태로, 이를 끝내기 위해 입력한 값이 생성된 무작위 값과 같으면 break를 통해 반복문이 종료되도록 만들었습니다.

 

마지막으로 반복문이 종료되면 몇회의 입력을 시도했는지 보여줍니다.

//While문 종료후, 입력시도횟수 출력
Console.WriteLine($"축하합니다! {inputCounter}번 만에 숫자를 맞추었습니다.");

 

아래는 답안 코드 입니다.

더보기
using System;

class Program
{
    static void Main()
    {
        Random random = new Random();
        int numberToGuess = random.Next(1, 101);
        int numberOfTries = 0;
        bool isNumberGuessed = false;

        Console.WriteLine("숫자 맞추기 게임을 시작합니다. 1에서 100까지의 숫자 중 하나를 맞춰보세요.");

        while (!isNumberGuessed)
        {
            Console.Write("숫자를 입력하세요: ");
            string userInput = Console.ReadLine();

            int userGuess;
            if (int.TryParse(userInput, out userGuess))
            {
                numberOfTries++;

                if (userGuess > numberToGuess)
                {
                    Console.WriteLine("너무 큽니다!");
                }
                else if (userGuess < numberToGuess)
                {
                    Console.WriteLine("너무 작습니다!");
                }
                else
                {
                    Console.WriteLine("축하합니다! {0}번 만에 숫자를 맞추었습니다.", numberOfTries);
                    isNumberGuessed = true;
                }
            }
            else
            {
                Console.WriteLine("유효한 숫자가 아닙니다. 다시 시도해주세요.");
            }
        }
    }
}

bool 값을 사용하여 while 문에 조건을 주어 조건이 false가 되면 반복이 종료되도록 만들어져 있습니다.

또한 1에서 100 사이가 아닌 정수값을 입력했을때 또한 크다 작다가 정상 작동하도록 만들어져있습니다.


틱텍토

두 플레이어가 번갈아가며 칸을 선택하고, 먼저 한줄을 채우면 이기는 게임인 틱텍토를 만들어야 합니다.

아래는 코드입니다.

더보기
namespace TicTakToe
{
    internal class Program
    {
        //입력 관련 변수
        static string input;
        static int inputNum;
        //각 타일의 숫자를 나타낼 변수
        static char[] arr = { '1', '2', '3', '4', '5', '6', '7', '8', '9' };

        //현재 플레이어
        static int nowPlayer = 1;
        //게임종료 판정
        static bool endGame = false;

        static void Main(string[] args)
        {
            while (!endGame)
            {
                Console.WriteLine("플레이어 1 : X | 플레이어 2 : O");
                Console.WriteLine();
                Console.WriteLine($"플레이어 {nowPlayer}의 차례");
                Console.WriteLine();

                //보드생성
                BoardMaker();

                //입력받음
                input = Console.ReadLine();
                if (int.TryParse(input, out inputNum))
                {
                    if (inputNum >= 1 && inputNum <= 9)
                    {
                        //선택되지 않은 타일을 골랐을경우
                        if (arr[inputNum - 1] != 'O' && arr[inputNum - 1] != 'X')
                        {
                            //1번 플레이어 차례일경우 O, 2번 플레이어 차례일경우 X
                            if (nowPlayer == 1)
                            {
                                arr[inputNum - 1] = 'O';
                            }
                            else
                            {
                                arr[inputNum - 1] = 'X';
                            }

                            //게임종료 판정
                            WinChecker();

                            //승리가 정해지지 않았을경우, 턴을 넘김
                            if(endGame == false)
                            {
                                if(nowPlayer == 1)
                                {
                                    nowPlayer = 2;
                                }
                                else
                                {
                                    nowPlayer = 1;
                                }
                            }

                            Console.Clear();
                        }
                        else
                        {
                            Console.Clear();
                            Console.WriteLine("해당 타일은 이미 선택된 상태입니다.");
                        }
                    }
                    else
                    {
                        Console.Clear();
                        Console.WriteLine("잘못된 입력입니다.");
                    }
                }
                else
                {
                    Console.Clear();
                    Console.WriteLine("잘못된 입력입니다.");
                }
            }

            BoardMaker();
            //반복문 종료후, 승리 판정
            Console.WriteLine($"플레이어{nowPlayer} 의 승리입니다.");
        }

        //보드생성 함수
        private static void BoardMaker()
        {
            //보드 기반
            int[,] mapArr = new int[9, 17]
            {
                {0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0},
                {0, 0, 1, 0, 0, -1, 0, 0, 2, 0, 0, -1, 0, 0, 3, 0, 0},
                {-2, -2, -2, -2, -2, -1, -2, -2, -2, -2, -2, -1, -2, -2, -2, -2, -2},
                {0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0},
                {0, 0, 4, 0, 0, -1, 0, 0, 5, 0, 0, -1, 0, 0, 6, 0, 0},
                {-2, -2, -2, -2, -2, -1, -2, -2, -2, -2, -2, -1, -2, -2, -2, -2, -2},
                {0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0},
                {0, 0, 7, 0, 0, -1, 0, 0, 8, 0, 0, -1, 0, 0, 9, 0, 0},
                {0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0}
            };

            //보드 생성
            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 17; j++)
                {
                    if (mapArr[i, j] >= 1 && mapArr[i, j] <= 9)
                    {
                        Console.Write(arr[mapArr[i, j] - 1]);
                    }
                    else if (mapArr[i, j] == -1)
                    {
                        Console.Write("|");
                    }
                    else if (mapArr[i, j] == -2)
                    {
                        Console.Write("_");
                    }
                    else
                    {
                        Console.Write(' ');
                    }
                }
                Console.WriteLine();
            }
        }

        //승리 여부 판별
        private static void WinChecker()
        {
            //가로줄을 맞추었을때
            if (arr[0] == arr[1] && arr[1] == arr[2])
            {
                endGame = true;
            }
            else if (arr[3] == arr[4] && arr[4] == arr[5])
            {
                endGame = true;
            }
            else if (arr[6] == arr[7] && arr[7] == arr[8])
            {
                endGame = true;
            }

            //세로줄
            if (arr[0] == arr[3] && arr[3] == arr[6])
            {
                endGame = true;
            }
            else if (arr[1] == arr[4] && arr[4] == arr[7])
            {
                endGame = true;
            }
            else if (arr[2] == arr[5] && arr[5] == arr[8])
            {
                endGame = true;
            }

            //대각선
            if (arr[0] == arr[4] && arr[4] == arr[8])
            {
                endGame = true;
            }
            else if (arr[2] == arr[4] && arr[4] == arr[6])
            {
                endGame = true;
            }
        }
    }
}

 

먼저 변수들을 선언해주었습니다.

해당값들은 클래스의 필드에 선언하는 전역변수이며, static으로 사용되는 main에 사용해야하는 변수이기도 하기 때문에 변수에 static을 붙여서 사용하게 됩니다.

//입력 관련 변수
static string input;
static int inputNum;
//각 타일의 숫자를 나타낼 변수
static char[] arr = { '1', '2', '3', '4', '5', '6', '7', '8', '9' };

//현재 플레이어
static int nowPlayer = 1;
//게임종료 판정
static bool endGame = false;

또한 1 ~ 9 까지 char형으로 선언한 배열이 존재하는데, 각 배열의 자리가 게임 보드의 자리가 되어 배열의 내용을 비교하여 게임이 종료되었음을 판단할 예정입니다.

 

 

메인함수 입니다.

이 함수는 별도의 호출이 없어도 프로그램이 시작되면 호출되는 함수 입니다.

static void Main(string[] args)
{
    while (!endGame)
    {
        Console.WriteLine("플레이어 1 : X | 플레이어 2 : O");
        Console.WriteLine();
        Console.WriteLine($"플레이어 {nowPlayer}의 차례");
        Console.WriteLine();

        //보드생성
        BoardMaker();

        //입력받음
        input = Console.ReadLine();
        if (int.TryParse(input, out inputNum))
        {
            if (inputNum >= 1 && inputNum <= 9)
            {
                //선택되지 않은 타일을 골랐을경우
                if (arr[inputNum - 1] != 'O' && arr[inputNum - 1] != 'X')
                {
                    //1번 플레이어 차례일경우 O, 2번 플레이어 차례일경우 X
                    if (nowPlayer == 1)
                    {
                        arr[inputNum - 1] = 'O';
                    }
                    else
                    {
                        arr[inputNum - 1] = 'X';
                    }

                    //게임종료 판정
                    WinChecker();

                    //승리가 정해지지 않았을경우, 턴을 넘김
                    if(endGame == false)
                    {
                        if(nowPlayer == 1)
                        {
                            nowPlayer = 2;
                        }
                        else
                        {
                            nowPlayer = 1;
                        }
                    }

                    Console.Clear();
                }
                else
                {
                    Console.Clear();
                    Console.WriteLine("해당 타일은 이미 선택된 상태입니다.");
                }
            }
            else
            {
                Console.Clear();
                Console.WriteLine("잘못된 입력입니다.");
            }
        }
        else
        {
            Console.Clear();
            Console.WriteLine("잘못된 입력입니다.");
        }
    }

    BoardMaker();
    //반복문 종료후, 승리 판정
    Console.WriteLine($"플레이어{nowPlayer} 의 승리입니다.");
}

bool 변수인 endGame 을 사용하여 반복이 종료 여부를 판단합니다.

while 문은 true 값을 가질때 내용이 반복되게 되는데 부정 논리 연산자인 !endGame을 사용했기 때문에 endGame이 false일때 반복문이 작동하고 true일때 반복문이 종료되게 됩니다.

 

세부 내용입니다.

//선택되지 않은 타일을 골랐을경우
if (arr[inputNum - 1] != 'O' && arr[inputNum - 1] != 'X')
{
    //1번 플레이어 차례일경우 O, 2번 플레이어 차례일경우 X
    if (nowPlayer == 1)
    {
        arr[inputNum - 1] = 'O';
    }
    else
    {
        arr[inputNum - 1] = 'X';
    }

    //게임종료 판정
    WinChecker();

    //승리가 정해지지 않았을경우, 턴을 넘김
    if(endGame == false)
    {
        if(nowPlayer == 1)
        {
            nowPlayer = 2;
        }
        else
        {
            nowPlayer = 1;
        }
    }

    Console.Clear();
}
else
{
    Console.Clear();
    Console.WriteLine("해당 타일은 이미 선택된 상태입니다.");
}

 

배열은 0부터 시작하는데 입력은 1 ~ 9 를 유도하고 있기 때문에 입력값에 -1을 해주어 위치를 맞춰주게 됩니다.

 

입력한 값이 정상적이고, 이번 입력으로 게임이 종료되지 않았을경우 nowPlayer 의 값을 변경하여 상대 플레이어에게 턴을 넘겨주게 됩니다.

 

게임 종료 판정의경우 WinChecker() 함수를 통해 진행되게 됩니다.

 

먼저, 보드판을 만드는 함수 BoardMaker() 입니다.

2차원 배열을 기반으로 보드를 만들게되며, 9X17 크기의 보드판을 만들게됩니다.

//보드생성 함수
private static void BoardMaker()
{
    //보드 기반
    int[,] mapArr = new int[9, 17]
    {
        {0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0},
        {0, 0, 1, 0, 0, -1, 0, 0, 2, 0, 0, -1, 0, 0, 3, 0, 0},
        {-2, -2, -2, -2, -2, -1, -2, -2, -2, -2, -2, -1, -2, -2, -2, -2, -2},
        {0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0},
        {0, 0, 4, 0, 0, -1, 0, 0, 5, 0, 0, -1, 0, 0, 6, 0, 0},
        {-2, -2, -2, -2, -2, -1, -2, -2, -2, -2, -2, -1, -2, -2, -2, -2, -2},
        {0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0},
        {0, 0, 7, 0, 0, -1, 0, 0, 8, 0, 0, -1, 0, 0, 9, 0, 0},
        {0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0}
    };

    //보드 생성
    for (int i = 0; i < 9; i++)
    {
        for (int j = 0; j < 17; j++)
        {
            if (mapArr[i, j] >= 1 && mapArr[i, j] <= 9)
            {
                Console.Write(arr[mapArr[i, j] - 1]);
            }
            else if (mapArr[i, j] == -1)
            {
                Console.Write("|");
            }
            else if (mapArr[i, j] == -2)
            {
                Console.Write("_");
            }
            else
            {
                Console.Write(' ');
            }
        }
        Console.WriteLine();
    }
}

 

각 배열의 값이 0일경우 공백칸을, -1 일경우 | 를 -2 일경우 _ 을 출력하게 됩니다.

또한 1 ~ 9 의 값에는 arr배열의 [0] 번슬롯부터 [8] 번까지의 내용을 출력하도록 만들었습니다.

 

승리 여부를 판단하여 endGame을 바꾸는 함수 WinChecker() 입니다.

//승리 여부 판별
private static void WinChecker()
{
    //가로줄을 맞추었을때
    if (arr[0] == arr[1] && arr[1] == arr[2])
    {
        endGame = true;
    }
    else if (arr[3] == arr[4] && arr[4] == arr[5])
    {
        endGame = true;
    }
    else if (arr[6] == arr[7] && arr[7] == arr[8])
    {
        endGame = true;
    }

    //세로줄
    if (arr[0] == arr[3] && arr[3] == arr[6])
    {
        endGame = true;
    }
    else if (arr[1] == arr[4] && arr[4] == arr[7])
    {
        endGame = true;
    }
    else if (arr[2] == arr[5] && arr[5] == arr[8])
    {
        endGame = true;
    }

    //대각선
    if (arr[0] == arr[4] && arr[4] == arr[8])
    {
        endGame = true;
    }
    else if (arr[2] == arr[4] && arr[4] == arr[6])
    {
        endGame = true;
    }
}

틱택토는 가로줄, 세로줄, 대각선 중 한줄이라도 한쪽 플레이어가 완성시키게되면 게임이 종료됩니다.

이 조건을 if의 else if 를 사용하여 승리 조건이 하나라도 만족되면 endGame이 true 가 되도록 만들었으며,

그 외의 조건에서는 이 함수는 아무런 행동을 취하지 않게됩니다.

 

이 아래는 정답 코드 입니다.

더보기
namespace _2_6
{
    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; }
            
        }
    }
}

이번 강의에서 배열을 통해 2차원 게임맵을 만들어내는 방법을 사용했기 때문에 해당 방법을 사용하여 보드를 만들었지만 강의에서는 사용하지 않은 모습을 볼 수 있습니다

 

또한 제 코드에서 무승부 조건을 만들지 않았기 때문에 무승부 발생시 어떤 입력을 해도 빈칸이 아니라는 내용이 출력되고 게임이 종료되지 않는 상태가 된다는걸 알 수 있습니다.