ios 13일차

bin·2026년 1월 15일

회고

오늘은 어제 다짐했던, 야구 게임 과제 코드 리팩토링과 절차지향 프로그래밍에서 객체지향으로 변경을 시도했다. 먼저, 객체를 어떻게 구성할 것인지 생각을 정리한 후 객체 별로 소스코드 파일을 분리하여 진행했다. 요구사항이 제대로 구현이 되어있는지 연결을 확인하기에는 조금 힘들 수도 있지만, 객체의 구성요소별로 분리하여 진행하는 것이 맞다고 판단했다. 또한, 과제 리팩토링을 마친 후 튜터님께서 제안해주셨던 수정방안에 대해 공부하고 이를 적용했다. 남는 시간에는 늘 그렇듯이 알고리즘 문제를 풀며 하루를 마무리 한다.

과제에 관한 기록

GameRule.swift

GameRule 소스코드는 게임의 규칙을 생성하기 위한 객체를 만들었습니다. 때문에, 기존에 합쳐져있던 힌트의 생성과 출력은 분리하여 본 파일에서는 힌트 결과 생성만을 구현합니다.

1. 소스코드

class GameRule {
    private var result: [Int] = []
    
    /* (Lv.1) 1. 1~9 서로 다른 난수 3개 생성 -> Set 사용 -> 문제점 : 정답 생성용 Set, 저장용 Array 별도로 필요*/
    func randomAnswer(){
        var answer: Set<Int> = []
        var result: [Int] = []

        while answer.count < 3 { //정답 생성용(Set 사용) : 같은 수 반복 x
            let num = Int.random(in: 1...9)
            answer.insert(num)
        }
        result = Array(answer) // 정답 저장용
    //    print(answer)
    //    print(result)
        self.result = result // 변경사항 -> return 사용하지않고 객체에 result로 담아둠
    }
    
    /* (Lv 3.) 1. 첫 자리(1~9), 나머지(0~9): Set 사용하지 않고 contains로 조건 사용 */
    func randomAnswer2(){
        var result: [Int] = []
        
        let fst = Int.random(in: 1...9)
        result.append(fst)
        while result.count < 3{
            let rndnum = Int.random(in: 0...9)
            if !result.contains(rndnum){
                result.append(rndnum)
            }
        }
        //    print(result)
        self.result = result // 변경사항 -> return 사용하지않고 객체에 result로 담아둠
    }
    
    /* (Lv.2) 2. 힌트 생성, 계산 -> 출력은 game.hint를 이용하여 사용하도록 분리 */
    func gameScore(inputChars: [Int]) -> (strike: Int, ball: Int){
        var strike: Int = 0
        var ball: Int = 0
        
        for i in 0..<3 {
            if result[i] == inputChars[i] { // 변경사항 -> inputChars는 배열이라 inputChars[i]로 접근 가능
                strike += 1
            } else if result.contains(inputChars[i]) {
                ball += 1
            }
        }
        return (strike, ball)
    }
}

2. 정리

GameRule 소스코드는 게임의 규칙을 생성하기 위한 객체를 만들었습니다. 때문에, 기존에 합쳐져있던 힌트의 생성과 출력은 분리하여 본 파일에서는 힌트 결과 생성만을 구현합니다.

GameInput.swift

GameIput 소스코드는 사용자의 입력을 받고, 입력된 값이 정상적인지 판단하기 위한 객체를 만들었습니다. 조건(1. 숫자가 아닌 다른 값의 입력, 2. 중복된 값의 입력)을 통해 정상적인 값이 아니라는 것이 판단되면 nil을 반환하여 game의 while 구문 guard let에서 걸리게 되어 입력을 다시 받도록 구현되어있습니다. 또한, guard let에 조건을 모두 넣어보고자 Optional을 감싸서 compactMap을 사용하였습니다. map대신 compactMap을 사용하고자 한 이유에 대해서는 아래 코드의 주석을 참고해주시면 될 것 같습니다.

1. 소스코드

import Foundation

struct GameInput {
    func getInput() -> [Int]? {
        /* (Lv.2) 1. 사용자 입력 검증 */
        print("숫자를 입력하세요: ", terminator: "\n")
        guard let num = readLine().map({ $0.compactMap(\.wholeNumberValue) }), //숫자 외의 다른 값이 들어왔는지 확인, map 대신 compactMap 사용 : map 사용시 nil로 처리 되기 때문에 처리가 바르게 일어나지않음(ex. 1ab입력시 num = [1,nil,nil] -> 중복으로 처리됨
              num.count == 3
        else {
            print("올바르지 않은 입력값입니다.")
            return nil
        }
        
        if Set(num).count != 3 { //중복된 값이 있는지 확인
            print("올바르지 않은 입력값입니다.(중복)")
            return nil
        }
        return num
    }
}

