-
Notifications
You must be signed in to change notification settings - Fork 355
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
Step2 로또 자동 #992
base: sendkite
Are you sure you want to change the base?
Step2 로또 자동 #992
Changes from all commits
07bfad7
38dc8e5
1a3b887
ddd0a8e
d510057
789d055
c40fc18
bb5a273
01355a9
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 |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package calculator | ||
|
||
fun isNumber(number: String): Boolean { | ||
return Regex("[0-9]+").matches(number) | ||
} | ||
|
||
fun findCustomDelimiter(text: String): String { | ||
return Regex("//(.)\n(.*)").find(text)?.groupValues?.get(1) ?: "" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,13 @@ | ||
package calculator | ||
|
||
class StringAddCalculator { | ||
object StringAddCalculator { | ||
fun add(text: String): Int { | ||
if (text.isBlank()) return 0 | ||
if (text.matches(Regex("[0-9]+"))) { | ||
if (isNumber(text)) { | ||
return text.toInt() | ||
} | ||
|
||
val customDelimiterRegex = Regex("//(.)\n(.*)").find(text) | ||
val customDelimiter = customDelimiterRegex?.groupValues?.get(1) ?: "" | ||
val tokens = parseTokens(text, customDelimiter) | ||
|
||
return tokens.sum() | ||
} | ||
|
||
private fun parseTokens(text: String, customDelimiter: String): List<Int> { | ||
require(text.contains("-").not()) { "음수를 입력할 수 없습니다." } | ||
require(text.contains(Regex("[,:${customDelimiter}]"))) { "숫자와 지정된 구분자만 입력할 수 있습니다." } | ||
|
||
if (customDelimiter.isNotBlank()) { | ||
val replacedText = text.replace("//$customDelimiter\n", "") | ||
return replacedText.split(Regex("[,:${customDelimiter}\n]")).map { | ||
require(it.matches(Regex("[0-9]+"))) { "숫자와 지정된 구분자만 입력할 수 있습니다." } | ||
it.toInt() | ||
} | ||
} | ||
|
||
return text.split(Regex("[,:\n]")).map { | ||
require(it.matches(Regex("[0-9]+"))) { "숫자와 지정된 구분자만 입력할 수 있습니다." } | ||
it.toInt() | ||
} | ||
val inputNumbers = StringParser().parseString(text) | ||
return inputNumbers.sum() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package calculator | ||
|
||
class StringParser { | ||
fun parseString(text: String): List<Int> { | ||
|
||
val customDelimiter = findCustomDelimiter(text) | ||
|
||
require(text.contains("-").not()) { "음수를 입력할 수 없습니다." } | ||
require(text.contains(Regex("[,:${customDelimiter}]"))) { "숫자와 지정된 구분자만 입력할 수 있습니다." } | ||
|
||
if (customDelimiter.isNotBlank()) { | ||
val replacedText = text.replace("//$customDelimiter\n", "") | ||
return replacedText.split(Regex("[,:${customDelimiter}\n]")).map { | ||
require(isNumber(it)) { "숫자와 지정된 구분자만 입력할 수 있습니다." } | ||
it.toInt() | ||
} | ||
} | ||
|
||
return text.split(Regex("[,:\n]")).map { | ||
require(isNumber(it)) { "숫자와 지정된 구분자만 입력할 수 있습니다." } | ||
it.toInt() | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package lotto | ||
|
||
class Lotto( | ||
val numbers: List<Int> = generateLottoNumbers(), | ||
Comment on lines
+3
to
+4
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. Int에는 1~45 라는 로또 번호를 제한하기에는 너무 범용적인 자료형이라 생각해요. |
||
var matchCount: Int = 0 | ||
) { | ||
|
||
companion object { | ||
const val PRICE: Int = 1000 | ||
} | ||
|
||
fun match(target: Lotto) { | ||
matchCount = numbers.intersect(target.numbers).count() | ||
} | ||
Comment on lines
+12
to
+14
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. target 과 비교한 후, 이 로또는 matchCount를 그대로 갖고있을 것 같아요. |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package lotto | ||
|
||
class LottoBuyer( | ||
private val money: Int | ||
) { | ||
|
||
fun buyLottoFrom(lottoStore: LottoStore): List<Lotto> { | ||
require(money >= Lotto.PRICE) { "돈이 부족합니다." } | ||
return lottoStore.sell(money) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package lotto | ||
|
||
fun generateLottoNumbers(): List<Int> { | ||
return (1..45).shuffled().take(6).sorted() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package lotto | ||
|
||
class LottoStore { | ||
|
||
fun sell(money: Int): List<Lotto> { | ||
Comment on lines
+3
to
+5
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. LottoStore입장에서는 sell 하는 것이 맞는데, 이 메서드를 사용하는 사람 입장에서는 굉장히 어색할 것으로 예상돼요. |
||
val count = money / Lotto.PRICE | ||
val lottos = mutableListOf<Lotto>() | ||
for (i in 1..count) { | ||
lottos.add(Lotto()) | ||
} | ||
return lottos | ||
Comment on lines
+7
to
+11
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. 이런 형태는 코틀린의 확장함수를 활용해보셔도 좋겠어요 :) (1..count).map { } 혹은, List의 fake constructor를 활용해보셔도 좋겟네요 :) List(count) { ... } |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package lotto | ||
|
||
fun main(args: Array<String>) { | ||
println("구매금액을 입력해주세요.") | ||
var inputMoney = readLine()!!.toInt() | ||
|
||
val lottoBuyer = LottoBuyer(inputMoney) | ||
val lottos = lottoBuyer.buyLottoFrom(LottoStore()) | ||
|
||
println(String.format("%s개를 구매했습니다.", lottos.size)) | ||
lottos.forEach { lotto -> | ||
println(lotto.numbers) | ||
} | ||
|
||
println("지난 주 당첨 번호를 입력해주세요.") | ||
val lastWeekWinningNumbers = readLine()!!.split(", ").map { it.toInt() } | ||
|
||
lottos.forEach { lotto -> | ||
lotto.match(Lotto(lastWeekWinningNumbers)) | ||
} | ||
|
||
println("당첨 통계") | ||
println("---------") | ||
println("3개 일치 (5000원) - ${lottos.count { it.matchCount == 3 }}개") | ||
println("4개 일치 (50000원) - ${lottos.count { it.matchCount == 4 }}개") | ||
println("5개 일치 (1500000원) - ${lottos.count { it.matchCount == 5 }}개") | ||
println("6개 일치 (2000000000원) - ${lottos.count { it.matchCount == 6 }}개") | ||
println("총 수익률은 ${lottos.size * Lotto.PRICE / inputMoney}입니다.") | ||
Comment on lines
+24
to
+28
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. 로또 등수에 대해 도메인 객체로 표현해보는 건 어떨까요? |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package lotto | ||
|
||
import io.kotest.matchers.collections.haveSize | ||
import io.kotest.matchers.should | ||
import io.kotest.matchers.shouldBe | ||
import org.junit.jupiter.api.Test | ||
import org.junit.jupiter.params.ParameterizedTest | ||
import org.junit.jupiter.params.provider.CsvSource | ||
|
||
class LottoStoreTest { | ||
|
||
@ParameterizedTest | ||
@CsvSource(value = ["1000,1", "2000,2", "3000,3"]) | ||
fun `로또를 여라장 구매한다`(money: Int, expected: Int) { | ||
val lotto = LottoStore().sell(money) | ||
lotto should haveSize(expected) | ||
Comment on lines
+13
to
+16
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. 몇 장을 샀는지 검증하는 것은 테스트에 큰 의미를 가지지 못한다고 생각해요. |
||
} | ||
|
||
@Test | ||
fun `로또를 구매할 돈이 부족하면 예외가 발생한다`() { | ||
val lottoStore = LottoStore() | ||
val money = 900 | ||
val lotto = lottoStore.sell(money) | ||
lotto shouldBe emptyList() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package lotto | ||
|
||
import io.kotest.matchers.collections.shouldBeOneOf | ||
import io.kotest.matchers.ints.shouldBeInRange | ||
import io.kotest.matchers.shouldBe | ||
import org.junit.jupiter.api.Test | ||
|
||
class LottoTest { | ||
|
||
@Test | ||
fun `로또는 6자리 숫자를 가진다`() { | ||
val lotto = Lotto() | ||
lotto.numbers.size shouldBe 6 | ||
} | ||
|
||
@Test | ||
fun `로또는 1부터 45까지의 숫자를 가진다`() { | ||
val lotto = Lotto() | ||
lotto.numbers.forEach { | ||
it.shouldBeInRange(1..45) | ||
} | ||
} | ||
|
||
@Test | ||
fun `로또는 중복되지 않는 숫자를 가진다`() { | ||
val lotto = Lotto() | ||
lotto.numbers.toSet().size shouldBe 6 | ||
} | ||
|
||
@Test | ||
fun `로또는 숫자는 오름차순 정렬`() { | ||
val lotto = Lotto() | ||
lotto.numbers shouldBe lotto.numbers.sorted() | ||
} | ||
|
||
@Test | ||
fun `로또 숫자는 중복되지 않는다`() { | ||
val lotto = Lotto() | ||
lotto.numbers.forEach { | ||
it shouldBeOneOf lotto.numbers | ||
} | ||
} | ||
|
||
@Test | ||
fun `test lotto matching`() { | ||
|
||
val targetLottoNumbers = listOf(1, 2, 3, 4, 5, 6) | ||
val targetLotto = Lotto(targetLottoNumbers) | ||
|
||
val countAndLotto = listOf( | ||
Pair(1, listOf(1, 7, 8, 9, 10, 11)), | ||
Pair(2, listOf(1, 2, 8, 9, 10, 11)), | ||
Pair(3, listOf(1, 2, 3, 9, 10, 11)), | ||
Pair(4, listOf(1, 2, 3, 4, 10, 11)), | ||
Pair(5, listOf(1, 2, 3, 4, 5, 11)), | ||
Pair(6, listOf(1, 2, 3, 4, 5, 6)) | ||
) | ||
|
||
|
||
for ((expectedMatchCount, lottoNumbers) in countAndLotto) { | ||
|
||
val lotto = Lotto(lottoNumbers) | ||
targetLotto.match(lotto) | ||
|
||
val actualMatchCount = targetLotto.matchCount | ||
actualMatchCount shouldBe expectedMatchCount | ||
} | ||
} | ||
Comment on lines
+44
to
+68
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. 이런 종류의 테스트는 |
||
} |
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.
#981 (comment)
여전히 함수를 부를 때마다 Regex 객체를 생성하고 있어요!
Regex를 상수로 분리해보는 건 어떨까요?
또, 해당 메서드들은 이 역할을 가져야할 객체에게 위치시켜보는 건 어떨까요?