새로운 과제의 등장, 새로운 일정의 등장, 알고리즘의 지옥
오늘은 새로운 과제가 출제 되었다. 프로그래밍을 배운다면 기본적으로 한번씩은 구현해보게 되는 야구 게임이다.
동작 원리는 단순하며, 본인 또한 시간이 꽤 지났지만 대학교 1학년 때 수업을 들으며 만들어본 경험이 있었다. 때문에, 구현에 관해 완전히 기억을 하지는 못하지만, "어떻게 접근을 하는 것이 좋을까"에 대한 생각은 가지고 시작할 수 있었다. 요구사항은 모두 충족시키며 생각보다 더 빠르게 과제를 끝냈지만, 내일부터는 튜터님이 말씀하셨듯이 기존 함수로 동작하는 방식에서 벗어나 protocol 또는 class를 사용하여 구현하는 방식으로 수정을 진행해보고자 한다.
또한, 지난 과제에 대한 피드백을 받고 이를 적용하여 보다 신중하게 작업에 임했다. 완벽하다고는 할 수 없을지 몰라도, 조금씩 피드백을 적용하다보면 문제를 야기하지않는 좋은 습관을 들이게 될 것이라 생각한다.
현재까지 구현한 과제는 함수 별로 파일을 분리하여 개발하였다.
startGame()
main에서는 단순히, 함수를 호출하여 시작한다.
// 프로그램 시작 함수
func startGame() {
var scoreRecord: [Int] = []
/* (Lv 4.) 1. 프로그램 시작 시 안내 문구 출력 */
while true {
print("""
환영합니다! 원하시는 번호를 입력해주세요.
1. 게임 시작하기 2. 게임 기록 보기 3. 종료하기
""")
guard let input = readLine()
else { continue }
switch input {
/* (Lv 4.) 2. 게임 시작하기 선택 시 play로 연결 */
case "1":
let score = play() //게임 후 결과를 Int로 반환받아서 score에 할당
scoreRecord.append(score) // score을 Record에 저장
/* (Lv 5.) 1. 게임 기록 보기 선택 시 시도 횟수 출력 */
case "2":
print("게임 기록 보기")
for (idx, score) in scoreRecord.enumerated() { //고차함수 enumerated를 사용하여 idx: 인덱스 score: 점수 반환 후 출력
print("\(idx + 1)번째 게임 : 시도 횟수 - \(score)")
}
/* (Lv 6.) 1. 종료하기 선택 시 프로그램 종료(기록 초기화) */
case "3":
print("게임을 종료합니다.")
return
/* (Lv 6.) 2. 이외 입력값에 대한 오류 출력 */
default:
print ("올바른 숫자를 입력해주세요!")
}
}
}
// 게임 시작 함수
func play() -> Int {
// let result = randomAnswer()
let result = randomAnswer2()
var totalCount = 0
while true {
/* (Lv.2) 1. 정답 맞추기 + 힌트 받기 */
print("숫자를 입력하세요: ", terminator: "\n")
guard let input = readLine(),
let num = Optional(input.compactMap{Int(String($0))}), //숫자 외의 다른 값이 들어왔는지 확인, map 대신 compactMap 사용 : map 사용시 nil로 처리 되기 때문에 처리가 바르게 일어나지않음(ex. 1ab입력시 num = [1,nil,nil] -> 중복으로 처리됨
num.count == 3
else {
print("올바르지 않은 입력값입니다.")
continue
}
if Set(num).count != 3 { //중복된 값이 있는지 확인
print("올바르지 않은 입력값입니다.(중복)")
continue
}
totalCount += 1 //기록 저장용 Count + 1
let inputChars = Array(input) //입력받은 값을 배열에 담음
/* (Lv.2) 2. 힌트 생성, 게산 */
var strike: Int = 0
var ball: Int = 0
for i in 0..<3 {
if result[i] == Int(String(inputChars[i]))! { //Int는 Character를 바로 받지 못함 -> String으로 감싸기
strike += 1
} else if result.contains(Int(String(inputChars[i]))!) {
ball += 1
}
}
if strike == 3 {
print("정답입니다.")
return totalCount//정답일 경우, 게임 종료 -> (break를 사용하지 않고 return을 사용한 이유 -> 기록을 남기기 위해서)
}else if strike >= 1 && ball >= 1 {
print("\(strike)스트라이크 \(ball)볼")
}else if strike >= 1 {
print("\(strike)스트라이크")
}else if ball >= 1 {
print("\(ball)볼")
}else {
print("Nothing")
}
}
}
// 3자리 난수 생성 함수(1~9) -> Set 사용 -> 문제점 : 정답 생성용 Set, 정답 저장용 Array가 별도로 필요함.
func randomAnswer() -> [Int] {
var answer: Set<Int> = []
var result: [Int] = []
/* (Lv.1) 1. 1~9 서로 다른 난수 3개 생성 */
while answer.count < 3 { //정답 생성용(Set 사용) : 같은 수 반복 x
let num = Int.random(in: 1...9)
answer.insert(num)
}
result = Array(answer) // 정답 저장용
// print(answer)
// print(result)
return result
}
/* (Lv 3.) 1. 첫 자리(1~9), 나머지(0~9): Set 사용하지 않고 contains로 조건 사용 */
func randomAnswer2() -> [Int] {
var result: [Int] = []
let fst = Int.random(in: 1...9)
result.append(fst)
while result.count < 3{
let rndnum = Int.random(in: 0...9)
if !result.contains(rndnum){
result.append(rndnum)
}
}
// print(result)
return result
}
사실 이번 과제의 기본적인 구현 난이도 자체는 매우 쉽다고 생각을 한다. 실제로, 지난 과제의 필수 문제보다 접근이 쉬울 것이다. 이미 한번 접해본 문제?이기도 해서 더 그렇게 느껴질 수 있겠지만, 새로운 문법을 적용하는 공부를 하기 위해서는 좋은 문제라고 생각한다. 또한, 현재 과제의 풀이를 누군가 보더라도 이해하기 쉽도록 주석을 최대한 자세하게 사용했다.


기본적으로, 본인의 코드는 기본적으로 Swift 가이드라인을 준수하지 않고 작성된 느낌을 준다. 튜터님의 피드백 속에도 있듯이, Swift Convention을 읽어보고 이를 정리하여 적용하도록 해야할 것이며 매개변수 명칭에도 약어를 사용하는 좋지 않은 습관?이 존재하는데 첨부하신 링크의 API Design Guideline을 확인하고 적용하도록 하자.