[내일배움캠프 12일차] 야구게임 만들기 - 도전 구현 기능 적용

NH·2025년 3월 18일

내일배움캠프

목록 보기
12/62
post-thumbnail

📝 TIL - 숫자 야구 게임 업그레이드

📌 오늘 한 일

오늘은 기존에 만든 숫자 야구 게임 기능에 도전 구현 기능을 추가했다.
새로운 기능을 추가하고, 더 좋은 코드 구조를 만들기 위해 고민한 내용을 정리한다.


🎯 도전 구현 기능 적용

✅ Lv3: 정답 숫자 규칙 변경

  • 정답 숫자는 0~9서로 다른 3자리 숫자로 설정
  • 맨 앞자리에 0이 오면 안 됨
    • 예시: 092 ❌, 870 ✅, 300 ❌
  • 내가 개발한 코드
     struct RandomNumberGenerator: RandomNumberGeneratable {
       /**
        중복되지 않는 서로 다른 숫자 랜덤 생성 함수
    
        - Returns: 중복되지 않는 랜덤으로 생성한 3개의 숫자를 배열로 반환
       */
       func randomNumberGenerator() -> [Int] {
           let randomNumber1 = Int.random(in: 1...9) // 첫번째 자리수는 변경 X
           var randomNumber2: Int
           var randomNumber3: Int
    
           // 첫번째 수와 동일하지 않을 때 까지 랜덤 숫자 생성
           repeat {
               randomNumber2 = Int.random(in: 0...9) // 1...9 -> 0...9 로 변경
           } while randomNumber2 == randomNumber1
          
           // 두번째 수와 세번째 수와 중복되지 않을 때 까지 랜덤 숫자 생성
           repeat {
               randomNumber3 = Int.random(in: 0...9) // 1...9 -> 0...9 로 변경
           } while randomNumber3 == randomNumber1 || randomNumber3 == randomNumber2
          
           // 디버깅
           //let test = [randomNumber1, randomNumber2, randomNumber3]
           //print(test)
          
           return [randomNumber1, randomNumber2, randomNumber3]
       }
    }

✅ Lv4: 게임 메뉴 추가

  • 프로그램 실행 시 메뉴를 출력
    환영합니다! 원하시는 번호를 입력해주세요  
    1. 게임 시작하기  2. 게임 기록 보기  3. 종료하기
  • 게임 종료 후 메뉴 재출력
  • 잘못된 입력 예외 처리
    • 1, 2, 3이 아닌 값 입력 시 오류 메시지 출력
  • 내가 개발한 코드
     func mainMenu() {
           var isQuit: Bool = true // 게임 반복 여부 저장 변수
           var gameCount: Int = 0 // 게임 횟수
           var logArray: [String] = []
          
           while isQuit {
               message.menu() // 메인메뉴 선택 안내 메시지 출력
              
               switch readLine() ?? "" {
               case "1":
                   var tryCount = startGame() // 게임 시작 매소드 실행
                  
                   gameCount += 1 // 개임 횟수 증가
    
                   logArray.append(log.gameLog(gameCount, tryCount)) // 게임 로그 저장
                  
                   tryCount = 0 // 시도 횟수 초기화
               case "2":
                   print("< 게임 기록 보기 >")
                   message.gameLog(logArray) // 로그 출력
               case "3":
                   print("Bye!")
                   isQuit = false // 반복문 종료
               default:
                   print("올바른 번호를 입력해주세요.")
               }
           }
       }
      
       // 예상 출력 결과 --
       // 환영합니다! 원하시는 번호를 입력해주세요
       //
       // 1. 게임 시작하기 2. 게임 기록 보기 3. 종료하기

✅ Lv5: 게임 기록 보기 기능 추가

  • 게임 기록 보기(2번)을 선택하면, 지금까지 완료한 게임의 시도 횟수 출력
    < 게임 기록 보기 >  
    1번째 게임 : 시도 횟수 - 14  
    2번째 게임 : 시도 횟수 - 9  
    3번째 게임 : 시도 횟수 - 12  
  • 내가 작성한 코드

    아래의 gameLog 메소드가 현재 게임 횟수와 시도 횟수를 반환한다.

    struct Log: LogProtocol {
        func gameLog(_ gameCount: Int, _ tryCount: Int) -> String {
            return "\(gameCount)번째 게임 : 시도 횟수 - \(tryCount)"
        }
    }

    mainMenu 메소드에서 아래와 같이 처리한다

    while isQuit {
    	message.menu() // 메인메뉴 선택 안내 메시지 출력
               
    	switch readLine() ?? "" {
    		case "1":
    			var tryCount = startGame() // 게임 시작 매소드 실행
                   
    			gameCount += 1 // 개임 횟수 증가
    
    			// 위에서 설명한 `gameLog` 메소드를 반환값인 게임 로그를 `logArray` 저장
    			logArray.append(log.gameLog(gameCount, tryCount))
                   
    			tryCount = 0 // 시도 횟수 초기화
    		case "2":
    			print("< 게임 기록 보기 >")
    			message.gameLog(logArray) // 로그 출력
    		case "3":
    			print("Bye!")
    			isQuit = false // 반복문 종료
    		default:
    			print("올바른 번호를 입력해주세요.")
    	}
    }

