-
Notifications
You must be signed in to change notification settings - Fork 56
[자동차 경주] 김수대 미션 제출합니다. #52
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
base: main
Are you sure you want to change the base?
Changes from all commits
ce8c73c
05f734d
36f53af
40055bc
7891178
9981224
2f77b20
721c898
5ed9cfc
8b7878d
cd7ad15
ea8e372
49d6ab2
50d2d04
83a4636
27d7447
40a6a4c
72871a6
a7b1a95
ae67b87
82112e6
cb5c76a
f5b722e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,126 @@ | ||
| # kotlin-racingcar-precourse | ||
| ## 기능 요구 사항 리스트 | ||
|
|
||
| ### 입력 | ||
|
|
||
| 자동차 이름 입력 | ||
|
|
||
| - 쉼표로 구분 | ||
| - 5자 이하만 가능 | ||
|
|
||
| 시도할 횟수 입력 | ||
|
|
||
| - 정수로 입력 | ||
|
|
||
| ### 처리 | ||
|
|
||
| 0에서 9까지 무작위 값을 구한 후 4 이상일 경우만 전진 | ||
|
|
||
| 4 미만이면 정지 후 다음 턴 | ||
|
|
||
| 우승자는 한 명 이상 가능 | ||
|
|
||
| 우승자가 나올때 까지 진행 | ||
|
|
||
| ### 출력 | ||
|
|
||
| 차수별 실행 결과 출력 | ||
|
|
||
| -를 이용하여 진척도 나타내기 | ||
|
|
||
| 최종 우승자 출력 | ||
|
|
||
| - 공동인 경우 , 쉼표를 이용하여 구분 | ||
|
|
||
| ### 예외발생 | ||
|
|
||
| - 사용자의 입력값이 잘못될 경우 IllegalArgumentException 예외 발생 후 종료 | ||
| 1. 이름이 5자 초과일 경우 | ||
| 2. 이름을 구분 지을때 쉼표가 아닌 다른 문자가 적힐 경우 | ||
| 3. 몇 번 이동할지 정할때 숫자가 0이하일 경우 | ||
| 4. 아무런 입력이 없을 때 | ||
| 5. 이름이 알파벳이 아닐때 | ||
|
|
||
| ## 작성한 코드의 사용 이유 | ||
|
|
||
| ### 1. 자동차 이름 입력 및 저장 | ||
|
|
||
| ```kotlin | ||
| fun carNameSetting() { | ||
| val carNames = processCarNaming() // input().split(",") | ||
| validateNamesLength(carNames) | ||
| validateNamesType(carNames) | ||
|
|
||
| for (carName in carNames) { | ||
| cars.add(carName) | ||
| carRunStatus[carName] = 0 | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| - `validate`~ 함수들은 자동차 이름을 설정할 때 예외처리를 위함 | ||
|
|
||
| ### 2. 자동차 이름 유효성 검사 | ||
|
|
||
| ```kotlin | ||
| fun validateNamesLength(names: List<String>) { | ||
| if (!names.all { it.length <= 5 }) { | ||
| throw IllegalArgumentException("이름은 5자 이하여야 합니다.") | ||
| } | ||
| if (!names.all { it.isNotEmpty() }) { | ||
| throw IllegalArgumentException("이름을 비워둘 수 없습니다.") | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| - `names.all` 코틀린의 컬렉션 함수 `all`을 사용한 이유는 모든 요소가 조건`it.length <= 5`을 만족하는지 검사 | ||
| - `it.isNotEmpty()`는 비어있는 문자열이 있는지 검사하기 위해 사용 | ||
|
|
||
| ### 3. 시도 횟수 입력 및 검증 | ||
|
|
||
| ```kotlin | ||
| fun timeSetting() { | ||
| val inputTime = input() | ||
| movingTime = inputTime.toIntOrNull() | ||
| ?: throw IllegalArgumentException("숫자만 가능합니다.") | ||
| if (movingTime <= 0) { | ||
| throw IllegalArgumentException("횟수는 1 이상이어야 합니다.") | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| - `inputTime.toIntOrNull()` `toIntOrNull()`은 숫자가 아니면 null 을 반환하여 엘비스 연산자와 함께 예외를 발생 | ||
| - `?:` 엘비스 연산자. `toIntOrNull()`이 null 을 반환할 경우 `throw` 구문이 실행되어 예외를 발생 | ||
|
|
||
| ### 4. 핵심 레이싱 로직 (전진 및 상태 누적) | ||
|
|
||
| ```kotlin | ||
| fun judgeFourMoreRole(car: String) { | ||
| val randomNumber = pickRandomNumber() | ||
| if (randomNumber >= 4) { | ||
| val currentStatus = carRunStatus.getOrDefault(car, 0) | ||
| carRunStatus[car] = currentStatus + 1 | ||
| } | ||
| } | ||
|
|
||
| ``` | ||
|
|
||
| - `getOrDefault` 는 맵에 Key가 없더라도 null 대신 기본값 0을 반환 받음 | ||
| - `carRunStatus[car] = currentStatus + 1` 자동차의 bar 전진 상태가 누적 | ||
|
|
||
| ### 5. 최종 우승자 판별 | ||
|
|
||
| ```kotlin | ||
| fun winner() { | ||
| val maxBar = carRunStatus.values.maxOrNull() ?: 0 | ||
| val winners = carRunStatus | ||
| .filter { it.value == maxBar } | ||
| .keys | ||
|
|
||
| println("최종 우승자 : ${winners.joinToString(", ")}") | ||
| } | ||
|
|
||
| ``` | ||
|
|
||
| - `maxOrNull()` 맵이 비어있을 경우 `null`을 반환할 수 있어`?: 0`으로 처리 | ||
| - `.filter { it.value == maxBar }filter`를 사용해 맵의 모든 항목 중 최대거리와 같은 경우 반환 |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,125 @@ | ||||||||||||||||||||||||||||||||||||||||||
| package racingcar | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| import camp.nextstep.edu.missionutils.Console | ||||||||||||||||||||||||||||||||||||||||||
| import camp.nextstep.edu.missionutils.Randoms | ||||||||||||||||||||||||||||||||||||||||||
| import kotlin.collections.all | ||||||||||||||||||||||||||||||||||||||||||
| import kotlin.text.all | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| var cars = mutableListOf<String>() | ||||||||||||||||||||||||||||||||||||||||||
| var movingTime: Int = 0 | ||||||||||||||||||||||||||||||||||||||||||
| var carRunStatus = mutableMapOf<String, Int>() | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| fun main() { | ||||||||||||||||||||||||||||||||||||||||||
| // TODO: 프로그램 구현 | ||||||||||||||||||||||||||||||||||||||||||
| game() | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| fun game() { | ||||||||||||||||||||||||||||||||||||||||||
| println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)") | ||||||||||||||||||||||||||||||||||||||||||
| carNameSetting() | ||||||||||||||||||||||||||||||||||||||||||
| println("시도할 횟수는 몇 회인가요?") | ||||||||||||||||||||||||||||||||||||||||||
| timeSetting() | ||||||||||||||||||||||||||||||||||||||||||
| println() | ||||||||||||||||||||||||||||||||||||||||||
| println("실행 결과") | ||||||||||||||||||||||||||||||||||||||||||
| stepControl() | ||||||||||||||||||||||||||||||||||||||||||
| winner() | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| fun input(): String { | ||||||||||||||||||||||||||||||||||||||||||
| return Console.readLine() | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| fun processCarNaming(): List<String> { | ||||||||||||||||||||||||||||||||||||||||||
| return input().split(",") | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| fun validateNamesLength(names: List<String>) { | ||||||||||||||||||||||||||||||||||||||||||
| if (!names.all { it.length <= 5 }) { | ||||||||||||||||||||||||||||||||||||||||||
| throw IllegalArgumentException("이름은 5자 이하여야 합니다.") | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| if (!names.all { it.isNotEmpty() }) { | ||||||||||||||||||||||||||||||||||||||||||
| throw IllegalArgumentException("이름을 비워둘 수 없습니다.") | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| fun validateNamesType(names: List<String>) { | ||||||||||||||||||||||||||||||||||||||||||
| for (name in names) { | ||||||||||||||||||||||||||||||||||||||||||
| validateNamesCharType(name) | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| private fun validateNamesCharType(name: String) { | ||||||||||||||||||||||||||||||||||||||||||
| if (!name.all { it.isLetter() }) { | ||||||||||||||||||||||||||||||||||||||||||
| throw IllegalArgumentException("이름은 알파벳만 입력 가능합니다.") | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+51
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 함수에는 private이 있는데 다른 함수에는 private이 없네요. 다른 함수에도 붙이시거나 이 함수에서 private을 제거하면 더 통일성 있는 코드가 될 거 같습니다~
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 맞아요 아직 private 활용도가 이해가 되지 않아, 몇 번 시도를 해봤지만 앞으로는 더 자세히 써야겠다 다짐하게 됩니다. |
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| fun pickRandomNumber(): Int { | ||||||||||||||||||||||||||||||||||||||||||
| return Randoms.pickNumberInRange(0, 9) | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| fun timeSetting() { | ||||||||||||||||||||||||||||||||||||||||||
| val inputTime = input() | ||||||||||||||||||||||||||||||||||||||||||
| movingTime = inputTime.toIntOrNull() | ||||||||||||||||||||||||||||||||||||||||||
| ?: throw IllegalArgumentException("숫자만 가능합니다.") | ||||||||||||||||||||||||||||||||||||||||||
| if (movingTime <= 0) { | ||||||||||||||||||||||||||||||||||||||||||
| throw IllegalArgumentException("횟수는 1 이상이어야 합니다.") | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| fun stepControl() { | ||||||||||||||||||||||||||||||||||||||||||
| for (i in 1..movingTime) { | ||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for문 대신 repeat을 활용할 수도 있을 것 같습니다!
Suggested change
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 우와 간편하네요. repeat...정말 좋은 거 같아요!! |
||||||||||||||||||||||||||||||||||||||||||
| racingFourMoreRole() | ||||||||||||||||||||||||||||||||||||||||||
| currentResult() | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| fun racingFourMoreRole() { | ||||||||||||||||||||||||||||||||||||||||||
| for (carName in cars) { | ||||||||||||||||||||||||||||||||||||||||||
| judgeFourMoreRole(carName) | ||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. judgeFourMoreRole이라는 함수가 내용이 그리 길지 않아서 따로 함수로 빼기보다 racingFourMoreRole에서 그대로 쓰는 것도 괜찮았을 거 같습니다~
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 일단 리뷰 달아주셔서 감사하다는 말씀 드리고 싶어요! |
||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| fun judgeFourMoreRole(car: String) { | ||||||||||||||||||||||||||||||||||||||||||
| val randomNumber = pickRandomNumber() | ||||||||||||||||||||||||||||||||||||||||||
| if (randomNumber >= 4) { | ||||||||||||||||||||||||||||||||||||||||||
| val currentStatus = carRunStatus.getOrDefault(car, 0) | ||||||||||||||||||||||||||||||||||||||||||
| carRunStatus[car] = currentStatus + 1 | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| fun currentResult() { | ||||||||||||||||||||||||||||||||||||||||||
| for (carName in cars) { | ||||||||||||||||||||||||||||||||||||||||||
| print("$carName : ") | ||||||||||||||||||||||||||||||||||||||||||
| barCreator(carRunStatus[carName]) | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| println() | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| fun barCreator(time: Int?) { | ||||||||||||||||||||||||||||||||||||||||||
| val bar = "-" | ||||||||||||||||||||||||||||||||||||||||||
| for (i in 1..time!!) { | ||||||||||||||||||||||||||||||||||||||||||
| print(bar) | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| println() | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+91
to
+105
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 전진 횟수만큼 "-"를 출력할 때
Suggested change
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 일단 리뷰 달아주셔서 정말 감사드린다는 말씀드리고 싶습니다!! 정말 감사합니다!!
Comment on lines
+99
to
+105
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. carNameSetting에서 이미 carNames에 대해 기본값으로 0을 넣어주고 있어서 time의 nullable하지 않을 것 같아요. |
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| fun carNameSetting() { | ||||||||||||||||||||||||||||||||||||||||||
| val carNames = processCarNaming() | ||||||||||||||||||||||||||||||||||||||||||
| validateNamesLength(carNames) | ||||||||||||||||||||||||||||||||||||||||||
| validateNamesType(carNames) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| for (carName in carNames) { | ||||||||||||||||||||||||||||||||||||||||||
| cars.add(carName) | ||||||||||||||||||||||||||||||||||||||||||
| carRunStatus[carName] = 0 | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| fun winner() { | ||||||||||||||||||||||||||||||||||||||||||
| val maxBar = carRunStatus.values.maxOrNull() ?: 0 | ||||||||||||||||||||||||||||||||||||||||||
| val winners = carRunStatus | ||||||||||||||||||||||||||||||||||||||||||
| .filter { it.value == maxBar } | ||||||||||||||||||||||||||||||||||||||||||
| .keys | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| println("최종 우승자 : ${winners.joinToString(", ")}") | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
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.
!names.all { it.isNotEmpty() }이 이중 부정(!+isNotEmpty) 형태라코드를 읽을 때 _"이름이 모두 빈 값이 아닌게 아니라면~"_으로 해석되어 가독성이 조금 떨어질 수 있을 것 같습니다.
동작은 동일하지만
names.any { it.isEmpty() }를 사용하면"이름이 하나라도 빈 값이라면~" 으로 해석되어 의도가 더 명확하게 드러날 것 같습니다!
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.
아...이런 부분은 생각도 못했어요. all, any 등 가독성에 용이한 활용에 맞게 써야한다고 배워갑니다. 감사합니다!