2. 정리

GameIput 소스코드는 사용자의 입력을 받고, 입력된 값이 정상적인지 판단하기 위한 객체를 만들었습니다. 조건(1. 숫자가 아닌 다른 값의 입력, 2. 중복된 값의 입력)을 통해 정상적인 값이 아니라는 것이 판단되면 nil을 반환하여 game의 while 구문 guard let에서 걸리게 되어 입력을 다시 받도록 구현되어있습니다. 또한, guard let에 조건을 모두 넣어보고자 Optional을 감싸서 compactMap을 사용하였습니다. map대신 compactMap을 사용하고자 한 이유에 대해서는 아래 코드의 주석을 참고해주시면 될 것 같습니다.

Game.swift

Game 소스코드는 GameRule, GameInput의 객체를 이용하여 게임 내부에서 동작하는 기능들을 구현하는 객체를 만들었습니다. GameRule에서 계산한 힌트 정보를 hint 함수를 통해서 출력하도록 구현했습니다. 또한, 정답을 맞췄을 경우도 hint 함수 내부에서 동작하도록 구현할 수 있으나, 정답일 경우만 play 함수에 넣어둔 이유는 hint 함수는 함수명 그 자체로 힌트만을 출력하는 함수를 구현하고자 생각하여 분리하였습니다.

1. 소스코드

class Game{
    private let rule = GameRule()
    private let gameinput = GameInput()
    
    func play() -> Int{
        rule.randomAnswer2()
        var totalCount = 0
        
        while true {
            guard let inputNumber = gameinput.getInput() else {
                continue
            }
            
            totalCount += 1 //기록 저장용 Count + 1
            
            let score = rule.gameScore(inputChars: inputNumber)
            
            if score.strike == 3 {
                print("정답입니다.")
                return totalCount
            } else {
                hint(strike : score.strike, ball: score.ball)
            }
        }
    }
    /* (Lv.2) 3. 힌트 출력 */
    func hint(strike: Int, ball: Int){
        if strike >= 1 && ball >= 1 {
            print("\(strike)스트라이크 \(ball)볼")
        }else if strike >= 1 {
            print("\(strike)스트라이크")
        }else if ball >= 1 {
            print("\(ball)볼")
        }else {
            print("Nothing")
        }
    }
}

2. 정리

Game 소스코드는 GameRule, GameInput의 객체를 이용하여 게임 내부에서 동작하는 기능들을 구현하는 객체를 만들었습니다. GameRule에서 계산한 힌트 정보를 hint 함수를 통해서 출력하도록 구현했습니다. 또한, 정답을 맞췄을 경우도 hint 함수 내부에서 동작하도록 구현할 수 있으나, 정답일 경우만 play 함수에 넣어둔 이유는 hint 함수는 함수명 그 자체로 힌트만을 출력하는 함수를 구현하고자 생각하여 분리하였습니다.
import Foundation

main.swift

Main 소스코드는 게임의 시작선택메뉴 나타내기 위한 객체와 실행을 위한 app인스턴스생성 및 실행을 위해 만들었습니다.

1. 소스코드

import Foundation

class Main{
    private var scoreRecord: [Int] = []
    private let game = Game()
    
    func startGame(){
        
        /* (Lv 4.) 1. 프로그램 시작 시 안내 문구 출력 */
        while true {
            print("""
                환영합니다! 원하시는 번호를 입력해주세요.
                1. 게임 시작하기 2. 게임 기록 보기 3. 종료하기
                """)
            guard let input = readLine()
            else { continue }
            
            switch input {
            /* (Lv 4.) 2. 게임 시작하기 선택 시 play로 연결 */
            case "1":
                let score = game.play()
                scoreRecord.append(score) // score을 Record에 저장
            /* (Lv 5.) 1. 게임 기록 보기 선택 시 시도 횟수 출력 */
            case "2":
                print("게임 기록 보기")
                for (idx, score) in scoreRecord.enumerated() { //고차함수 enumerated를 사용하여 idx: 인덱스 score: 점수 반환 후 출력
                    print("\(idx + 1)번째 게임 : 시도 횟수 - \(score)")
                }
                /* (Lv 6.) 1. 종료하기 선택 시 프로그램 종료(기록 초기화) */
            case "3":
                print("게임을 종료합니다.")
                return
                /* (Lv 6.) 2. 이외 입력값에 대한 오류 출력 */
            default:
                print ("올바른 숫자를 입력해주세요!")
            }
        }
    }
}

let app = Main()
app.startGame()

2. 정리

Main 소스코드는 게임의 시작선택메뉴 나타내기 위한 객체와 실행을 위한 app인스턴스생성 및 실행을 위해 만들었습니다.

0개의 댓글