Skip to content
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

[숫자 야구 게임] 짱구 미션 제출합니다. #4

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions .idea/jarRepositories.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/kotlin-study.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/kotlinc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 45 additions & 0 deletions kotlin-baseball/src/main/kotlin/KtInActionPractice/JointKt.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package KtInActionPractice

const val UNIT_LINE_SEPARATOR = "\n"

fun <T> joinToString(collection: Collection<T>,
separator: String,
prefix: String,
postfix: String): String {
val result = StringBuilder(prefix)
for ((index, element) in collection.withIndex()) {
if (index > 0) result.append(separator)
result.append(element)
}

result.append(postfix)
return result.toString()
}

fun String.lastChar(): Char = this.get(length - 1)

var StringBuilder.lastChar: Char
get() = get(length - 1)
set(value: Char) {
this.setCharAt(length - 1, value)
}

fun <T> Collection<T>.joinToString2(
separator: String = ", ",
prefix: String = "",
postfix: String = ""
): String {
val result = StringBuilder(prefix)
for ((index, element) in this.withIndex()) {
if (index > 0) result.append(separator)
result.append(element)
}
result.append(postfix)
return result.toString()
}

fun Collection<String>.joinToString3(
separator: String = ", ",
prefix: String = "",
postfix: String = ""
) = joinToString2(separator, prefix, postfix)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package KtInActionPractice

/*
* 코틀린의 주요 특성
* 코틀린을 도입하면 더 적은 코드로 더 편하게 프로그래머의 목표를 달성할 수 있다.
*
* 정적 타입 지정 언어
* 자바랑 똑같이 타입을 컴파일 시점에 알 수 있음.
* 이로 인해서 오는 장점이 성능, 신뢰성, 유지 보수성, 도구 지원 등이 있다고 함. 개인적으로는 컴파일 안 하는 언어는 왜 쓰는지 모르겠음 ㅋㅋ;
*
* 함수형 프로그래밍과 객체지향 프로그래밍
* 어제 자면서 본 레퍼런스에서 코틀린과 스칼라가 자주 비교됨. 이유는 스칼라가 함수형 프로그래밍 언어이기 때문인 것 같은데 코틀린도 함수형 프로그래밍 언어를 좋아함.
* 함수형 프로그래밍의 장점
* 1. 간결함. 명령형(imperative) 코드에 비해 더 간결하며 우아함.
* 2. 다중 스레드에도 안전함. 불변 데이터 구조를 사용하고 순수 함수를 그 데이터 구조에 적용하면 같은 데이터를 여러 스레드가 변경할 수 없음. 그래서 복잡한 동기화를 적용하지 않아도 됨. (아직 이해 x)
* 3. 테스트하기 쉬움. 자바는 전체 환경을 구성하는 준비 코드가 필요한데 순수 함수는 그런 준비 없이 독립적으로 테스트가 가능하다고 함. 이번 스터디에서 이 부분에 대해 공부해 봐야 할 듯!!
*
* 코틀린의 철학
* 실용성
* 코틀린은 실제 문제를 해결하기 위해 만들어진 언어라고 함. 연구를 하기 위한 언어가 아니고 이미 성공적으로 검증된 해법과 기능에 의존함. 이 부분이 개인적으로 마음에 듬.
*
* 간결성
* 개인적으로 자바에서 쓸모없는 코드가 몇 개 보였음. private final 같은.. 자바는 쓸모 없는 코드 때문에 코드의 양이 엄청 많아짐.
* 이렇게 코드의 양이 많아지면 읽기가 싫어지고 유지보수하기 싫어짐. 이는 공학작문및발표 수업에서 배웠고 매우 동의하는 바임.
* 코드가 간결할수록 내용을 파악하기 더 쉬움. 코틀린은 간결하면서도 의도를 쉽게 파악할 수 있는 구문 구조를 제공함. 이 부분도 개인적으로 마음에 듬.
* 하지만 책을 조금 읽어봤을 때 너무 간결해 오히려 불편함.. 조금 적응하면 나아질 수 있다고 생각함.
*
* 안전성
* 코틀린은 자바보다 NPE에 안전함. 보통 언어가 안전하다면 생산성이 하락하는데 코틀린에서는 ? 한 글자만 추가하면 됨. 굉장히 간결함.
* 또, 타입 캐스팅을 할 때도 극도로 간결함. 자바의 필요 없는 구문들이 거의 제거 됨.
*
* */

