From 442c3a04bb1b7550b99c53ed757c47dc3f3154ae Mon Sep 17 00:00:00 2001 From: jamong-mini Date: Mon, 11 Nov 2024 23:04:11 +0900 Subject: [PATCH] =?UTF-8?q?Feat:=20=ED=8A=9C=ED=84=B0=EB=8B=98=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=EB=B0=B1=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...54\210\230\352\265\254\355\230\204).swift" | 0 .../Classes/BaseballGame.swift" | 128 --------------- .../ErrorHandling/InputErrorLv7.swift" | 18 +++ .../GameLogic/BaseballGameLv7.swift" | 146 ++++++++++++++++++ .../GameLogic/GameHistory.swift" | 19 +++ .../Protocols/BaseballGameProtocolLv7.swift" | 14 ++ .../Utilities/AnswerCreatorLv7.swift" | 16 ++ .../Utilities/Constants.swift" | 10 ++ .../Week2-BaseballGame/main.swift | 8 +- 9 files changed, 229 insertions(+), 130 deletions(-) rename "Week2-BaseballGame/Week2-BaseballGame/Lv_1(\355\225\204\354\210\230\352\265\254\355\230\204).swift" => "Week2-BaseballGame/Week2-BaseballGame/Lv_1(\355\225\204\354\210\230\352\265\254\355\230\204)/Lv_1(\355\225\204\354\210\230\352\265\254\355\230\204).swift" (100%) delete mode 100644 "Week2-BaseballGame/Week2-BaseballGame/Lv_2(\355\225\204\354\210\230\352\265\254\355\230\204)/Classes/BaseballGame.swift" create mode 100644 "Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/ErrorHandling/InputErrorLv7.swift" create mode 100644 "Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/GameLogic/BaseballGameLv7.swift" create mode 100644 "Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/GameLogic/GameHistory.swift" create mode 100644 "Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/Protocols/BaseballGameProtocolLv7.swift" create mode 100644 "Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/Utilities/AnswerCreatorLv7.swift" create mode 100644 "Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/Utilities/Constants.swift" diff --git "a/Week2-BaseballGame/Week2-BaseballGame/Lv_1(\355\225\204\354\210\230\352\265\254\355\230\204).swift" "b/Week2-BaseballGame/Week2-BaseballGame/Lv_1(\355\225\204\354\210\230\352\265\254\355\230\204)/Lv_1(\355\225\204\354\210\230\352\265\254\355\230\204).swift" similarity index 100% rename from "Week2-BaseballGame/Week2-BaseballGame/Lv_1(\355\225\204\354\210\230\352\265\254\355\230\204).swift" rename to "Week2-BaseballGame/Week2-BaseballGame/Lv_1(\355\225\204\354\210\230\352\265\254\355\230\204)/Lv_1(\355\225\204\354\210\230\352\265\254\355\230\204).swift" diff --git "a/Week2-BaseballGame/Week2-BaseballGame/Lv_2(\355\225\204\354\210\230\352\265\254\355\230\204)/Classes/BaseballGame.swift" "b/Week2-BaseballGame/Week2-BaseballGame/Lv_2(\355\225\204\354\210\230\352\265\254\355\230\204)/Classes/BaseballGame.swift" deleted file mode 100644 index 0be1083..0000000 --- "a/Week2-BaseballGame/Week2-BaseballGame/Lv_2(\355\225\204\354\210\230\352\265\254\355\230\204)/Classes/BaseballGame.swift" +++ /dev/null @@ -1,128 +0,0 @@ -// Lv 2 (11/06 까지) - -/* -- 정답을 맞추기 위해 3자리수를 입력하고 힌트를 받습니다 - - 힌트는 야구용어인 **볼**과 **스트라이크**입니다. - - 같은 자리에 같은 숫자가 있는 경우 **스트라이크**, 다른 자리에 숫자가 있는 경우 **볼**입니다 - - ex) 정답 : 456 인 경우 - - 435를 입력한 경우 → 1스트라이크 1볼 - - 357를 입력한 경우 → 1스트라이크 - - 678를 입력한 경우 → 1볼 - - 123를 입력한 경우 → Nothing - - 만약 올바르지 않은 입력값에 대해서는 오류 문구를 보여주세요 -- 3자리 숫자가 정답과 같은 경우 게임이 종료됩니다 -- 실행 예시(정답 : 456)13123213 -*/ - -// Command Line Tool Project file for BaseballGame Lv_2 - Classes/BaseballGame -// 작성일: 2024.11.05 (화요일) -// -// 작성자: Jamong -// 이 파일은 Lv_2의 BaseballGameLv2 클래스를 정의하고 BaseballGameProtocolLv2를 준수하여 야구 게임의 주요 로직을 포함한다. - -import Foundation - -/// BaseballGame 클래스는 BaseballGameProtocolLv2을 구현하여 게임의 주요 로직을 관리 -/// 사용자 입력을 검증하고, 스트라이크와 볼 개수를 판정 -class BaseballGameLv2: BaseballGameProtocolLv2 { - - /// 정답 배열, 스트라이크, 볼을 저장 - var answer: [Int] - var strike: Int - var ball: Int - - /// 게임 시작 시 필요한 초기값을 설정 - init() { - self.answer = AnswerCreatorLv2.create() // 정답 생성 (중복되지 않는 세 자리 숫자 생성) - self.strike = 0 // 초기 스트라이크 개수 - self.ball = 0 // 초기 볼 개수 - } - - /// 야구 게임 시작 메서드 - /// 사용자가 정답을 맞출 때까지 입력을 받고, 입력에 대한 결과(스트라이크, 볼)을 제공 - func startGame() { - print("야구 게임 시작") - print(answer) - - while true { - print("세 자리 숫자를 입력하세요:") - - // 사용자 입력 받기 - guard let userInput = readLine() else { continue } - - do { - // 사용자의 입력에 대한 스트라이크 볼 개수를 판정 - let (strike, ball) = try checkAnswer(userInput: userInput) - - // 게임 결과에 따른 메세지를 출력 - if strike == 3 { - print("정답입니다.") - break - } else if strike == 0 && ball == 0 { - print("아웃") - } else if strike != 0 && ball == 0 { - print("\(strike)스트라이크") - } else if strike == 0 && ball != 0 { - print("\(ball)볼") - } else { - print("\(strike)스트라이크 \(ball)볼") - } - - } catch InputErrorLv2.invalidLength { - print("제한사항: 세 자리 숫자를 입력해주세요.") - } catch InputErrorLv2.notNumber { - print("제한사항: 아라비아 숫자만 입력해주세요.") - } catch InputErrorLv2.duplicateNumbers { - print("제한사항: 서로 다른 숫자를 입력해주세요.") - } catch { - print("알 수 없는 오류가 발생했습니다.") - } - } - } - - /// 사용자 입력을 검증하고 스트라이크와 볼 개수를 계산 - /// - Parameter userInput: 사용자가 입력한 세 자리 숫자 문자열 - /// - Throws: 입력 오류가 발생할 경우 InputErrorLv2를 던짐 - /// - Returns: (strike: Int, ball: Int) 형식으로 스트라이크와 볼의 개수를 반환 - func checkAnswer(userInput: String) throws -> (strike: Int, ball: Int) { - guard userInput.count == 3 else { - throw InputErrorLv2.invalidLength - } - - var userAnswerArray: [Int] = [] - - for char in userInput { - guard let userAnswerChar = Int(String(char)) else { - throw InputErrorLv2.notNumber - } - userAnswerArray.append(userAnswerChar) - } - - guard Set(userAnswerArray).count == 3 else { - throw InputErrorLv2.duplicateNumbers - } - - return evaluateBall(userAnswer: userAnswerArray, answer: answer) - } - - /// 정답과 사용자의 입력을 비교하여 스트라이크와 볼 개수를 계산 - /// - Parameters: - /// - userAnswer: 사용자가 입력한 숫자 배열 - /// - answer: 정답으로 설정된 숫자 배열 - /// - Returns: (strike: Int, ball: Int) 형식으로 스트라이크와 볼의 개수를 반환 - func evaluateBall(userAnswer: [Int], answer: [Int]) -> (strike: Int, ball: Int) { - // 변수 초기화 - strike = 0 - ball = 0 - - for index in userAnswer.indices { - if userAnswer[index] == answer[index] { - strike += 1 - } else if answer.contains(userAnswer[index]) { - ball += 1 - } - } - - return (strike, ball) - } -} diff --git "a/Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/ErrorHandling/InputErrorLv7.swift" "b/Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/ErrorHandling/InputErrorLv7.swift" new file mode 100644 index 0000000..bc74d67 --- /dev/null +++ "b/Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/ErrorHandling/InputErrorLv7.swift" @@ -0,0 +1,18 @@ +import Foundation + +enum InputErrorLv7: Error { + case invalidLength + case notNumber + case duplicateNumbers + + var errorMessage: String { + switch self { + case .invalidLength: + return Constants.invalidLengthMessage + case .notNumber: + return Constants.notNumberMessage + case .duplicateNumbers: + return Constants.duplicateNumbersMessage + } + } +} diff --git "a/Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/GameLogic/BaseballGameLv7.swift" "b/Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/GameLogic/BaseballGameLv7.swift" new file mode 100644 index 0000000..d3a4c96 --- /dev/null +++ "b/Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/GameLogic/BaseballGameLv7.swift" @@ -0,0 +1,146 @@ +import Foundation + +class BaseballGameLv7: BaseballGameProtocolLv7 { + var answer: [Int] + var strike: Int = 0 + var ball: Int = 0 + var gameRecords: [Int] = [] + var gameAttempts: Int = 0 + + private var answerCreator: AnswerCreatorLv7 + + init(answerCreator: AnswerCreatorLv7) { + self.answerCreator = answerCreator + self.answer = AnswerCreatorLv7.create() + } + + // 게임 시작을 안내하는 메서드 + func startGame() { + while true { + print(Constants.welcomeMessage) + print(Constants.menuOptions) + print("ㄴ ", terminator: "") + + guard let userInput = readLine(), let userIntInput = Int(userInput), (1...3).contains(userIntInput) else { + print("유효하지 않은 입력으로 1, 2, 3 중 하나를 선택하세요.") + continue + } + + switch userIntInput { + case 1: + gameRecords.append(startBaseballGame()) + case 2: + displayGameRecords() + case 3: + exitGame() + default: + continue + } + } + } + + // 야구 게임의 핵심 로직 + func startBaseballGame() -> Int { + resetGame() + print("야구 게임을 시작합니다!") + + while true { + let userInput = getUserInput() + + do { + let (strikeCount, ballCount) = try checkAnswer(userInput: userInput) + displayResult(strike: strikeCount, ball: ballCount) + + if strikeCount == 3 { + print("정답입니다!\n") + return gameAttempts + } + + } catch let error as InputErrorLv7 { + print(error.errorMessage) + } catch { + print("알 수 없는 오류가 발생했습니다.") + } + } + } + + // 사용자 입력받기 + private func getUserInput() -> String { + print("세 자리 숫자를 입력하세요: ", terminator: "") + guard let userInput = readLine() else { return "" } + return userInput + } + + // 입력한 답을 확인하고 스트라이크와 볼을 계산 + func checkAnswer(userInput: String) throws -> (strike: Int, ball: Int) { + guard let userNumber = Int(userInput), Constants.validNumberRange.contains(userNumber) else { + throw InputErrorLv7.invalidLength + } + + let userAnswerArray = try parseUserAnswer(userInput) + + guard Set(userAnswerArray).count == 3 else { + throw InputErrorLv7.duplicateNumbers + } + + gameAttempts += 1 + return evaluateBall(userAnswer: userAnswerArray, answer: answer) + } + + private func parseUserAnswer(_ userInput: String) throws -> [Int] { + var userAnswerArray: [Int] = [] + for char in userInput { + guard let userAnswerChar = Int(String(char)) else { + throw InputErrorLv7.notNumber + } + userAnswerArray.append(userAnswerChar) + } + return userAnswerArray + } + + // 정답과 사용자의 입력 비교 + func evaluateBall(userAnswer: [Int], answer: [Int]) -> (strike: Int, ball: Int) { + strike = 0 + ball = 0 + for index in userAnswer.indices { + if userAnswer[index] == answer[index] { + strike += 1 + } else if answer.contains(userAnswer[index]) { + ball += 1 + } + } + return (strike, ball) + } + + private func displayResult(strike: Int, ball: Int) { + if strike == 0 && ball == 0 { + print("아웃!") + } else { + print("\(strike)스트라이크 \(ball)볼") + } + } + + private func displayGameRecords() { + print("< 게임 기록 보기 >") + if gameRecords.isEmpty { + print("기록이 없습니다.") + } else { + for (index, attempts) in gameRecords.enumerated() { + print("\(index + 1)번째 게임: 시도 횟수 - \(attempts)") + } + } + print("") + } + + private func resetGame() { + self.answer = AnswerCreatorLv7.create() + self.strike = 0 + self.ball = 0 + self.gameAttempts = 0 + } + + private func exitGame() { + print("숫자 야구 게임을 종료합니다.") + exit(0) + } +} diff --git "a/Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/GameLogic/GameHistory.swift" "b/Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/GameLogic/GameHistory.swift" new file mode 100644 index 0000000..75d86cf --- /dev/null +++ "b/Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/GameLogic/GameHistory.swift" @@ -0,0 +1,19 @@ +import Foundation + +class GameHistory { + private var records: [Int] = [] + + func addRecord(_ attempts: Int) { + records.append(attempts) + } + + func displayRecords() { + if records.isEmpty { + print("기록이 없습니다.") + } else { + for (index, attempts) in records.enumerated() { + print("\(index + 1)번째 게임: 시도 횟수 - \(attempts)") + } + } + } +} diff --git "a/Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/Protocols/BaseballGameProtocolLv7.swift" "b/Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/Protocols/BaseballGameProtocolLv7.swift" new file mode 100644 index 0000000..0f478d9 --- /dev/null +++ "b/Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/Protocols/BaseballGameProtocolLv7.swift" @@ -0,0 +1,14 @@ +import Foundation + +protocol BaseballGameProtocolLv7 { + var answer: [Int] { get set } + var strike: Int { get set } + var ball: Int { get set } + var gameRecords: [Int] { get set } + var gameAttempts: Int { get set } + + func startGame() + func startBaseballGame() -> Int + func checkAnswer(userInput: String) throws -> (strike: Int, ball: Int) + func evaluateBall(userAnswer: [Int], answer: [Int]) -> (strike: Int, ball: Int) +} diff --git "a/Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/Utilities/AnswerCreatorLv7.swift" "b/Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/Utilities/AnswerCreatorLv7.swift" new file mode 100644 index 0000000..6109c73 --- /dev/null +++ "b/Week2-BaseballGame/Week2-BaseballGame/Lv_6(\355\224\274\353\223\234\353\260\261 \354\210\230\354\240\225)/Utilities/AnswerCreatorLv7.swift" @@ -0,0 +1,16 @@ +import Foundation + +class AnswerCreatorLv7 { + static func create() -> [Int] { + var answerSet: Set = [] + while answerSet.count < 3 { + let randomNum = Int.random(in: 0...9) + answerSet.insert(randomNum) + } + var answerArray = Array(answerSet) + if answerArray[0] == 0 { + answerArray.swapAt(0, Int.random(in: 1..