24-04-30 Kotlin 숫자 야구 게임 만들기 (2)
오늘은 어제 만든 숫자 야구 게임의 기능을 추가하고 수정해나가는 작업을 해보았다.
작업 수정 내역 일부를 정리해본다.
전체 코드는 아래 주소에 올려놓았다.
https://github.com/Moo-moo-11/BaseballGame
GitHub - Moo-moo-11/BaseballGame: 스파르타코딩클럽 Kotlin 문법 심화 과제입니다
스파르타코딩클럽 Kotlin 문법 심화 과제입니다. Contribute to Moo-moo-11/BaseballGame development by creating an account on GitHub.
github.com
1. 숫자 야구 게임 만들기
1-1. 다양한 게임 추가
객체 지향적으로 프로그램을 작성하는 연습을 하기 위해 고민을 하다가 비슷하지만 약간씩 다른 다양한 야구 게임을 만들기로 했다.
일반 모드, Easy 모드, Hard 모드 3가지를 만들었다. Easy 모드는 숫자가 1~7까지 밖에 쓰이지 않는다. Hard 모드는 숫자대신 26자리 소문자 알파벳이 쓰인다.
사실상 세 게임은 거의 동일한 구조를 가졌기 때문에 추상 클래스 abstractBaseballGame을 만들어 겹치는 기능들을 넣어주었다.
추상 클래스 안에는 게임 기록이 저장되는 리스트, 정답 배열과 입력받은 배열을 넣어주면 스트라이크와 볼의 갯수를 계산해주는 함수, 스트라이크와 볼의 갯수에 따라 안내문을 출력해주는 함수, 여태까지 저장된 플레이 기록들을 출력해주는 함수등이 공통으로 들어있다. 실제 게임이 동작하는 부분(함수)은 상속받은 클래스에서 반드시 구현해줘야한다. 또한 게임 타입 변수도 상속받은 클래스에서 지정해줘야한다.(Easy인지 일반인지 Hard인지)
코드가 상당히 길기 때문에 여기에는 코드를 올리지는 않는다. 위의 GitHub 링크에 들어가서 보면 확인해 볼 수 있다.
1-2. 이름 저장 기능 추가
게임 기록을 단순히 저장하고 출력하기만 하는 것은 심심하다고 생각했다. 오락실에 가면 게임이 종료될 때 이름을 입력하여 높은 기록들이 출력되는 것에서 아이디어를 얻어 비슷하게 제작해보려고 했다.
게임이 끝나면 이름을 저장하게 되고, 출력될 때는 저장된 리스트를 기록에 따라 정렬해 3위까지만 출력하도록 만들었다.
이름과 게임 기록은 각각 문자열과 Int정수이기 때문에 어떤 형식으로 저장할까 고민이 많았다. 결론적으로는 가변형 리스트에 Pair(문자열, Int)를 하나의 요소로 게임 기록을 저장하기로 했다. 그냥 Map으로 저장하면 key값을 이름으로 할 경우 같은 이름으로 저장할 경우 게임 기록이 덮어씌워지는 문제가 있었다. 중복으로도 저장이 가능하도록 위와 같은 방법을 선택했다.
출력할 때는 리스트를 정렬한 뒤 상위 3개를 출력하는 방식을 택했다. 정렬은 게임 기록이 좋은 순서로, 기록이 같다면 이름순으로 정렬하였다.
정렬한 뒤에 그냥 인덱스로 가장 앞에 있는 것 3개를 가져와 출력하는 방식을 택했다. 게임 기록이 3개 미만으로 있다면 인덱싱 에러가 발생하는 것 역시 고려하여 예외처리를 해두었다. 게임 기록이 없다면 아직 게임 기록이 없다는 문구가 출력된다.
작성한 코드들 중 일부를 가져와서 정리해보았다.
// 가변형 리스트에 Pair(문자열,Int정수)의 형태로 기록을 저장했다.
var gamePlayResultList: MutableList<Pair<String,Int>> = mutableListOf()
// 게임이 끝나면 이름을 입력받고 입력받은 이름과 게임 기록이 함께 리스트에 저장된다.
gamePlayResultList.add(name to score)
if (gamePlayResultList.isNotEmpty()) {// 리스트가 비어있는지 확인
/* 출력하기 전 리스트는 Pair의 두 번째 요소인 게임 기록 순으로,
만약 게임 기록이 동률이라면 이름순으로 정렬된다. */
gamePlayResultList.sortWith(compareBy({ it.second }, { it.first }))
// 게임 기록이 3개 미만인 경우 그에 맞춘 숫자만큼 출력된다.
try {
println("\uD83E\uDD48 이름: ${gamePlayResultList[1].first}, 시도 횟수 - ${gamePlayResultList[1].second}")
println("\uD83E\uDD49 이름: ${gamePlayResultList[2].first}, 시도 횟수 - ${gamePlayResultList[2].second}")
println("_____________________________________________")
} catch (e: Exception) {
println("_____________________________________________")
} else {
// 플레이 기록이 없으면 아래 문구가 출력된다.
println("아직 플레이 기록이 없습니다")
}
1-3. 알파벳 야구 게임 추가
프로젝트 이름은 숫자 야구 게임이지만 숫자 9개로만 진행하는 것보다 알파벳 26글자를 이용하면 더 재밌을 것 같았다. 숫자 대신 알파벳을 이용하는 것이지 기본 구조는 모두 같다.
그런데 이미 이전 숫자 야구 게임들을 만들면서 IntArray로 통일을 해놨었던 부분이 문제가 되었다. 따라서 모두 하나의 배열로 통일하기 위해 모두 CharArray로 바꾸었더니 깔끔하게 문제가 해결되었다. 이전 숫자 야구에서는 입력받은 문자열을 Int로 변환하는 과정이 있었는데 이 또한 필요없게 되어 오히려 더 깔끔해진 것 같다.
아래는 새로 작성한 알파벳 야구 게임 랜덤 알파벳 생성 함수이다.
fun getRandomThreeAlphabetArray():CharArray {
val alphabetArray = charArrayOf('a','b','c','d','e','f','g','h','i','j',
'k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z')
alphabetArray.shuffle()
return alphabetArray.sliceArray(0..2)
}
아래는 기존 IntArray로 반환되었던 랜덤 숫자 생성 함수를 CharArray로 변경한 함수이다.
fun getRandomThreeNumbArray(): CharArray {
val numberArray = charArrayOf('1','2','3','4','5','6','7','8','9')
numberArray.shuffle()
return numberArray.sliceArray(0..2)
}
1-4. 게임 기록 출력하기
야구 게임을 총 3개 만들었기 때문에 게임을 처음 시작하면 객체가 3개 생성되고 각각 게임 플레이 기록이 저장되게 된다.
게임 기록이 저장되고 출력되는 방식이 3게임 모두 통일되어 있기 때문에 게임 타입 선택 이후 출력하는 부분은 어떤 게임을 선택하건 상관 없이 공통적으로 처리할 수 있다.
그 부분을 직접 코드로 짜보았고 아래가 그 결과물이다.
while(true) {
println("⚾⚾⚾⚾⚾ 기록을 볼 모드를 선택해주세요 ⚾⚾⚾⚾⚾")
println("1. 일반 게임, 2. Easy 모드, 3. Hard 모드, 4. 이전 메뉴")
val gameSelectInput = getNumberInputOneToi(4)
when (gameSelectInput) {
1 -> baseballGame
2 -> baseballGameEasy
3 -> baseballGameHard
else -> break
}.let { playGame.printHighScore(it) }
}
여기까지가 정리한 내용이다.
2. 오늘 배운 것
- 야구 게임에 여러 가지 기능을 추가해보는 작업을 해보며 코드를 작성하는 연습을 많이 해볼 수 있었다.
- 문자열을 다루는 알고리즘 문제들을 풀어보고 해설을 보며 익혀보았다.