✅ Lv6: 종료 기능 추가

  • 종료하기(3번)을 선택하면 프로그램이 종료됨
  • 이전 게임 기록도 초기화됨
  • 내가 작성한 코드

    반복문을 종료할수있게 3번을 입력하면 isQuit 변수의 값이 false 변하며, 반복문이 끝나 게임이 종료된다.

    while isQuit {
    	switch readLine() ?? "" {
    		case "3":
    			print("Bye!")
    			isQuit = false // 반복문 종료
        }
    }

🛠️ 트러블 슈팅

🚨 tryCount를 지역 변수로 변경

  • 기존에는 tryCountBaseballGame 클래스의 클래스 변수로 선언했음.
  • 하지만 이 방식은 게임이 새로 시작될 때 초기화가 필요한 문제가 있었음.
  • 그래서 tryCount를 startGame()의 반환값으로 변경하여 해결함.
func startGame() -> Int {  // tryCount를 반환
    message.start()
    let gameAnswer = randomNumber.randomNumberGenerator()
    var tryCount = 0  // 지역 변수로 변경

    while true {
        message.input()
        tryCount += 1  // 시도 횟수 증가

        let userAnswer = userInput.getUserInput()
        let gameResult = checkAnswer.check(gameAnswer, userAnswer)

        if gameResult.strike == 3 {
            message.success()
            break
        } else if gameResult.strike == 0 && gameResult.ball == 0 {
            message.lose()
        } else {
            message.resultMessage(strike: gameResult.strike, ball: gameResult.ball)
        }
        print("") // 줄바꿈
    }
    
    return tryCount  // tryCount를 반환
}

그리고 mainMenu()에서 startGame()의 반환값을 받아서 logArray에 추가하도록 변경함.

	switch readLine() ?? "" {
	case "1":
    	let tryCount = startGame()  // startGame()에서 반환된 tryCount를 받음
    	gameCount += 1
    	logArray.append(log.gameLog1(gameCount, tryCount))  // logArray에 추가
  • 개선한 이유
    • tryCount가 게임 내에서만 사용되므로 지역 변수로 관리하는 것이 더 적절
    • startGame()을 호출할 때마다 새로운 tryCount를 사용하여 값의 일관성 유지
    • 불필요한 클래스 변수 제거로 코드가 더욱 깔끔하고 유지보수하기 쉬워짐

🚨 Lv5 게임 기록 보기 기능 구현


🧐 오늘 배운 것

🔹 1. 지역 변수 vs 클래스 변수

  • 지역 변수해당 함수 내부에서만 사용되므로 코드가 명확하고 캡슐화가 쉬움
  • 클래스 변수는 상태를 저장하지만, 의도치 않은 값 변경이 발생할 가능성이 있음
  • 이번 개선에서는 tryCount를 지역 변수로 변경하여 코드의 일관성을 높이고 불필요한 클래스 변수를 제거했음

🔹 2. inout을 이용한 값 변경 방법 (적용 X)

  • tryCountinout 매개변수로 사용하면 startGame()에서 직접 값을 변경할 수도 있음
  • 하지만 이 방식은 Side Effect(부작용)이 발생할 가능성이 있음
  • 따라서 이번 개선에서는 inout 대신 반환값을 활용하는 방식을 선택함
func startGame(_ tryCount: inout Int) {
    message.start()
    let gameAnswer = randomNumber.randomNumberGenerator()

    while true {
        message.input()
        tryCount += 1  // 시도 횟수 증가
        let userAnswer = userInput.getUserInput()
        let gameResult = checkAnswer.check(gameAnswer, userAnswer)

        if gameResult.strike == 3 {
            message.success()
            break
        } else if gameResult.strike == 0 && gameResult.ball == 0 {
            message.lose()
        } else {
            message.resultMessage(strike: gameResult.strike, ball: gameResult.ball)
        }
        print("")
    }
}

📌 이 방식은 사용하지 않음

  • inout값이 변경된다는 것을 명확하게 나타내지만, 함수의 부작용이 커질 수 있음
  • 따라서 더 명확한 반환값을 이용하는 방식을 선택!

✨ 배운점 및 느낀점

오늘은 숫자 야구 게임을 개선하면서

  • 지역 변수 vs 클래스 변수 차이
  • tryCount를 지역 변수로 변경하는 방법
  • inout과 반환값을 활용하는 방식 비교

👍 이제 코드를 더 깔끔하게 개선할 수 있을 것 같다.

profile
iOS 개발 블로그

0개의 댓글