diff --git a/README.md b/README.md index 340697e..a77423e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,162 @@ -# ⚾ 숫자 야구 게임 +# ⚾️ 숫자 야구 게임 -숫자 야구 게임은 컴퓨터가 생성한 중복 없는 숫자를 맞히는 콘솔 기반 게임입니다. -사용자는 숫자를 입력하고 **스트라이크 / 볼 / 아웃** 결과를 통해 정답을 추론합니다. +Swift로 구현한 콘솔 기반 숫자 야구 게임입니다. +게임의 흐름, 규칙, 기록을 각각의 책임으로 나누어 설계하는 것을 목표로 했습니다. + +--- + +## 📌 프로젝트 목적 + +* Swift 기본 문법 및 제어 흐름 이해 +* 클래스 단위 책임 분리 연습 +* Set / Array 활용 +* 접근 제어자를 통한 캡슐화 경험 +* 계산 로직과 출력 로직 분리 + +--- + +## 🎮 게임 규칙 + +* 정답은 **중복되지 않는 3자리 숫자** +* **백의 자리는 1~9**, 나머지는 **0~9** +* 입력값에 따라 다음 힌트를 제공 + + * **Strike**: 숫자와 위치가 모두 일치 + * **Ball**: 숫자는 같지만 위치가 다름 + * **Nothing**: 일치하는 숫자 없음 +* 3 스트라이크 시 게임 종료 +* 각 게임의 시도 횟수를 기록으로 저장 + +--- + +## 🧱 프로젝트 구조 + +``` +📁 Project + ┣ 📄 main.swift + ┣ 📄 BaseballGame.swift + ┣ 📄 GameCenter.swift + ┣ 📄 RecordManager.swift +``` + +--- + +## 📂 클래스별 역할 설명 + +이 프로젝트는 **각 클래스가 하나의 책임만 가지도록 분리**했습니다. + +--- + +### `main.swift` + +**프로그램의 진입점 역할** + +* 게임 로직을 직접 다루지 않음 +* `BaseballGame`을 생성하고 실행만 담당 + +```swift +let game = BaseballGame() +game.start() +``` + +프로그램의 시작 지점을 명확히 하기 위한 구조입니다. + +--- + +### `BaseballGame` + +**게임 전체 흐름을 제어하는 클래스** + +* 메뉴 출력 +* 사용자 입력 처리 +* 게임 시작 / 종료 판단 +* 다른 객체들을 조합하여 사용 + +```swift +private var gameCenter = GameCenter() +private var recordManager = RecordManager() +``` + +계산 로직이나 기록 관리는 직접 처리하지 않고, +각각의 책임을 가진 객체에 위임합니다. + +→ 게임의 **흐름과 순서만 책임지는 조율자 역할**입니다. + +--- + +### `GameCenter` + +**숫자 야구 게임의 규칙과 계산을 담당하는 클래스** + +* 정답 숫자 생성 +* 입력값 유효성 검사 +* 스트라이크 / 볼 계산 + +```swift +enum GameResult { + case correct + case nothing + case progress(strike: Int, ball: Int) +} +``` + +* 계산 결과를 문자열이 아닌 `enum`으로 반환 +* 출력은 하지 않고, 결과만 전달 + +```swift +private func splitNum(_ num: Int) +``` + +내부 계산 로직은 `private`로 숨겨 외부에서 알 필요 없도록 설계했습니다. + +→ 게임 규칙이 변경되더라도 이 클래스만 수정하면 되도록 구성했습니다. + +--- + +### `RecordManager` + +**게임 기록과 상태를 관리하는 클래스** + +* 시도 횟수 증가 +* 게임 결과 저장 +* 기록 출력 + +```swift +private(set) var trialCounts: [Int] +``` + +* 외부에서는 읽기만 가능 +* 수정은 메서드를 통해서만 가능 + +→ 상태 변경 책임을 명확히 하기 위한 설계입니다. + +--- + +## 🔐 접근 제어 설계 의도 + +* `private` + + * 내부 구현 세부사항 은닉 +* `private(set)` + + * 상태는 보호하고 읽기만 허용 +* 불필요한 `public` 노출 최소화 + +객체 간 결합도를 낮추고, 책임을 명확히 하기 위함입니다. + +--- + +## ✨ 리팩토링 포인트 + +* 문자열 비교 대신 `enum` 사용 +* 계산 로직과 출력 로직 분리 +* Set은 중복 검사 용도로만 사용 +* 클래스별 책임을 기준으로 코드 정리 + +--- + +## 📝 느낀 점 + +* 클래스 분리를 통해 코드 가독성이 좋아졌고 +* 책임이 명확해지면서 수정 포인트를 찾기 쉬워졌음 +* 단순히 동작하는 코드보다 구조를 고민하는 경험을 할 수 있었음 diff --git a/juhee/juhee/BaseballGame.swift b/juhee/juhee/BaseballGame.swift new file mode 100644 index 0000000..268836e --- /dev/null +++ b/juhee/juhee/BaseballGame.swift @@ -0,0 +1,70 @@ +// +// BaseballGame.swift +// juhee +// +// Created by 김주희 on 1/13/26. +// + +import Foundation + +public class BaseballGame { // 야구 게임 진행 클래스 + + private var gameCenter = GameCenter() // 게임 연산 계산 인스턴스 생성 + private var recordManager = RecordManager() // 게임 기록을 관리하는 인스턴스 생성 + + // MARK: - 게임 선택 함수 + func start() { + + while true { + print("환영합니다!🤗 원하시는 번호를 입력해주세요 💬") + print("1. 게임 시작하기 ⚾️ 2. 게임 기록 보기 📋 3. 종료하기 ⛔️") + + switch readLine() { + case "1": + print("\n< Round \(recordManager.trialCounts.count + 1): 게임을 시작합니다 >") + playGame() // 야구게임 진행 메소드 실행 + case "2": + print("\n< 게임 기록 보기 📋 >") + recordManager.showRecords() // showRecords 함수 호출 + case "3": + print("\n< 숫자 야구 게임을 종료합니다. ⛔️ >") + exit(0) // 강제 종료 함수 실행 + default: + print("올바른 숫자를 입력해주세요! 😤") + } + } + + + // MARK: - 농구 게임 시작 함수 + func playGame(){ + + let answer = gameCenter.makeAnswer() // 정답 만드는 함수 호출 + var isplay = true + while isplay { // 입력값 검사 반복문 + print("숫자를 입력하세요:") + guard let inputNumber = readLine().flatMap(Int.init), // 올바른 입력값인지 검사 + gameCenter.checkInput(inputNumber) // 입력값 검사 함수 호출 + else { + print("올바르지 않은 입력값입니다.😤 다시 입력해주세요!\n") + continue + } + + recordManager.addTrial() // 올바른 숫자를 입력하였으므로 시도횟수 +1 + + let result = gameCenter.compare(inputNumber, answer) + + switch result { + case .correct: + print("정답입니다!✔️\n") + recordManager.add(recordManager.trial) // 정답이므로 배열에 최종 시도 횟수 입력 + recordManager.trial = 0 // 게임 시도 횟수 0으로 초기화 + isplay = false + case .nothing: + print("Nothing 😵\n") + case .progress(let s, let b): + print("\(s) 스트라이크 \(b) 볼\n") + } + } + } + } +} diff --git a/juhee/juhee/GameCenter.swift b/juhee/juhee/GameCenter.swift new file mode 100644 index 0000000..7a0627c --- /dev/null +++ b/juhee/juhee/GameCenter.swift @@ -0,0 +1,74 @@ +// +// GameCenter.swift +// juhee +// +// Created by 김주희 on 1/14/26. +// + +import Foundation + +class GameCenter { // 게임에 필요한 계산을 하는 클래스 + var gameNumber = 3 + + // MARK: - 입력한 세자리 수를 숫자 각 한개씩으로 배열로 쪼개는 내부 로직 함수 + private func splitNum(_ num: Int) -> [Int] { + return String(num).compactMap { $0.wholeNumberValue } + } + + + // MARK: - 정답 만드는 함수 + func makeAnswer() -> [Int] { + let arr = (0...9).map { $0 } + + let shuffledArr = arr.shuffled() // 배열을 랜덤으로 섞어줌 + + if shuffledArr[0] == 0 { + return [Int](shuffledArr[1...gameNumber]) // Int 배열로 형변환 필수 + } else { + return [Int](shuffledArr[0...gameNumber - 1]) + } + } + + + // MARK: - 사용자가 입력한 값 검증 함수 + func checkInput(_ inputNumber: Int) -> Bool { + let set = Set(splitNum(inputNumber)) + + return set.count == gameNumber // Array를 Set으로 변환하여 중복을 제외한 값이 3이어야 함 + && Int(pow(10.0,Double(gameNumber - 1))) - 1 < inputNumber + && inputNumber < Int(pow(10.0,Double(gameNumber))) + } + + + // MARK: - GameResult 구조체 + enum GameResult { + case correct + case nothing + case progress(strike: Int, ball: Int) + } + + + // MARK: - 입력값과 정답을 비교해 힌트 계산하는 함수 + func compare(input: Int, with answer: [Int]) -> GameResult { + var strike = 0 + var ball = 0 + let inputArray = splitNum(input) // 입력값을 쪼개서 세 원소를 가진 배열로 + + // strike, ball에 결과값 입력 + for i in 0.. = [] // 시도 횟수 저장할 빈 배열 + + // 게임 시도 횟수 증가 함수 + func addTrial() { + trial += 1 + } + + // 배열에 시도 횟수 추가 함수 + func add(_ trialcount: Int) { + trialCounts.append(trialcount) + } + + // 기록 출력 함수 + func showRecords() { + for (idx, value) in trialCounts.enumerated() { + print("\(idx + 1)번째 게임: 시도 횟수 - \(value)") + } + } + +} diff --git a/juhee/juhee/main.swift b/juhee/juhee/main.swift new file mode 100644 index 0000000..638c27c --- /dev/null +++ b/juhee/juhee/main.swift @@ -0,0 +1,11 @@ +// +// main.swift +// juhee +// +// Created by 김주희 on 1/13/26. +// + +import Foundation + +let game = BaseballGame() +game.start()