From 38e9dde915c16bd6243b4a54da5a50b1c0ebf150 Mon Sep 17 00:00:00 2001 From: kth990303 Date: Tue, 10 May 2022 15:38:57 +0900 Subject: [PATCH 01/25] =?UTF-8?q?docs:=20=EA=B8=B0=EB=8A=A5=20=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=EC=82=AC=ED=95=AD=20=EB=AA=A9=EB=A1=9D=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 docs/README.md diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..582e856 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,21 @@ +## 기능 요구사항 +### 입출력 + - [ ] 게임 방법 안내를 출력한다. + - [ ] `예측 단어`를 입력받는다. + - [ ] `예측 단어`가 유효한 단어가 아니면 안내 문구를 출력한 후 재입력받는다. + - [ ] 최대 6번 입력받을 수 있다. + - [ ] `예측 결과`를 출력한다. + - [ ] 맞는 글자는 초록색🟩, 위치만 틀리면 노란색🟨, 없으면 회색⬜으로 출력한다. + - [ ] 게임을 종료한다. + - [ ] 6번 이내에 `정답 단어`를 맞추지 못할 경우 정답 단어를 출력한 후에 종료한다. + +### 도메인 + - [ ] `유효한 단어`를 불러온다. + - [ ] `유효한 단어`란, `words.txt`에 존재하는 단어이다. + - [ ] `정답 단어`를 불러온다. + - [ ] 정답은 유효한 단어에서 뽑는다. + - [ ] 정답은 매일 바뀌며 `((현재 날짜 - 2021년 6월 19일) % 배열의 크기)` 번째의 단어이다. + - [ ] `예측 단어`와 `정답 단어`를 비교한다. + - [ ] 맞는 글자가 있는지 먼저 비교한다. + - [ ] 위치만 틀린 글자가 있는지 그 다음으로 비교한다. + - [ ] 나머지는 모두 틀린 글자로 간주한다. From 80f767beb9bbfeec4bf3af30af1dab4a0ad06a63 Mon Sep 17 00:00:00 2001 From: kth990303 Date: Tue, 10 May 2022 16:14:26 +0900 Subject: [PATCH 02/25] =?UTF-8?q?feat:=20=EA=B8=80=EC=9E=90=20=ED=95=98?= =?UTF-8?q?=EB=82=98=EB=A5=BC=20=EB=82=98=ED=83=80=EB=82=B4=EB=8A=94=20`Le?= =?UTF-8?q?tter`=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/wordle/domain/Letter.kt | 8 +++++ src/test/kotlin/wordle/domain/LetterTest.kt | 36 +++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/main/kotlin/wordle/domain/Letter.kt create mode 100644 src/test/kotlin/wordle/domain/LetterTest.kt diff --git a/src/main/kotlin/wordle/domain/Letter.kt b/src/main/kotlin/wordle/domain/Letter.kt new file mode 100644 index 0000000..e596236 --- /dev/null +++ b/src/main/kotlin/wordle/domain/Letter.kt @@ -0,0 +1,8 @@ +package wordle.domain + +data class Letter(private val value: Char) { + + init { + require(value.isLowerCase()) { "글자는 알파벳 소문자여야합니다." } + } +} \ No newline at end of file diff --git a/src/test/kotlin/wordle/domain/LetterTest.kt b/src/test/kotlin/wordle/domain/LetterTest.kt new file mode 100644 index 0000000..dfbe3b6 --- /dev/null +++ b/src/test/kotlin/wordle/domain/LetterTest.kt @@ -0,0 +1,36 @@ +package wordle.domain + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +class LetterTest { + + @Test + @DisplayName("Letter는 알파벳 소문자여야한다.") + fun constructWithLowerAlphabet() { + shouldThrow { Letter('1') } + } + + @Test + @DisplayName("글자가 일치하면 true를 반환한다.") + fun isSameLetter_true() { + val letterA = Letter('a') + + val letterB = Letter('a') + + letterA shouldBe letterB + } + + @Test + @DisplayName("글자가 일치하지 않으면 false를 반환한다.") + fun isSameLetter_false() { + val letterA = Letter('a') + + val letterB = Letter('b') + + letterA shouldNotBe letterB + } +} \ No newline at end of file From da55951819ba919dc939a622a45a4ccd9ea536aa Mon Sep 17 00:00:00 2001 From: kth990303 Date: Tue, 10 May 2022 17:05:18 +0900 Subject: [PATCH 03/25] =?UTF-8?q?feat:=20=EC=9C=A0=ED=9A=A8=ED=95=9C=20?= =?UTF-8?q?=EB=8B=A8=EC=96=B4=EB=A5=BC=20=EB=82=98=ED=83=80=EB=82=B4?= =?UTF-8?q?=EB=8A=94=20`Word`=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/wordle/domain/Word.kt | 19 ++++++++++++++++ src/test/kotlin/wordle/domain/WordTest.kt | 27 +++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 src/main/kotlin/wordle/domain/Word.kt create mode 100644 src/test/kotlin/wordle/domain/WordTest.kt diff --git a/src/main/kotlin/wordle/domain/Word.kt b/src/main/kotlin/wordle/domain/Word.kt new file mode 100644 index 0000000..8b6ae82 --- /dev/null +++ b/src/main/kotlin/wordle/domain/Word.kt @@ -0,0 +1,19 @@ +package wordle.domain + +import java.io.File + +class Word(val word: String) { + init { + require(word.length == 5) { "단어는 5글자여야 합니다." } + require(contains(word)) { "유효하지 않은 단어입니다." } + } + + companion object { + val CACHE: List = File("src/main/resources/words.txt") + .readLines() + + fun contains(word: String): Boolean { + return CACHE.contains(word) + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/wordle/domain/WordTest.kt b/src/test/kotlin/wordle/domain/WordTest.kt new file mode 100644 index 0000000..b4ea77a --- /dev/null +++ b/src/test/kotlin/wordle/domain/WordTest.kt @@ -0,0 +1,27 @@ +package wordle.domain + +import io.kotest.assertions.throwables.shouldNotThrow +import io.kotest.assertions.throwables.shouldThrow +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +class WordTest { + + @Test + @DisplayName("단어는 5글자여야 한다.") + fun constructor() { + shouldThrow { (Word("word")) } + } + + @Test + @DisplayName("유효한 단어여야 한다.") + fun constructorWithValidWord() { + shouldNotThrow { Word("cigar") } + } + + @Test + @DisplayName("유효하지 않은 단어일 경우 예외를 발생시킨다.") + fun constructorWithInvalidWord() { + shouldThrow { Word("abcde") } + } +} \ No newline at end of file From 6377d1de5fc99ed983154b93b79c9c1b1c67dc7a Mon Sep 17 00:00:00 2001 From: kth990303 Date: Wed, 11 May 2022 15:07:20 +0900 Subject: [PATCH 04/25] =?UTF-8?q?feat:=20`Word`=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=8B=A4=EB=A5=B8=20=EB=8B=A8=EC=96=B4=EC=99=80=20=EB=B9=84?= =?UTF-8?q?=EA=B5=90=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/domain/Word.kt | 13 +++++++++++-- src/test/kotlin/wordle/domain/WordTest.kt | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/wordle/domain/Word.kt b/src/main/kotlin/wordle/domain/Word.kt index 8b6ae82..0dcc6c0 100644 --- a/src/main/kotlin/wordle/domain/Word.kt +++ b/src/main/kotlin/wordle/domain/Word.kt @@ -2,14 +2,23 @@ package wordle.domain import java.io.File +private const val WORD_LENGTH = 5 + class Word(val word: String) { + var letters: List + init { - require(word.length == 5) { "단어는 5글자여야 합니다." } + require(word.length == WORD_LENGTH) { "단어는 5글자여야 합니다." } require(contains(word)) { "유효하지 않은 단어입니다." } + letters = word.map(::Letter) + } + + fun compareByIndex(other: Word, myIndex: Int, otherIndex: Int = myIndex): Boolean { + return letters[myIndex] == other.letters[otherIndex] } companion object { - val CACHE: List = File("src/main/resources/words.txt") + private val CACHE: List = File("src/main/resources/words.txt") .readLines() fun contains(word: String): Boolean { diff --git a/src/test/kotlin/wordle/domain/WordTest.kt b/src/test/kotlin/wordle/domain/WordTest.kt index b4ea77a..17c2df8 100644 --- a/src/test/kotlin/wordle/domain/WordTest.kt +++ b/src/test/kotlin/wordle/domain/WordTest.kt @@ -2,6 +2,7 @@ package wordle.domain import io.kotest.assertions.throwables.shouldNotThrow import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test @@ -24,4 +25,22 @@ class WordTest { fun constructorWithInvalidWord() { shouldThrow { Word("abcde") } } + + @Test + @DisplayName("다른 단어와 같은 인덱스로 비교한다") + fun compareBySameIndex() { + val wordA = Word("cigar") + val wordB = Word("clear") + + wordA.compareByIndex(wordB, 0) shouldBe true + } + + @Test + @DisplayName("다른 단어와 다른 인덱스로 비교한다") + fun compareByDifferentIndex() { + val wordA = Word("cigar") + val wordB = Word("clear") + + wordA.compareByIndex(wordB, 0, 1) shouldBe false + } } \ No newline at end of file From ba0bba118f773fe659108161fdfc820a8c2e694c Mon Sep 17 00:00:00 2001 From: kth990303 Date: Wed, 11 May 2022 15:07:41 +0900 Subject: [PATCH 05/25] =?UTF-8?q?feat:=20=EC=98=88=EC=B8=A1=20=EB=8B=A8?= =?UTF-8?q?=EC=96=B4=EC=99=80=20=EC=A0=95=EB=8B=B5=20=EB=8B=A8=EC=96=B4=20?= =?UTF-8?q?=EB=B9=84=EA=B5=90=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/wordle/domain/Answer.kt | 39 +++++++++++ src/main/kotlin/wordle/domain/Color.kt | 7 ++ src/test/kotlin/wordle/domain/AnswerTest.kt | 73 +++++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 src/main/kotlin/wordle/domain/Answer.kt create mode 100644 src/main/kotlin/wordle/domain/Color.kt create mode 100644 src/test/kotlin/wordle/domain/AnswerTest.kt diff --git a/src/main/kotlin/wordle/domain/Answer.kt b/src/main/kotlin/wordle/domain/Answer.kt new file mode 100644 index 0000000..8bdb931 --- /dev/null +++ b/src/main/kotlin/wordle/domain/Answer.kt @@ -0,0 +1,39 @@ +package wordle.domain + +private const val RANGE_START = 0 +private const val RANGE_END = 4 + +class Answer(private val word: Word) { + + fun compare(word: Word): List { + val exactIndices = compareExact(word) + val anyIndices = compareAny(word) + return merge(exactIndices, anyIndices) + } + + private fun merge(greenIndices: List, yellowIndices: List): List { + return (RANGE_START..RANGE_END).map { defineColor(it, greenIndices, yellowIndices) } + } + + private fun defineColor(index: Int, greenIndices: List, yellowIndices: List): Color { + if (greenIndices.contains(index)) { + return Color.GREEN + } + if (yellowIndices.contains(index)) { + return Color.YELLOW + } + return Color.GRAY + } + + private fun compareExact(word: Word): List { + return (RANGE_START..RANGE_END).filter { this.word.compareByIndex(word, it) } + } + + private fun compareAny(word: Word): List { + return (RANGE_START..RANGE_END).filter { isAnyMatch(word, it) } + } + + private fun isAnyMatch(word: Word, outerIndex: Int): Boolean { + return (RANGE_START..RANGE_END).any { this.word.compareByIndex(word, it, outerIndex) } + } +} diff --git a/src/main/kotlin/wordle/domain/Color.kt b/src/main/kotlin/wordle/domain/Color.kt new file mode 100644 index 0000000..9a8ff2e --- /dev/null +++ b/src/main/kotlin/wordle/domain/Color.kt @@ -0,0 +1,7 @@ +package wordle.domain + +enum class Color { + GREEN, + YELLOW, + GRAY +} \ No newline at end of file diff --git a/src/test/kotlin/wordle/domain/AnswerTest.kt b/src/test/kotlin/wordle/domain/AnswerTest.kt new file mode 100644 index 0000000..f52eace --- /dev/null +++ b/src/test/kotlin/wordle/domain/AnswerTest.kt @@ -0,0 +1,73 @@ +package wordle.domain + +import io.kotest.matchers.collections.shouldContainAll +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import wordle.domain.Color.* + +class AnswerTest { + + @Test + @DisplayName("완전히 일치하는 글자들과 틀린 글자가 존재하는 예측을 비교한다.") + fun compareWithGreenAndGray() { + val answer = Answer(Word("cigar")) + + answer.compare(Word("clear")) shouldContainAll listOf(GREEN, GRAY, GRAY, GREEN, GREEN) + } + + @Test + @DisplayName("완전히 일치하는 글자들만 존재하는 예측을 비교한다. - 정답인 경우") + fun compareWithAllGreens() { + val answer = Answer(Word("cigar")) + + answer.compare(Word("cigar")) shouldContainAll listOf(GREEN, GREEN, GREEN, GREEN, GREEN) + } + + @Test + @DisplayName("모든 종류의 글자들이 존재하는 예측을 비교한다.") + fun compareWithAllColors() { + val answer = Answer(Word("spill")) + + answer.compare(Word("hello")) shouldContainAll listOf(GRAY, GRAY, YELLOW, GREEN, GRAY) + } + + @Test + @DisplayName("위치만 일치하는 글자와 틀린 글자들이 존재하는 예측을 비교한다.") + fun compareWithYellowAndGray1() { + val answer = Answer(Word("front")) + + answer.compare(Word("totem")) shouldContainAll listOf(YELLOW, YELLOW, GRAY, GRAY, GRAY) + } + + @Test + @DisplayName("위치만 일치하는 글자들과 틀린 글자들이 존재하는 예측을 비교한다.") + fun compareWithYellowAndGray2() { + val answer = Answer(Word("totem")) + + answer.compare(Word("start")) shouldContainAll listOf(GRAY, YELLOW, GRAY, GRAY, YELLOW) + } + + @Test + @DisplayName("전부 틀린 글자들이 존재하는 예측을 비교한다.") + fun compareWithAllGrays() { + val answer = Answer(Word("parry")) + + answer.compare(Word("biome")) shouldContainAll listOf(GRAY, GRAY, GRAY, GRAY, GRAY) + } + + @Test + @DisplayName("전부 위치만 일치하는 글자들이 존재하는 예측을 비교한다.") + fun compareWithAllYellows() { + val answer = Answer(Word("parse")) + + answer.compare(Word("spear")) shouldContainAll listOf(YELLOW, YELLOW, YELLOW, YELLOW, YELLOW) + } + + @Test + @DisplayName("test.") + fun test1() { + val answer = Answer(Word("witch")) + + answer.compare(Word("timid")) shouldContainAll listOf(YELLOW, GREEN, GRAY, GRAY, GRAY) + } +} \ No newline at end of file From 17aca8c274cbf5841f85a55148110fd4c8683331 Mon Sep 17 00:00:00 2001 From: kth990303 Date: Wed, 11 May 2022 15:08:34 +0900 Subject: [PATCH 06/25] =?UTF-8?q?refactor:=20=EC=A0=91=EA=B7=BC=EC=A0=9C?= =?UTF-8?q?=EC=96=B4=EC=9E=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/domain/Word.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/wordle/domain/Word.kt b/src/main/kotlin/wordle/domain/Word.kt index 0dcc6c0..917896e 100644 --- a/src/main/kotlin/wordle/domain/Word.kt +++ b/src/main/kotlin/wordle/domain/Word.kt @@ -4,8 +4,8 @@ import java.io.File private const val WORD_LENGTH = 5 -class Word(val word: String) { - var letters: List +class Word(word: String) { + private var letters: List init { require(word.length == WORD_LENGTH) { "단어는 5글자여야 합니다." } From 35e26f7d150e2934fc6f4f4f6ddff594dbb3ebb9 Mon Sep 17 00:00:00 2001 From: kth990303 Date: Wed, 11 May 2022 15:09:49 +0900 Subject: [PATCH 07/25] =?UTF-8?q?docs:=20=EB=88=84=EB=9D=BD=EB=90=9C=20?= =?UTF-8?q?=EC=9A=94=EA=B5=AC=EC=82=AC=ED=95=AD=20=EC=B2=B4=ED=81=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/README.md b/docs/README.md index 582e856..5b6a015 100644 --- a/docs/README.md +++ b/docs/README.md @@ -10,12 +10,12 @@ - [ ] 6번 이내에 `정답 단어`를 맞추지 못할 경우 정답 단어를 출력한 후에 종료한다. ### 도메인 - - [ ] `유효한 단어`를 불러온다. - - [ ] `유효한 단어`란, `words.txt`에 존재하는 단어이다. + - [x] `유효한 단어`를 불러온다. + - [x] `유효한 단어`란, `words.txt`에 존재하는 단어이다. - [ ] `정답 단어`를 불러온다. - [ ] 정답은 유효한 단어에서 뽑는다. - [ ] 정답은 매일 바뀌며 `((현재 날짜 - 2021년 6월 19일) % 배열의 크기)` 번째의 단어이다. - - [ ] `예측 단어`와 `정답 단어`를 비교한다. - - [ ] 맞는 글자가 있는지 먼저 비교한다. - - [ ] 위치만 틀린 글자가 있는지 그 다음으로 비교한다. - - [ ] 나머지는 모두 틀린 글자로 간주한다. + - [x] `예측 단어`와 `정답 단어`를 비교한다. + - [x] 맞는 글자가 있는지 먼저 비교한다. + - [x] 위치만 틀린 글자가 있는지 그 다음으로 비교한다. + - [x] 나머지는 모두 틀린 글자로 간주한다. From 80cdae757d7293917b38d09a72e5c3a9695c748e Mon Sep 17 00:00:00 2001 From: kth990303 Date: Wed, 11 May 2022 15:50:21 +0900 Subject: [PATCH 08/25] =?UTF-8?q?feat:=20=EC=A0=95=EB=8B=B5=20=EB=8B=A8?= =?UTF-8?q?=EC=96=B4=EB=A5=BC=20=EB=82=A0=EC=A7=9C=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=9D=BC=20=EB=BD=91=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 6 ++-- src/main/kotlin/wordle/domain/Letter.kt | 8 ----- src/main/kotlin/wordle/domain/Word.kt | 12 ++++--- src/main/kotlin/wordle/domain/WordPicker.kt | 16 +++++++++ src/test/kotlin/wordle/domain/LetterTest.kt | 36 ------------------- .../kotlin/wordle/domain/WordPickerTest.kt | 20 +++++++++++ 6 files changed, 46 insertions(+), 52 deletions(-) delete mode 100644 src/main/kotlin/wordle/domain/Letter.kt create mode 100644 src/main/kotlin/wordle/domain/WordPicker.kt delete mode 100644 src/test/kotlin/wordle/domain/LetterTest.kt create mode 100644 src/test/kotlin/wordle/domain/WordPickerTest.kt diff --git a/docs/README.md b/docs/README.md index 5b6a015..ecd4d10 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,9 +12,9 @@ ### 도메인 - [x] `유효한 단어`를 불러온다. - [x] `유효한 단어`란, `words.txt`에 존재하는 단어이다. - - [ ] `정답 단어`를 불러온다. - - [ ] 정답은 유효한 단어에서 뽑는다. - - [ ] 정답은 매일 바뀌며 `((현재 날짜 - 2021년 6월 19일) % 배열의 크기)` 번째의 단어이다. + - [x] `정답 단어`를 불러온다. + - [x] 정답은 유효한 단어에서 뽑는다. + - [x] 정답은 매일 바뀌며 `((현재 날짜 - 2021년 6월 19일) % 배열의 크기)` 번째의 단어이다. - [x] `예측 단어`와 `정답 단어`를 비교한다. - [x] 맞는 글자가 있는지 먼저 비교한다. - [x] 위치만 틀린 글자가 있는지 그 다음으로 비교한다. diff --git a/src/main/kotlin/wordle/domain/Letter.kt b/src/main/kotlin/wordle/domain/Letter.kt deleted file mode 100644 index e596236..0000000 --- a/src/main/kotlin/wordle/domain/Letter.kt +++ /dev/null @@ -1,8 +0,0 @@ -package wordle.domain - -data class Letter(private val value: Char) { - - init { - require(value.isLowerCase()) { "글자는 알파벳 소문자여야합니다." } - } -} \ No newline at end of file diff --git a/src/main/kotlin/wordle/domain/Word.kt b/src/main/kotlin/wordle/domain/Word.kt index 917896e..aafabbf 100644 --- a/src/main/kotlin/wordle/domain/Word.kt +++ b/src/main/kotlin/wordle/domain/Word.kt @@ -4,17 +4,15 @@ import java.io.File private const val WORD_LENGTH = 5 -class Word(word: String) { - private var letters: List - +data class Word(private var word: String) { init { require(word.length == WORD_LENGTH) { "단어는 5글자여야 합니다." } require(contains(word)) { "유효하지 않은 단어입니다." } - letters = word.map(::Letter) + word = word.lowercase() } fun compareByIndex(other: Word, myIndex: Int, otherIndex: Int = myIndex): Boolean { - return letters[myIndex] == other.letters[otherIndex] + return word[myIndex] == other.word[otherIndex] } companion object { @@ -24,5 +22,9 @@ class Word(word: String) { fun contains(word: String): Boolean { return CACHE.contains(word) } + + fun findWordByDay(day: Int): Word { + return Word(CACHE[day % CACHE.size]) + } } } \ No newline at end of file diff --git a/src/main/kotlin/wordle/domain/WordPicker.kt b/src/main/kotlin/wordle/domain/WordPicker.kt new file mode 100644 index 0000000..fa16e01 --- /dev/null +++ b/src/main/kotlin/wordle/domain/WordPicker.kt @@ -0,0 +1,16 @@ +package wordle.domain + +import java.time.LocalDate +import java.time.Period + +private const val YEAR = 2021 +private const val MONTH = 6 +private const val DAY_OF_MONTH = 19 + +class WordPicker(private val today: LocalDate = LocalDate.now()) { + fun pickTodayAnswer(): Word { + val fixed = LocalDate.of(YEAR, MONTH, DAY_OF_MONTH) + val day = Period.between(fixed, today).days + return Word.findWordByDay(day) + } +} \ No newline at end of file diff --git a/src/test/kotlin/wordle/domain/LetterTest.kt b/src/test/kotlin/wordle/domain/LetterTest.kt deleted file mode 100644 index dfbe3b6..0000000 --- a/src/test/kotlin/wordle/domain/LetterTest.kt +++ /dev/null @@ -1,36 +0,0 @@ -package wordle.domain - -import io.kotest.assertions.throwables.shouldThrow -import io.kotest.matchers.shouldBe -import io.kotest.matchers.shouldNotBe -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test - -class LetterTest { - - @Test - @DisplayName("Letter는 알파벳 소문자여야한다.") - fun constructWithLowerAlphabet() { - shouldThrow { Letter('1') } - } - - @Test - @DisplayName("글자가 일치하면 true를 반환한다.") - fun isSameLetter_true() { - val letterA = Letter('a') - - val letterB = Letter('a') - - letterA shouldBe letterB - } - - @Test - @DisplayName("글자가 일치하지 않으면 false를 반환한다.") - fun isSameLetter_false() { - val letterA = Letter('a') - - val letterB = Letter('b') - - letterA shouldNotBe letterB - } -} \ No newline at end of file diff --git a/src/test/kotlin/wordle/domain/WordPickerTest.kt b/src/test/kotlin/wordle/domain/WordPickerTest.kt new file mode 100644 index 0000000..fb2f7a9 --- /dev/null +++ b/src/test/kotlin/wordle/domain/WordPickerTest.kt @@ -0,0 +1,20 @@ +package wordle.domain + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import java.time.LocalDate + +class WordPickerTest { + + @Test + @DisplayName("오늘의 단어를 가져온다.") + fun pickTodayAnswer() { + + val wordPicker = WordPicker(LocalDate.of(2021, 6, 20)) + + val todayAnswer = wordPicker.pickTodayAnswer() + + todayAnswer shouldBe Word("rebut") + } +} \ No newline at end of file From d28413164c7f0785e62864826912342981c09d1b Mon Sep 17 00:00:00 2001 From: kth990303 Date: Wed, 11 May 2022 16:25:46 +0900 Subject: [PATCH 09/25] =?UTF-8?q?feat:=20=EB=8B=A8=EC=96=B4=EA=B0=80=20?= =?UTF-8?q?=EC=86=8C=EB=AC=B8=EC=9E=90=EB=A1=9C=20=EC=9D=B4=EB=A3=A8?= =?UTF-8?q?=EC=96=B4=EC=A1=8C=EB=8A=94=EC=A7=80=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80=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 --- src/main/kotlin/wordle/domain/Word.kt | 8 ++++++-- src/test/kotlin/wordle/domain/WordTest.kt | 6 ++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/wordle/domain/Word.kt b/src/main/kotlin/wordle/domain/Word.kt index aafabbf..e11b19f 100644 --- a/src/main/kotlin/wordle/domain/Word.kt +++ b/src/main/kotlin/wordle/domain/Word.kt @@ -4,11 +4,15 @@ import java.io.File private const val WORD_LENGTH = 5 -data class Word(private var word: String) { +data class Word(val word: String) { init { require(word.length == WORD_LENGTH) { "단어는 5글자여야 합니다." } + require(isLowerCase(word)) { "단어는 소문자로 이루어져야 합니다." } require(contains(word)) { "유효하지 않은 단어입니다." } - word = word.lowercase() + } + + private fun isLowerCase(value: String): Boolean { + return value.all { it.isLowerCase() } } fun compareByIndex(other: Word, myIndex: Int, otherIndex: Int = myIndex): Boolean { diff --git a/src/test/kotlin/wordle/domain/WordTest.kt b/src/test/kotlin/wordle/domain/WordTest.kt index 17c2df8..39c3d74 100644 --- a/src/test/kotlin/wordle/domain/WordTest.kt +++ b/src/test/kotlin/wordle/domain/WordTest.kt @@ -14,6 +14,12 @@ class WordTest { shouldThrow { (Word("word")) } } + @Test + @DisplayName("단어는 소문자로 이루어져야 한다.") + fun constructorWithLowercaseWord() { + shouldThrow { Word("CIGAR") } + } + @Test @DisplayName("유효한 단어여야 한다.") fun constructorWithValidWord() { From 052464fe70bd1de1bf2a270cf278dfd331c88916 Mon Sep 17 00:00:00 2001 From: kth990303 Date: Wed, 11 May 2022 16:40:43 +0900 Subject: [PATCH 10/25] =?UTF-8?q?feat:=20=EA=B2=8C=EC=9E=84=EC=9D=84=20?= =?UTF-8?q?=EC=8B=A4=ED=96=89=ED=95=98=EB=8A=94=20=EC=95=A0=ED=94=8C?= =?UTF-8?q?=EB=A6=AC=EC=BC=80=EC=9D=B4=EC=85=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 16 ++++---- src/main/kotlin/wordle/Application.kt | 47 +++++++++++++++++++++++ src/main/kotlin/wordle/domain/Answer.kt | 2 +- src/main/kotlin/wordle/domain/Color.kt | 8 ++-- src/main/kotlin/wordle/view/InputView.kt | 9 +++++ src/main/kotlin/wordle/view/OutputView.kt | 35 +++++++++++++++++ 6 files changed, 104 insertions(+), 13 deletions(-) create mode 100644 src/main/kotlin/wordle/Application.kt create mode 100644 src/main/kotlin/wordle/view/InputView.kt create mode 100644 src/main/kotlin/wordle/view/OutputView.kt diff --git a/docs/README.md b/docs/README.md index ecd4d10..6f6ab46 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,13 +1,13 @@ ## 기능 요구사항 ### 입출력 - - [ ] 게임 방법 안내를 출력한다. - - [ ] `예측 단어`를 입력받는다. - - [ ] `예측 단어`가 유효한 단어가 아니면 안내 문구를 출력한 후 재입력받는다. - - [ ] 최대 6번 입력받을 수 있다. - - [ ] `예측 결과`를 출력한다. - - [ ] 맞는 글자는 초록색🟩, 위치만 틀리면 노란색🟨, 없으면 회색⬜으로 출력한다. - - [ ] 게임을 종료한다. - - [ ] 6번 이내에 `정답 단어`를 맞추지 못할 경우 정답 단어를 출력한 후에 종료한다. + - [x] 게임 방법 안내를 출력한다. + - [x] `예측 단어`를 입력받는다. + - [x] `예측 단어`가 유효한 단어가 아니면 안내 문구를 출력한 후 재입력받는다. + - [x] 최대 6번 입력받을 수 있다. + - [x] `예측 결과`를 출력한다. + - [x] 맞는 글자는 초록색🟩, 위치만 틀리면 노란색🟨, 없으면 회색⬜으로 출력한다. + - [x] 게임을 종료한다. + - [x] 6번 이내에 `정답 단어`를 맞추지 못할 경우 정답 단어를 출력한 후에 종료한다. ### 도메인 - [x] `유효한 단어`를 불러온다. diff --git a/src/main/kotlin/wordle/Application.kt b/src/main/kotlin/wordle/Application.kt new file mode 100644 index 0000000..d704a63 --- /dev/null +++ b/src/main/kotlin/wordle/Application.kt @@ -0,0 +1,47 @@ +package wordle + +import wordle.domain.Answer +import wordle.domain.Color +import wordle.domain.Color.GREEN +import wordle.domain.Word +import wordle.domain.WordPicker +import wordle.view.InputView +import wordle.view.OutputView + +fun main() { + val wordPicker = WordPicker() + val answer = Answer(wordPicker.pickTodayAnswer()) + OutputView.printIntroduction() + + if (playWordle(answer)) { + return + } + OutputView.printAnswer(answer) +} + +private fun playWordle(answer: Answer): Boolean { + repeat(6) { + val guessWord = guessAnswer() + val result = answer.compare(guessWord) + OutputView.printResult(result) + if (isAllGreen(result)) { + OutputView.printCount(it + 1) + return true + } + } + return false +} + +private fun isAllGreen(result: List): Boolean { + return result.all { it == GREEN } +} + + +fun guessAnswer(): Word { + return try { + Word(InputView.inputGuess()) + } catch (e: IllegalArgumentException) { + OutputView.printError(e.message) + guessAnswer() + } +} \ No newline at end of file diff --git a/src/main/kotlin/wordle/domain/Answer.kt b/src/main/kotlin/wordle/domain/Answer.kt index 8bdb931..481cdc1 100644 --- a/src/main/kotlin/wordle/domain/Answer.kt +++ b/src/main/kotlin/wordle/domain/Answer.kt @@ -3,7 +3,7 @@ package wordle.domain private const val RANGE_START = 0 private const val RANGE_END = 4 -class Answer(private val word: Word) { +class Answer(val word: Word) { fun compare(word: Word): List { val exactIndices = compareExact(word) diff --git a/src/main/kotlin/wordle/domain/Color.kt b/src/main/kotlin/wordle/domain/Color.kt index 9a8ff2e..4e493d6 100644 --- a/src/main/kotlin/wordle/domain/Color.kt +++ b/src/main/kotlin/wordle/domain/Color.kt @@ -1,7 +1,7 @@ package wordle.domain -enum class Color { - GREEN, - YELLOW, - GRAY +enum class Color(val representation: String) { + GREEN("🟩"), + YELLOW("🟨"), + GRAY("⬜") } \ No newline at end of file diff --git a/src/main/kotlin/wordle/view/InputView.kt b/src/main/kotlin/wordle/view/InputView.kt new file mode 100644 index 0000000..336f57c --- /dev/null +++ b/src/main/kotlin/wordle/view/InputView.kt @@ -0,0 +1,9 @@ +package wordle.view + +object InputView { + fun inputGuess(): String { + println("정답을 입력해 주세요.") + return readln() + } + +} \ No newline at end of file diff --git a/src/main/kotlin/wordle/view/OutputView.kt b/src/main/kotlin/wordle/view/OutputView.kt new file mode 100644 index 0000000..83cbc80 --- /dev/null +++ b/src/main/kotlin/wordle/view/OutputView.kt @@ -0,0 +1,35 @@ +package wordle.view + +import wordle.domain.Answer +import wordle.domain.Color + +object OutputView { + private val results: MutableList> = mutableListOf() + + fun printIntroduction() { + println("WORDLE을 6번 만에 맞춰 보세요.") + println("시도의 결과는 타일의 색 변화로 나타납니다.") + } + + fun printResult(newResult: List) { + results.add(newResult) + for (result in results) { + result.forEach { print(it.representation) } + println() + } + println() + } + + fun printAnswer(answer: Answer) { + println("아쉽습니다! 정답은 ${answer.word.word}입니다.") + } + + fun printCount(tryCount: Int) { + println("$tryCount/6") + } + + fun printError(message: String?) { + println(message) + } + +} From b80b2e37786a41e0e6c69cbd3a10c6db4fa97e29 Mon Sep 17 00:00:00 2001 From: kth990303 Date: Thu, 12 May 2022 14:09:44 +0900 Subject: [PATCH 11/25] =?UTF-8?q?refactor:=20EOF=20=EC=BB=A8=EB=B2=A4?= =?UTF-8?q?=EC=85=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/Application.kt | 2 +- src/main/kotlin/wordle/domain/Color.kt | 2 +- src/main/kotlin/wordle/domain/Word.kt | 2 +- src/main/kotlin/wordle/domain/WordPicker.kt | 2 +- src/main/kotlin/wordle/view/InputView.kt | 1 - src/test/kotlin/wordle/domain/AnswerTest.kt | 2 +- src/test/kotlin/wordle/domain/WordPickerTest.kt | 2 +- src/test/kotlin/wordle/domain/WordTest.kt | 2 +- 8 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/wordle/Application.kt b/src/main/kotlin/wordle/Application.kt index d704a63..e77dc02 100644 --- a/src/main/kotlin/wordle/Application.kt +++ b/src/main/kotlin/wordle/Application.kt @@ -44,4 +44,4 @@ fun guessAnswer(): Word { OutputView.printError(e.message) guessAnswer() } -} \ No newline at end of file +} diff --git a/src/main/kotlin/wordle/domain/Color.kt b/src/main/kotlin/wordle/domain/Color.kt index 4e493d6..448ba55 100644 --- a/src/main/kotlin/wordle/domain/Color.kt +++ b/src/main/kotlin/wordle/domain/Color.kt @@ -4,4 +4,4 @@ enum class Color(val representation: String) { GREEN("🟩"), YELLOW("🟨"), GRAY("⬜") -} \ No newline at end of file +} diff --git a/src/main/kotlin/wordle/domain/Word.kt b/src/main/kotlin/wordle/domain/Word.kt index e11b19f..bb0dfe4 100644 --- a/src/main/kotlin/wordle/domain/Word.kt +++ b/src/main/kotlin/wordle/domain/Word.kt @@ -31,4 +31,4 @@ data class Word(val word: String) { return Word(CACHE[day % CACHE.size]) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/wordle/domain/WordPicker.kt b/src/main/kotlin/wordle/domain/WordPicker.kt index fa16e01..ef5dfdd 100644 --- a/src/main/kotlin/wordle/domain/WordPicker.kt +++ b/src/main/kotlin/wordle/domain/WordPicker.kt @@ -13,4 +13,4 @@ class WordPicker(private val today: LocalDate = LocalDate.now()) { val day = Period.between(fixed, today).days return Word.findWordByDay(day) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/wordle/view/InputView.kt b/src/main/kotlin/wordle/view/InputView.kt index 336f57c..d6eb30e 100644 --- a/src/main/kotlin/wordle/view/InputView.kt +++ b/src/main/kotlin/wordle/view/InputView.kt @@ -5,5 +5,4 @@ object InputView { println("정답을 입력해 주세요.") return readln() } - } \ No newline at end of file diff --git a/src/test/kotlin/wordle/domain/AnswerTest.kt b/src/test/kotlin/wordle/domain/AnswerTest.kt index f52eace..ab489b4 100644 --- a/src/test/kotlin/wordle/domain/AnswerTest.kt +++ b/src/test/kotlin/wordle/domain/AnswerTest.kt @@ -70,4 +70,4 @@ class AnswerTest { answer.compare(Word("timid")) shouldContainAll listOf(YELLOW, GREEN, GRAY, GRAY, GRAY) } -} \ No newline at end of file +} diff --git a/src/test/kotlin/wordle/domain/WordPickerTest.kt b/src/test/kotlin/wordle/domain/WordPickerTest.kt index fb2f7a9..7e59129 100644 --- a/src/test/kotlin/wordle/domain/WordPickerTest.kt +++ b/src/test/kotlin/wordle/domain/WordPickerTest.kt @@ -17,4 +17,4 @@ class WordPickerTest { todayAnswer shouldBe Word("rebut") } -} \ No newline at end of file +} diff --git a/src/test/kotlin/wordle/domain/WordTest.kt b/src/test/kotlin/wordle/domain/WordTest.kt index 39c3d74..40df80d 100644 --- a/src/test/kotlin/wordle/domain/WordTest.kt +++ b/src/test/kotlin/wordle/domain/WordTest.kt @@ -49,4 +49,4 @@ class WordTest { wordA.compareByIndex(wordB, 0, 1) shouldBe false } -} \ No newline at end of file +} From ce0abfefbd51ab84622ba495150cacafae3e02fe Mon Sep 17 00:00:00 2001 From: kth990303 Date: Thu, 12 May 2022 14:22:19 +0900 Subject: [PATCH 12/25] =?UTF-8?q?style:=20`ktlintCheck`=EC=97=90=20?= =?UTF-8?q?=EB=A7=9E=EA=B2=8C=20=EC=BB=A8=EB=B2=A4=EC=85=98=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/Application.kt | 1 - src/main/kotlin/wordle/view/InputView.kt | 2 +- src/main/kotlin/wordle/view/OutputView.kt | 1 - src/test/kotlin/wordle/domain/AnswerTest.kt | 30 +++++++++++++------ .../kotlin/wordle/domain/WordPickerTest.kt | 1 - 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/main/kotlin/wordle/Application.kt b/src/main/kotlin/wordle/Application.kt index e77dc02..522e398 100644 --- a/src/main/kotlin/wordle/Application.kt +++ b/src/main/kotlin/wordle/Application.kt @@ -36,7 +36,6 @@ private fun isAllGreen(result: List): Boolean { return result.all { it == GREEN } } - fun guessAnswer(): Word { return try { Word(InputView.inputGuess()) diff --git a/src/main/kotlin/wordle/view/InputView.kt b/src/main/kotlin/wordle/view/InputView.kt index d6eb30e..1d37031 100644 --- a/src/main/kotlin/wordle/view/InputView.kt +++ b/src/main/kotlin/wordle/view/InputView.kt @@ -5,4 +5,4 @@ object InputView { println("정답을 입력해 주세요.") return readln() } -} \ No newline at end of file +} diff --git a/src/main/kotlin/wordle/view/OutputView.kt b/src/main/kotlin/wordle/view/OutputView.kt index 83cbc80..720c1f1 100644 --- a/src/main/kotlin/wordle/view/OutputView.kt +++ b/src/main/kotlin/wordle/view/OutputView.kt @@ -31,5 +31,4 @@ object OutputView { fun printError(message: String?) { println(message) } - } diff --git a/src/test/kotlin/wordle/domain/AnswerTest.kt b/src/test/kotlin/wordle/domain/AnswerTest.kt index ab489b4..d781b5d 100644 --- a/src/test/kotlin/wordle/domain/AnswerTest.kt +++ b/src/test/kotlin/wordle/domain/AnswerTest.kt @@ -3,7 +3,6 @@ package wordle.domain import io.kotest.matchers.collections.shouldContainAll import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test -import wordle.domain.Color.* class AnswerTest { @@ -12,7 +11,13 @@ class AnswerTest { fun compareWithGreenAndGray() { val answer = Answer(Word("cigar")) - answer.compare(Word("clear")) shouldContainAll listOf(GREEN, GRAY, GRAY, GREEN, GREEN) + answer.compare(Word("clear")) shouldContainAll listOf( + Color.GREEN, + Color.GRAY, + Color.GRAY, + Color.GREEN, + Color.GREEN + ) } @Test @@ -20,7 +25,8 @@ class AnswerTest { fun compareWithAllGreens() { val answer = Answer(Word("cigar")) - answer.compare(Word("cigar")) shouldContainAll listOf(GREEN, GREEN, GREEN, GREEN, GREEN) + answer.compare(Word("cigar")) shouldContainAll + listOf(Color.GREEN, Color.GREEN, Color.GREEN, Color.GREEN, Color.GREEN) } @Test @@ -28,7 +34,8 @@ class AnswerTest { fun compareWithAllColors() { val answer = Answer(Word("spill")) - answer.compare(Word("hello")) shouldContainAll listOf(GRAY, GRAY, YELLOW, GREEN, GRAY) + answer.compare(Word("hello")) shouldContainAll + listOf(Color.GRAY, Color.GRAY, Color.YELLOW, Color.GREEN, Color.GRAY) } @Test @@ -36,7 +43,8 @@ class AnswerTest { fun compareWithYellowAndGray1() { val answer = Answer(Word("front")) - answer.compare(Word("totem")) shouldContainAll listOf(YELLOW, YELLOW, GRAY, GRAY, GRAY) + answer.compare(Word("totem")) shouldContainAll + listOf(Color.YELLOW, Color.YELLOW, Color.GRAY, Color.GRAY, Color.GRAY) } @Test @@ -44,7 +52,8 @@ class AnswerTest { fun compareWithYellowAndGray2() { val answer = Answer(Word("totem")) - answer.compare(Word("start")) shouldContainAll listOf(GRAY, YELLOW, GRAY, GRAY, YELLOW) + answer.compare(Word("start")) shouldContainAll + listOf(Color.GRAY, Color.YELLOW, Color.GRAY, Color.GRAY, Color.YELLOW) } @Test @@ -52,7 +61,8 @@ class AnswerTest { fun compareWithAllGrays() { val answer = Answer(Word("parry")) - answer.compare(Word("biome")) shouldContainAll listOf(GRAY, GRAY, GRAY, GRAY, GRAY) + answer.compare(Word("biome")) shouldContainAll + listOf(Color.GRAY, Color.GRAY, Color.GRAY, Color.GRAY, Color.GRAY) } @Test @@ -60,7 +70,8 @@ class AnswerTest { fun compareWithAllYellows() { val answer = Answer(Word("parse")) - answer.compare(Word("spear")) shouldContainAll listOf(YELLOW, YELLOW, YELLOW, YELLOW, YELLOW) + answer.compare(Word("spear")) shouldContainAll + listOf(Color.YELLOW, Color.YELLOW, Color.YELLOW, Color.YELLOW, Color.YELLOW) } @Test @@ -68,6 +79,7 @@ class AnswerTest { fun test1() { val answer = Answer(Word("witch")) - answer.compare(Word("timid")) shouldContainAll listOf(YELLOW, GREEN, GRAY, GRAY, GRAY) + answer.compare(Word("timid")) shouldContainAll + listOf(Color.YELLOW, Color.GREEN, Color.GRAY, Color.GRAY, Color.GRAY) } } diff --git a/src/test/kotlin/wordle/domain/WordPickerTest.kt b/src/test/kotlin/wordle/domain/WordPickerTest.kt index 7e59129..32b2631 100644 --- a/src/test/kotlin/wordle/domain/WordPickerTest.kt +++ b/src/test/kotlin/wordle/domain/WordPickerTest.kt @@ -10,7 +10,6 @@ class WordPickerTest { @Test @DisplayName("오늘의 단어를 가져온다.") fun pickTodayAnswer() { - val wordPicker = WordPicker(LocalDate.of(2021, 6, 20)) val todayAnswer = wordPicker.pickTodayAnswer() From d2b992ba7aace1e5b1015b215bd8cb6eeb954b3b Mon Sep 17 00:00:00 2001 From: cjlee38 Date: Fri, 13 May 2022 20:50:20 +0900 Subject: [PATCH 13/25] =?UTF-8?q?style:=20=EB=A9=94=EC=86=8C=EB=93=9C=20?= =?UTF-8?q?=EC=88=9C=EC=84=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/Application.kt | 8 ++++---- src/main/kotlin/wordle/domain/Answer.kt | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/wordle/Application.kt b/src/main/kotlin/wordle/Application.kt index 522e398..8ceafec 100644 --- a/src/main/kotlin/wordle/Application.kt +++ b/src/main/kotlin/wordle/Application.kt @@ -32,10 +32,6 @@ private fun playWordle(answer: Answer): Boolean { return false } -private fun isAllGreen(result: List): Boolean { - return result.all { it == GREEN } -} - fun guessAnswer(): Word { return try { Word(InputView.inputGuess()) @@ -44,3 +40,7 @@ fun guessAnswer(): Word { guessAnswer() } } + +private fun isAllGreen(result: List): Boolean { + return result.all { it == GREEN } +} diff --git a/src/main/kotlin/wordle/domain/Answer.kt b/src/main/kotlin/wordle/domain/Answer.kt index 481cdc1..222e7fc 100644 --- a/src/main/kotlin/wordle/domain/Answer.kt +++ b/src/main/kotlin/wordle/domain/Answer.kt @@ -11,6 +11,14 @@ class Answer(val word: Word) { return merge(exactIndices, anyIndices) } + private fun compareExact(word: Word): List { + return (RANGE_START..RANGE_END).filter { this.word.compareByIndex(word, it) } + } + + private fun compareAny(word: Word): List { + return (RANGE_START..RANGE_END).filter { isAnyMatch(word, it) } + } + private fun merge(greenIndices: List, yellowIndices: List): List { return (RANGE_START..RANGE_END).map { defineColor(it, greenIndices, yellowIndices) } } @@ -25,14 +33,6 @@ class Answer(val word: Word) { return Color.GRAY } - private fun compareExact(word: Word): List { - return (RANGE_START..RANGE_END).filter { this.word.compareByIndex(word, it) } - } - - private fun compareAny(word: Word): List { - return (RANGE_START..RANGE_END).filter { isAnyMatch(word, it) } - } - private fun isAnyMatch(word: Word, outerIndex: Int): Boolean { return (RANGE_START..RANGE_END).any { this.word.compareByIndex(word, it, outerIndex) } } From 0d96a6d2ea008f7421f63eeefc804733646def3a Mon Sep 17 00:00:00 2001 From: cjlee38 Date: Wed, 18 May 2022 18:37:20 +0900 Subject: [PATCH 14/25] =?UTF-8?q?test:=20DSL=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/kotlin/wordle/study/DSLTest.kt | 90 +++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/test/kotlin/wordle/study/DSLTest.kt diff --git a/src/test/kotlin/wordle/study/DSLTest.kt b/src/test/kotlin/wordle/study/DSLTest.kt new file mode 100644 index 0000000..abc5476 --- /dev/null +++ b/src/test/kotlin/wordle/study/DSLTest.kt @@ -0,0 +1,90 @@ +package wordle.study + +import org.junit.jupiter.api.Test + +class DSLTest { + @Test + fun test() { + val introduce = introduce { + name("이찬주") + company("우아한형제들") + skills { + soft("A passion for problem solving") + soft("Good communication skills") + hard("Kotlin") + } + languages { + "Korean" level 5 + "English" level 3 + } + } + println(introduce) + } +} + +fun introduce(builder: PersonBuilder.() -> Unit): Person { + return PersonBuilder().apply(builder).build() +} + +class PersonBuilder { + lateinit var name: String + lateinit var company: String + lateinit var skills: List + lateinit var languages: List + + fun name(value: String) { + this.name = value + } + + fun company(value: String) { + this.company = value + } + + fun skills(skillsBuilder: SkillsBuilder.() -> Unit) { + this.skills = SkillsBuilder().apply(skillsBuilder).build() + } + + fun languages(languagesBuilder: LanguagesBuilder.() -> Unit) { + this.languages = LanguagesBuilder().apply(languagesBuilder).build() + } + + fun build(): Person { + return Person(name, company, skills, languages) + } +} + + +class SkillsBuilder { + var skills: MutableList = mutableListOf() + + fun soft(description: String) { + skills.add(Skill("soft", description)) + } + + fun hard(description: String) { + skills.add(Skill("hard", description)) + } + + fun build(): List { + return skills + } +} + +class LanguagesBuilder { + var languages: MutableList = mutableListOf() + + infix fun String.level(value: Int) { + languages.add(Language(this, value)) + } + + fun build(): List { + return languages + } +} + + +data class Person(val name: String, val company: String, val skills: List, val languages: List) + +data class Language(val lang: String, val level: Int) + +data class Skill(val type: String, val description: String) \ No newline at end of file From df8a5b00f3a91f75a59e283b98dbf7d232e49f43 Mon Sep 17 00:00:00 2001 From: cjlee38 Date: Thu, 19 May 2022 13:51:19 +0900 Subject: [PATCH 15/25] =?UTF-8?q?fix:=20=EC=9B=8C=EB=93=A4=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/domain/Answer.kt | 17 ++++++--- src/test/kotlin/wordle/domain/AnswerTest.kt | 40 ++++++++++----------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/main/kotlin/wordle/domain/Answer.kt b/src/main/kotlin/wordle/domain/Answer.kt index 222e7fc..d557263 100644 --- a/src/main/kotlin/wordle/domain/Answer.kt +++ b/src/main/kotlin/wordle/domain/Answer.kt @@ -16,7 +16,18 @@ class Answer(val word: Word) { } private fun compareAny(word: Word): List { - return (RANGE_START..RANGE_END).filter { isAnyMatch(word, it) } + val consumed = mutableListOf() + return (RANGE_START..RANGE_END).filter { isAnyMatch(word, it, consumed) } + } + + private fun isAnyMatch(word: Word, outerIndex: Int, consumed: MutableList): Boolean { + return (RANGE_START..RANGE_END).any { + if (this.word.compareByIndex(word, it, outerIndex) && !consumed.contains(it)) { + consumed.add(it) + return true + } + return@any false + } } private fun merge(greenIndices: List, yellowIndices: List): List { @@ -32,8 +43,4 @@ class Answer(val word: Word) { } return Color.GRAY } - - private fun isAnyMatch(word: Word, outerIndex: Int): Boolean { - return (RANGE_START..RANGE_END).any { this.word.compareByIndex(word, it, outerIndex) } - } } diff --git a/src/test/kotlin/wordle/domain/AnswerTest.kt b/src/test/kotlin/wordle/domain/AnswerTest.kt index d781b5d..b169a82 100644 --- a/src/test/kotlin/wordle/domain/AnswerTest.kt +++ b/src/test/kotlin/wordle/domain/AnswerTest.kt @@ -1,6 +1,7 @@ package wordle.domain import io.kotest.matchers.collections.shouldContainAll +import io.kotest.matchers.collections.shouldContainExactly import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test @@ -11,13 +12,8 @@ class AnswerTest { fun compareWithGreenAndGray() { val answer = Answer(Word("cigar")) - answer.compare(Word("clear")) shouldContainAll listOf( - Color.GREEN, - Color.GRAY, - Color.GRAY, - Color.GREEN, - Color.GREEN - ) + answer.compare(Word("clear")) shouldContainExactly + listOf(Color.GREEN, Color.GRAY, Color.GRAY, Color.GREEN, Color.GREEN) } @Test @@ -25,7 +21,7 @@ class AnswerTest { fun compareWithAllGreens() { val answer = Answer(Word("cigar")) - answer.compare(Word("cigar")) shouldContainAll + answer.compare(Word("cigar")) shouldContainExactly listOf(Color.GREEN, Color.GREEN, Color.GREEN, Color.GREEN, Color.GREEN) } @@ -34,7 +30,7 @@ class AnswerTest { fun compareWithAllColors() { val answer = Answer(Word("spill")) - answer.compare(Word("hello")) shouldContainAll + answer.compare(Word("hello")) shouldContainExactly listOf(Color.GRAY, Color.GRAY, Color.YELLOW, Color.GREEN, Color.GRAY) } @@ -43,7 +39,7 @@ class AnswerTest { fun compareWithYellowAndGray1() { val answer = Answer(Word("front")) - answer.compare(Word("totem")) shouldContainAll + answer.compare(Word("totem")) shouldContainExactly listOf(Color.YELLOW, Color.YELLOW, Color.GRAY, Color.GRAY, Color.GRAY) } @@ -52,16 +48,25 @@ class AnswerTest { fun compareWithYellowAndGray2() { val answer = Answer(Word("totem")) - answer.compare(Word("start")) shouldContainAll + answer.compare(Word("start")) shouldContainExactly listOf(Color.GRAY, Color.YELLOW, Color.GRAY, Color.GRAY, Color.YELLOW) } + @Test + @DisplayName("위치만 일치하는 글자들과 틀린 글자들이 존재하는 예측을 비교한다..") + fun compareWithYellowAndGray3() { + val answer = Answer(Word("witch")) + + answer.compare(Word("timid")) shouldContainExactly + listOf(Color.YELLOW, Color.GREEN, Color.GRAY, Color.GRAY, Color.GRAY) + } + @Test @DisplayName("전부 틀린 글자들이 존재하는 예측을 비교한다.") fun compareWithAllGrays() { val answer = Answer(Word("parry")) - answer.compare(Word("biome")) shouldContainAll + answer.compare(Word("biome")) shouldContainExactly listOf(Color.GRAY, Color.GRAY, Color.GRAY, Color.GRAY, Color.GRAY) } @@ -70,16 +75,7 @@ class AnswerTest { fun compareWithAllYellows() { val answer = Answer(Word("parse")) - answer.compare(Word("spear")) shouldContainAll + answer.compare(Word("spear")) shouldContainExactly listOf(Color.YELLOW, Color.YELLOW, Color.YELLOW, Color.YELLOW, Color.YELLOW) } - - @Test - @DisplayName("test.") - fun test1() { - val answer = Answer(Word("witch")) - - answer.compare(Word("timid")) shouldContainAll - listOf(Color.YELLOW, Color.GREEN, Color.GRAY, Color.GRAY, Color.GRAY) - } } From 80b79212d41ed11f10dc8f75338fb1828f4f819e Mon Sep 17 00:00:00 2001 From: cjlee38 Date: Thu, 19 May 2022 13:51:37 +0900 Subject: [PATCH 16/25] =?UTF-8?q?refactor:=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=ED=99=95=EC=9E=A5=ED=95=A8=EC=88=98?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/domain/Word.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/wordle/domain/Word.kt b/src/main/kotlin/wordle/domain/Word.kt index bb0dfe4..e66de92 100644 --- a/src/main/kotlin/wordle/domain/Word.kt +++ b/src/main/kotlin/wordle/domain/Word.kt @@ -7,12 +7,12 @@ private const val WORD_LENGTH = 5 data class Word(val word: String) { init { require(word.length == WORD_LENGTH) { "단어는 5글자여야 합니다." } - require(isLowerCase(word)) { "단어는 소문자로 이루어져야 합니다." } - require(contains(word)) { "유효하지 않은 단어입니다." } + require(word.isLowerCase()) { "단어는 소문자로 이루어져야 합니다." } + require(word.containsInWord()) { "유효하지 않은 단어입니다." } } - private fun isLowerCase(value: String): Boolean { - return value.all { it.isLowerCase() } + private fun String.isLowerCase(): Boolean { + return this.all { it.isLowerCase() } } fun compareByIndex(other: Word, myIndex: Int, otherIndex: Int = myIndex): Boolean { @@ -23,8 +23,8 @@ data class Word(val word: String) { private val CACHE: List = File("src/main/resources/words.txt") .readLines() - fun contains(word: String): Boolean { - return CACHE.contains(word) + fun String.containsInWord(): Boolean { + return CACHE.contains(this) } fun findWordByDay(day: Int): Word { From 53f36be9184fdf066535af0236fcdc084dc6dbb7 Mon Sep 17 00:00:00 2001 From: cjlee38 Date: Thu, 19 May 2022 14:50:21 +0900 Subject: [PATCH 17/25] =?UTF-8?q?feat:=20Game=20=EA=B0=9D=EC=B2=B4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EA=B2=8C=EC=9E=84=EC=A7=84?= =?UTF-8?q?=ED=96=89=20=EC=B1=85=EC=9E=84=20=EC=9C=84=EC=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/Application.kt | 40 ++++++----------------- src/main/kotlin/wordle/domain/Colors.kt | 16 +++++++++ src/main/kotlin/wordle/domain/Game.kt | 27 +++++++++++++++ src/main/kotlin/wordle/view/OutputView.kt | 23 ++++++------- 4 files changed, 63 insertions(+), 43 deletions(-) create mode 100644 src/main/kotlin/wordle/domain/Colors.kt create mode 100644 src/main/kotlin/wordle/domain/Game.kt diff --git a/src/main/kotlin/wordle/Application.kt b/src/main/kotlin/wordle/Application.kt index 8ceafec..49c2e2a 100644 --- a/src/main/kotlin/wordle/Application.kt +++ b/src/main/kotlin/wordle/Application.kt @@ -1,46 +1,26 @@ package wordle -import wordle.domain.Answer -import wordle.domain.Color -import wordle.domain.Color.GREEN +import wordle.domain.Game import wordle.domain.Word -import wordle.domain.WordPicker import wordle.view.InputView import wordle.view.OutputView fun main() { - val wordPicker = WordPicker() - val answer = Answer(wordPicker.pickTodayAnswer()) - OutputView.printIntroduction() - - if (playWordle(answer)) { - return - } - OutputView.printAnswer(answer) -} - -private fun playWordle(answer: Answer): Boolean { - repeat(6) { - val guessWord = guessAnswer() - val result = answer.compare(guessWord) - OutputView.printResult(result) - if (isAllGreen(result)) { - OutputView.printCount(it + 1) - return true - } + val game = Game() + OutputView.printIntroduction(Game.maxCount) + while (!game.isOver()) { + val word = inputGuessWord() + val results = game.playWordle(word) + OutputView.printResult(results) } - return false + OutputView.printCount(game.count) } -fun guessAnswer(): Word { +fun inputGuessWord(): Word { return try { Word(InputView.inputGuess()) } catch (e: IllegalArgumentException) { OutputView.printError(e.message) - guessAnswer() + return inputGuessWord() } } - -private fun isAllGreen(result: List): Boolean { - return result.all { it == GREEN } -} diff --git a/src/main/kotlin/wordle/domain/Colors.kt b/src/main/kotlin/wordle/domain/Colors.kt new file mode 100644 index 0000000..8f07123 --- /dev/null +++ b/src/main/kotlin/wordle/domain/Colors.kt @@ -0,0 +1,16 @@ +package wordle.domain + +class Colors(val values: List) { + + companion object { + private const val SIZE = 5 + } + + init { + require(values.size == SIZE) { "색깔의 개수가 %d개가 아닙니다".format(SIZE) } + } + + fun isCorrect(): Boolean { + return values.all { it == Color.GREEN } + } +} \ No newline at end of file diff --git a/src/main/kotlin/wordle/domain/Game.kt b/src/main/kotlin/wordle/domain/Game.kt new file mode 100644 index 0000000..ce0dca4 --- /dev/null +++ b/src/main/kotlin/wordle/domain/Game.kt @@ -0,0 +1,27 @@ +package wordle.domain + +class Game { + + var count = 0 + private set + + val answer: Answer = Answer(WordPicker().pickTodayAnswer()) + + private val _guessResults: MutableList = mutableListOf() + + fun playWordle(word: Word): List { + if (!isOver()) { + _guessResults.add(Colors(answer.compare(word))) + ++count + } + return _guessResults + } + + fun isOver(): Boolean { + return count >= maxCount || _guessResults.any { it.isCorrect() } + } + + companion object { + const val maxCount = 6 + } +} diff --git a/src/main/kotlin/wordle/view/OutputView.kt b/src/main/kotlin/wordle/view/OutputView.kt index 720c1f1..02968e4 100644 --- a/src/main/kotlin/wordle/view/OutputView.kt +++ b/src/main/kotlin/wordle/view/OutputView.kt @@ -1,31 +1,28 @@ package wordle.view -import wordle.domain.Answer -import wordle.domain.Color +import wordle.domain.Colors +import wordle.domain.Game object OutputView { - private val results: MutableList> = mutableListOf() - fun printIntroduction() { - println("WORDLE을 6번 만에 맞춰 보세요.") + fun printIntroduction(maxCount: Int) { + println("WORDLE을 $maxCount 번 만에 맞춰 보세요.") println("시도의 결과는 타일의 색 변화로 나타납니다.") } - fun printResult(newResult: List) { - results.add(newResult) - for (result in results) { - result.forEach { print(it.representation) } + fun printResult(colorResults: List) { + for (colorResult in colorResults) { + colorResult.printColors() println() } - println() } - fun printAnswer(answer: Answer) { - println("아쉽습니다! 정답은 ${answer.word.word}입니다.") + private fun Colors.printColors() { + this.values.forEach { print(it.representation) } } fun printCount(tryCount: Int) { - println("$tryCount/6") + println("$tryCount/${Game.maxCount}") } fun printError(message: String?) { From 7feb21fc518a648bfe04233e4f3d8c6c9e0c5885 Mon Sep 17 00:00:00 2001 From: cjlee38 Date: Thu, 19 May 2022 15:30:54 +0900 Subject: [PATCH 18/25] =?UTF-8?q?refactor:=20=EC=A0=91=EA=B7=BC=EC=A0=9C?= =?UTF-8?q?=EC=96=B4=EC=9E=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/Application.kt | 4 ++-- src/main/kotlin/wordle/domain/Answer.kt | 2 +- src/main/kotlin/wordle/domain/Game.kt | 9 +++++---- src/main/kotlin/wordle/domain/Word.kt | 3 ++- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/wordle/Application.kt b/src/main/kotlin/wordle/Application.kt index 49c2e2a..62391d4 100644 --- a/src/main/kotlin/wordle/Application.kt +++ b/src/main/kotlin/wordle/Application.kt @@ -10,8 +10,8 @@ fun main() { OutputView.printIntroduction(Game.maxCount) while (!game.isOver()) { val word = inputGuessWord() - val results = game.playWordle(word) - OutputView.printResult(results) + game.playWordle(word) + OutputView.printResult(game.guessResults) } OutputView.printCount(game.count) } diff --git a/src/main/kotlin/wordle/domain/Answer.kt b/src/main/kotlin/wordle/domain/Answer.kt index d557263..ece255a 100644 --- a/src/main/kotlin/wordle/domain/Answer.kt +++ b/src/main/kotlin/wordle/domain/Answer.kt @@ -3,7 +3,7 @@ package wordle.domain private const val RANGE_START = 0 private const val RANGE_END = 4 -class Answer(val word: Word) { +class Answer(private val word: Word) { fun compare(word: Word): List { val exactIndices = compareExact(word) diff --git a/src/main/kotlin/wordle/domain/Game.kt b/src/main/kotlin/wordle/domain/Game.kt index ce0dca4..971f589 100644 --- a/src/main/kotlin/wordle/domain/Game.kt +++ b/src/main/kotlin/wordle/domain/Game.kt @@ -5,16 +5,17 @@ class Game { var count = 0 private set - val answer: Answer = Answer(WordPicker().pickTodayAnswer()) + private val answer: Answer = Answer(WordPicker().pickTodayAnswer()) private val _guessResults: MutableList = mutableListOf() + val guessResults: List + get() = _guessResults - fun playWordle(word: Word): List { + fun playWordle(word: Word) { if (!isOver()) { _guessResults.add(Colors(answer.compare(word))) - ++count + count++ } - return _guessResults } fun isOver(): Boolean { diff --git a/src/main/kotlin/wordle/domain/Word.kt b/src/main/kotlin/wordle/domain/Word.kt index e66de92..d21c971 100644 --- a/src/main/kotlin/wordle/domain/Word.kt +++ b/src/main/kotlin/wordle/domain/Word.kt @@ -4,7 +4,8 @@ import java.io.File private const val WORD_LENGTH = 5 -data class Word(val word: String) { +data class Word(private val word: String) { + init { require(word.length == WORD_LENGTH) { "단어는 5글자여야 합니다." } require(word.isLowerCase()) { "단어는 소문자로 이루어져야 합니다." } From c77d6049691f012fbd050d6331c4fed4c96e6393 Mon Sep 17 00:00:00 2001 From: cjlee38 Date: Thu, 19 May 2022 15:33:11 +0900 Subject: [PATCH 19/25] style: reformat code --- src/main/kotlin/wordle/domain/Colors.kt | 2 +- src/main/kotlin/wordle/domain/Word.kt | 2 +- src/test/kotlin/wordle/domain/AnswerTest.kt | 1 - src/test/kotlin/wordle/study/DSLTest.kt | 4 +--- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/wordle/domain/Colors.kt b/src/main/kotlin/wordle/domain/Colors.kt index 8f07123..8cca81f 100644 --- a/src/main/kotlin/wordle/domain/Colors.kt +++ b/src/main/kotlin/wordle/domain/Colors.kt @@ -13,4 +13,4 @@ class Colors(val values: List) { fun isCorrect(): Boolean { return values.all { it == Color.GREEN } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/wordle/domain/Word.kt b/src/main/kotlin/wordle/domain/Word.kt index d21c971..9300d7f 100644 --- a/src/main/kotlin/wordle/domain/Word.kt +++ b/src/main/kotlin/wordle/domain/Word.kt @@ -5,7 +5,7 @@ import java.io.File private const val WORD_LENGTH = 5 data class Word(private val word: String) { - + init { require(word.length == WORD_LENGTH) { "단어는 5글자여야 합니다." } require(word.isLowerCase()) { "단어는 소문자로 이루어져야 합니다." } diff --git a/src/test/kotlin/wordle/domain/AnswerTest.kt b/src/test/kotlin/wordle/domain/AnswerTest.kt index b169a82..1be7505 100644 --- a/src/test/kotlin/wordle/domain/AnswerTest.kt +++ b/src/test/kotlin/wordle/domain/AnswerTest.kt @@ -1,6 +1,5 @@ package wordle.domain -import io.kotest.matchers.collections.shouldContainAll import io.kotest.matchers.collections.shouldContainExactly import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test diff --git a/src/test/kotlin/wordle/study/DSLTest.kt b/src/test/kotlin/wordle/study/DSLTest.kt index abc5476..0008af0 100644 --- a/src/test/kotlin/wordle/study/DSLTest.kt +++ b/src/test/kotlin/wordle/study/DSLTest.kt @@ -53,7 +53,6 @@ class PersonBuilder { } } - class SkillsBuilder { var skills: MutableList = mutableListOf() @@ -82,9 +81,8 @@ class LanguagesBuilder { } } - data class Person(val name: String, val company: String, val skills: List, val languages: List) data class Language(val lang: String, val level: Int) -data class Skill(val type: String, val description: String) \ No newline at end of file +data class Skill(val type: String, val description: String) From 21c766d27e0d473767e35da3e75a8c6e1968ab56 Mon Sep 17 00:00:00 2001 From: cjlee38 Date: Sat, 21 May 2022 18:39:47 +0900 Subject: [PATCH 20/25] =?UTF-8?q?refactor:=20=EB=82=A0=EC=A7=9C=EB=A5=BC?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=EC=9E=90=20=EC=A3=BC=EC=9E=85=EC=9D=84=20?= =?UTF-8?q?=ED=86=B5=ED=95=B4=20=EB=B0=9B=EC=9D=84=20=EC=88=98=20=EC=9E=88?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/Application.kt | 3 ++- src/main/kotlin/wordle/domain/Game.kt | 6 ++++-- src/main/kotlin/wordle/domain/WordPicker.kt | 4 ++-- src/test/kotlin/wordle/domain/WordPickerTest.kt | 4 ++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/wordle/Application.kt b/src/main/kotlin/wordle/Application.kt index 62391d4..54e6c7c 100644 --- a/src/main/kotlin/wordle/Application.kt +++ b/src/main/kotlin/wordle/Application.kt @@ -4,9 +4,10 @@ import wordle.domain.Game import wordle.domain.Word import wordle.view.InputView import wordle.view.OutputView +import java.time.LocalDate fun main() { - val game = Game() + val game = Game(LocalDate.now()) OutputView.printIntroduction(Game.maxCount) while (!game.isOver()) { val word = inputGuessWord() diff --git a/src/main/kotlin/wordle/domain/Game.kt b/src/main/kotlin/wordle/domain/Game.kt index 971f589..298eb25 100644 --- a/src/main/kotlin/wordle/domain/Game.kt +++ b/src/main/kotlin/wordle/domain/Game.kt @@ -1,11 +1,13 @@ package wordle.domain -class Game { +import java.time.LocalDate + +class Game(today: LocalDate) { var count = 0 private set - private val answer: Answer = Answer(WordPicker().pickTodayAnswer()) + private val answer: Answer = Answer(WordPicker().pickAnswer(today)) private val _guessResults: MutableList = mutableListOf() val guessResults: List diff --git a/src/main/kotlin/wordle/domain/WordPicker.kt b/src/main/kotlin/wordle/domain/WordPicker.kt index ef5dfdd..d65429b 100644 --- a/src/main/kotlin/wordle/domain/WordPicker.kt +++ b/src/main/kotlin/wordle/domain/WordPicker.kt @@ -7,8 +7,8 @@ private const val YEAR = 2021 private const val MONTH = 6 private const val DAY_OF_MONTH = 19 -class WordPicker(private val today: LocalDate = LocalDate.now()) { - fun pickTodayAnswer(): Word { +class WordPicker { + fun pickAnswer(today: LocalDate = LocalDate.now()): Word { val fixed = LocalDate.of(YEAR, MONTH, DAY_OF_MONTH) val day = Period.between(fixed, today).days return Word.findWordByDay(day) diff --git a/src/test/kotlin/wordle/domain/WordPickerTest.kt b/src/test/kotlin/wordle/domain/WordPickerTest.kt index 32b2631..088c1f8 100644 --- a/src/test/kotlin/wordle/domain/WordPickerTest.kt +++ b/src/test/kotlin/wordle/domain/WordPickerTest.kt @@ -10,9 +10,9 @@ class WordPickerTest { @Test @DisplayName("오늘의 단어를 가져온다.") fun pickTodayAnswer() { - val wordPicker = WordPicker(LocalDate.of(2021, 6, 20)) + val wordPicker = WordPicker() - val todayAnswer = wordPicker.pickTodayAnswer() + val todayAnswer = wordPicker.pickAnswer(LocalDate.of(2021, 6, 20)) todayAnswer shouldBe Word("rebut") } From 4aa60e309c5916e19f93410347e036dfc3c3eb16 Mon Sep 17 00:00:00 2001 From: cjlee38 Date: Mon, 23 May 2022 00:37:06 +0900 Subject: [PATCH 21/25] =?UTF-8?q?fix:=20=EC=9D=B8=EB=8D=B1=EC=8A=A4=20?= =?UTF-8?q?=EC=83=81=EA=B4=80=EC=97=86=EC=9D=B4=20=EC=9D=BC=EC=B9=98?= =?UTF-8?q?=ED=95=9C=20=EB=8B=A8=EC=96=B4=EB=A5=BC=20=EC=86=8C=EC=A7=84?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/domain/Answer.kt | 50 ++++++++++++------------- src/main/kotlin/wordle/domain/Colors.kt | 12 +++++- src/main/kotlin/wordle/domain/Game.kt | 6 ++- src/main/kotlin/wordle/domain/Word.kt | 9 ++--- 4 files changed, 42 insertions(+), 35 deletions(-) diff --git a/src/main/kotlin/wordle/domain/Answer.kt b/src/main/kotlin/wordle/domain/Answer.kt index ece255a..1fd8cdf 100644 --- a/src/main/kotlin/wordle/domain/Answer.kt +++ b/src/main/kotlin/wordle/domain/Answer.kt @@ -5,42 +5,40 @@ private const val RANGE_END = 4 class Answer(private val word: Word) { - fun compare(word: Word): List { - val exactIndices = compareExact(word) - val anyIndices = compareAny(word) - return merge(exactIndices, anyIndices) + fun compare(word: Word): Colors { + val colors = Colors.createEmpty() + val paint = createPainter(colors, mutableListOf()) + paint({ index: Int, consumed: MutableList -> isExactMatch(word, index, consumed) }, Color.GREEN) + paint({ index: Int, consumed: MutableList -> isAnyMatch(word, index, consumed) }, Color.YELLOW) + return colors } - private fun compareExact(word: Word): List { - return (RANGE_START..RANGE_END).filter { this.word.compareByIndex(word, it) } + private fun createPainter( + colors: Colors, + consumedIndices: MutableList, + ): ((Int, MutableList) -> Boolean, Color) -> Unit { + return fun(predicate: (Int, MutableList) -> Boolean, color: Color) { + for (index in (RANGE_START..RANGE_END).filter { predicate.invoke(it, consumedIndices) }) { + colors.paint(color, index) + } + } } - private fun compareAny(word: Word): List { - val consumed = mutableListOf() - return (RANGE_START..RANGE_END).filter { isAnyMatch(word, it, consumed) } + private fun isExactMatch(word: Word, index: Int, consumedIndices: MutableList): Boolean { + return this.word.compareByIndex(word, index) && consumeIfMatched(consumedIndices, index) } - private fun isAnyMatch(word: Word, outerIndex: Int, consumed: MutableList): Boolean { + private fun isAnyMatch(word: Word, outerIndex: Int, consumedIndices: MutableList): Boolean { return (RANGE_START..RANGE_END).any { - if (this.word.compareByIndex(word, it, outerIndex) && !consumed.contains(it)) { - consumed.add(it) - return true - } - return@any false + this.word.compareByIndex(word, it, outerIndex) && consumeIfMatched(consumedIndices, it) } } - private fun merge(greenIndices: List, yellowIndices: List): List { - return (RANGE_START..RANGE_END).map { defineColor(it, greenIndices, yellowIndices) } - } - - private fun defineColor(index: Int, greenIndices: List, yellowIndices: List): Color { - if (greenIndices.contains(index)) { - return Color.GREEN - } - if (yellowIndices.contains(index)) { - return Color.YELLOW + private fun consumeIfMatched(consumedIndices: MutableList, index: Int): Boolean { + if (!consumedIndices.contains(index)) { + consumedIndices.add(index) + return true } - return Color.GRAY + return false } } diff --git a/src/main/kotlin/wordle/domain/Colors.kt b/src/main/kotlin/wordle/domain/Colors.kt index 8cca81f..5d323ed 100644 --- a/src/main/kotlin/wordle/domain/Colors.kt +++ b/src/main/kotlin/wordle/domain/Colors.kt @@ -1,9 +1,13 @@ package wordle.domain -class Colors(val values: List) { +class Colors(val values: MutableList) { companion object { private const val SIZE = 5 + + fun createEmpty(): Colors { + return Colors(mutableListOf(Color.GRAY, Color.GRAY, Color.GRAY, Color.GRAY, Color.GRAY)) + } } init { @@ -13,4 +17,10 @@ class Colors(val values: List) { fun isCorrect(): Boolean { return values.all { it == Color.GREEN } } + + fun paint(color: Color, index: Int) { + if (values[index] == Color.GRAY) { + values[index] = color + } + } } diff --git a/src/main/kotlin/wordle/domain/Game.kt b/src/main/kotlin/wordle/domain/Game.kt index 298eb25..2489502 100644 --- a/src/main/kotlin/wordle/domain/Game.kt +++ b/src/main/kotlin/wordle/domain/Game.kt @@ -7,7 +7,9 @@ class Game(today: LocalDate) { var count = 0 private set - private val answer: Answer = Answer(WordPicker().pickAnswer(today)) + // private val answer: Answer = Answer(WordPicker().pickAnswer(today)) + private val answer: Answer = Answer(Word("marry")) + private val _guessResults: MutableList = mutableListOf() val guessResults: List @@ -15,7 +17,7 @@ class Game(today: LocalDate) { fun playWordle(word: Word) { if (!isOver()) { - _guessResults.add(Colors(answer.compare(word))) + _guessResults.add(answer.compare(word)) count++ } } diff --git a/src/main/kotlin/wordle/domain/Word.kt b/src/main/kotlin/wordle/domain/Word.kt index 9300d7f..6c43dae 100644 --- a/src/main/kotlin/wordle/domain/Word.kt +++ b/src/main/kotlin/wordle/domain/Word.kt @@ -12,13 +12,10 @@ data class Word(private val word: String) { require(word.containsInWord()) { "유효하지 않은 단어입니다." } } - private fun String.isLowerCase(): Boolean { - return this.all { it.isLowerCase() } - } + private fun String.isLowerCase(): Boolean = this.all { it.isLowerCase() } - fun compareByIndex(other: Word, myIndex: Int, otherIndex: Int = myIndex): Boolean { - return word[myIndex] == other.word[otherIndex] - } + fun compareByIndex(other: Word, myIndex: Int, otherIndex: Int = myIndex): Boolean = + word[myIndex] == other.word[otherIndex] companion object { private val CACHE: List = File("src/main/resources/words.txt") From 86c784f8513cc5ecd7eb5d94c019800225f3c278 Mon Sep 17 00:00:00 2001 From: cjlee38 Date: Mon, 23 May 2022 00:38:32 +0900 Subject: [PATCH 22/25] =?UTF-8?q?test:=20=EB=88=84=EB=9D=BD=ED=95=9C=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/domain/Game.kt | 4 +--- src/test/kotlin/wordle/domain/AnswerTest.kt | 16 ++++++++-------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/wordle/domain/Game.kt b/src/main/kotlin/wordle/domain/Game.kt index 2489502..9d72bf2 100644 --- a/src/main/kotlin/wordle/domain/Game.kt +++ b/src/main/kotlin/wordle/domain/Game.kt @@ -7,9 +7,7 @@ class Game(today: LocalDate) { var count = 0 private set - // private val answer: Answer = Answer(WordPicker().pickAnswer(today)) - private val answer: Answer = Answer(Word("marry")) - + private val answer: Answer = Answer(WordPicker().pickAnswer(today)) private val _guessResults: MutableList = mutableListOf() val guessResults: List diff --git a/src/test/kotlin/wordle/domain/AnswerTest.kt b/src/test/kotlin/wordle/domain/AnswerTest.kt index 1be7505..cfd68be 100644 --- a/src/test/kotlin/wordle/domain/AnswerTest.kt +++ b/src/test/kotlin/wordle/domain/AnswerTest.kt @@ -11,7 +11,7 @@ class AnswerTest { fun compareWithGreenAndGray() { val answer = Answer(Word("cigar")) - answer.compare(Word("clear")) shouldContainExactly + answer.compare(Word("clear")).values shouldContainExactly listOf(Color.GREEN, Color.GRAY, Color.GRAY, Color.GREEN, Color.GREEN) } @@ -20,7 +20,7 @@ class AnswerTest { fun compareWithAllGreens() { val answer = Answer(Word("cigar")) - answer.compare(Word("cigar")) shouldContainExactly + answer.compare(Word("cigar")).values shouldContainExactly listOf(Color.GREEN, Color.GREEN, Color.GREEN, Color.GREEN, Color.GREEN) } @@ -29,7 +29,7 @@ class AnswerTest { fun compareWithAllColors() { val answer = Answer(Word("spill")) - answer.compare(Word("hello")) shouldContainExactly + answer.compare(Word("hello")).values shouldContainExactly listOf(Color.GRAY, Color.GRAY, Color.YELLOW, Color.GREEN, Color.GRAY) } @@ -38,7 +38,7 @@ class AnswerTest { fun compareWithYellowAndGray1() { val answer = Answer(Word("front")) - answer.compare(Word("totem")) shouldContainExactly + answer.compare(Word("totem")).values shouldContainExactly listOf(Color.YELLOW, Color.YELLOW, Color.GRAY, Color.GRAY, Color.GRAY) } @@ -47,7 +47,7 @@ class AnswerTest { fun compareWithYellowAndGray2() { val answer = Answer(Word("totem")) - answer.compare(Word("start")) shouldContainExactly + answer.compare(Word("start")).values shouldContainExactly listOf(Color.GRAY, Color.YELLOW, Color.GRAY, Color.GRAY, Color.YELLOW) } @@ -56,7 +56,7 @@ class AnswerTest { fun compareWithYellowAndGray3() { val answer = Answer(Word("witch")) - answer.compare(Word("timid")) shouldContainExactly + answer.compare(Word("timid")).values shouldContainExactly listOf(Color.YELLOW, Color.GREEN, Color.GRAY, Color.GRAY, Color.GRAY) } @@ -65,7 +65,7 @@ class AnswerTest { fun compareWithAllGrays() { val answer = Answer(Word("parry")) - answer.compare(Word("biome")) shouldContainExactly + answer.compare(Word("biome")).values shouldContainExactly listOf(Color.GRAY, Color.GRAY, Color.GRAY, Color.GRAY, Color.GRAY) } @@ -74,7 +74,7 @@ class AnswerTest { fun compareWithAllYellows() { val answer = Answer(Word("parse")) - answer.compare(Word("spear")) shouldContainExactly + answer.compare(Word("spear")).values shouldContainExactly listOf(Color.YELLOW, Color.YELLOW, Color.YELLOW, Color.YELLOW, Color.YELLOW) } } From 04c9896d28ea8ff50bf0d5b36d688ad866e308b4 Mon Sep 17 00:00:00 2001 From: cjlee38 Date: Mon, 23 May 2022 00:46:59 +0900 Subject: [PATCH 23/25] =?UTF-8?q?refactor:=20=EB=8F=84=EB=A9=94=EC=9D=B8?= =?UTF-8?q?=EC=97=90=EC=84=9C=20view=20=EA=B0=92=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/domain/Color.kt | 8 ++++---- src/main/kotlin/wordle/view/OutputView.kt | 9 ++++++++- src/test/kotlin/wordle/domain/AnswerTest.kt | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/wordle/domain/Color.kt b/src/main/kotlin/wordle/domain/Color.kt index 448ba55..3740760 100644 --- a/src/main/kotlin/wordle/domain/Color.kt +++ b/src/main/kotlin/wordle/domain/Color.kt @@ -1,7 +1,7 @@ package wordle.domain -enum class Color(val representation: String) { - GREEN("🟩"), - YELLOW("🟨"), - GRAY("⬜") +enum class Color { + GREEN, + YELLOW, + GRAY } diff --git a/src/main/kotlin/wordle/view/OutputView.kt b/src/main/kotlin/wordle/view/OutputView.kt index 02968e4..ae0d5ba 100644 --- a/src/main/kotlin/wordle/view/OutputView.kt +++ b/src/main/kotlin/wordle/view/OutputView.kt @@ -1,5 +1,6 @@ package wordle.view +import wordle.domain.Color import wordle.domain.Colors import wordle.domain.Game @@ -18,7 +19,13 @@ object OutputView { } private fun Colors.printColors() { - this.values.forEach { print(it.representation) } + this.values.forEach { + print(when (it) { + Color.GREEN -> "🟩" + Color.YELLOW -> "🟨" + Color.GRAY -> "⬜" + }) + } } fun printCount(tryCount: Int) { diff --git a/src/test/kotlin/wordle/domain/AnswerTest.kt b/src/test/kotlin/wordle/domain/AnswerTest.kt index cfd68be..3304e68 100644 --- a/src/test/kotlin/wordle/domain/AnswerTest.kt +++ b/src/test/kotlin/wordle/domain/AnswerTest.kt @@ -52,7 +52,7 @@ class AnswerTest { } @Test - @DisplayName("위치만 일치하는 글자들과 틀린 글자들이 존재하는 예측을 비교한다..") + @DisplayName("위치만 일치하는 글자들과 틀린 글자들이 존재하는 예측을 비교한다.") fun compareWithYellowAndGray3() { val answer = Answer(Word("witch")) From 0a3af017b71fecf22088ae51fbade722abc572fc Mon Sep 17 00:00:00 2001 From: cjlee38 Date: Mon, 23 May 2022 00:49:20 +0900 Subject: [PATCH 24/25] =?UTF-8?q?style:=20ktlint=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/view/OutputView.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/wordle/view/OutputView.kt b/src/main/kotlin/wordle/view/OutputView.kt index ae0d5ba..b9c61ab 100644 --- a/src/main/kotlin/wordle/view/OutputView.kt +++ b/src/main/kotlin/wordle/view/OutputView.kt @@ -20,11 +20,13 @@ object OutputView { private fun Colors.printColors() { this.values.forEach { - print(when (it) { - Color.GREEN -> "🟩" - Color.YELLOW -> "🟨" - Color.GRAY -> "⬜" - }) + print( + when (it) { + Color.GREEN -> "🟩" + Color.YELLOW -> "🟨" + Color.GRAY -> "⬜" + } + ) } } From d5216d27c23642ec6e2a425c9972f1626d2a0496 Mon Sep 17 00:00:00 2001 From: cjlee38 Date: Mon, 23 May 2022 00:54:01 +0900 Subject: [PATCH 25/25] =?UTF-8?q?style:=20=ED=95=9C=EC=A4=84=20=EB=A9=94?= =?UTF-8?q?=EC=86=8C=EB=93=9C=EB=8A=94=20=EC=8B=9D=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/domain/Answer.kt | 10 ++++------ src/main/kotlin/wordle/domain/Colors.kt | 4 +--- src/main/kotlin/wordle/domain/Game.kt | 4 +--- src/main/kotlin/wordle/domain/Word.kt | 8 ++------ src/main/kotlin/wordle/view/OutputView.kt | 8 ++------ 5 files changed, 10 insertions(+), 24 deletions(-) diff --git a/src/main/kotlin/wordle/domain/Answer.kt b/src/main/kotlin/wordle/domain/Answer.kt index 1fd8cdf..e2aaecf 100644 --- a/src/main/kotlin/wordle/domain/Answer.kt +++ b/src/main/kotlin/wordle/domain/Answer.kt @@ -24,15 +24,13 @@ class Answer(private val word: Word) { } } - private fun isExactMatch(word: Word, index: Int, consumedIndices: MutableList): Boolean { - return this.word.compareByIndex(word, index) && consumeIfMatched(consumedIndices, index) - } + private fun isExactMatch(word: Word, index: Int, consumedIndices: MutableList): Boolean = + this.word.compareByIndex(word, index) && consumeIfMatched(consumedIndices, index) - private fun isAnyMatch(word: Word, outerIndex: Int, consumedIndices: MutableList): Boolean { - return (RANGE_START..RANGE_END).any { + private fun isAnyMatch(word: Word, outerIndex: Int, consumedIndices: MutableList): Boolean = + (RANGE_START..RANGE_END).any { this.word.compareByIndex(word, it, outerIndex) && consumeIfMatched(consumedIndices, it) } - } private fun consumeIfMatched(consumedIndices: MutableList, index: Int): Boolean { if (!consumedIndices.contains(index)) { diff --git a/src/main/kotlin/wordle/domain/Colors.kt b/src/main/kotlin/wordle/domain/Colors.kt index 5d323ed..f2f672f 100644 --- a/src/main/kotlin/wordle/domain/Colors.kt +++ b/src/main/kotlin/wordle/domain/Colors.kt @@ -5,9 +5,7 @@ class Colors(val values: MutableList) { companion object { private const val SIZE = 5 - fun createEmpty(): Colors { - return Colors(mutableListOf(Color.GRAY, Color.GRAY, Color.GRAY, Color.GRAY, Color.GRAY)) - } + fun createEmpty(): Colors = Colors(mutableListOf(Color.GRAY, Color.GRAY, Color.GRAY, Color.GRAY, Color.GRAY)) } init { diff --git a/src/main/kotlin/wordle/domain/Game.kt b/src/main/kotlin/wordle/domain/Game.kt index 9d72bf2..bd02f85 100644 --- a/src/main/kotlin/wordle/domain/Game.kt +++ b/src/main/kotlin/wordle/domain/Game.kt @@ -20,9 +20,7 @@ class Game(today: LocalDate) { } } - fun isOver(): Boolean { - return count >= maxCount || _guessResults.any { it.isCorrect() } - } + fun isOver(): Boolean = count >= maxCount || _guessResults.any { it.isCorrect() } companion object { const val maxCount = 6 diff --git a/src/main/kotlin/wordle/domain/Word.kt b/src/main/kotlin/wordle/domain/Word.kt index 6c43dae..7b33fa6 100644 --- a/src/main/kotlin/wordle/domain/Word.kt +++ b/src/main/kotlin/wordle/domain/Word.kt @@ -21,12 +21,8 @@ data class Word(private val word: String) { private val CACHE: List = File("src/main/resources/words.txt") .readLines() - fun String.containsInWord(): Boolean { - return CACHE.contains(this) - } + fun String.containsInWord(): Boolean = CACHE.contains(this) - fun findWordByDay(day: Int): Word { - return Word(CACHE[day % CACHE.size]) - } + fun findWordByDay(day: Int): Word = Word(CACHE[day % CACHE.size]) } } diff --git a/src/main/kotlin/wordle/view/OutputView.kt b/src/main/kotlin/wordle/view/OutputView.kt index b9c61ab..2e06213 100644 --- a/src/main/kotlin/wordle/view/OutputView.kt +++ b/src/main/kotlin/wordle/view/OutputView.kt @@ -30,11 +30,7 @@ object OutputView { } } - fun printCount(tryCount: Int) { - println("$tryCount/${Game.maxCount}") - } + fun printCount(tryCount: Int) = println("$tryCount/${Game.maxCount}") - fun printError(message: String?) { - println(message) - } + fun printError(message: String?) = println(message) }