-
Notifications
You must be signed in to change notification settings - Fork 2
김주희) 야구게임 과제 전체 문제 풀이 #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
coduhee
wants to merge
23
commits into
main
Choose a base branch
from
juhee
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
3c0eefd
feat: 문제 1번 커밋
coduhee a6c06c9
feat: LV.2 풀이 커밋
coduhee b26458f
feat: LV.3 문제 커밋
coduhee 4a0c836
feat: LV.4 과제 커밋
coduhee 19a86d4
feat: LV.5 커밋
coduhee e1bb9dd
feat: LV.6 커밋
coduhee 5f8014d
refactor: 클래스 추가, 함수 생성
coduhee 9b740e3
refactor: .contains 함수 사용으로 코드 간결화
coduhee 6e045cf
refactor: playGame() 메소드로 분리
coduhee 024507f
refactor: GameCounter 클래스 분리
coduhee 92c324c
feat: array->set으로 중복 체크
coduhee b575a57
refactor: Set을 이용해 중복 체크
coduhee 8bf3f01
refactor
coduhee 7c411c9
chore: 접근제어자 설정
coduhee 286109d
refactor
coduhee 914cfa9
Enhance README with project details and structure
coduhee 4aa3e11
refactor
coduhee 15170da
chore
coduhee 2ecc356
refactor
coduhee f6d0662
refactor
coduhee 2231475
refactor
coduhee 1b8c78f
feat
coduhee e0d0b08
refactor
coduhee File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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은 중복 검사 용도로만 사용 | ||
| * 클래스별 책임을 기준으로 코드 정리 | ||
|
|
||
| --- | ||
|
|
||
| ## 📝 느낀 점 | ||
|
|
||
| * 클래스 분리를 통해 코드 가독성이 좋아졌고 | ||
| * 책임이 명확해지면서 수정 포인트를 찾기 쉬워졌음 | ||
| * 단순히 동작하는 코드보다 구조를 고민하는 경험을 할 수 있었음 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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") | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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..<gameNumber { | ||
| if inputArray[i] == answer[i] { | ||
| strike += 1 | ||
| } else if answer.contains(inputArray[i]){ | ||
| ball += 1 | ||
| } | ||
| } | ||
|
|
||
| if (strike == gameNumber) { | ||
| return GameResult.correct | ||
| } else if (strike == 0 && ball == 0){ | ||
| return GameResult.nothing | ||
| } else { | ||
| return GameResult.progress(strike: strike, ball: ball) | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| // | ||
| // RecordManager.swift | ||
| // juhee | ||
| // | ||
| // Created by 김주희 on 1/13/26. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| // MARK: - 게임 기록 관리 클래스 | ||
| class RecordManager { // 기록 관리 클래스 | ||
| var trial = 0 // 게임 시도 횟수 | ||
| private(set) var trialCounts: Array<Int> = [] // 시도 횟수 저장할 빈 배열 | ||
|
|
||
| // 게임 시도 횟수 증가 함수 | ||
| func addTrial() { | ||
| trial += 1 | ||
| } | ||
|
|
||
| // 배열에 시도 횟수 추가 함수 | ||
| func add(_ trialcount: Int) { | ||
| trialCounts.append(trialcount) | ||
| } | ||
|
|
||
| // 기록 출력 함수 | ||
| func showRecords() { | ||
| for (idx, value) in trialCounts.enumerated() { | ||
| print("\(idx + 1)번째 게임: 시도 횟수 - \(value)") | ||
| } | ||
| } | ||
|
|
||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| // | ||
| // main.swift | ||
| // juhee | ||
| // | ||
| // Created by 김주희 on 1/13/26. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| let game = BaseballGame() | ||
| game.start() |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
readLine()은 옵셔널 값으로 알고있는데, 바인딩 안해주어도 switch문 잘 돌아가나용??There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
switch문에 readLine()을 넣으면 case에 해당하지않는 nil값은 바로 default문이 실행되어서 옵셔널 바인딩 + 값 비교를 동시에 해주는거라 더 깔끔하게 작성할 수 있습니당!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아...!! 옵셔널 타입이라서☺️
case Optional("1")처럼 되어야하지 않나...라고 헷갈렸었습니다ㅠㅠㅠ맞네요 비교는 바로 되겠네요ㅋㅋㅋ default로 한번에 처리할 수 있다니 좋은 활용법인 것 같습니다! 설명 감사드려요!!