내일배움캠프/달리기반

[내배캠][Unity_6기][과제] 8. 숫자 야구 게임

binary는 호남선 2024. 9. 2. 16:39

- 과제 내용 이해 어려워 일반적인 규칙의 야구 게임을 기준으로 작성했습니다.

  • 숫자 야구 게임을 작성하세요. 컴퓨터가 3자리의 숫자를 선택하면 사용자가 그 숫자를 맞추는 게임을 구현하세요. 각 자리의 숫자를 비교하여 스트라이크와 볼의 개수를 출력합니다.
    • 숫자 야구 게임 설명
    • 숫자 야구 게임은 3자리의 숫자를 맞추는 게임입니다. 컴퓨터가 고른 3자리 숫자는 모두 다른 숫자로 이루어져 있습니다. 사용자는 3자리 숫자를 입력하고, 컴퓨터는 입력한 숫자와 자리수를 비교하여 스트라이크와 볼의 개수를 알려줍니다.
    • 스트라이크: 숫자와 자리수가 모두 맞는 경우
    • : 숫자는 맞지만 자리수가 다른 경우
    예를 들어, 컴퓨터가 427을 선택하고 사용자가 123을 입력했을 때, 2는 숫자와 자리가 같으므로 1스트라이크입니다. 나머지는 숫자가 같지 않으므로 무시됩니다. 이 과정은 사용자가 정확한 숫자를 맞출 때까지 반복됩니다.

예상 출력)

Enter your guess (3 digits): 123 0 Strike(s), 0 Ball(s)
Enter your guess (3 digits): 456 0 Strike(s), 0 Ball(s)
Enter your guess (3 digits): 789 1 Strike(s), 2 Ball(s)
Enter your guess (3 digits): 987 3 Strike(s), 0 Ball(s)
Congratulations! You've guessed the number in 4 attempts.

고민했던 부분

- 4자리 숫자 게임도 있으므로 확장성을 고려하여 정답 숫자 배열 길이를 변수로 생성

- 중복 숫자 방지를 위해 0~9숫자를 문자열로 만들어 하나씩 선택하도록 로직 구성

- 문자열 쉽게 비교하기 위해 만들어진 정답 숫자 배열을 문자열로 변환

 

- 사용자 입력 받는 부분 길어져서 함수로 분리

- 사용자 입력은 유효한 입력을 받을 때까지 반복

- 볼과 스트라이크 변수는 매반복에서 0으로 초기화

 

- 입력 문자열과 정답 문자열 비교 가독성 위해 함수로 분리

- 스트라이크와 볼 변수를 참조로 넘기기

- 결과 출력 후 스트라이크와 정답문자열 길이 같으면 프로그램 종료(정답 맞췄는지 확인하는 변수 생략)

 

놓쳐서 고생한 부분...

- 난수 생성 로직에서 자꾸 이상한 값이 들어가서 디버깅해보니 숫자의 아스키코드 값을 받아오고 있었다!

    int num = random.Next(0, digits.Length);
    targetNumber[i] = digits[num];

 

/*********** 야구 게임 ***********/
/* 게임 초기 설정 */
// 컴퓨터가 랜덤한 3자리 숫자 정함
int length = 3;
Random random = new Random();
int[] targetNumber = new int[length];   // 랜덤한 숫자 배열
string digits = "0123456789";   // 중복 방지용 배열
// 중복 없이 숫자 입력 받기
for (int i = 0; i < length; i++)
{
    int num = random.Next(0, digits.Length);    // 0 ~ 선택숫자배열 사이의 난수 생성
    targetNumber[i] = digits[num] - '0';    // 난수를 인덱스로 i번째 자리에 저장
    digits = digits.Remove(num, 1); // 중복 방지하여 사용한 숫자 제거
}
// 정해진 랜덤한 숫자 배열을 문자열로 전환
string targetNum = string.Concat(targetNumber);
int attempts = 0;   // 시도 횟수

/* 게임 실행부 */
while (true)
{
    // 사용자 입력 받기
    string input = InputNum();
    if (input == null)
    {
        Console.WriteLine("Input Error...");
        return;
    }

    // 매 반복시 초기화
    int ball = 0;
    int strike = 0;
    
    // 사용자 입력 숫자와 정답 숫자 비교
    CompareNum(input, ref strike, ref ball);
    attempts++;

    // 결과 출력
    Console.WriteLine($"{strike} Strike(s), {ball} Ball(s)");
    if (strike == length)
    {
        Console.WriteLine($"Congratulations! You've guessed the number in {attempts} attempts.");
        return;
    }
}

/* 함수부 */
// 사용자가 입력한 숫자 유효성 체크하고 문자열로 반환하는 함수
string InputNum()
{
    while (true)
    {
        Console.Write($"Enter your guess ({length} digits): ");
        string input = Console.ReadLine();
        if (input.Length != length)  // 3자리 아니면 개행 후 반복
        {
            Console.WriteLine();
            continue;
        }
        // 문자열이 정수인지 확인
        int temp;
        bool isNum = int.TryParse(input, out temp);
        if (isNum)
            return input;
    }
    // 예상치 못한 에러 발생하는 경우 null 반환
    return null;
}
// 입력된 숫자와 정답 숫자 비교하여 스트라이크와 볼 계산
void CompareNum(string num, ref int strike, ref int ball)
{
    for (int i = 0; i < length; i++)
    {
        // 추측한 숫자와 정답 숫자의 값과 자리가 같으면
        if (num[i] == targetNum[i])
        {
            strike++;   // 스트라이크 증가
        }
        // 추측한 숫자와 정답 숫자 같은 같으나 자리가 다르면
        // (이전 조건은 만족하지 않지만 숫자가 정답 숫자열에 포함됨)
        else if (targetNum.Contains(num[i]))
        {
            ball++; // 볼 증가
        }
    }
}