Skip to content
Open
37 changes: 37 additions & 0 deletions BaseballGame/BaseballGame/Controller/GameComputer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// BaseballGameManager.swift
// BaseballGame
//
// Created by 변예린 on 1/15/26.
//

import Foundation

// 게임과 관련된 연산을 담당하는 클래스입니다.
class GameComputer {
// 게임 정답 생성 함수
func setAnswer() -> [Int] {
return Array((0...9).shuffled() // 숫자 섞기
.trimmingPrefix(while: { $0 == 0 }) // while 조건에 맞는 첫 글자 삭제
.prefix(3))
}

// 정답 & 유저 입력 비교 함수
func check(_ user: [Int], with answer: [Int]) -> CheckResult {
var strike = 0
var ball = 0

// 힌트 설정(스트라이크, 볼)
for (i, element) in user.enumerated(){
if answer[i] == element {
strike += 1 // 숫자의 자리와 요소가 동일할 경우
} else if answer.contains(element) {
ball += 1 // 숫자의 요소가 동일할 경우
}
}

let isCorrect = strike == 3 ? true : false // 정답 여부

return CheckResult(strike: strike, ball: ball, correct: isCorrect)
}
}
115 changes: 115 additions & 0 deletions BaseballGame/BaseballGame/Controller/GameController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//
// Game.swift
// BaseballGame
//
// Created by 변예린 on 1/13/26.
//

import Foundation

/* 게임의 시스템을 관리하는 클래스입니다.
각 기능을 담당하는 클래스에게 명령을 내려 핵심 기능을 수행합니다. */

class GameController {
private let messagePrinter: MessagePrinter
private let recordManager: RecordManager
private let inputManager: InputManager
private let gameComputer: GameComputer

init(messagePrinter: MessagePrinter, recordManager: RecordManager, inputManager: InputManager, gameComputer: GameComputer) {
self.messagePrinter = messagePrinter
self.recordManager = recordManager
self.inputManager = inputManager
self.gameComputer = gameComputer
}

// 게임 시작 함수
func start() {
var isExit = false

while !isExit {
messagePrinter.welcome()
let selected = selectMenu() // 메뉴 선택

// 선택 메뉴에 따른 함수 실행
switch selected {
case .play: // 게임 시작
messagePrinter.startGame()
play()
case .record: // 기록 조회
messagePrinter.showRecordTitle()
showRecord()
case .exit: // 게임 종료
recordManager.resetRecord()
messagePrinter.endGame()
isExit = true
}
}
}

// 메뉴 선택 함수
private func selectMenu() -> Menu {
while true {
do {
let input = try inputManager.inputMenu() // 유저 입력값
return input // 유효할 경우
} catch InputError.invalid(for: .menu) {
messagePrinter.error(.invalid(for: .menu)) // 오류 메세지 출력
} catch {
messagePrinter.unknownError() // 알 수 없는 오류
}
}
}

// 게임 플레이 함수
private func play() {
recordManager.addRound() // 게임 기록 생성
let answer = gameComputer.setAnswer() // 정답 생성

debugPrint(answer) // 디버깅용 정답 출력

while true { // 정답을 맞힐 때까지 반복
let userAnswer = getUserAnswer() // 유저 정답 생성

// 정답 확인
let result = gameComputer.check(userAnswer, with: answer)
messagePrinter.result(result) // 결과 출력

// 기록 변경
recordManager.addAttempt()
if result.correct { break } // 정답 시 게임 종료
}
}

// 유저 정답 생성 함수
private func getUserAnswer() -> [Int] {
while true {
do {
let input = try inputManager.inputUserAnswer()
return input
} catch InputError.duplicate {
messagePrinter.error(.duplicate)
} catch InputError.invalid(for: .answer) {
messagePrinter.error(.invalid(for: .answer))
} catch {
messagePrinter.unknownError()
}
}
}

// 기록 조회 함수
private func showRecord() {
let record = recordManager.fetchRecord() // 기록 불러오기

// 기록이 없는 경우
if record.attempts.isEmpty {
messagePrinter.noRecord()
} else {
// 게임 기록 출력
for i in record.attempts.indices {
print(GameMessage.getRecord(for: i, attempt: record.attempts[i]))
}
print("\n", terminator: "")
}
}
}
38 changes: 38 additions & 0 deletions BaseballGame/BaseballGame/Controller/InputManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// InputDesk.swift
// BaseballGame
//
// Created by 변예린 on 1/16/26.
//

import Foundation

// 유저에게 값을 입력받고 검증하는 클래스입니다.
class InputManager {
// 유저로부터 실행할 메뉴를 입력받는 함수
func inputMenu() throws -> Menu {
let input = (readLine() ?? "").trimmingCharacters(in: .whitespacesAndNewlines)

if let result = Menu(rawValue: input) {
return result
} else {
throw InputError.invalid(for: .menu)
}

}

// 유저로부터 정답을 입력받는 함수
func inputUserAnswer() throws -> [Int] {
// 유저 입력값
let stringInput = (readLine() ?? "").trimmingCharacters(in: .whitespacesAndNewlines).map{ String($0) } // [String] 변환
let input = stringInput.compactMap{ Int($0) } // [Int] 변환

if input.count != 3 || input.count != stringInput.count {
throw InputError.invalid(for: .answer) // 유저 입력이 3자리 숫자가 아닐 경우 혹은 유저 입력에 문자가 같이 입력되었을 경우
} else if Set(input).count != 3 {
throw InputError.duplicate // 유저 입력에 중복 숫자가 있을 경우
} else {
return input //정상 입력일 경우
}
}
}
41 changes: 41 additions & 0 deletions BaseballGame/BaseballGame/Controller/RecordManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// RecordManager.swift
// BaseballGame
//
// Created by 변예린 on 1/16/26.
//

import Foundation

// 게임 기록을 관리하는 클래스입니다.
class RecordManager {
static let shared = RecordManager()
private var record = Record()

private init() {}

// 게임 플레이 횟수 증가
func addRound() {
// - round 기본값은 0, round는 attempts 배열의 인덱스로 사용됨: attempts[round]
// - 기본값이 attempts 배열보다 앞서있으므로 배열이 비어있을 때는 round를 증가시키지 않음
// --> round를 증가시킬 경우, 시도 횟수는 attempts[0]에 저장되지만 round == 1이므로 attempts[round]로 조회 불가능
record.round = record.attempts.isEmpty ? record.round : record.round + 1
record.attempts.append(0) // record.attempts 배열 확장
}

// 게임 시도 횟수 증가
func addAttempt() {
record.attempts[record.round] += 1
}

// 기록 초기화
func resetRecord() {
record.round = 0
record.attempts = []
}

// 기록 전달
func fetchRecord() -> Record {
return record
}
}
Loading