기능 설명
중복되지 않는 3자리 숫자를 맞추는 게임입니다.
각 자리수는 서로 중복되지 않고, 앞자리에는 0이 올 수 없습니다.
입력
입력은 문자열을 포함하지 않은 숫자 3자리를 입력받습니다.
아래와 같이 올바르지 않는 입력 경우의 수는 다음과 같습니다.
- 빈 문자열
- 중복된 값이 존재하는 3자리 문자열
- 3자리가 아닌 문자열
- String값이 섞인 문자열
- 0으로 시작하는 문자열
// 올바르지 않는 입력값
input1 = ""
input2 = "122"
input3 = "12"
input4 = "12e"
input5 = "dfe"
input6 = "012"
이를 아래와 같이 필터링하는 메서드를 만들었습니다.
private func isCorrectInput(input: String) -> Bool {
// 문자열의 앞 문자가 0인경우
if input.prefix(1) == "0" { return false }
// 빈문자열인 경우, Set타입으로 타입변환 시 중복된 원소가 제거되므로 변환해도 3자리 문자열인 경우
if Int(input) == nil || Set(input.map{$0}).count != 3 {
return false
}
return true
}
- Int(input): String값을 Int로 형변환 했을 때 숫자가 아닌 문자인 경우 nil로 변환되기에 여기서 문자열 안에 문자를 검사한다.
정답값 생성
중복없는 3자리 문자열 생성
private func initAnswer() {
var answerArray = [Int]()
func randomNumber(range: Range<Int>) -> Int {
return Int.random(in: range)
}
while answerArray.count < 3 {
let num = randomNumber(range: 1..<10)
if !answerArray.contains(num) { answerArray.append(num)}
}
answer = answerArray
}
입력값과 정답 비교
각 자리마다 숫자를 비교하는데 결과는 3종류로 나타낼 수 있습니다.
- 자리도 같고 숫자도 같다면 Strike
- 자리는 다르지만 숫자가 존재한다면 Ball
- 자리도 없고 숫자도 존재하지 않으면 x
private func compareAnswer(with numArray: [Int]) -> (equalPoint: Int, containsPoint: Int) {
var equalPoint = 0
var containsPoint = 0
for i in 0..<numArray.count {
if answer[i] == numArray[i] {
equalPoint += 1
} else if answer.contains(numArray[i]) {
containsPoint += 1
}
}
return (equalPoint: equalPoint, containsPoint: containsPoint)
}
그에 따른 결과도 3가지로 나눌 수 있습니다.
- 정답
- N Strike N ball
- Nothing
이를 Strike와 Ball의 개수로 판단하도록 설계하였습니다.
private func playBaseball() {
var currentTryCount = 0
var isFindAnswer = false
currentGameIndex += 1
initAnswer()
print("<게임을 시작합니다.>")
while !isFindAnswer {
print("숫자를 입력하세요")
guard let input = readLine() else { continue }
if !isCorrectInput(input: input) {
print("올바르지 않은 입력값입니다.\n")
continue
}
currentTryCount += 1
let inputIntegerArray = input.map{Int(String($0))!}
let result = compareAnswer(with: inputIntegerArray)
if result.equalPoint == 3 {
print("정답입니다!")
isFindAnswer = true
records.append((game: currentGameIndex, tryCount: currentTryCount))
} else if result.equalPoint == 0 && result.containsPoint == 0 {
print("Nothing")
} else {
print("\(result.equalPoint) Strike \(result.containsPoint) Ball")
}
}
}
게임 모드
게임을 시작하면 [1. 게임진행], [2. 이전기록확인], [3.종료하기] 로 세가지 동작을 실행할 수 있습니다.
이를 열거형을 사용하여 didSet 관찰자를 사용해 입력받은 값에 따라 설정하여 didSet에 선언된 메서드가 실행되도록 구현하였습니다.
enum GameState: Int {
case play = 1
case record = 2
case exit = 3
case none
}
private var currentState: GameState = .none {
didSet {
switch currentState {
case .play:
playBaseball()
case .record:
openRecord()
case .exit:
exitGame()
default:
print("올바른 숫자를 입력해주세요!")
}
}
}
func play() {
while currentState != .exit {
selectMode()
}
}
private func selectMode() {
print("환영합니다! 원하시는 번호를 입력해주세요\n1. 게임 시작하기 2. 게임 기록 보기 3. 종료하기")
guard let input = readLine(),
let inputInteger = Int(input),
let state = GameState(rawValue: inputInteger) else { return }
currentState = state
}
종합해보면 아래와 같습니다.
class NumberBaseballGame {
private var answer: [Int] = []
private var records: [(game: Int, tryCount: Int)] = []
private var currentGameIndex = 0
private var currentState: GameState = .none {
didSet {
switch currentState {
case .play:
playBaseball()
case .record:
openRecord()
case .exit:
exitGame()
default:
print("올바른 숫자를 입력해주세요!")
}
}
}
enum GameState: Int {
case play = 1
case record = 2
case exit = 3
case none
}
init() { }
func play() {
while currentState != .exit {
selectMode()
}
}
private func selectMode() {
print("환영합니다! 원하시는 번호를 입력해주세요\n1. 게임 시작하기 2. 게임 기록 보기 3. 종료하기")
guard let input = readLine(),
let inputInteger = Int(input),
let state = GameState(rawValue: inputInteger) else { return }
currentState = state
}
private func exitGame() {
records.removeAll()
currentGameIndex = 0
}
private func openRecord() {
records.forEach { (game, count) in
print("\(game)번째 게임 : 시도 횟수 - \(count)\n")
}
}
private func playBaseball() {
var currentTryCount = 0
var isFindAnswer = false
currentGameIndex += 1
initAnswer()
print("<게임을 시작합니다.>")
while !isFindAnswer {
print("숫자를 입력하세요")
guard let input = readLine() else { continue }
if !isCorrectInput(input: input) {
print("올바르지 않은 입력값입니다.\n")
continue
}
currentTryCount += 1
let inputIntegerArray = input.map{Int(String($0))!}
let result = compareAnswer(with: inputIntegerArray)
if result.equalPoint == 3 {
print("정답입니다!")
isFindAnswer = true
records.append((game: currentGameIndex, tryCount: currentTryCount))
} else if result.equalPoint == 0 && result.containsPoint == 0 {
print("Nothing")
} else {
print("\(result.equalPoint) Strike \(result.containsPoint) Ball")
}
}
}
private func isCorrectInput(input: String) -> Bool {
if input.prefix(1) == "0" { return false }
if Int(input) == nil || Set(input.map{$0}).count != 3 {
return false
}
return true
}
private func initAnswer() {
var answerArray = [Int]()
func randomNumber(range: Range<Int>) -> Int {
return Int.random(in: range)
}
while answerArray.count < 3 {
let num = randomNumber(range: 1..<10)
if !answerArray.contains(num) { answerArray.append(num)}
}
answer = answerArray
}
private func compareAnswer(with numArray: [Int]) -> (equalPoint: Int, containsPoint: Int) {
var equalPoint = 0
var containsPoint = 0
for i in 0..<numArray.count {
if answer[i] == numArray[i] {
equalPoint += 1
} else if answer.contains(numArray[i]) {
containsPoint += 1
}
}
return (equalPoint: equalPoint, containsPoint: containsPoint)
}
}
'코딩테스트 > 알고리즘' 카테고리의 다른 글
분할 정복 Swift (1) | 2024.04.26 |
---|---|
플로이드-워셜 알고리즘 Swift (0) | 2024.04.25 |
다익스트라 알고리즘 Swift (0) | 2024.04.24 |
투포인터 알고리즘 Swift (0) | 2024.04.14 |
순열과 조합 Swift (0) | 2024.04.10 |