From 35ac2ae0643e8c247f24072273582504f820f4c0 Mon Sep 17 00:00:00 2001 From: songusika Date: Thu, 11 May 2023 19:55:32 +0900 Subject: [PATCH 1/7] =?UTF-8?q?docs:=20=EA=B8=B0=EB=8A=A5=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/README.md b/README.md index b4b1377..73852e2 100644 --- a/README.md +++ b/README.md @@ -55,3 +55,31 @@ spill 🟩🟩⬜🟩🟩 🟩🟩🟩🟩🟩 ``` +## πŸ“„κΈ°λŠ₯ λͺ©λ‘ + +### 도메인 +#### κ²Œμž„ + - [ ] μ‹œλ„ 회수λ₯Ό λ‹€ μ‚¬μš©ν•˜λ©΄ κ²Œμž„μ€ νŒ¨λ°°ν•œλ‹€. + - [ ] κ²Œμž„ ν•œ ν„΄ λ‹Ή κ²°κ³Όλ₯Ό 계산 및 μ €μž₯ν•œλ‹€ + - [ ] μ΄ˆλ‘μƒ‰μ€ μœ„μΉ˜μ™€ 단어가 λ§žμ€ 경우 + - [ ] λ…Έλž€μƒ‰μ€ μž…λ ₯ν•œ λ¬Έμžκ°€ 정닡에 μžˆμ§€λ§Œ μœ„μΉ˜λŠ” ν‹€λ¦° 경우 + - [ ] νšŒμƒ‰μ€ λ‹€ ν‹€λ¦° 경우 + +#### 문제 + - [ ] words.txt μ•ˆμ— μ‘΄μž¬ν•΄μ•Όν•œλ‹€. + - [ ] λ¬Έμ œλŠ” 맀일 λ°”λ€Œμ–΄μ•Όν•œλ‹€. + - [ ] ((ν˜„μž¬ λ‚ μ§œ - 2021λ…„ 6μ›” 19일) % λ°°μ—΄μ˜ 크기) 번째의 단어 + +#### μ‹œλ„ 회수 + - [ ] μ‹œλ„ νšŒμˆ˜λŠ” 총 6번 μ£Όμ›Œμ§„λ‹€. + - [ ] λͺ‡ 번 μ‹œλ„ν•˜μ˜€λŠ”μ§€λ₯Ό μ €μž₯ν•œλ‹€. + +### μž…λ ₯ + - [ ] 정닡을 μž…λ ₯λ°›λŠ”λ‹€. + - [ ] μ‚¬μš©μžκ°€ 5κΈ€μžλ₯Ό μž…λ ₯ν•˜μ§€ μ•ŠμœΌλ©΄ μž¬μž…λ ₯을 μš”κ΅¬ν•œλ‹€. + - [ ] μ‚¬μš©μžκ°€ μ˜μ–΄ μ΄μ™Έμ˜ κΈ€μžλ₯Ό μž…λ ₯ν•˜λ©΄ μž¬μž…λ ₯을 μš”κ΅¬ν•œλ‹€. + +### 좜λ ₯ + - [ ] νƒ€μΌμ—λŠ” μ΄ˆλ‘μƒ‰/λ…Έλž€μƒ‰/νšŒμƒ‰μ΄ μ‘΄μž¬ν•œλ‹€ + - [ ] μ΅œμ’… μ‹œλ„ 횟수λ₯Ό 좜λ ₯ν•œλ‹€. + From c63730a5d1f1818572b6e7e730a356605600685e Mon Sep 17 00:00:00 2001 From: songusika Date: Thu, 11 May 2023 20:35:22 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat:=20=EC=8B=9C=EB=8F=84=ED=9A=8C?= =?UTF-8?q?=EC=88=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/main/kotlin/domain/TryCount.kt | 20 ++++++++++++ src/test/kotlin/domain/TryCountTest.kt | 43 ++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/domain/TryCount.kt create mode 100644 src/test/kotlin/domain/TryCountTest.kt diff --git a/README.md b/README.md index 73852e2..06980b0 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ spill #### μ‹œλ„ 회수 - [ ] μ‹œλ„ νšŒμˆ˜λŠ” 총 6번 μ£Όμ›Œμ§„λ‹€. - - [ ] λͺ‡ 번 μ‹œλ„ν•˜μ˜€λŠ”μ§€λ₯Ό μ €μž₯ν•œλ‹€. + - [x] λͺ‡ 번 μ‹œλ„ν•˜μ˜€λŠ”μ§€λ₯Ό μ €μž₯ν•œλ‹€. ### μž…λ ₯ - [ ] 정닡을 μž…λ ₯λ°›λŠ”λ‹€. diff --git a/src/main/kotlin/domain/TryCount.kt b/src/main/kotlin/domain/TryCount.kt new file mode 100644 index 0000000..c198308 --- /dev/null +++ b/src/main/kotlin/domain/TryCount.kt @@ -0,0 +1,20 @@ +package domain + +data class TryCount(val tryCount: Int = 0) { + + init { + validatePositive() + } + + private fun validatePositive() { + require(tryCount >= 0) { ERROR_MESSAGE } + } + + fun plus():TryCount { + return TryCount(tryCount.inc()) + } + + companion object { + private const val ERROR_MESSAGE ="μ–‘μˆ˜λ₯Ό μž…λ ₯ν•΄μ£Όμ„Έμš”" + } +} diff --git a/src/test/kotlin/domain/TryCountTest.kt b/src/test/kotlin/domain/TryCountTest.kt new file mode 100644 index 0000000..440de33 --- /dev/null +++ b/src/test/kotlin/domain/TryCountTest.kt @@ -0,0 +1,43 @@ +package domain + +import org.assertj.core.api.Assertions.* +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.DisplayNameGeneration +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores +import org.junit.jupiter.api.Test + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores::class) +class TryCountTest { + + @Test + fun μ‹œλ„νšŒμˆ˜λŠ”_μ–‘μˆ˜λ₯Ό_λ°›μ•„_μƒμ„±λœλ‹€() { + // given + val count: Int = 10 + + // expect + assertDoesNotThrow { TryCount(count) } + } + + @Test + fun μ‹œλ„νšŒμˆ˜λŠ”_음수λ₯Ό_λ°›μœΌλ©΄_μ˜ˆμ™Έκ°€_λ°œμƒν•œλ‹€() { + // given + val count: Int = -1 + + // expect + assertThrows(IllegalArgumentException::class.java) { TryCount(count) } + } + + @Test + fun μ‹œλ„νšŒμˆ˜λŠ”_올라갈_수_μžˆλ‹€() { + // given + val tryCount1 = TryCount() + val tryCount2 = TryCount(1) + + // when + val tryCount3: TryCount = tryCount1.plus() + + // then + assertEquals(tryCount2, tryCount3) + } +} From cb4954730dcf15b9afa7791be54c946970d7cdbc Mon Sep 17 00:00:00 2001 From: songusika Date: Thu, 11 May 2023 22:07:17 +0900 Subject: [PATCH 3/7] =?UTF-8?q?feat:=20=EB=AC=B8=EC=A0=9C=EB=A5=BC=20?= =?UTF-8?q?=EA=B0=80=EC=A0=B8=EC=98=A4=EB=8A=94=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=EA=B3=BC=20=EC=A0=95=EB=8B=B5=EC=9D=84=20=EB=A7=9E=EC=B6=94?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 13 ++++-- src/main/kotlin/domain/Question.kt | 23 +++++++++++ src/main/kotlin/domain/TodayWordDictionary.kt | 26 ++++++++++++ src/main/kotlin/domain/WordDictionary.kt | 8 ++++ src/test/kotlin/domain/FixedWordDictionary.kt | 13 ++++++ src/test/kotlin/domain/QuestionTest.kt | 41 +++++++++++++++++++ 6 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/domain/Question.kt create mode 100644 src/main/kotlin/domain/TodayWordDictionary.kt create mode 100644 src/main/kotlin/domain/WordDictionary.kt create mode 100644 src/test/kotlin/domain/FixedWordDictionary.kt create mode 100644 src/test/kotlin/domain/QuestionTest.kt diff --git a/README.md b/README.md index 06980b0..810dc76 100644 --- a/README.md +++ b/README.md @@ -66,9 +66,15 @@ spill - [ ] νšŒμƒ‰μ€ λ‹€ ν‹€λ¦° 경우 #### 문제 - - [ ] words.txt μ•ˆμ— μ‘΄μž¬ν•΄μ•Όν•œλ‹€. - - [ ] λ¬Έμ œλŠ” 맀일 λ°”λ€Œμ–΄μ•Όν•œλ‹€. - - [ ] ((ν˜„μž¬ λ‚ μ§œ - 2021λ…„ 6μ›” 19일) % λ°°μ—΄μ˜ 크기) 번째의 단어 + - [x] words.txt μ•ˆμ— μ‘΄μž¬ν•΄μ•Όν•œλ‹€. + - [x] λ¬Έμ œλŠ” 맀일 λ°”λ€Œμ–΄μ•Όν•œλ‹€. + - [x] ((ν˜„μž¬ λ‚ μ§œ - 2021λ…„ 6μ›” 19일) % 단어 μ „μ²΄μ˜ 개수) 번째의 단어 + - [x] 정닡을 λ§žμΆ”λ©΄ true, 틀리면 false λ₯Ό λ°˜ν™˜ν•œλ‹€. + - [ ] 힌트λ₯Ό λ°˜ν™˜ν•œλ‹€ + - [ ] 처음 Green 을 κ²€μ‚¬ν•œλ‹€.(같은 μΈλ±μŠ€μ™€μ˜ 문자 검사) + - [ ] Green이 맞으면 μ •λ‹΅κ³Ό μž…λ ₯μ—μ„œ ν•΄λ‹Ή 값을 빈 걸둜 μΉ˜ν™˜ν•œλ‹€. + - [ ] μˆœνšŒν•˜λ©΄μ„œ λ‚˜λ¨Έμ§€λ₯Ό κ²€μ‚¬ν•œλ‹€. + - [ ] Yelloκ°€ 맞으면 μ •λ‹΅κ³Ό μž…λ ₯μ—μ„œ ν•΄λ‹Ή 값을 빈 걸둜 μΉ˜ν™˜ν•œλ‹€. #### μ‹œλ„ 회수 - [ ] μ‹œλ„ νšŒμˆ˜λŠ” 총 6번 μ£Όμ›Œμ§„λ‹€. @@ -78,6 +84,7 @@ spill - [ ] 정닡을 μž…λ ₯λ°›λŠ”λ‹€. - [ ] μ‚¬μš©μžκ°€ 5κΈ€μžλ₯Ό μž…λ ₯ν•˜μ§€ μ•ŠμœΌλ©΄ μž¬μž…λ ₯을 μš”κ΅¬ν•œλ‹€. - [ ] μ‚¬μš©μžκ°€ μ˜μ–΄ μ΄μ™Έμ˜ κΈ€μžλ₯Ό μž…λ ₯ν•˜λ©΄ μž¬μž…λ ₯을 μš”κ΅¬ν•œλ‹€. + - [ ] word.txt μ•ˆμ— μ‘΄μž¬ν•˜λŠ” 단어λ₯Ό μž…λ ₯ν•˜μ§€ μ•ŠμœΌλ©΄ μž¬μž…λ ₯을 μš”κ΅¬ν•œλ‹€. ### 좜λ ₯ - [ ] νƒ€μΌμ—λŠ” μ΄ˆλ‘μƒ‰/λ…Έλž€μƒ‰/νšŒμƒ‰μ΄ μ‘΄μž¬ν•œλ‹€ diff --git a/src/main/kotlin/domain/Question.kt b/src/main/kotlin/domain/Question.kt new file mode 100644 index 0000000..66a93c4 --- /dev/null +++ b/src/main/kotlin/domain/Question.kt @@ -0,0 +1,23 @@ +package domain + +class Question(private val wordDictionary: WordDictionary) { + + private val questionWord: String = wordDictionary.pickWord() + + fun isAnswer(word: String): Boolean { + validateWord(word) + return checkAnswer(word) + } + + private fun validateWord(word: String) { + require(wordDictionary.contains(word)) { word + NO_SUCH_WORD_MESSAGE } + } + + private fun checkAnswer(word: String): Boolean { + return questionWord.equals(word) + } + + companion object { + private const val NO_SUCH_WORD_MESSAGE = "word.txt λ‚΄μ˜ 단어λ₯Ό μ„ νƒν•΄μ£Όμ„Έμš”" + } +} diff --git a/src/main/kotlin/domain/TodayWordDictionary.kt b/src/main/kotlin/domain/TodayWordDictionary.kt new file mode 100644 index 0000000..9089d28 --- /dev/null +++ b/src/main/kotlin/domain/TodayWordDictionary.kt @@ -0,0 +1,26 @@ +package domain + +import java.io.FileReader +import java.time.LocalDate +import java.time.temporal.ChronoUnit + +class TodayWordDictionary: WordDictionary { + + private val words: List = FileReader(WORD_RESOURCE).readText().split(DELIMITERS) + + override fun pickWord(): String { + val until = ChronoUnit.DAYS.between(TARGET_DATE, LocalDate.now()) + + return words[(until % words.size).toInt()] + } + + override fun contains(target: String): Boolean { + return words.contains(target) + } + + companion object { + private const val WORD_RESOURCE = "src/main/resources/words.txt" + private const val DELIMITERS = "\n" + private val TARGET_DATE = LocalDate.of(2021, 6,19) + } +} diff --git a/src/main/kotlin/domain/WordDictionary.kt b/src/main/kotlin/domain/WordDictionary.kt new file mode 100644 index 0000000..af7b588 --- /dev/null +++ b/src/main/kotlin/domain/WordDictionary.kt @@ -0,0 +1,8 @@ +package domain + +interface WordDictionary { + + fun pickWord(): String + + fun contains(target:String): Boolean +} diff --git a/src/test/kotlin/domain/FixedWordDictionary.kt b/src/test/kotlin/domain/FixedWordDictionary.kt new file mode 100644 index 0000000..d1ceedd --- /dev/null +++ b/src/test/kotlin/domain/FixedWordDictionary.kt @@ -0,0 +1,13 @@ +package domain + +class FixedWordDictionary(val word: String) : WordDictionary { + + override fun pickWord(): String { + return word + } + + override fun contains(target: String): Boolean { + return true + } + +} diff --git a/src/test/kotlin/domain/QuestionTest.kt b/src/test/kotlin/domain/QuestionTest.kt new file mode 100644 index 0000000..2fa5193 --- /dev/null +++ b/src/test/kotlin/domain/QuestionTest.kt @@ -0,0 +1,41 @@ +package domain + +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.DisplayNameGeneration +import org.junit.jupiter.api.DisplayNameGenerator +import org.junit.jupiter.api.Test + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores::class) +class QuestionTest{ + + @Test + fun νƒ€κΉƒνŒŒμΌμ—_μ—†λŠ”_단어λ₯Ό_쀄_μ‹œ_μ˜ˆμ™Έκ°€_λ°œμƒν•œλ‹€(){ + // given + val word = "abcde" + val question = Question(TodayWordDictionary()) + + // expect + assertThrows(IllegalArgumentException::class.java) { question.isAnswer(word) } + } + + @Test + fun 정닡이라면_trueλ₯Ό_λ°˜ν™˜ν•œλ‹€(){ + // given + val word = "abcde" + val question = Question(FixedWordDictionary(word)) + + // expect + assertTrue(question.isAnswer(word)) + } + + @Test + fun 정닡이_μ•„λ‹ˆ_라면_falseλ₯Ό_λ°˜ν™˜ν•œλ‹€(){ + // given + val word = "cigar" + val question = Question(FixedWordDictionary(word)) + + // expect + assertFalse(question.isAnswer("sissy")) + } +} From 75a25a7eeeb23369447cf418a7dc94864cdeba78 Mon Sep 17 00:00:00 2001 From: songusika Date: Fri, 12 May 2023 14:45:13 +0900 Subject: [PATCH 4/7] =?UTF-8?q?feat:=20=EC=A0=95=EB=8B=B5=20=ED=9E=8C?= =?UTF-8?q?=ED=8A=B8=EB=A5=BC=20=EA=B3=84=EC=82=B0=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 13 ++--- src/main/kotlin/domain/Hint.kt | 7 +++ src/main/kotlin/domain/Question.kt | 43 ++++++++++++++- src/main/kotlin/domain/TodayWordDictionary.kt | 4 +- src/main/kotlin/domain/WordDictionary.kt | 2 +- src/test/kotlin/domain/FixedWordDictionary.kt | 1 - src/test/kotlin/domain/QuestionTest.kt | 55 +++++++++++++++++-- 7 files changed, 107 insertions(+), 18 deletions(-) create mode 100644 src/main/kotlin/domain/Hint.kt diff --git a/README.md b/README.md index 810dc76..1c917a7 100644 --- a/README.md +++ b/README.md @@ -61,20 +61,17 @@ spill #### κ²Œμž„ - [ ] μ‹œλ„ 회수λ₯Ό λ‹€ μ‚¬μš©ν•˜λ©΄ κ²Œμž„μ€ νŒ¨λ°°ν•œλ‹€. - [ ] κ²Œμž„ ν•œ ν„΄ λ‹Ή κ²°κ³Όλ₯Ό 계산 및 μ €μž₯ν•œλ‹€ - - [ ] μ΄ˆλ‘μƒ‰μ€ μœ„μΉ˜μ™€ 단어가 λ§žμ€ 경우 - - [ ] λ…Έλž€μƒ‰μ€ μž…λ ₯ν•œ λ¬Έμžκ°€ 정닡에 μžˆμ§€λ§Œ μœ„μΉ˜λŠ” ν‹€λ¦° 경우 - - [ ] νšŒμƒ‰μ€ λ‹€ ν‹€λ¦° 경우 #### 문제 - [x] words.txt μ•ˆμ— μ‘΄μž¬ν•΄μ•Όν•œλ‹€. - [x] λ¬Έμ œλŠ” 맀일 λ°”λ€Œμ–΄μ•Όν•œλ‹€. - [x] ((ν˜„μž¬ λ‚ μ§œ - 2021λ…„ 6μ›” 19일) % 단어 μ „μ²΄μ˜ 개수) 번째의 단어 - [x] 정닡을 λ§žμΆ”λ©΄ true, 틀리면 false λ₯Ό λ°˜ν™˜ν•œλ‹€. - - [ ] 힌트λ₯Ό λ°˜ν™˜ν•œλ‹€ - - [ ] 처음 Green 을 κ²€μ‚¬ν•œλ‹€.(같은 μΈλ±μŠ€μ™€μ˜ 문자 검사) - - [ ] Green이 맞으면 μ •λ‹΅κ³Ό μž…λ ₯μ—μ„œ ν•΄λ‹Ή 값을 빈 걸둜 μΉ˜ν™˜ν•œλ‹€. - - [ ] μˆœνšŒν•˜λ©΄μ„œ λ‚˜λ¨Έμ§€λ₯Ό κ²€μ‚¬ν•œλ‹€. - - [ ] Yelloκ°€ 맞으면 μ •λ‹΅κ³Ό μž…λ ₯μ—μ„œ ν•΄λ‹Ή 값을 빈 걸둜 μΉ˜ν™˜ν•œλ‹€. + - [x] 힌트λ₯Ό λ°˜ν™˜ν•œλ‹€ + - [x] 처음 Green 을 κ²€μ‚¬ν•œλ‹€.(같은 μΈλ±μŠ€μ™€μ˜ 문자 검사) + - [x] Green이 맞으면 μ •λ‹΅κ³Ό μž…λ ₯μ—μ„œ ν•΄λ‹Ή 값을 빈 걸둜 μΉ˜ν™˜ν•œλ‹€. + - [x] μˆœνšŒν•˜λ©΄μ„œ λ‚˜λ¨Έμ§€λ₯Ό κ²€μ‚¬ν•œλ‹€. + - [x] Yelloκ°€ 맞으면 μ •λ‹΅κ³Ό μž…λ ₯μ—μ„œ ν•΄λ‹Ή 값을 빈 걸둜 μΉ˜ν™˜ν•œλ‹€. #### μ‹œλ„ 회수 - [ ] μ‹œλ„ νšŒμˆ˜λŠ” 총 6번 μ£Όμ›Œμ§„λ‹€. diff --git a/src/main/kotlin/domain/Hint.kt b/src/main/kotlin/domain/Hint.kt new file mode 100644 index 0000000..888f041 --- /dev/null +++ b/src/main/kotlin/domain/Hint.kt @@ -0,0 +1,7 @@ +package domain + +enum class Hint { + GRAY, + YELLOW, + GREEN, +} diff --git a/src/main/kotlin/domain/Question.kt b/src/main/kotlin/domain/Question.kt index 66a93c4..2eb1df9 100644 --- a/src/main/kotlin/domain/Question.kt +++ b/src/main/kotlin/domain/Question.kt @@ -14,10 +14,51 @@ class Question(private val wordDictionary: WordDictionary) { } private fun checkAnswer(word: String): Boolean { - return questionWord.equals(word) + return questionWord == word + } + + fun getHint(word: String): List { + validateWord(word) + val hint = MutableList(questionWord.length) { Hint.GRAY }.toMutableList() + + val slicedQuestion: MutableList = questionWord.toMutableList() + val slicedWord: MutableList = word.toMutableList() + + checkGreen(slicedWord, slicedQuestion, hint) + checkYellow(slicedWord, slicedQuestion, hint) + + return hint + } + + private fun checkYellow( + slicedWord: MutableList, + slicedQuestion: MutableList, + hint: MutableList, + ) { + slicedWord.forEachIndexed { index, word -> + if (word != BLANK && slicedQuestion.contains(word)) { + hint[index] = Hint.YELLOW + slicedQuestion[slicedQuestion.indexOf(word)] = BLANK + } + } + } + + private fun checkGreen( + slicedWord: MutableList, + slicedQuestion: MutableList, + hint: MutableList, + ) { + slicedWord.forEachIndexed { index, _ -> + if (slicedQuestion[index] == slicedWord[index]) { + hint[index] = Hint.GREEN + slicedQuestion[index] = BLANK + slicedWord[index] = BLANK + } + } } companion object { private const val NO_SUCH_WORD_MESSAGE = "word.txt λ‚΄μ˜ 단어λ₯Ό μ„ νƒν•΄μ£Όμ„Έμš”" + private const val BLANK = ' ' } } diff --git a/src/main/kotlin/domain/TodayWordDictionary.kt b/src/main/kotlin/domain/TodayWordDictionary.kt index 9089d28..19450b4 100644 --- a/src/main/kotlin/domain/TodayWordDictionary.kt +++ b/src/main/kotlin/domain/TodayWordDictionary.kt @@ -4,7 +4,7 @@ import java.io.FileReader import java.time.LocalDate import java.time.temporal.ChronoUnit -class TodayWordDictionary: WordDictionary { +class TodayWordDictionary : WordDictionary { private val words: List = FileReader(WORD_RESOURCE).readText().split(DELIMITERS) @@ -21,6 +21,6 @@ class TodayWordDictionary: WordDictionary { companion object { private const val WORD_RESOURCE = "src/main/resources/words.txt" private const val DELIMITERS = "\n" - private val TARGET_DATE = LocalDate.of(2021, 6,19) + private val TARGET_DATE = LocalDate.of(2021, 6, 19) } } diff --git a/src/main/kotlin/domain/WordDictionary.kt b/src/main/kotlin/domain/WordDictionary.kt index af7b588..f64946e 100644 --- a/src/main/kotlin/domain/WordDictionary.kt +++ b/src/main/kotlin/domain/WordDictionary.kt @@ -4,5 +4,5 @@ interface WordDictionary { fun pickWord(): String - fun contains(target:String): Boolean + fun contains(target: String): Boolean } diff --git a/src/test/kotlin/domain/FixedWordDictionary.kt b/src/test/kotlin/domain/FixedWordDictionary.kt index d1ceedd..723592d 100644 --- a/src/test/kotlin/domain/FixedWordDictionary.kt +++ b/src/test/kotlin/domain/FixedWordDictionary.kt @@ -9,5 +9,4 @@ class FixedWordDictionary(val word: String) : WordDictionary { override fun contains(target: String): Boolean { return true } - } diff --git a/src/test/kotlin/domain/QuestionTest.kt b/src/test/kotlin/domain/QuestionTest.kt index 2fa5193..98cc67b 100644 --- a/src/test/kotlin/domain/QuestionTest.kt +++ b/src/test/kotlin/domain/QuestionTest.kt @@ -1,16 +1,22 @@ package domain -import org.junit.jupiter.api.Assertions.* +import domain.Hint.GRAY +import domain.Hint.GREEN +import domain.Hint.YELLOW +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.DisplayNameGeneration import org.junit.jupiter.api.DisplayNameGenerator import org.junit.jupiter.api.Test @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores::class) -class QuestionTest{ +class QuestionTest { @Test - fun νƒ€κΉƒνŒŒμΌμ—_μ—†λŠ”_단어λ₯Ό_쀄_μ‹œ_μ˜ˆμ™Έκ°€_λ°œμƒν•œλ‹€(){ + fun νƒ€κΉƒνŒŒμΌμ—_μ—†λŠ”_단어λ₯Ό_쀄_μ‹œ_μ˜ˆμ™Έκ°€_λ°œμƒν•œλ‹€() { // given val word = "abcde" val question = Question(TodayWordDictionary()) @@ -20,7 +26,7 @@ class QuestionTest{ } @Test - fun 정닡이라면_trueλ₯Ό_λ°˜ν™˜ν•œλ‹€(){ + fun 정닡이라면_trueλ₯Ό_λ°˜ν™˜ν•œλ‹€() { // given val word = "abcde" val question = Question(FixedWordDictionary(word)) @@ -30,7 +36,7 @@ class QuestionTest{ } @Test - fun 정닡이_μ•„λ‹ˆ_라면_falseλ₯Ό_λ°˜ν™˜ν•œλ‹€(){ + fun 정닡이_μ•„λ‹ˆ_라면_falseλ₯Ό_λ°˜ν™˜ν•œλ‹€() { // given val word = "cigar" val question = Question(FixedWordDictionary(word)) @@ -38,4 +44,43 @@ class QuestionTest{ // expect assertFalse(question.isAnswer("sissy")) } + + @Test + fun 같은_μžλ¦¬μ—_같은_κΈ€μžμ΄λ©΄_ν•΄λ‹Ή_μžλ¦¬μ—_GREEN_힌트λ₯Ό_μ€€λ‹€() { + // given + val word = "cigar" + val question = Question(FixedWordDictionary(word)) + + // when + val hint: List = question.getHint("cbdef") + + // then + assertThat(hint).containsExactly(GREEN, GRAY, GRAY, GRAY, GRAY) + } + + @Test + fun λ‹€λ₯Έ_μžλ¦¬μ—_같은_κΈ€μžμ΄λ©΄_μž…λ ₯ν•œ_ν•΄λ‹Ή_μžλ¦¬μ—_YELLOW_힌트λ₯Ό_μ€€λ‹€() { + // given + val word = "cigar" + val question = Question(FixedWordDictionary(word)) + + // when + val hint: List = question.getHint("bgbgi") + + // then + assertThat(hint).containsExactly(GRAY, YELLOW, GRAY, GRAY, YELLOW) + } + + @Test + fun GREENκ³Ό_YELLOWκ°€_μ„žμΈ_경우() { + // given + val word = "spill" + val question = Question(FixedWordDictionary(word)) + + // when + val hint: List = question.getHint("hello") + + // then + assertThat(hint).containsExactly(GRAY, GRAY, YELLOW, GREEN, GRAY) + } } From b058b8d30f9b1c7e84922a678174921f2d6bae0c Mon Sep 17 00:00:00 2001 From: songusika Date: Fri, 12 May 2023 14:45:48 +0900 Subject: [PATCH 5/7] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=20=EB=A6=AC?= =?UTF-8?q?=ED=8F=AC=EB=A7=A4=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/domain/TryCount.kt | 4 ++-- src/test/kotlin/domain/TryCountTest.kt | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/domain/TryCount.kt b/src/main/kotlin/domain/TryCount.kt index c198308..c5dab60 100644 --- a/src/main/kotlin/domain/TryCount.kt +++ b/src/main/kotlin/domain/TryCount.kt @@ -10,11 +10,11 @@ data class TryCount(val tryCount: Int = 0) { require(tryCount >= 0) { ERROR_MESSAGE } } - fun plus():TryCount { + fun plus(): TryCount { return TryCount(tryCount.inc()) } companion object { - private const val ERROR_MESSAGE ="μ–‘μˆ˜λ₯Ό μž…λ ₯ν•΄μ£Όμ„Έμš”" + private const val ERROR_MESSAGE = "μ–‘μˆ˜λ₯Ό μž…λ ₯ν•΄μ£Όμ„Έμš”" } } diff --git a/src/test/kotlin/domain/TryCountTest.kt b/src/test/kotlin/domain/TryCountTest.kt index 440de33..2877980 100644 --- a/src/test/kotlin/domain/TryCountTest.kt +++ b/src/test/kotlin/domain/TryCountTest.kt @@ -1,10 +1,11 @@ package domain -import org.assertj.core.api.Assertions.* -import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertThrows import org.junit.jupiter.api.DisplayNameGeneration import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertDoesNotThrow @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(ReplaceUnderscores::class) From 7888976ded865bf89a8d43aa57d7c10aab665dc4 Mon Sep 17 00:00:00 2001 From: songusika Date: Fri, 12 May 2023 16:17:18 +0900 Subject: [PATCH 6/7] =?UTF-8?q?feat:=20=EA=B0=99=EC=9D=80=20=ED=9A=8C?= =?UTF-8?q?=EC=88=98=EC=9D=B8=EC=A7=80=20=EA=B2=80=EC=82=AC=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/domain/TryCount.kt | 4 ++++ src/test/kotlin/domain/TryCountTest.kt | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/main/kotlin/domain/TryCount.kt b/src/main/kotlin/domain/TryCount.kt index c5dab60..bb8f36f 100644 --- a/src/main/kotlin/domain/TryCount.kt +++ b/src/main/kotlin/domain/TryCount.kt @@ -14,6 +14,10 @@ data class TryCount(val tryCount: Int = 0) { return TryCount(tryCount.inc()) } + fun isSame(other: TryCount): Boolean { + return this.tryCount == other.tryCount + } + companion object { private const val ERROR_MESSAGE = "μ–‘μˆ˜λ₯Ό μž…λ ₯ν•΄μ£Όμ„Έμš”" } diff --git a/src/test/kotlin/domain/TryCountTest.kt b/src/test/kotlin/domain/TryCountTest.kt index 2877980..99eb4ee 100644 --- a/src/test/kotlin/domain/TryCountTest.kt +++ b/src/test/kotlin/domain/TryCountTest.kt @@ -41,4 +41,14 @@ class TryCountTest { // then assertEquals(tryCount2, tryCount3) } + + @Test + fun νšŒμˆ˜κ°€_κ°™λ‹€λ©΄_trueλ₯Ό_λ°˜ν™˜ν•œλ‹€() { + // given + val tryCount1 = TryCount(1) + val tryCount2 = TryCount(1) + + // when + assertEquals(true, tryCount1.isSame(tryCount2)) + } } From 5539ee522e29acdfe03748b2ccd866d615b9bc5b Mon Sep 17 00:00:00 2001 From: songusika Date: Fri, 12 May 2023 16:20:08 +0900 Subject: [PATCH 7/7] =?UTF-8?q?feat:=20=EC=9B=8C=EB=93=A4=20=EA=B2=8C?= =?UTF-8?q?=EC=9E=84=20=ED=94=8C=EB=A0=88=EC=9D=B4=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 +++--- src/main/kotlin/WordleApplication.kt | 8 +++ .../kotlin/controller/WordleController.kt | 61 +++++++++++++++++++ src/main/kotlin/view/InputView.kt | 9 +++ src/main/kotlin/view/OutputView.kt | 28 +++++++++ src/main/kotlin/view/utils/HintEmojiMapper.kt | 26 ++++++++ 6 files changed, 141 insertions(+), 9 deletions(-) create mode 100644 src/main/kotlin/WordleApplication.kt create mode 100644 src/main/kotlin/controller/WordleController.kt create mode 100644 src/main/kotlin/view/InputView.kt create mode 100644 src/main/kotlin/view/OutputView.kt create mode 100644 src/main/kotlin/view/utils/HintEmojiMapper.kt diff --git a/README.md b/README.md index 1c917a7..c20b917 100644 --- a/README.md +++ b/README.md @@ -59,8 +59,8 @@ spill ### 도메인 #### κ²Œμž„ - - [ ] μ‹œλ„ 회수λ₯Ό λ‹€ μ‚¬μš©ν•˜λ©΄ κ²Œμž„μ€ νŒ¨λ°°ν•œλ‹€. - - [ ] κ²Œμž„ ν•œ ν„΄ λ‹Ή κ²°κ³Όλ₯Ό 계산 및 μ €μž₯ν•œλ‹€ + - [x] μ‹œλ„ 회수λ₯Ό λ‹€ μ‚¬μš©ν•˜λ©΄ κ²Œμž„μ€ νŒ¨λ°°ν•œλ‹€. + - [x] κ²Œμž„ ν•œ ν„΄ λ‹Ή κ²°κ³Όλ₯Ό 계산 및 μ €μž₯ν•œλ‹€ #### 문제 - [x] words.txt μ•ˆμ— μ‘΄μž¬ν•΄μ•Όν•œλ‹€. @@ -74,16 +74,16 @@ spill - [x] Yelloκ°€ 맞으면 μ •λ‹΅κ³Ό μž…λ ₯μ—μ„œ ν•΄λ‹Ή 값을 빈 걸둜 μΉ˜ν™˜ν•œλ‹€. #### μ‹œλ„ 회수 - - [ ] μ‹œλ„ νšŒμˆ˜λŠ” 총 6번 μ£Όμ›Œμ§„λ‹€. + - [x] μ‹œλ„ νšŒμˆ˜λŠ” 총 6번 μ£Όμ›Œμ§„λ‹€. - [x] λͺ‡ 번 μ‹œλ„ν•˜μ˜€λŠ”μ§€λ₯Ό μ €μž₯ν•œλ‹€. ### μž…λ ₯ - - [ ] 정닡을 μž…λ ₯λ°›λŠ”λ‹€. - - [ ] μ‚¬μš©μžκ°€ 5κΈ€μžλ₯Ό μž…λ ₯ν•˜μ§€ μ•ŠμœΌλ©΄ μž¬μž…λ ₯을 μš”κ΅¬ν•œλ‹€. - - [ ] μ‚¬μš©μžκ°€ μ˜μ–΄ μ΄μ™Έμ˜ κΈ€μžλ₯Ό μž…λ ₯ν•˜λ©΄ μž¬μž…λ ₯을 μš”κ΅¬ν•œλ‹€. - - [ ] word.txt μ•ˆμ— μ‘΄μž¬ν•˜λŠ” 단어λ₯Ό μž…λ ₯ν•˜μ§€ μ•ŠμœΌλ©΄ μž¬μž…λ ₯을 μš”κ΅¬ν•œλ‹€. + - [x] 정닡을 μž…λ ₯λ°›λŠ”λ‹€. + - [x] μ‚¬μš©μžκ°€ 5κΈ€μžλ₯Ό μž…λ ₯ν•˜μ§€ μ•ŠμœΌλ©΄ μž¬μž…λ ₯을 μš”κ΅¬ν•œλ‹€. + - [x] μ‚¬μš©μžκ°€ μ˜μ–΄ μ΄μ™Έμ˜ κΈ€μžλ₯Ό μž…λ ₯ν•˜λ©΄ μž¬μž…λ ₯을 μš”κ΅¬ν•œλ‹€. + - [x] word.txt μ•ˆμ— μ‘΄μž¬ν•˜λŠ” 단어λ₯Ό μž…λ ₯ν•˜μ§€ μ•ŠμœΌλ©΄ μž¬μž…λ ₯을 μš”κ΅¬ν•œλ‹€. ### 좜λ ₯ - - [ ] νƒ€μΌμ—λŠ” μ΄ˆλ‘μƒ‰/λ…Έλž€μƒ‰/νšŒμƒ‰μ΄ μ‘΄μž¬ν•œλ‹€ - - [ ] μ΅œμ’… μ‹œλ„ 횟수λ₯Ό 좜λ ₯ν•œλ‹€. + - [x] νƒ€μΌμ—λŠ” μ΄ˆλ‘μƒ‰/λ…Έλž€μƒ‰/νšŒμƒ‰μ΄ μ‘΄μž¬ν•œλ‹€ + - [x] μ΅œμ’… μ‹œλ„ 횟수λ₯Ό 좜λ ₯ν•œλ‹€. diff --git a/src/main/kotlin/WordleApplication.kt b/src/main/kotlin/WordleApplication.kt new file mode 100644 index 0000000..f2808e1 --- /dev/null +++ b/src/main/kotlin/WordleApplication.kt @@ -0,0 +1,8 @@ +import controller.WordleController +import view.InputView +import view.OutputView + +fun main() { + val wordleController = WordleController(InputView(), OutputView()) + wordleController.play() +} diff --git a/src/main/kotlin/controller/WordleController.kt b/src/main/kotlin/controller/WordleController.kt new file mode 100644 index 0000000..1e42bfd --- /dev/null +++ b/src/main/kotlin/controller/WordleController.kt @@ -0,0 +1,61 @@ +package controller + +import domain.Hint +import domain.Question +import domain.TodayWordDictionary +import domain.TryCount +import view.InputView +import view.OutputView + +class WordleController(val inputView: InputView, val outputView: OutputView) { + + private val wordDictionary = TodayWordDictionary() + private val question: Question = Question(wordDictionary) + private val maxTryCount: TryCount = TryCount(MAX_TRY_COUNT) + private var tryCount: TryCount = TryCount() + + fun play() { + val result: MutableList> = mutableListOf() + outputView.printWelcomeMessage() + + while (isGameNotEnd()) { + val userAnswer: String = readAnswer() + val hint: List = question.getHint(userAnswer) + + result.add(hint) + tryCount = tryCount.plus() + + if (question.isAnswer(userAnswer)) { + outputView.printTryCount(tryCount.tryCount, maxTryCount.tryCount) + outputView.printHint(result) + return + } + + outputView.printHint(result) + } + } + + fun readAnswer(): String { + return repeatInput { + val readAnswer = inputView.readAnswer() + require(wordDictionary.contains(readAnswer)) { "word.txt λ‚΄μ˜ 단어λ₯Ό μ„ νƒν•΄μ£Όμ„Έμš”" } + readAnswer + } + } + + fun isGameNotEnd(): Boolean { + return !maxTryCount.isSame(tryCount) + } + + private fun repeatInput(input: () -> T): T { + return runCatching { input() } + .getOrElse { e -> + outputView.printErrorMessage(e.message) + repeatInput(input) + } + } + + companion object { + private const val MAX_TRY_COUNT = 6 + } +} diff --git a/src/main/kotlin/view/InputView.kt b/src/main/kotlin/view/InputView.kt new file mode 100644 index 0000000..a6d7b65 --- /dev/null +++ b/src/main/kotlin/view/InputView.kt @@ -0,0 +1,9 @@ +package view + +class InputView { + + fun readAnswer(): String { + println("정닡을 μž…λ ₯ν•΄ μ£Όμ„Έμš”.") + return readlnOrNull() ?: throw IllegalArgumentException("정닡을 μž…λ ₯ν•΄ μ£Όμ„Έμš”.") + } +} diff --git a/src/main/kotlin/view/OutputView.kt b/src/main/kotlin/view/OutputView.kt new file mode 100644 index 0000000..3802c35 --- /dev/null +++ b/src/main/kotlin/view/OutputView.kt @@ -0,0 +1,28 @@ +package view + +import domain.Hint +import view.utils.HintEmojiMapper + +class OutputView { + + fun printWelcomeMessage() { + println( + "WORDLE을 6번 λ§Œμ— 맞좰 λ³΄μ„Έμš”.\n" + + "μ‹œλ„μ˜ κ²°κ³ΌλŠ” νƒ€μΌμ˜ 색 λ³€ν™”λ‘œ λ‚˜νƒ€λ‚©λ‹ˆλ‹€.", + ) + } + + fun printTryCount(userTryCount: Int, maxTryCount: Int) { + println("$userTryCount / $maxTryCount") + } + + fun printHint(allHints: List>) { + for (hints in allHints) { + println(HintEmojiMapper.emojiMapping(hints)) + } + } + + fun printErrorMessage(errorMessage: String?) { + println(errorMessage) + } +} diff --git a/src/main/kotlin/view/utils/HintEmojiMapper.kt b/src/main/kotlin/view/utils/HintEmojiMapper.kt new file mode 100644 index 0000000..5949da1 --- /dev/null +++ b/src/main/kotlin/view/utils/HintEmojiMapper.kt @@ -0,0 +1,26 @@ +package view.utils + +import domain.Hint + +enum class HintEmojiMapper(val hintEmoji: String, val hint: Hint) { + + GRAY("⬜", Hint.GRAY), + YELLOW("🟨", Hint.YELLOW), + GREEN("🟩", Hint.GREEN), + ; + + companion object { + private const val CANNOT_FOUND_HINT_EMOJI_ERROR_MESSAGE = "ν•΄λ‹Ήν•˜λŠ” 힌트 이λͺ¨μ§€λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€." + + fun emojiMapping(hints: List): String { + return hints.joinToString("") { convertHintEmoji(it) } + } + + private fun convertHintEmoji(hint: Hint): String { + val emojiMapper = HintEmojiMapper.values().find { + it.hint == hint + } ?: throw IllegalArgumentException(CANNOT_FOUND_HINT_EMOJI_ERROR_MESSAGE) + return emojiMapper.hintEmoji + } + } +}