class KtInActionPart1 {
}
9 changes: 8 additions & 1 deletion kotlin-baseball/src/main/kotlin/baseball/Application.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
package baseball

import step1.*
import step2.api.BaseballController
import step2.application.BaseballService
import step2.global.BaseballApplication
import step2.view.BaseballView

fun main() {
TODO("프로그램 구현")
BaseballApplication(BaseballView(), BaseballController(BaseballService())).run()
}

52 changes: 52 additions & 0 deletions kotlin-baseball/src/main/kotlin/step1/BaseBallGame.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package step1

import camp.nextstep.edu.missionutils.Randoms

fun announceBaseballGameStartMessage() = println("숫자 야구 게임을 시작합니다.")

fun announceEnterNumbersMessage() = print("숫자를 입력해주세요 : ")

fun announceThreeStrike() = println("3개의 숫자를 모두 맞히셨습니다! 게임 종료")

fun announceBaseballRestartMessage() = println("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.")

fun announceBaseballResult(userInputNumbers: String, randomNumbers: String) = println(calculateBaseballGameResult(userInputNumbers, randomNumbers))
Comment on lines +5 to +13
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

말씀대로 Util 역할을 하는 코틀린 파일에서 최상위 함수로 쓰면 더 좋을 것 같습니다. 지금은 BaseballGame 클래스에 너무 많은 역할이 들어있는 느낌이에용


fun pickRandomNumbers(): String {
val randomNumbers = mutableListOf<Int>()
while (randomNumbers.size < 3) {
val randomNumber = Randoms.pickNumberInRange(1, 9)
if (!randomNumbers.contains(randomNumber)) {
randomNumbers.add(randomNumber)
}
}
return randomNumbers.joinToString(separator = "")
}

fun isUserInputValid(userInputNumbers: String): Boolean = userInputNumbers.length == 3 && userInputNumbers.toIntOrNull() != null
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사용자가 0을 입력한 경우도 Invalid하다고 판단해줘야 하지 않을까요?



private fun calculateBaseballGameResult(userInputNumbers: String, randomNumbers: String): String {
val result = StringBuilder()
var strikeCount = 0
var ballCount = 0

for (i in randomNumbers.indices) {
if (userInputNumbers[i] == randomNumbers[i]) {
strikeCount++
} else if (userInputNumbers[i] in randomNumbers) {
ballCount++
}
}

if (ballCount > 0) {
result.append("${ballCount}볼 ")
}

if (strikeCount > 0) {
result.append("${strikeCount}스트라이크")
}

return if (ballCount == 0 && strikeCount == 0) "낫싱" else result.toString()

}
28 changes: 28 additions & 0 deletions kotlin-baseball/src/main/kotlin/step1/StepOneBaseballGameMain.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package step1

import camp.nextstep.edu.missionutils.Console

fun stepOneRunner() {
announceBaseballGameStartMessage()
var randomNumbers = pickRandomNumbers()

while (true) {
announceEnterNumbersMessage()
val readLine = Console.readLine()
if (!isUserInputValid(readLine)) {
throw IllegalArgumentException()
}

announceBaseballResult(readLine, randomNumbers)
if (readLine.isThreeStrike(readLine, randomNumbers)) {
announceThreeStrike()
announceBaseballRestartMessage()
if (Console.readLine() == "2") break
randomNumbers = pickRandomNumbers()
}
}
}


fun String.isThreeStrike(numbers: String, randomNumbers: String): Boolean =
numbers[0] == randomNumbers[0] && numbers[1] == randomNumbers[1] && numbers[2] == randomNumbers[2]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 간단하게 numbers == randomNumbers로 바꿔도 좋을 것 같아요!

20 changes: 20 additions & 0 deletions kotlin-baseball/src/main/kotlin/step2/api/BaseballController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package step2.api

import step2.application.BaseballService
import step2.dto.BaseballResultDto
import step2.view.BaseballView

class BaseballController(
val baseballService: BaseballService
) {

fun saveRandomNumbers() {
baseballService.createBaseball()
}

fun getBaseballGameResult(userInputNumbers: String): BaseballResultDto =
requireNotNull(userInputNumbers.takeIf { it.length == 3 && it.toIntOrNull() != null }) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 몇몇 선배님들께서 상수,Enum로 관리하시던데 그렇게 바꿔보는 것도 좋을 것 같습니다!! (저도 바꿀 예정..헷)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그리고 required 쓰는거 좋은 것 같습니다!

"유효하지 않은 숫자 또는 문자입니다."
}.let { baseballService.compareRandomNumbers(it) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let 알아갑니다


}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

