본문 바로가기

코딩테스트/알고리즘

숫자 야구게임 만들기 Swift

기능 설명

중복되지 않는 3자리 숫자를 맞추는 게임입니다.

각 자리수는 서로 중복되지 않고, 앞자리에는 0이 올 수 없습니다.

 

 

입력

입력은 문자열을 포함하지 않은 숫자 3자리를 입력받습니다.

아래와 같이 올바르지 않는 입력 경우의 수는 다음과 같습니다.

- 빈 문자열

- 중복된 값이 존재하는 3자리 문자열

- 3자리가 아닌 문자열

- String값이 섞인 문자열

- 0으로 시작하는 문자열

// 올바르지 않는 입력값
input1 = ""
input2 = "122"
input3 = "12"
input4 = "12e"
input5 = "dfe"
input6 = "012"

 

이를 아래와 같이 필터링하는 메서드를 만들었습니다.

private func isCorrectInput(input: String) -> Bool {
	// 문자열의 앞 문자가 0인경우
    if input.prefix(1) == "0" { return false }
    // 빈문자열인 경우, Set타입으로 타입변환 시 중복된 원소가 제거되므로 변환해도 3자리 문자열인 경우
    if Int(input) == nil || Set(input.map{$0}).count != 3 {
        return false
    }
    
    return true
}

- Int(input): String값을 Int로 형변환 했을 때 숫자가 아닌 문자인 경우 nil로 변환되기에 여기서 문자열 안에 문자를 검사한다.

 

 

정답값 생성

중복없는 3자리 문자열 생성

private func initAnswer() {
    var answerArray = [Int]()
    
    func randomNumber(range: Range<Int>) -> Int {
        return Int.random(in: range)
    }
    
    while answerArray.count < 3 {
        let num = randomNumber(range: 1..<10)
        if !answerArray.contains(num) { answerArray.append(num)}
    }
    
    answer = answerArray
}

 

 

입력값과 정답 비교

각 자리마다 숫자를 비교하는데 결과는 3종류로 나타낼 수 있습니다.

- 자리도 같고 숫자도 같다면 Strike

- 자리는 다르지만 숫자가 존재한다면 Ball

- 자리도 없고 숫자도 존재하지 않으면 x

private func compareAnswer(with numArray: [Int]) -> (equalPoint: Int, containsPoint: Int) {
    var equalPoint = 0
    var containsPoint = 0
    
    for i in 0..<numArray.count {
        if answer[i] == numArray[i] {
            equalPoint += 1
        } else if answer.contains(numArray[i]) {
            containsPoint += 1
        }
    }
    return (equalPoint: equalPoint, containsPoint: containsPoint)
}

그에 따른 결과도 3가지로 나눌 수 있습니다.

- 정답

- N Strike N ball

- Nothing

이를 Strike와 Ball의 개수로 판단하도록 설계하였습니다.

private func playBaseball() {
    var currentTryCount = 0
    var isFindAnswer = false
    currentGameIndex += 1
    initAnswer()
    print("<게임을 시작합니다.>")
    while !isFindAnswer {
        print("숫자를 입력하세요")
        guard let input = readLine() else { continue }
        if !isCorrectInput(input: input) {
            print("올바르지 않은 입력값입니다.\n")
            continue
        }
        
        currentTryCount += 1
        let inputIntegerArray = input.map{Int(String($0))!}
        let result = compareAnswer(with: inputIntegerArray)
        
        if result.equalPoint == 3 {
            print("정답입니다!")
            isFindAnswer = true
            records.append((game: currentGameIndex, tryCount: currentTryCount))
        } else if result.equalPoint == 0 && result.containsPoint == 0 {
            print("Nothing")
        } else {
            print("\(result.equalPoint) Strike  \(result.containsPoint) Ball")
        }
    }
}

 

게임 모드

게임을 시작하면 [1. 게임진행], [2. 이전기록확인], [3.종료하기] 로 세가지 동작을 실행할 수 있습니다.

이를 열거형을 사용하여 didSet 관찰자를 사용해 입력받은 값에 따라 설정하여 didSet에 선언된 메서드가 실행되도록 구현하였습니다.

enum GameState: Int {
    case play = 1
    case record = 2
    case exit = 3
    case none
}

