구현 기능
- Lv1
- [✅] 1에서 9까지의 서로 다른 임의의 수 3개를 정하고 맞추는 게임입니다
- [✅] 정답은 랜덤으로 만듭니다.(1에서 9까지의 서로 다른 임의의 수 3자리)
- Lv2
- [✅] 정답을 맞추기 위해 3자리수를 입력하고 힌트를 받습니다
- [✅] 힌트는 야구용어인 볼과 스트라이크입니다.
- [✅] 같은 자리에 같은 숫자가 있는 경우 스트라이크, 다른 자리에 숫자가 있는 경우 볼입니다
- ex) 정답 : 456 인 경우
- 435를 입력한 경우 → 1스트라이크 1볼
- 357를 입력한 경우 → 1스트라이크
- 678를 입력한 경우 → 1볼
- 123를 입력한 경우 → Nothing
- 만약 올바르지 않은 입력값에 대해서는 오류 문구를 보여주세요
- 3자리 숫자가 정답과 같은 경우 게임이 종료됩니다
- [✅] 정답을 맞추기 위해 3자리수를 입력하고 힌트를 받습니다
- Lv3
- [✅] 정답이 되는 숫자를 0에서 9까지의 서로 다른 3자리의 숫자로 바꿔주세요
- 맨 앞자리에 0이 오는 것은 불가능합니다
- 092 → 불가능
- 870 → 가능
- 300 → 불가능
- 맨 앞자리에 0이 오는 것은 불가능합니다
- [✅] 정답이 되는 숫자를 0에서 9까지의 서로 다른 3자리의 숫자로 바꿔주세요
코드
func start
// 게임 시작 메서드 (게임 흐름을 관리)
func start() {
gameIntroMessage() // 게임 시작 메시지 출력
let correctAnswer = makeAnswer() // 랜덤 정답 생성
while true {
let userInputAnswer = inputAnswer() // 사용자 입력 받기
// 사용자의 입력과 정답을 비교
if validateAnswer(userInputAnswer, correctAnswer) {
print("게임을 종료합니다.") // 정답을 맞추면 게임 종료
break
}
}
}
기본적인 게임의 흐름을 관리하는 메서드 입니다.
게임 시작 시 랜덤한 값의 정답을 생성하고 user에게 입력을 받으며, 유효성검사에 통과 후 user의 값과 정답을 비교해 정답이면 반복문이 끝나게 구현했습니다.
makeAnswer
// 0~9 사이의 서로 다른 3자리 랜덤 숫자 생성
func makeAnswer() -> [Int] {
var numbers = (0...9).shuffled() // 0~9까지 무작위 섞기
// 첫 번째 숫자가 0이면 0이 아닌 숫자와 교환
if numbers[0] == 0 {
if let nonZeroIndex = numbers.firstIndex(where: { $0 != 0 }) {
numbers.swapAt(0, nonZeroIndex)
}
}
return Array(numbers.prefix(3)) // 앞에서 3개의 숫자 선택하여 반환
}
랜덤한 값을 생성해주는 함수 입니다.
Lv3의 조건에 맞게 0 ~ 9까지의 숫자를 무작위로 shuffled 메서드를 사용하여 0 - 9를 무작위로 섞어주었습니다.
.shuffled를 쓴 이유
구현 기능을 보면 서로 다른 임의의 수 0 ~9 라는 조건이 있습니다.
random을 통해 구현을 할 수 있지만 직접 중복을 체크해줘야 하기때문에, 위 조건을 생각했을때 직접 중복을 체크할 필요가 없는 .shuffled가 더 적합할 수 있다고 생각했습니다.
+ Set을 사용하면 중복값은 알아서 걸러준다.
.shuffled를 사용한 코드
// 0~9 사이의 서로 다른 3자리 랜덤 숫자 생성
func makeAnswer() -> [Int] {
var numbers = (0...9).shuffled() // 0~9까지 무작위 섞기
// 첫 번째 숫자가 0이면 0이 아닌 숫자와 교환
if numbers[0] == 0 {
if let nonZeroIndex = numbers.firstIndex(where: { $0 != 0 }) {
numbers.swapAt(0, nonZeroIndex)
}
}
return Array(numbers.prefix(3)) // 앞에서 3개의 숫자 선택하여 반환
}
.random을 사용한 코드
// 랜덤한 값을 생성하는 함수
func makeAnswerRandom() -> [Int] {
var numbers: Set<Int> = [] // 중복 방지를 위해 Set 사용
while numbers.count < 3 {
let num = Int.random(in: 0...9) // 0~9 사이 랜덤 숫자 선택
numbers.insert(num) // 중복되지 않는 숫자만 추가
}
var result = Array(numbers)
// 첫 번째 숫자가 0이면 위치 변경
if result[0] == 0 {
if let nonZeroIndex = result.firstIndex(where: { $0 != 0 }) {
result.swapAt(0, nonZeroIndex)
}
}
return result
}
.shuffle와 .shuffled의 차이
.shuffled을 찾아보면서 .shuffle 메서드도 있다는걸 알게되었습니다.
두 메서드의 가장 큰 차이점은 원본의 값이 변하냐 안변하냐입니다.
.shuffled의 경우 원본의 값 변경 없이 새로운 섞인 배열을 반환하지만, .shuffle의 경우 원본의 컬렉션을 직접 섞는 차이가 있습니다.
비교 요약
방법코드 | 길이 | 중복 | 0 방지 | 처리실행 속도 |
shuffled() 사용 | 짧음 | 자동 보장 | 있음 | 빠름 |
random 사용 | 김 | 직접 체크해야 함 | 있음 | 느림 |
GameIntroMessage
// 게임 시작 메시지 출력
func gameIntroMessage() {
print(" < 게임을 시작합니다 > ")
}
게임 시작 메세지를 담당합니다.
inputAnswer
// 사용자 입력을 받는 함수(올바른 입력이 들어올 때까지 반복)
func inputAnswer() -> [Int] {
while true {
print("숫자를 입력하세요.")
if let input = readLine() { // 사용자 입력 받기
let answer = input.compactMap { $0.wholeNumberValue } // 문자열을 숫자로 변환
if validateInputAnswer(answer) { // 입력 검증 성공 시 반환
return answer
}
}
}
}
사용자의 입력을 받는 함수입니다.
사용자의 입력은 LeadLine()을 통해 받았습니다.
LeadLine을 사용하게 되면 문자열로 반환이 되기 때문에 문자열을 숫자로 반환하는 작업을 해주었습니다.
.compactMap
.compachtMap은 옵셔널 값을 변환하면서 자동으로 nil을 제거해주는 함수입니다.
.map과 다른점은 옵셔널 바인딩과 .map의 역할을 한번에 하는 장점을 가지고 있습니다.
let input = "123abc"
let mapped = input.map { $0.wholeNumberValue }
print(mapped)
// 출력: [Optional(1), Optional(2), Optional(3), nil, nil, nil] (옵셔널 포함)
let compactMapped = input.compactMap { $0.wholeNumberValue }
print(compactMapped)
// 출력: [1, 2, 3] (옵셔널 제거)
옵셔널을 제거해야되는 이유는 배열의 요소가 Optional(Int)가 되어서 사용할 때마다 if let 또는 !로 언래핑해야 해서 코드가 복잡해지기 때문에 옵셔널 제거가 필요합니다.
$0.wholeNumberValue
$0은 개별문자를 의미.
wholeNumberValue는 문자가 숫자로 변환될 수 있으면 Int 값으로 변환하고, 숫자가 아니면 nil을 반환합니다.
- 예: "12a3b" → [1, 2, 3]
+ TIL을 작성하면서 생각한 문제점
.wholeNumberValue를 사용하면 문자를 자동으로 걸러줘서 "5ㅁ32" 같은 입력값도 받아들이게 되는 오류가 있습니다.
내일 수정 후 TIL 업로드 예정...🥹
간편하다고 다 좋은게 아니다....
validateInputAnswer
// 입력값 검증 (유효한 3자리 숫자인지 확인)
func validateInputAnswer(_ inputAnswer: [Int]) -> Bool {
guard !inputAnswer.isEmpty else {
print("공백 입력은 허용되지 않습니다. 숫자를 입력해주세요.\n")
return false
}
guard inputAnswer.count == 3 else {
print("3개의 숫자만 입력 가능합니다.\n")
return false
}
guard inputAnswer.allSatisfy({ (0...9).contains($0) }) else {
print("0 - 9까지의 숫자만 입력 가능합니다.\n")
return false
}
guard Set(inputAnswer).count == inputAnswer.count else {
print("숫자는 각각 한개씩만 사용할 수 있습니다. ( 두번 사용 불가능 )\n")
return false
}
guard inputAnswer.first != 0 else {
print("첫번째 숫자는 0이 될 수 없습니다.\n")
return false
}
return true // 모든 검증 통과 시 true 반환
}
입력값의 유효성을 검증하는 함수입니다.
위와 같은 유효성 검사를 넣고 Bool 형식으로 반환하여 모두 통과하면 true로 inputAnswer의 반복문이 끝날 수 있게 구현했습니다.
validateAnswer
// 정답과 사용자 입력 비교 (스트라이크 & 볼 판정)
func validateAnswer(_ userInput: [Int], _ correctAnswer: [Int]) -> Bool {
let (strikeCount, ballCount) = calculateScore(userInput, correctAnswer) // 점수 계산
displayResult(strikeCount, ballCount) // 결과 출력
return strikeCount == 3 // 3 스트라이크면 정답
}
user의 값과 랜덤 값을 비교해주는 함수입니다.
튜플(Tuple) 구조로 받아서 strikeCount와 ballCount 두 개의 변수에 할당하는 방식을 사용했습니다.
만약 strikeCount가 3이면 True값을 반환합니다.
calculateScore
// 스트라이크 & 볼 개수 계산
private func calculateScore(_ userInput: [Int], _ correctAnswer: [Int]) -> (strike: Int, ball: Int) {
var strike = 0
var ball = 0
for (index, number) in userInput.enumerated() {
if number == correctAnswer[index] {
strike += 1 // 같은 자리 같은 숫자 → 스트라이크 증가
} else if correctAnswer.contains(number) {
ball += 1 // 숫자가 포함되지만 위치가 다르면 → 볼 증가
}
}
return (strike, ball) // 튜플로 결과 반환
}
스트라이크와 볼의 개수를 계산해주는 함수입니다.
user의 값과 랜덤 값을 받아 같은 숫자인지 비교 후 튜플로 스트라이크와 볼을 반환합니다.
displayResult
//결과 출력 (스트라이크 & 볼 개수에 따른 메시지)
private func displayResult(_ strike: Int, _ ball: Int) {
if strike == 3 {
print("정답입니다!") // 3 스트라이크 시 정답 처리
} else if strike == 0 && ball == 0 {
print("Nothing") // 스트라이크와 볼이 모두 없을 때
} else {
print("\(strike) 스트라이크, \(ball) 볼") // 결과 출력
}
}
입력받은 스트라이크와 볼의 개수로 스트라이크가 3이면 정답 메세지를, 스트라이크와 볼이 모두 없으면 "Noting"을 두개에 모두 해당되지 않는다면 스트라이크의 개수와 볼의 개수를 출력하도록 하였습니다.
최종 코드
GitHub - nbcampMasterChapter2Team4/YWSBaseBallGame: 내일배움캠프 iOS 마스터 6기 2주차
내일배움캠프 iOS 마스터 6기 2주차. Contribute to nbcampMasterChapter2Team4/YWSBaseBallGame development by creating an account on GitHub.
github.com
'내배캠 iOS 마스터 6기' 카테고리의 다른 글
[ 내배캠 ] 숫자 야구 게임 트러블슈팅 (0) | 2025.03.14 |
---|---|
[ 내배캠 ] 팀소개카드 KPT (0) | 2025.03.07 |
[ 내배캠 ] Chapter 1 온보딩 주차 (0) | 2025.03.03 |
[ 사전캠프 6일차 ] 비동기 프로그래밍/ 제네릭 (0) | 2025.02.10 |
[ 사전캠프 4일차 ] 클로저/ 객체지향 프로그래밍 (0) | 2025.02.06 |