마지막 줄이 없네용!

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package step2.application

import step2.dto.BaseballResultDto
import step2.entity.RandomNumbers
import step2.global.createUniqueRandomNumbers

class BaseballService {

fun createBaseball() {
val createUniqueRandomNumbers = createUniqueRandomNumbers(3)
RandomNumbers.changeRandomNumbers(createUniqueRandomNumbers)
}
Comment on lines +9 to +12
Copy link
Contributor

@shkisme shkisme Feb 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createBaseball() 메서드를 통해 랜덤한 야구 게임 숫자를 생성해준다는 걸 메서드 명으로 더 표현하면 좋을 것 같습니다~


fun compareRandomNumbers(userInputNumbers: String): BaseballResultDto =
BaseballResultDto(RandomNumbers.countStrike(userInputNumbers), RandomNumbers.countBall(userInputNumbers))

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package step2.dto

data class BaseballResultDto(
val strikeCount: Int,
val ballCount: Int,
)
Comment on lines +3 to +6
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹시 포매터 어떤 거 쓰시나요??
코틀린은 인텔리제이가 자동으로 포매팅을 지원해준다고 알고 있는데
탭 간격이 (우테코 자바 포매터가 적용된건지ㅠㅠ) 자꾸 바뀌네요...😵‍💫 왜이럴까요

14 changes: 14 additions & 0 deletions kotlin-baseball/src/main/kotlin/step2/entity/RandomNumbers.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package step2.entity

object RandomNumbers {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RandomNumbersobject로 선언하셨네요!!
찾아보니 싱글톤을 보장해주는 것 같은데, 아직 잘 모르겠어서 이렇게 정의하신 이유가 궁금해요!

private var randomNumber: String? = null

fun changeRandomNumbers(number: String): String {
this.randomNumber = number
return this.randomNumber!!
}
Comment on lines +6 to +9
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

클래스 내부 상태를 바꾸기보단 불변으로 만들고 매번 새로 만드는게 유지보수하기엔 더 좋을 것 같습니다~

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

randomNumber 을 nullable하게 만드신 이유가 따로 있으신가요??

만약 changeRandomNumbers 이 함수가 호출되지 않았는데 다른 함수가 호출되면 NPE가 발생할 것 같아요!


nullable을 사용하고 싶다면 not-null 보다는 ?: 을 쓰는게 더 안전할 것 같아요..!!

제가 옛날에 뭣모르고 !! 남발하다가 NPE 많이 터졌어서 괜히 신경쓰게 되네요 ㅎㅎ ㅠㅠ


fun countStrike(number: String): Int = number.indices.count { i -> number[i] == randomNumber!![i] }

fun countBall(number: String): Int = number.indices.count { i -> number[i] != randomNumber!![i] && number[i] in randomNumber!! }
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 이거 비교하는 역할을 service나 아니면 baseball controller와 같이 다른 곳에 뒀는데 random numbers클래스 안에 두고 개수를 세는게 더 좋은지 궁금합니다!

service에서 간편하게 비교하기 위해서 사용하셨나요??

12 changes: 12 additions & 0 deletions kotlin-baseball/src/main/kotlin/step2/global/ExtensionUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package step2.global

import camp.nextstep.edu.missionutils.Console
import camp.nextstep.edu.missionutils.Randoms

fun createUniqueRandomNumbers(count: Int): String =
generateSequence { Randoms.pickNumberInRange(1, 9) }
.distinct()
.take(count)
.joinToString("")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 저는 set으로 사용했는데 distinct()를 사용하면 되군요!!
그리고 숫자를 String으로 받으셨군요!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우왕... 굳이네요!! 👍

그런데 @stopmin님이 말씀하신 것 처럼 메서드 명은 createUniqueRandomNumbers()인데 반환형은 String이라서 이 함수를 사용하는 사람이 혼란이 올 수도 있을 것 같습니다~!


fun getThreeUniqueNumbers(): String = Console.readLine() ?: throw IllegalArgumentException()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

함수명은 getThreeUniqueNumbers()인데 함수에서는 그저 Console로 값을 입력받고, 그것이 null이라면 예외를 반환하는 거라서 함수 명과 실제 동작이 다른 것 같아요!

Loading