private var currentState: GameState = .none {
    didSet {
        switch currentState {
        case .play:
            playBaseball()
        case .record:
            openRecord()
        case .exit:
            exitGame()
        default:
            print("올바른 숫자를 입력해주세요!")
        }
    }
}

func play() {
    while currentState != .exit {
        selectMode()
    }
}

private func selectMode() {
    print("환영합니다! 원하시는 번호를 입력해주세요\n1. 게임 시작하기  2. 게임 기록 보기  3. 종료하기")
    guard let input = readLine(),
    let inputInteger = Int(input),
    let state = GameState(rawValue: inputInteger) else { return }
    currentState = state
}

 

 

종합해보면 아래와 같습니다.

class NumberBaseballGame {
    
    private var answer: [Int] = []
    private var records: [(game: Int, tryCount: Int)] = []
    private var currentGameIndex = 0
    private var currentState: GameState = .none {
        didSet {
            switch currentState {
            case .play:
                playBaseball()
            case .record:
                openRecord()
            case .exit:
                exitGame()
            default:
                print("올바른 숫자를 입력해주세요!")
            }
        }
    }
   
    enum GameState: Int {
        case play = 1
        case record = 2
        case exit = 3
        case none
    }
    
    init() { }
    
    func play() {
        while currentState != .exit {
            selectMode()
        }
    }
    
    private func selectMode() {
        print("환영합니다! 원하시는 번호를 입력해주세요\n1. 게임 시작하기  2. 게임 기록 보기  3. 종료하기")
        guard let input = readLine(),
        let inputInteger = Int(input),
        let state = GameState(rawValue: inputInteger) else { return }
        currentState = state
    }
    
    private func exitGame() {
        records.removeAll()
        currentGameIndex = 0
    }
    
    private func openRecord() {
        records.forEach { (game, count) in
            print("\(game)번째 게임 : 시도 횟수 - \(count)\n")
        }
    }
    
    private func playBaseball() {
        var currentTryCount = 0
        var isFindAnswer = false
        currentGameIndex += 1
        initAnswer()
        print("<게임을 시작합니다.>")
        while !isFindAnswer {
            print("숫자를 입력하세요")
            guard let input = readLine() else { continue }
            if !isCorrectInput(input: input) {
                print("올바르지 않은 입력값입니다.\n")
                continue
            }
            
            currentTryCount += 1
            let inputIntegerArray = input.map{Int(String($0))!}
            let result = compareAnswer(with: inputIntegerArray)
            
            if result.equalPoint == 3 {
                print("정답입니다!")
                isFindAnswer = true
                records.append((game: currentGameIndex, tryCount: currentTryCount))
            } else if result.equalPoint == 0 && result.containsPoint == 0 {
                print("Nothing")
            } else {
                print("\(result.equalPoint) Strike  \(result.containsPoint) Ball")
            }
        }
    }
    
    private func isCorrectInput(input: String) -> Bool {
        if input.prefix(1) == "0" { return false }
        if Int(input) == nil || Set(input.map{$0}).count != 3 {
            return false
        }
        
        return true
    }
    
    private func initAnswer() {
        var answerArray = [Int]()
        
        func randomNumber(range: Range<Int>) -> Int {
            return Int.random(in: range)
        }
        
        while answerArray.count < 3 {
            let num = randomNumber(range: 1..<10)
            if !answerArray.contains(num) { answerArray.append(num)}
        }
        
        answer = answerArray
    }
    
    private func compareAnswer(with numArray: [Int]) -> (equalPoint: Int, containsPoint: Int) {
        var equalPoint = 0
        var containsPoint = 0
        
        for i in 0..<numArray.count {
            if answer[i] == numArray[i] {
                equalPoint += 1
            } else if answer.contains(numArray[i]) {
                containsPoint += 1
            }
        }
        return (equalPoint: equalPoint, containsPoint: containsPoint)
    }
    
}

'코딩테스트 > 알고리즘' 카테고리의 다른 글

분할 정복 Swift  (1) 2024.04.26
플로이드-워셜 알고리즘 Swift  (0) 2024.04.25
다익스트라 알고리즘 Swift  (0) 2024.04.24
투포인터 알고리즘 Swift  (0) 2024.04.14
순열과 조합 Swift  (0) 2024.04.10