From 300705c21719893ef05fad9dd0937d40b73604b4 Mon Sep 17 00:00:00 2001 From: cousim46 Date: Sun, 16 Jun 2024 19:15:35 +0900 Subject: [PATCH 01/28] =?UTF-8?q?test=20:=20=EB=8B=A8=EC=96=B4=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20-=20=EB=8B=A8=EC=96=B4=EA=B0=80=20?= =?UTF-8?q?=EC=A1=B4=EC=9E=AC=ED=95=98=EC=A7=80=20=EC=95=8A=EC=9D=84=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20-=20=EB=8B=A8=EC=96=B4=EA=B0=80=20?= =?UTF-8?q?=EC=A1=B4=EC=9E=AC=20=ED=95=A0=20=EA=B2=BD=EC=9A=B0=20-=20?= =?UTF-8?q?=ED=98=84=EC=9E=AC=EB=82=A0=EC=A7=9C=20=EA=B8=B0=EC=A4=80?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=8B=A8=EC=96=B4=20=EC=B6=94=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/kotlin/DictionaryTest.kt | 58 +++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/test/kotlin/DictionaryTest.kt diff --git a/src/test/kotlin/DictionaryTest.kt b/src/test/kotlin/DictionaryTest.kt new file mode 100644 index 0000000..ee475ed --- /dev/null +++ b/src/test/kotlin/DictionaryTest.kt @@ -0,0 +1,58 @@ +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import java.io.File +import java.time.LocalDate +import java.time.Period + +class DictionaryTest { + + @ParameterizedTest + @ValueSource(strings = ["asder", "wrfdx", "bljwq"]) + fun `단어가 존재하지 않으면 false`(word: String) { + //when + val result = Dictionary.hasWord(word) + + //then + assertThat(result).isFalse() + } + + @ParameterizedTest + @ValueSource(strings = ["hello", "organ", "mercy"]) + fun `단어가 존재하면 true`(word: String) { + //when + val result = Dictionary.hasWord(word) + + //then + assertThat(result).isTrue() + } + + @Test + fun `현재날짜 기준으로 단어를 추출한다`() { + val now = LocalDate.now() + val findTodayWord = Dictionary.findTodayWord(now) + //then + assertThat(findTodayWord).isNotNull() + } +} + +object Dictionary { + private const val PATH = "./src/main/resources" + private const val FILE_NAME = "words.txt" + private val words: List = File(PATH, FILE_NAME).readLines() + private val BASE_DATE = LocalDate.of(2021, 6, 19) + fun hasWord(word: String): Boolean { + return words.contains(word) + } + + operator fun get(index: Int): String{ + return words[index] + } + + fun findTodayWord(nowDate: LocalDate): String { + val calcDate = nowDate.toEpochDay().minus(BASE_DATE.toEpochDay()) + val index: Int = (calcDate % words.size).toInt() + return words[index] + } +} \ No newline at end of file From 0e29dbcbe1d0fbad9ecbe228a7d5816e1f197a4e Mon Sep 17 00:00:00 2001 From: cousim46 Date: Sun, 16 Jun 2024 19:17:28 +0900 Subject: [PATCH 02/28] =?UTF-8?q?test=20:=20=EA=B2=8C=EC=9E=84=20=EC=8B=A4?= =?UTF-8?q?=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/kotlin/GameTest.kt | 106 ++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 src/test/kotlin/GameTest.kt diff --git a/src/test/kotlin/GameTest.kt b/src/test/kotlin/GameTest.kt new file mode 100644 index 0000000..2379e49 --- /dev/null +++ b/src/test/kotlin/GameTest.kt @@ -0,0 +1,106 @@ +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import stage.Stage +import stage.step.Step +import stage.step.word.Word +import java.time.LocalDate +import java.util.Scanner + + +class GameTest { + +/** + * todo + * 테스트를 해야할까? + * */ + +} + + +/** + * 게임 시작시 + * - 시작문구 출력 + * - 정답만들기 + * 게임 진행시 + * - 문구 입력받기 + * - 결과 출력 + * - 스테이지 끝나면(COMPLETE/FAIL) COMPLETE: 몇 스텝만에 맞췄는지 / FAIL: 정답이 뭔지 + * + * 1. Dictionary 날짜 계산 + * 2. + * */ +class Game { + + companion object { + fun start(now: LocalDate) { + val answer = Dictionary.findTodayWord(now) + val stage = Stage(answer = answer) + Print.start() + while(stage.state == Stage.State.PROGRESS) { + try { + val scanner = Scanner(System.`in`) // input 역할 나누기 + Print.requestInput() + val inputValue = scanner.nextLine() + val value = Word.fromInput(inputValue){ Dictionary.hasWord(inputValue) }.value + stage.play(value) + Print.resultStage(stage, answer) + } catch (e: Exception) { + println(e.message) + } + } + } + } +} + +class Print { + + companion object { + fun start() { + println(""" + WORDLE을 6번 만에 맞춰 보세요. + 시도의 결과는 타일의 색 변화로 나타납니다. + """.trimIndent()) + } + + fun requestInput() { + println("정답을 입력해 주세요.") + } + + + fun resultStage(stage: Stage, answer: String) { + when(stage.state) { + Stage.State.FAIL -> { + resultAllStep(stage) + println("answer = $answer") + } + Stage.State.COMPLETE -> { + println("${stage.steps.size}/6") + resultAllStep(stage) + } + Stage.State.PROGRESS -> { + resultAllStep(stage) + } + } + } + + fun resultAllStep(stage: Stage) { + stage.steps.forEach{ resultStep(it) } + } + + fun resultStep(step: Step) { + val str: StringBuilder = StringBuilder() + step.code.forEach { + when(it) { + Step.Result.CORRECT -> str.append("\uD83D\uDFE9") + Step.Result.MISMATCH -> str.append("\uD83D\uDFE8") + Step.Result.WRONG -> str.append("⬜") + } + } + println(str) + } + } +} +fun main(args: Array) { + Game.start(LocalDate.now()) +} \ No newline at end of file From b034aabff0e3c331840db6aadd08a36a8987f7f5 Mon Sep 17 00:00:00 2001 From: cousim46 Date: Sun, 16 Jun 2024 19:18:39 +0900 Subject: [PATCH 03/28] =?UTF-8?q?test=20:=20=EC=8A=A4=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20-=20=EC=8A=A4?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EC=A7=80=20=EC=83=81=ED=83=9C=EA=B0=80=20fai?= =?UTF-8?q?l=20-=20=EC=8A=A4=ED=85=8C=EC=9D=B4=EC=A7=80=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EA=B0=80=20progress=EC=9D=BC=EB=95=8C=20-=20=EC=A0=95?= =?UTF-8?q?=EB=8B=B5=EC=9D=84=20=EB=A7=9E=EC=B7=84=EC=9D=84=EB=95=8C=20-?= =?UTF-8?q?=20=EC=A0=95=EB=8B=B5=EC=9D=84=20=EB=A7=9E=EC=B6=94=EC=A7=80=20?= =?UTF-8?q?=EB=AA=BB=ED=96=88=EC=9D=84=EB=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/kotlin/stage/StageTest.kt | 92 ++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 src/test/kotlin/stage/StageTest.kt diff --git a/src/test/kotlin/stage/StageTest.kt b/src/test/kotlin/stage/StageTest.kt new file mode 100644 index 0000000..bd515fa --- /dev/null +++ b/src/test/kotlin/stage/StageTest.kt @@ -0,0 +1,92 @@ +package stage + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import stage.step.Step + +/** + * stage는 answer을 입력받아서 생성된다 + * stage를 play 할 때 마다 step이 만들어짐 + * play를 할 때 step이 isCorrect -> true이면 맞춘 종료 + * play를 6번 했는데 모든 step이 isCorrect -> false면 틀린 종료 + * + * stage 상태: 진행중(Progress)/맞춘 종료(Complete)/틀린 종료(Fail) -> + * 워들 -> 정답: xxx (틀린여부) + * */ +class StageTest { + + @Test + fun `스테이지가 Progress일 때 play할 수 있음`() { + val stage = Stage("answer", Stage.State.PROGRESS) + assertThat(stage.canPlay()).isTrue() + } + + @Test + fun `스테이지가 Complete일 때 더이상 play를 할 수 없음`() { + val stage = Stage("answer", Stage.State.COMPLETE) + assertThat(stage.canPlay()).isFalse() + } + + + @Test + fun `스테이지가 Fail일 때 더이상 play를 할 수 없음`() { + val stage = Stage("answer", Stage.State.FAIL) + assertThat(stage.canPlay()).isFalse() + } + + @Test + fun `스테이지가 play시 정답을 맞추면(step이 isCorrect) Complete`() { + // given + val stage = Stage("answer", Stage.State.PROGRESS) + + // when + stage.play("answer") + + // then + assertThat(stage.state).isEqualTo(Stage.State.COMPLETE) + } + + @Test + fun `스테이지가 play시 6번 모두 정답을 맞추지 못하면(step이 모두 isCorrect false) FAIL`() { + // given + val stage = Stage("answer", Stage.State.PROGRESS) + + // when + stage.play("world") + stage.play("world") + stage.play("world") + stage.play("world") + stage.play("world") + stage.play("world") + + // then + assertThat(stage.state).isEqualTo(Stage.State.FAIL) + } +} + +data class Stage(val answer: String, var state: State = State.PROGRESS) { + + val steps = mutableListOf() + + enum class State { + PROGRESS, COMPLETE, FAIL + } + + fun play(word: String) { + if (!canPlay()) return + val step = Step.create(answer, word) + steps.add(step) + + if (step.isCorrect) { + state = State.COMPLETE + } + + if (steps.size == 6 && state != State.COMPLETE) { + state = State.FAIL + } + } + + fun canPlay(): Boolean { + return state != State.COMPLETE && state != State.FAIL + } +} \ No newline at end of file From 31846ceff2efae13a958d6ce8dcff75bec5b2f9e Mon Sep 17 00:00:00 2001 From: cousim46 Date: Sun, 16 Jun 2024 19:20:58 +0900 Subject: [PATCH 04/28] =?UTF-8?q?test=20:=20=EC=8A=A4=ED=85=9D=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20-=20=EC=99=84=EC=A0=84=ED=9E=88=20?= =?UTF-8?q?=EB=8F=99=EC=9D=BC=ED=95=9C=20=EB=8B=A8=EC=96=B4=EB=A6=AC=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20-=20=ED=8F=AC=ED=95=A8=EB=90=98=EC=96=B4?= =?UTF-8?q?=EC=9E=88=EC=A7=80=EB=A7=8C=20=EC=9C=84=EC=B9=98=EA=B0=80=20?= =?UTF-8?q?=EC=9D=BC=EC=B9=98=ED=95=98=EC=A7=80=20=EC=95=8A=EC=9D=84=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20-=20=EB=8B=A8=EC=96=B4=20=ED=8F=AC?= =?UTF-8?q?=ED=95=A8,=20=EC=9C=84=EC=B9=98=20=EC=9D=BC=EC=B9=98=20-=20?= =?UTF-8?q?=EB=8B=A8=EC=96=B4=EB=8A=94=20=ED=8F=AC=ED=95=A8=EB=90=98?= =?UTF-8?q?=EC=96=B4=20=EC=9E=88=EC=9C=BC=EB=82=98=20=EC=9C=84=EC=B9=98?= =?UTF-8?q?=EA=B0=80=20=EC=9D=BC=EC=B9=98=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EA=B3=A0=20=EC=A0=95=EB=8B=B5=EA=B0=9C=EC=88=98=EC=9D=98=20?= =?UTF-8?q?=EB=8B=A8=EC=96=B4=EB=B3=B4=EB=8B=A4=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=EA=B0=AF=EC=88=98=EA=B0=80=20=EB=8D=94=20=EB=A7=8E=EC=9D=84=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=EC=9D=BC=EC=B9=98=ED=95=9C=20=ED=9A=9F?= =?UTF-8?q?=EC=88=98(=EC=99=BC=EC=AA=BD=EC=97=90=EC=84=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=B8=EC=AA=BD=EC=9C=BC=EB=A1=9C=20=EA=B3=84=EC=82=B0)?= =?UTF-8?q?=EA=B0=80=20=EC=A0=81=EC=9D=84=20=EA=B2=BD=EC=9A=B0=20-=20?= =?UTF-8?q?=EB=8B=A8=EC=96=B4=EB=8A=94=20=ED=8F=AC=ED=95=A8=EC=A1=B0?= =?UTF-8?q?=EC=B0=A8=20=EC=95=88=EB=90=98=EC=96=B4=EC=9E=88=EC=9D=84=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/kotlin/stage/step/StepTest.kt | 151 +++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 src/test/kotlin/stage/step/StepTest.kt diff --git a/src/test/kotlin/stage/step/StepTest.kt b/src/test/kotlin/stage/step/StepTest.kt new file mode 100644 index 0000000..84cb8c9 --- /dev/null +++ b/src/test/kotlin/stage/step/StepTest.kt @@ -0,0 +1,151 @@ +package stage.step + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test + +/** + * 남은 기능 + * - 결과 값 담기 - Stage + * - 출력하기 (UI) + * + * */ + + +class StepTest { + @Nested + inner class ResultTest { + @Test + fun `완전히 동일한 단어일 경우 전부 CORRECT`() { + val answer = "hello" + val word = "hello" + + assertThat(Step.create(answer, word).code).isEqualTo(Array(5){ Step.Result.CORRECT }.toList()) + } + + @Test + fun `단어는 포함되어 있고 위치가 일치하면 CORRECT`() { + val answer = "hello" + val word = "helol" + + val resultCode = Step.create(answer, word).code + + // then + assertThat(resultCode[0]).isEqualTo(Step.Result.CORRECT) + assertThat(resultCode[2]).isEqualTo(Step.Result.CORRECT) + } + @Test + fun `단어는 포함되어 있으나 위치가 일치하지 않을경우 MISMATCH`() { + val answer = "hello" + val word = "helol" + + val resultCode = Step.create(answer, word).code + + // then + assertThat(resultCode[3]).isEqualTo(Step.Result.MISMATCH) + assertThat(resultCode[4]).isEqualTo(Step.Result.MISMATCH) + } + + @Test + fun `단어는 포함되어 있으나 위치가 일치하지 않고 정답개수의 단어보다 입력 갯수가 더 많을 경우 일치한 횟수(왼쪽에서 오른쪽으로 계산)가 적으면 MISMATCH`() { + val answer = "hello" + val word = "heool" + + val resultCode = Step.create(answer, word).code + + // then + assertThat(resultCode[2]).isEqualTo(Step.Result.MISMATCH) + } + + @Test + fun `단어는 포함되어 있으나 위치가 일치하지 않고 정답개수의 단어보다 입력 갯수가 더 많을 경우 일치한 횟수(왼쪽에서 오른쪽으로 계산)가 많으면 WRONG`() { + val answer = "hello" + val word = "heool" + + val resultCode = Step.create(answer, word).code + + // then + assertThat(resultCode[3]).isEqualTo(Step.Result.WRONG) + } + + @Test + fun `단어는 포함조차 안되어있으면 WRONG`() { + val answer = "hello" + val word = "helok" + + val resultCode = Step.create(answer, word).code + + // then + assertThat(resultCode[4]).isEqualTo(Step.Result.WRONG) + } + } + + @Test + fun `Result가 전부 CORRECT면 맞춤(isCorrect가 true)`() { + assertThat(Step(listOf(Step.Result.CORRECT, Step.Result.CORRECT )).isCorrect).isTrue() + } + + @Test + fun `Result가 전부 CORRECT가 아닐 경우에는 틀림(isCorrect가 false)`() { + assertThat(Step(listOf(Step.Result.CORRECT, Step.Result.MISMATCH, Step.Result.WRONG)).isCorrect).isFalse() + } + +} + +data class Step(val code: List) { + + val isCorrect = code.all { it == Result.CORRECT } + + enum class Result { + CORRECT, + MISMATCH, + WRONG; + } + + companion object { + fun create(answer: String, word: String): Step { + if (answer == word) { + return Step(Array(5) { Result.CORRECT }.toList()) + } + val answerBuilder = StringBuilder(answer) + + val correctResult = correct(answerBuilder = answerBuilder, word = word) + mismatch(answerBuilder = answerBuilder, word = word, codes = correctResult) + return Step(correctResult.toList()) + } + + fun correct(answerBuilder: StringBuilder, word: String): Array { + val codes = Array(5) { Result.WRONG } + + word.forEachIndexed { index, c -> + if (answerBuilder[index] == c) { + codes[index] = Result.CORRECT + } + } + + (4 downTo 0).forEach { + if(codes[it] == Result.CORRECT) { + answerBuilder.deleteAt(it) + } + } + return codes + } + + + fun mismatch( + answerBuilder: StringBuilder, + word: String, + codes: Array + ): Array { + + word.forEachIndexed { index, c -> + if (codes[index] != Result.CORRECT &&answerBuilder.contains(c)) { + codes[index] = Result.MISMATCH + val indexOf = answerBuilder.indexOf(c) + answerBuilder.deleteAt(indexOf) + } + } + return codes + } + } +} From dcad2b3b97a68f141ccde9df861758a540d389f2 Mon Sep 17 00:00:00 2001 From: cousim46 Date: Sun, 16 Jun 2024 19:21:42 +0900 Subject: [PATCH 05/28] =?UTF-8?q?test=20:=20=EB=8B=A8=EC=96=B4=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20-=20=EB=AA=A8=EB=91=90=20=EC=98=81?= =?UTF-8?q?=EB=AC=B8=EC=9C=BC=EB=A1=9C=20=EA=B5=AC=EC=84=B1=20-=205?= =?UTF-8?q?=EA=B8=80=EC=9E=90=EA=B0=80=20=EC=95=84=EB=8B=90=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=20-=20=EC=98=81=EC=96=B4=EB=A5=BC=20=EB=8C=80?= =?UTF-8?q?=EB=AC=B8=EC=9E=90=EC=97=90=EC=84=9C=20=EC=86=8C=EB=AC=B8?= =?UTF-8?q?=EC=9E=90=EB=A1=9C=20=EC=B9=98=ED=99=98=20-=20=EB=8B=A8?= =?UTF-8?q?=EC=96=B4=EA=B0=80=20=EC=A1=B4=EC=9E=AC=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EC=9D=84=20=EA=B2=BD=EC=9A=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/kotlin/stage/step/word/WordTest.kt | 73 +++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/test/kotlin/stage/step/word/WordTest.kt diff --git a/src/test/kotlin/stage/step/word/WordTest.kt b/src/test/kotlin/stage/step/word/WordTest.kt new file mode 100644 index 0000000..b7a97f3 --- /dev/null +++ b/src/test/kotlin/stage/step/word/WordTest.kt @@ -0,0 +1,73 @@ +package stage.step.word + + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource + +class WordTest { + @ParameterizedTest + @ValueSource(strings = ["-1", "1", " ", "w!", " ;fs"]) + fun `모두 영문으로 구성된다`(value: String){ + //when + val errorResponse = assertThrows { Word.fromInput(value) } + + ///then + assertThat(errorResponse.message).isEqualTo("영문만 입력해야합니다.") + } + + @ParameterizedTest + @ValueSource(strings = ["test", "hi", "h", "tttttt"]) + fun `5글자가 아니면 IllegalArgumentException 예외가 발생한다`(value: String){ + //when + val errorResponse = assertThrows { Word.fromInput(value) } + + ///then + assertThat(errorResponse.message).isEqualTo("5글자여야 합니다.") + } + + @Test + fun `영문 대문자는 소문자로 치환된다`() { + assertThat(Word.fromInput("Hello")).isEqualTo(Word.fromInput("hello")) + } + + @ParameterizedTest + @ValueSource(strings = ["testa", "hiwww", "heeee", "tttaa"]) + fun `존재하지 않으면 IllegalArgumentException 예외가 발생한다`(value: String) { + val errorResponse = + assertThrows { Word.fromInput(value) { _ -> false } } + + //then + assertThat(errorResponse.message).isEqualTo("존재하지 않는 단어입니다.") + } +} + +data class Word (val value: String) { + companion object { + private val englishRegex = Regex("^[A-Za-z]*") + private fun validateInput(input: String) { + if (!input.matches(englishRegex)) { + throw IllegalArgumentException("영문만 입력해야합니다.") + } + if (input.length != 5) { + throw IllegalArgumentException("5글자여야 합니다.") + } + } + + private fun checkInDict(isWordInDic: Boolean) { + if (!isWordInDic) { + throw IllegalArgumentException("존재하지 않는 단어입니다.") + } + } + + fun fromInput(input: String, dicPolicy: (s: String) -> Boolean = {_ -> true}): Word { + validateInput(input) + val word = input.lowercase() + checkInDict(dicPolicy(word)) + return Word(word) + } + } +} + From 8c77714cc84ee4335434339a85ca1cbe49968fcd Mon Sep 17 00:00:00 2001 From: cousim46 Date: Sun, 16 Jun 2024 20:06:29 +0900 Subject: [PATCH 06/28] =?UTF-8?q?fix=20:=20jdk=20=EB=B2=84=EC=A0=84=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 19a215a..2b3dd38 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ group = "camp.nextstep.edu" version = "1.0-SNAPSHOT" kotlin { - jvmToolchain(21) + jvmToolchain(17) } repositories { From f0c3b84644035fa9d2eccd92e8ca4154fb33971f Mon Sep 17 00:00:00 2001 From: cousim46 Date: Sun, 16 Jun 2024 20:06:39 +0900 Subject: [PATCH 07/28] =?UTF-8?q?fix=20:=20indent=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/kotlin/DictionaryTest.kt | 88 ++++---- src/test/kotlin/GameTest.kt | 117 ++++++----- src/test/kotlin/stage/StageTest.kt | 128 ++++++------ src/test/kotlin/stage/step/StepTest.kt | 218 +++++++++++--------- src/test/kotlin/stage/step/word/WordTest.kt | 100 ++++----- 5 files changed, 335 insertions(+), 316 deletions(-) diff --git a/src/test/kotlin/DictionaryTest.kt b/src/test/kotlin/DictionaryTest.kt index ee475ed..b5bf93c 100644 --- a/src/test/kotlin/DictionaryTest.kt +++ b/src/test/kotlin/DictionaryTest.kt @@ -8,51 +8,51 @@ import java.time.Period class DictionaryTest { - @ParameterizedTest - @ValueSource(strings = ["asder", "wrfdx", "bljwq"]) - fun `단어가 존재하지 않으면 false`(word: String) { - //when - val result = Dictionary.hasWord(word) - - //then - assertThat(result).isFalse() - } - - @ParameterizedTest - @ValueSource(strings = ["hello", "organ", "mercy"]) - fun `단어가 존재하면 true`(word: String) { - //when - val result = Dictionary.hasWord(word) - - //then - assertThat(result).isTrue() - } - - @Test - fun `현재날짜 기준으로 단어를 추출한다`() { - val now = LocalDate.now() - val findTodayWord = Dictionary.findTodayWord(now) - //then - assertThat(findTodayWord).isNotNull() - } + @ParameterizedTest + @ValueSource(strings = ["asder", "wrfdx", "bljwq"]) + fun `단어가 존재하지 않으면 false`(word: String) { + //when + val result = Dictionary.hasWord(word) + + //then + assertThat(result).isFalse() + } + + @ParameterizedTest + @ValueSource(strings = ["hello", "organ", "mercy"]) + fun `단어가 존재하면 true`(word: String) { + //when + val result = Dictionary.hasWord(word) + + //then + assertThat(result).isTrue() + } + + @Test + fun `현재날짜 기준으로 단어를 추출한다`() { + val now = LocalDate.now() + val findTodayWord = Dictionary.findTodayWord(now) + //then + assertThat(findTodayWord).isNotNull() + } } object Dictionary { - private const val PATH = "./src/main/resources" - private const val FILE_NAME = "words.txt" - private val words: List = File(PATH, FILE_NAME).readLines() - private val BASE_DATE = LocalDate.of(2021, 6, 19) - fun hasWord(word: String): Boolean { - return words.contains(word) - } - - operator fun get(index: Int): String{ - return words[index] - } - - fun findTodayWord(nowDate: LocalDate): String { - val calcDate = nowDate.toEpochDay().minus(BASE_DATE.toEpochDay()) - val index: Int = (calcDate % words.size).toInt() - return words[index] - } + private const val PATH = "./src/main/resources" + private const val FILE_NAME = "words.txt" + private val words: List = File(PATH, FILE_NAME).readLines() + private val BASE_DATE = LocalDate.of(2021, 6, 19) + fun hasWord(word: String): Boolean { + return words.contains(word) + } + + operator fun get(index: Int): String { + return words[index] + } + + fun findTodayWord(nowDate: LocalDate): String { + val calcDate = nowDate.toEpochDay().minus(BASE_DATE.toEpochDay()) + val index: Int = (calcDate % words.size).toInt() + return words[index] + } } \ No newline at end of file diff --git a/src/test/kotlin/GameTest.kt b/src/test/kotlin/GameTest.kt index 2379e49..81480d2 100644 --- a/src/test/kotlin/GameTest.kt +++ b/src/test/kotlin/GameTest.kt @@ -10,10 +10,10 @@ import java.util.Scanner class GameTest { -/** - * todo - * 테스트를 해야할까? - * */ + /** + * todo + * 테스트를 해야할까? + * */ } @@ -32,75 +32,80 @@ class GameTest { * */ class Game { - companion object { - fun start(now: LocalDate) { - val answer = Dictionary.findTodayWord(now) - val stage = Stage(answer = answer) - Print.start() - while(stage.state == Stage.State.PROGRESS) { - try { - val scanner = Scanner(System.`in`) // input 역할 나누기 - Print.requestInput() - val inputValue = scanner.nextLine() - val value = Word.fromInput(inputValue){ Dictionary.hasWord(inputValue) }.value - stage.play(value) - Print.resultStage(stage, answer) - } catch (e: Exception) { - println(e.message) - } - } + companion object { + fun start(now: LocalDate) { + val answer = Dictionary.findTodayWord(now) + val stage = Stage(answer = answer) + Print.start() + while (stage.state == Stage.State.PROGRESS) { + try { + val scanner = Scanner(System.`in`) // input 역할 나누기 + Print.requestInput() + val inputValue = scanner.nextLine() + val value = Word.fromInput(inputValue) { Dictionary.hasWord(inputValue) }.value + stage.play(value) + Print.resultStage(stage, answer) + } catch (e: Exception) { + println(e.message) } + } } + } } class Print { - companion object { - fun start() { - println(""" + companion object { + fun start() { + println( + """ WORDLE을 6번 만에 맞춰 보세요. 시도의 결과는 타일의 색 변화로 나타납니다. - """.trimIndent()) - } + """.trimIndent() + ) + } - fun requestInput() { - println("정답을 입력해 주세요.") - } + fun requestInput() { + println("정답을 입력해 주세요.") + } - fun resultStage(stage: Stage, answer: String) { - when(stage.state) { - Stage.State.FAIL -> { - resultAllStep(stage) - println("answer = $answer") - } - Stage.State.COMPLETE -> { - println("${stage.steps.size}/6") - resultAllStep(stage) - } - Stage.State.PROGRESS -> { - resultAllStep(stage) - } - } + fun resultStage(stage: Stage, answer: String) { + when (stage.state) { + Stage.State.FAIL -> { + resultAllStep(stage) + println("answer = $answer") } - fun resultAllStep(stage: Stage) { - stage.steps.forEach{ resultStep(it) } + Stage.State.COMPLETE -> { + println("${stage.steps.size}/6") + resultAllStep(stage) } - fun resultStep(step: Step) { - val str: StringBuilder = StringBuilder() - step.code.forEach { - when(it) { - Step.Result.CORRECT -> str.append("\uD83D\uDFE9") - Step.Result.MISMATCH -> str.append("\uD83D\uDFE8") - Step.Result.WRONG -> str.append("⬜") - } - } - println(str) + Stage.State.PROGRESS -> { + resultAllStep(stage) } + } } + + fun resultAllStep(stage: Stage) { + stage.steps.forEach { resultStep(it) } + } + + fun resultStep(step: Step) { + val str: StringBuilder = StringBuilder() + step.code.forEach { + when (it) { + Step.Result.CORRECT -> str.append("\uD83D\uDFE9") + Step.Result.MISMATCH -> str.append("\uD83D\uDFE8") + Step.Result.WRONG -> str.append("⬜") + } + } + println(str) + } + } } + fun main(args: Array) { - Game.start(LocalDate.now()) + Game.start(LocalDate.now()) } \ No newline at end of file diff --git a/src/test/kotlin/stage/StageTest.kt b/src/test/kotlin/stage/StageTest.kt index bd515fa..f62c4cf 100644 --- a/src/test/kotlin/stage/StageTest.kt +++ b/src/test/kotlin/stage/StageTest.kt @@ -15,78 +15,78 @@ import stage.step.Step * */ class StageTest { - @Test - fun `스테이지가 Progress일 때 play할 수 있음`() { - val stage = Stage("answer", Stage.State.PROGRESS) - assertThat(stage.canPlay()).isTrue() - } - - @Test - fun `스테이지가 Complete일 때 더이상 play를 할 수 없음`() { - val stage = Stage("answer", Stage.State.COMPLETE) - assertThat(stage.canPlay()).isFalse() - } - - - @Test - fun `스테이지가 Fail일 때 더이상 play를 할 수 없음`() { - val stage = Stage("answer", Stage.State.FAIL) - assertThat(stage.canPlay()).isFalse() - } - - @Test - fun `스테이지가 play시 정답을 맞추면(step이 isCorrect) Complete`() { - // given - val stage = Stage("answer", Stage.State.PROGRESS) - - // when - stage.play("answer") - - // then - assertThat(stage.state).isEqualTo(Stage.State.COMPLETE) - } - - @Test - fun `스테이지가 play시 6번 모두 정답을 맞추지 못하면(step이 모두 isCorrect false) FAIL`() { - // given - val stage = Stage("answer", Stage.State.PROGRESS) - - // when - stage.play("world") - stage.play("world") - stage.play("world") - stage.play("world") - stage.play("world") - stage.play("world") - - // then - assertThat(stage.state).isEqualTo(Stage.State.FAIL) - } + @Test + fun `스테이지가 Progress일 때 play할 수 있음`() { + val stage = Stage("answer", Stage.State.PROGRESS) + assertThat(stage.canPlay()).isTrue() + } + + @Test + fun `스테이지가 Complete일 때 더이상 play를 할 수 없음`() { + val stage = Stage("answer", Stage.State.COMPLETE) + assertThat(stage.canPlay()).isFalse() + } + + + @Test + fun `스테이지가 Fail일 때 더이상 play를 할 수 없음`() { + val stage = Stage("answer", Stage.State.FAIL) + assertThat(stage.canPlay()).isFalse() + } + + @Test + fun `스테이지가 play시 정답을 맞추면(step이 isCorrect) Complete`() { + // given + val stage = Stage("answer", Stage.State.PROGRESS) + + // when + stage.play("answer") + + // then + assertThat(stage.state).isEqualTo(Stage.State.COMPLETE) + } + + @Test + fun `스테이지가 play시 6번 모두 정답을 맞추지 못하면(step이 모두 isCorrect false) FAIL`() { + // given + val stage = Stage("answer", Stage.State.PROGRESS) + + // when + stage.play("world") + stage.play("world") + stage.play("world") + stage.play("world") + stage.play("world") + stage.play("world") + + // then + assertThat(stage.state).isEqualTo(Stage.State.FAIL) + } } data class Stage(val answer: String, var state: State = State.PROGRESS) { - val steps = mutableListOf() + val steps = mutableListOf() - enum class State { - PROGRESS, COMPLETE, FAIL - } - - fun play(word: String) { - if (!canPlay()) return - val step = Step.create(answer, word) - steps.add(step) + enum class State { + PROGRESS, COMPLETE, FAIL + } - if (step.isCorrect) { - state = State.COMPLETE - } + fun play(word: String) { + if (!canPlay()) return + val step = Step.create(answer, word) + steps.add(step) - if (steps.size == 6 && state != State.COMPLETE) { - state = State.FAIL - } + if (step.isCorrect) { + state = State.COMPLETE } - fun canPlay(): Boolean { - return state != State.COMPLETE && state != State.FAIL + if (steps.size == 6 && state != State.COMPLETE) { + state = State.FAIL } + } + + fun canPlay(): Boolean { + return state != State.COMPLETE && state != State.FAIL + } } \ No newline at end of file diff --git a/src/test/kotlin/stage/step/StepTest.kt b/src/test/kotlin/stage/step/StepTest.kt index 84cb8c9..6ec62f2 100644 --- a/src/test/kotlin/stage/step/StepTest.kt +++ b/src/test/kotlin/stage/step/StepTest.kt @@ -13,139 +13,153 @@ import org.junit.jupiter.api.Test class StepTest { - @Nested - inner class ResultTest { - @Test - fun `완전히 동일한 단어일 경우 전부 CORRECT`() { - val answer = "hello" - val word = "hello" - - assertThat(Step.create(answer, word).code).isEqualTo(Array(5){ Step.Result.CORRECT }.toList()) - } - - @Test - fun `단어는 포함되어 있고 위치가 일치하면 CORRECT`() { - val answer = "hello" - val word = "helol" - - val resultCode = Step.create(answer, word).code + @Nested + inner class ResultTest { + @Test + fun `완전히 동일한 단어일 경우 전부 CORRECT`() { + val answer = "hello" + val word = "hello" + + assertThat( + Step.create( + answer, + word + ).code + ).isEqualTo(Array(5) { Step.Result.CORRECT }.toList()) + } - // then - assertThat(resultCode[0]).isEqualTo(Step.Result.CORRECT) - assertThat(resultCode[2]).isEqualTo(Step.Result.CORRECT) - } - @Test - fun `단어는 포함되어 있으나 위치가 일치하지 않을경우 MISMATCH`() { - val answer = "hello" - val word = "helol" + @Test + fun `단어는 포함되어 있고 위치가 일치하면 CORRECT`() { + val answer = "hello" + val word = "helol" - val resultCode = Step.create(answer, word).code + val resultCode = Step.create(answer, word).code - // then - assertThat(resultCode[3]).isEqualTo(Step.Result.MISMATCH) - assertThat(resultCode[4]).isEqualTo(Step.Result.MISMATCH) - } + // then + assertThat(resultCode[0]).isEqualTo(Step.Result.CORRECT) + assertThat(resultCode[2]).isEqualTo(Step.Result.CORRECT) + } - @Test - fun `단어는 포함되어 있으나 위치가 일치하지 않고 정답개수의 단어보다 입력 갯수가 더 많을 경우 일치한 횟수(왼쪽에서 오른쪽으로 계산)가 적으면 MISMATCH`() { - val answer = "hello" - val word = "heool" + @Test + fun `단어는 포함되어 있으나 위치가 일치하지 않을경우 MISMATCH`() { + val answer = "hello" + val word = "helol" - val resultCode = Step.create(answer, word).code + val resultCode = Step.create(answer, word).code - // then - assertThat(resultCode[2]).isEqualTo(Step.Result.MISMATCH) - } + // then + assertThat(resultCode[3]).isEqualTo(Step.Result.MISMATCH) + assertThat(resultCode[4]).isEqualTo(Step.Result.MISMATCH) + } - @Test - fun `단어는 포함되어 있으나 위치가 일치하지 않고 정답개수의 단어보다 입력 갯수가 더 많을 경우 일치한 횟수(왼쪽에서 오른쪽으로 계산)가 많으면 WRONG`() { - val answer = "hello" - val word = "heool" + @Test + fun `단어는 포함되어 있으나 위치가 일치하지 않고 정답개수의 단어보다 입력 갯수가 더 많을 경우 일치한 횟수(왼쪽에서 오른쪽으로 계산)가 적으면 MISMATCH`() { + val answer = "hello" + val word = "heool" - val resultCode = Step.create(answer, word).code + val resultCode = Step.create(answer, word).code - // then - assertThat(resultCode[3]).isEqualTo(Step.Result.WRONG) - } + // then + assertThat(resultCode[2]).isEqualTo(Step.Result.MISMATCH) + } - @Test - fun `단어는 포함조차 안되어있으면 WRONG`() { - val answer = "hello" - val word = "helok" + @Test + fun `단어는 포함되어 있으나 위치가 일치하지 않고 정답개수의 단어보다 입력 갯수가 더 많을 경우 일치한 횟수(왼쪽에서 오른쪽으로 계산)가 많으면 WRONG`() { + val answer = "hello" + val word = "heool" - val resultCode = Step.create(answer, word).code + val resultCode = Step.create(answer, word).code - // then - assertThat(resultCode[4]).isEqualTo(Step.Result.WRONG) - } + // then + assertThat(resultCode[3]).isEqualTo(Step.Result.WRONG) } @Test - fun `Result가 전부 CORRECT면 맞춤(isCorrect가 true)`() { - assertThat(Step(listOf(Step.Result.CORRECT, Step.Result.CORRECT )).isCorrect).isTrue() - } + fun `단어는 포함조차 안되어있으면 WRONG`() { + val answer = "hello" + val word = "helok" - @Test - fun `Result가 전부 CORRECT가 아닐 경우에는 틀림(isCorrect가 false)`() { - assertThat(Step(listOf(Step.Result.CORRECT, Step.Result.MISMATCH, Step.Result.WRONG)).isCorrect).isFalse() + val resultCode = Step.create(answer, word).code + + // then + assertThat(resultCode[4]).isEqualTo(Step.Result.WRONG) } + } + + @Test + fun `Result가 전부 CORRECT면 맞춤(isCorrect가 true)`() { + assertThat(Step(listOf(Step.Result.CORRECT, Step.Result.CORRECT)).isCorrect).isTrue() + } + + @Test + fun `Result가 전부 CORRECT가 아닐 경우에는 틀림(isCorrect가 false)`() { + assertThat( + Step( + listOf( + Step.Result.CORRECT, + Step.Result.MISMATCH, + Step.Result.WRONG + ) + ).isCorrect + ).isFalse() + } } data class Step(val code: List) { - val isCorrect = code.all { it == Result.CORRECT } + val isCorrect = code.all { it == Result.CORRECT } + + enum class Result { + CORRECT, + MISMATCH, + WRONG; + } - enum class Result { - CORRECT, - MISMATCH, - WRONG; + companion object { + fun create(answer: String, word: String): Step { + if (answer == word) { + return Step(Array(5) { Result.CORRECT }.toList()) + } + val answerBuilder = StringBuilder(answer) + + val correctResult = correct(answerBuilder = answerBuilder, word = word) + mismatch(answerBuilder = answerBuilder, word = word, codes = correctResult) + return Step(correctResult.toList()) } - companion object { - fun create(answer: String, word: String): Step { - if (answer == word) { - return Step(Array(5) { Result.CORRECT }.toList()) - } - val answerBuilder = StringBuilder(answer) + fun correct(answerBuilder: StringBuilder, word: String): Array { + val codes = Array(5) { Result.WRONG } - val correctResult = correct(answerBuilder = answerBuilder, word = word) - mismatch(answerBuilder = answerBuilder, word = word, codes = correctResult) - return Step(correctResult.toList()) + word.forEachIndexed { index, c -> + if (answerBuilder[index] == c) { + codes[index] = Result.CORRECT } + } - fun correct(answerBuilder: StringBuilder, word: String): Array { - val codes = Array(5) { Result.WRONG } - - word.forEachIndexed { index, c -> - if (answerBuilder[index] == c) { - codes[index] = Result.CORRECT - } - } - - (4 downTo 0).forEach { - if(codes[it] == Result.CORRECT) { - answerBuilder.deleteAt(it) - } - } - return codes + (4 downTo 0).forEach { + if (codes[it] == Result.CORRECT) { + answerBuilder.deleteAt(it) } + } + return codes + } + + fun mismatch( + answerBuilder: StringBuilder, + word: String, + codes: Array + ): Array { - fun mismatch( - answerBuilder: StringBuilder, - word: String, - codes: Array - ): Array { - - word.forEachIndexed { index, c -> - if (codes[index] != Result.CORRECT &&answerBuilder.contains(c)) { - codes[index] = Result.MISMATCH - val indexOf = answerBuilder.indexOf(c) - answerBuilder.deleteAt(indexOf) - } - } - return codes + word.forEachIndexed { index, c -> + if (codes[index] != Result.CORRECT && answerBuilder.contains(c)) { + codes[index] = Result.MISMATCH + val indexOf = answerBuilder.indexOf(c) + answerBuilder.deleteAt(indexOf) } + } + return codes } + } } diff --git a/src/test/kotlin/stage/step/word/WordTest.kt b/src/test/kotlin/stage/step/word/WordTest.kt index b7a97f3..1fa778f 100644 --- a/src/test/kotlin/stage/step/word/WordTest.kt +++ b/src/test/kotlin/stage/step/word/WordTest.kt @@ -8,66 +8,66 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource class WordTest { - @ParameterizedTest - @ValueSource(strings = ["-1", "1", " ", "w!", " ;fs"]) - fun `모두 영문으로 구성된다`(value: String){ - //when - val errorResponse = assertThrows { Word.fromInput(value) } + @ParameterizedTest + @ValueSource(strings = ["-1", "1", " ", "w!", " ;fs"]) + fun `모두 영문으로 구성된다`(value: String) { + //when + val errorResponse = assertThrows { Word.fromInput(value) } - ///then - assertThat(errorResponse.message).isEqualTo("영문만 입력해야합니다.") - } + ///then + assertThat(errorResponse.message).isEqualTo("영문만 입력해야합니다.") + } - @ParameterizedTest - @ValueSource(strings = ["test", "hi", "h", "tttttt"]) - fun `5글자가 아니면 IllegalArgumentException 예외가 발생한다`(value: String){ - //when - val errorResponse = assertThrows { Word.fromInput(value) } + @ParameterizedTest + @ValueSource(strings = ["test", "hi", "h", "tttttt"]) + fun `5글자가 아니면 IllegalArgumentException 예외가 발생한다`(value: String) { + //when + val errorResponse = assertThrows { Word.fromInput(value) } - ///then - assertThat(errorResponse.message).isEqualTo("5글자여야 합니다.") - } + ///then + assertThat(errorResponse.message).isEqualTo("5글자여야 합니다.") + } - @Test - fun `영문 대문자는 소문자로 치환된다`() { - assertThat(Word.fromInput("Hello")).isEqualTo(Word.fromInput("hello")) - } + @Test + fun `영문 대문자는 소문자로 치환된다`() { + assertThat(Word.fromInput("Hello")).isEqualTo(Word.fromInput("hello")) + } - @ParameterizedTest - @ValueSource(strings = ["testa", "hiwww", "heeee", "tttaa"]) - fun `존재하지 않으면 IllegalArgumentException 예외가 발생한다`(value: String) { - val errorResponse = - assertThrows { Word.fromInput(value) { _ -> false } } + @ParameterizedTest + @ValueSource(strings = ["testa", "hiwww", "heeee", "tttaa"]) + fun `존재하지 않으면 IllegalArgumentException 예외가 발생한다`(value: String) { + val errorResponse = + assertThrows { Word.fromInput(value) { _ -> false } } - //then - assertThat(errorResponse.message).isEqualTo("존재하지 않는 단어입니다.") - } + //then + assertThat(errorResponse.message).isEqualTo("존재하지 않는 단어입니다.") + } } -data class Word (val value: String) { - companion object { - private val englishRegex = Regex("^[A-Za-z]*") - private fun validateInput(input: String) { - if (!input.matches(englishRegex)) { - throw IllegalArgumentException("영문만 입력해야합니다.") - } - if (input.length != 5) { - throw IllegalArgumentException("5글자여야 합니다.") - } - } +data class Word(val value: String) { + companion object { + private val englishRegex = Regex("^[A-Za-z]*") + private fun validateInput(input: String) { + if (!input.matches(englishRegex)) { + throw IllegalArgumentException("영문만 입력해야합니다.") + } + if (input.length != 5) { + throw IllegalArgumentException("5글자여야 합니다.") + } + } - private fun checkInDict(isWordInDic: Boolean) { - if (!isWordInDic) { - throw IllegalArgumentException("존재하지 않는 단어입니다.") - } - } + private fun checkInDict(isWordInDic: Boolean) { + if (!isWordInDic) { + throw IllegalArgumentException("존재하지 않는 단어입니다.") + } + } - fun fromInput(input: String, dicPolicy: (s: String) -> Boolean = {_ -> true}): Word { - validateInput(input) - val word = input.lowercase() - checkInDict(dicPolicy(word)) - return Word(word) - } + fun fromInput(input: String, dicPolicy: (s: String) -> Boolean = { _ -> true }): Word { + validateInput(input) + val word = input.lowercase() + checkInDict(dicPolicy(word)) + return Word(word) } + } } From cc8fa0b90358ba5a5985fbab0dcfef3e90356b09 Mon Sep 17 00:00:00 2001 From: cousim46 Date: Sun, 16 Jun 2024 20:06:53 +0900 Subject: [PATCH 08/28] =?UTF-8?q?feat=20:=20=EC=8B=A4=ED=96=89=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/Computer.kt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/main/kotlin/Computer.kt diff --git a/src/main/kotlin/Computer.kt b/src/main/kotlin/Computer.kt new file mode 100644 index 0000000..1e4f1f6 --- /dev/null +++ b/src/main/kotlin/Computer.kt @@ -0,0 +1,5 @@ +import java.time.LocalDate + +fun main(args: Array) { + Game.start(LocalDate.now()) +} \ No newline at end of file From 41e9aae7fb5995b3de4106eecac271a9e166ee6d Mon Sep 17 00:00:00 2001 From: cousim46 Date: Sun, 16 Jun 2024 20:07:23 +0900 Subject: [PATCH 09/28] =?UTF-8?q?feat=20:=20=EC=82=AC=EC=A0=84=20=EC=97=AD?= =?UTF-8?q?=ED=95=A0=20=EC=83=9D=EC=84=B1=20-=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=ED=8A=B9=EC=A0=95=20=EB=8B=A8=EC=96=B4=20?= =?UTF-8?q?=EC=B6=94=EC=B6=9C=20-=20=EB=8B=A8=EC=96=B4=20=EC=A1=B4?= =?UTF-8?q?=EC=9E=AC=EC=97=AC=EB=B6=80=20=EC=B2=B4=ED=81=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/dictionary/Dictionary.kt | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/main/kotlin/dictionary/Dictionary.kt diff --git a/src/main/kotlin/dictionary/Dictionary.kt b/src/main/kotlin/dictionary/Dictionary.kt new file mode 100644 index 0000000..6df56ac --- /dev/null +++ b/src/main/kotlin/dictionary/Dictionary.kt @@ -0,0 +1,25 @@ +package dictionary + +import java.io.File +import java.time.LocalDate + +object Dictionary { + private const val PATH = "./src/main/resources" + private const val FILE_NAME = "words.txt" + private val words: List = File(PATH, FILE_NAME).readLines() + private val BASE_DATE = LocalDate.of(2021, 6, 19) + + fun hasWord(word: String): Boolean { + return words.contains(word) + } + + operator fun get(index: Int): String { + return words[index] + } + + fun findTodayWord(nowDate: LocalDate): String { + val calcDate = nowDate.toEpochDay().minus(BASE_DATE.toEpochDay()) + val index: Int = (calcDate % words.size).toInt() + return words[index] + } +} \ No newline at end of file From ef38d786b3960fe6c318ad8b0996583f002d0c9f Mon Sep 17 00:00:00 2001 From: cousim46 Date: Sun, 16 Jun 2024 20:07:40 +0900 Subject: [PATCH 10/28] =?UTF-8?q?feat=20:=20=EA=B2=8C=EC=9E=84=20=EC=97=AD?= =?UTF-8?q?=ED=95=A0=20=EC=83=9D=EC=84=B1=20-=20=EA=B2=8C=EC=9E=84=20?= =?UTF-8?q?=EC=8B=9C=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/Game.kt | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/main/kotlin/Game.kt diff --git a/src/main/kotlin/Game.kt b/src/main/kotlin/Game.kt new file mode 100644 index 0000000..0b361c7 --- /dev/null +++ b/src/main/kotlin/Game.kt @@ -0,0 +1,27 @@ +import dictionary.Dictionary +import stage.Stage +import stage.step.word.Word +import view.Input +import view.Print +import java.time.LocalDate + +class Game { + companion object { + fun start(now: LocalDate) { + val answer = Dictionary.findTodayWord(now) + val stage = Stage(answer = answer) + Print.start() + while (stage.state == Stage.State.PROGRESS) { + try { + Print.requestInput() + val inputValue = Input.write() + val value = Word.fromInput(inputValue) { Dictionary.hasWord(inputValue) }.value + stage.play(value) + Print.resultStage(stage, answer) + } catch (e: Exception) { + println(e.message) + } + } + } + } +} \ No newline at end of file From 226595382857cfd2db08a3ad81b286153430e105 Mon Sep 17 00:00:00 2001 From: cousim46 Date: Sun, 16 Jun 2024 20:07:51 +0900 Subject: [PATCH 11/28] =?UTF-8?q?feat=20:=20=EC=9E=85=EB=A0=A5=20=EC=97=AD?= =?UTF-8?q?=ED=95=A0=20=EC=83=9D=EC=84=B1=20-=20=EB=8B=A8=EC=96=B4=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/view/Input.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/main/kotlin/view/Input.kt diff --git a/src/main/kotlin/view/Input.kt b/src/main/kotlin/view/Input.kt new file mode 100644 index 0000000..c756b27 --- /dev/null +++ b/src/main/kotlin/view/Input.kt @@ -0,0 +1,13 @@ +package view + +import java.util.* + +class Input { + companion object { + val scanner = Scanner(System.`in`) + + fun write(): String { + return scanner.nextLine() + } + } +} \ No newline at end of file From f4e7ba76ca4030cfa8adfd523d676d320909d246 Mon Sep 17 00:00:00 2001 From: cousim46 Date: Sun, 16 Jun 2024 20:08:27 +0900 Subject: [PATCH 12/28] =?UTF-8?q?feat=20:=20=EC=B6=9C=EB=A0=A5=20=EC=97=AD?= =?UTF-8?q?=ED=95=A0=20=EC=83=9D=EC=84=B1=20-=20=EA=B2=8C=EC=9E=84=20?= =?UTF-8?q?=EC=8B=9C=EC=9E=91=20=EB=AC=B8=EA=B5=AC=20-=20=EC=A0=95?= =?UTF-8?q?=EB=8B=B5=20=EC=9E=85=EB=A0=A5=20=EC=9A=94=EC=B2=AD=20=EB=AC=B8?= =?UTF-8?q?=EA=B5=AC=20-=20=EA=B2=B0=EA=B3=BC=20=EB=AC=B8=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/view/Print.kt | 57 +++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/main/kotlin/view/Print.kt diff --git a/src/main/kotlin/view/Print.kt b/src/main/kotlin/view/Print.kt new file mode 100644 index 0000000..a8056ec --- /dev/null +++ b/src/main/kotlin/view/Print.kt @@ -0,0 +1,57 @@ +package view + +import stage.Stage +import stage.step.Step + +class Print { + + companion object { + fun start() { + println( + """ + WORDLE을 6번 만에 맞춰 보세요. + 시도의 결과는 타일의 색 변화로 나타납니다. + """.trimIndent() + ) + } + + fun requestInput() { + println("정답을 입력해 주세요.") + } + + + fun resultStage(stage: Stage, answer: String) { + when (stage.state) { + Stage.State.FAIL -> { + resultAllStep(stage) + println("answer = $answer") + } + + Stage.State.COMPLETE -> { + println("${stage.steps.size}/6") + resultAllStep(stage) + } + + Stage.State.PROGRESS -> { + resultAllStep(stage) + } + } + } + + fun resultAllStep(stage: Stage) { + stage.steps.forEach { resultStep(it) } + } + + fun resultStep(step: Step) { + val str: StringBuilder = StringBuilder() + step.code.forEach { + when (it) { + Step.Result.CORRECT -> str.append("\uD83D\uDFE9") + Step.Result.MISMATCH -> str.append("\uD83D\uDFE8") + Step.Result.WRONG -> str.append("⬜") + } + } + println(str) + } + } +} \ No newline at end of file From 77022470c237e40fec49dcda25cd6721fa95f792 Mon Sep 17 00:00:00 2001 From: cousim46 Date: Sun, 16 Jun 2024 20:08:54 +0900 Subject: [PATCH 13/28] =?UTF-8?q?feat=20:=20=EC=8A=A4=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=97=AD=ED=95=A0=20=EC=83=9D=EC=84=B1=20-=20?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=20=EC=A7=84=ED=96=89=20=EC=97=AC=EB=B6=80=20?= =?UTF-8?q?-=20=EA=B2=8C=EC=9E=84=20=ED=94=8C=EB=A0=88=EC=9D=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/stage/Stage.kt | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/main/kotlin/stage/Stage.kt diff --git a/src/main/kotlin/stage/Stage.kt b/src/main/kotlin/stage/Stage.kt new file mode 100644 index 0000000..93c12a8 --- /dev/null +++ b/src/main/kotlin/stage/Stage.kt @@ -0,0 +1,30 @@ +package stage + +import stage.step.Step + +data class Stage(val answer: String, var state: State = State.PROGRESS) { + + val steps = mutableListOf() + + enum class State { + PROGRESS, COMPLETE, FAIL + } + + fun play(word: String) { + if (!canPlay()) return + val step = Step.create(answer, word) + steps.add(step) + + if (step.isCorrect) { + state = State.COMPLETE + } + + if (steps.size == 6 && state != State.COMPLETE) { + state = State.FAIL + } + } + + fun canPlay(): Boolean { + return state != State.COMPLETE && state != State.FAIL + } +} From 7893ae78dc8069ee822c21511d9f644434bea0ae Mon Sep 17 00:00:00 2001 From: cousim46 Date: Sun, 16 Jun 2024 20:09:08 +0900 Subject: [PATCH 14/28] =?UTF-8?q?feat=20:=20=EB=8B=A8=EA=B3=84=20=EC=97=AD?= =?UTF-8?q?=ED=95=A0=20=EC=83=9D=EC=84=B1=20-=20=EB=8B=A8=EA=B3=84?= =?UTF-8?q?=EB=B3=84=20=EB=8B=B5=20=EC=B2=B4=ED=81=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/stage/step/Step.kt | 58 ++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/main/kotlin/stage/step/Step.kt diff --git a/src/main/kotlin/stage/step/Step.kt b/src/main/kotlin/stage/step/Step.kt new file mode 100644 index 0000000..9496273 --- /dev/null +++ b/src/main/kotlin/stage/step/Step.kt @@ -0,0 +1,58 @@ +package stage.step + +data class Step(val code: List) { + + val isCorrect = code.all { it == Result.CORRECT } + + enum class Result { + CORRECT, + MISMATCH, + WRONG; + } + + companion object { + fun create(answer: String, word: String): Step { + if (answer == word) { + return Step(Array(5) { Result.CORRECT }.toList()) + } + val answerBuilder = StringBuilder(answer) + + val correctResult = correct(answerBuilder = answerBuilder, word = word) + mismatch(answerBuilder = answerBuilder, word = word, codes = correctResult) + return Step(correctResult.toList()) + } + + fun correct(answerBuilder: StringBuilder, word: String): Array { + val codes = Array(5) { Result.WRONG } + + word.forEachIndexed { index, c -> + if (answerBuilder[index] == c) { + codes[index] = Result.CORRECT + } + } + + (4 downTo 0).forEach { + if (codes[it] == Result.CORRECT) { + answerBuilder.deleteAt(it) + } + } + return codes + } + + fun mismatch( + answerBuilder: StringBuilder, + word: String, + codes: Array + ): Array { + + word.forEachIndexed { index, c -> + if (codes[index] != Result.CORRECT && answerBuilder.contains(c)) { + codes[index] = Result.MISMATCH + val indexOf = answerBuilder.indexOf(c) + answerBuilder.deleteAt(indexOf) + } + } + return codes + } + } +} \ No newline at end of file From 37e4539c3d80d8adf7895caa635ab8425c117ed8 Mon Sep 17 00:00:00 2001 From: cousim46 Date: Sun, 16 Jun 2024 20:09:55 +0900 Subject: [PATCH 15/28] =?UTF-8?q?feat=20:=20=EB=8B=A8=EC=96=B4=20=EC=97=AD?= =?UTF-8?q?=ED=95=A0=20=EC=83=9D=EC=84=B1=20-=20=EB=8B=A8=EC=96=B4=205?= =?UTF-8?q?=EA=B8=80=EC=9E=90=20=EC=97=AC=EB=B6=80=20=EC=B2=B4=ED=81=AC=20?= =?UTF-8?q?-=20=EB=8B=A8=EC=96=B4=20=EC=98=81=EB=AC=B8=20=EC=97=AC?= =?UTF-8?q?=EB=B6=80=20=EC=B2=B4=ED=81=AC=20-=20=EC=A1=B4=EC=9E=AC=20?= =?UTF-8?q?=EC=95=BC=EB=B6=80=20=EC=B2=B4=ED=81=AC=20-=20=EC=86=8C?= =?UTF-8?q?=EB=AC=B8=EC=9E=90=EB=A1=9C=20=EC=B9=98=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/stage/step/word/Word.kt | 28 +++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/main/kotlin/stage/step/word/Word.kt diff --git a/src/main/kotlin/stage/step/word/Word.kt b/src/main/kotlin/stage/step/word/Word.kt new file mode 100644 index 0000000..e70c38e --- /dev/null +++ b/src/main/kotlin/stage/step/word/Word.kt @@ -0,0 +1,28 @@ +package stage.step.word + +data class Word(val value: String) { + companion object { + private val englishRegex = Regex("^[A-Za-z]*") + private fun validateInput(input: String) { + if (!input.matches(englishRegex)) { + throw IllegalArgumentException("영문만 입력해야합니다.") + } + if (input.length != 5) { + throw IllegalArgumentException("5글자여야 합니다.") + } + } + + private fun checkInDict(isWordInDic: Boolean) { + if (!isWordInDic) { + throw IllegalArgumentException("존재하지 않는 단어입니다.") + } + } + + fun fromInput(input: String, dicPolicy: (s: String) -> Boolean = { _ -> true }): Word { + validateInput(input) + val word = input.lowercase() + checkInDict(dicPolicy(word)) + return Word(word) + } + } +} From 9186d5ed86e4ffa55c9d40362d4254929511050c Mon Sep 17 00:00:00 2001 From: woo-yu Date: Sun, 16 Jun 2024 21:47:14 +0900 Subject: [PATCH 16/28] =?UTF-8?q?fix:=20Word=20=ED=8C=A8=ED=82=A4=EC=A7=80?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20require=EB=A1=9C=20validati?= =?UTF-8?q?on?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/Game.kt | 2 +- src/main/kotlin/stage/step/word/Word.kt | 28 -------- src/main/kotlin/wordle/Word.kt | 20 ++++++ src/test/kotlin/GameTest.kt | 5 +- src/test/kotlin/stage/step/word/WordTest.kt | 73 --------------------- src/test/kotlin/wordle/WordTest.kt | 48 ++++++++++++++ 6 files changed, 70 insertions(+), 106 deletions(-) delete mode 100644 src/main/kotlin/stage/step/word/Word.kt create mode 100644 src/main/kotlin/wordle/Word.kt delete mode 100644 src/test/kotlin/stage/step/word/WordTest.kt create mode 100644 src/test/kotlin/wordle/WordTest.kt diff --git a/src/main/kotlin/Game.kt b/src/main/kotlin/Game.kt index 0b361c7..a554868 100644 --- a/src/main/kotlin/Game.kt +++ b/src/main/kotlin/Game.kt @@ -1,8 +1,8 @@ import dictionary.Dictionary import stage.Stage -import stage.step.word.Word import view.Input import view.Print +import wordle.Word import java.time.LocalDate class Game { diff --git a/src/main/kotlin/stage/step/word/Word.kt b/src/main/kotlin/stage/step/word/Word.kt deleted file mode 100644 index e70c38e..0000000 --- a/src/main/kotlin/stage/step/word/Word.kt +++ /dev/null @@ -1,28 +0,0 @@ -package stage.step.word - -data class Word(val value: String) { - companion object { - private val englishRegex = Regex("^[A-Za-z]*") - private fun validateInput(input: String) { - if (!input.matches(englishRegex)) { - throw IllegalArgumentException("영문만 입력해야합니다.") - } - if (input.length != 5) { - throw IllegalArgumentException("5글자여야 합니다.") - } - } - - private fun checkInDict(isWordInDic: Boolean) { - if (!isWordInDic) { - throw IllegalArgumentException("존재하지 않는 단어입니다.") - } - } - - fun fromInput(input: String, dicPolicy: (s: String) -> Boolean = { _ -> true }): Word { - validateInput(input) - val word = input.lowercase() - checkInDict(dicPolicy(word)) - return Word(word) - } - } -} diff --git a/src/main/kotlin/wordle/Word.kt b/src/main/kotlin/wordle/Word.kt new file mode 100644 index 0000000..bf4dd13 --- /dev/null +++ b/src/main/kotlin/wordle/Word.kt @@ -0,0 +1,20 @@ +package wordle + +/** + * 사용자의 검증된 입력 단어 + */ +@JvmInline +value class Word(val value: String) { + + companion object { + private val englishRegex = Regex("^[A-Za-z]*") + + fun fromInput(input: String, checkInDic: (s: String) -> Boolean): Word { + require(input.matches(englishRegex)) { "영문만 입력해야합니다." } + require(input.length == 5) { "5글자여야 합니다." } + require(checkInDic(input)) { "존재하지 않는 단어입니다." } + return Word(input.lowercase()) + } + } + +} diff --git a/src/test/kotlin/GameTest.kt b/src/test/kotlin/GameTest.kt index 81480d2..8cf3148 100644 --- a/src/test/kotlin/GameTest.kt +++ b/src/test/kotlin/GameTest.kt @@ -1,9 +1,6 @@ -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test import stage.Stage import stage.step.Step -import stage.step.word.Word +import wordle.Word import java.time.LocalDate import java.util.Scanner diff --git a/src/test/kotlin/stage/step/word/WordTest.kt b/src/test/kotlin/stage/step/word/WordTest.kt deleted file mode 100644 index 1fa778f..0000000 --- a/src/test/kotlin/stage/step/word/WordTest.kt +++ /dev/null @@ -1,73 +0,0 @@ -package stage.step.word - - -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.ValueSource - -class WordTest { - @ParameterizedTest - @ValueSource(strings = ["-1", "1", " ", "w!", " ;fs"]) - fun `모두 영문으로 구성된다`(value: String) { - //when - val errorResponse = assertThrows { Word.fromInput(value) } - - ///then - assertThat(errorResponse.message).isEqualTo("영문만 입력해야합니다.") - } - - @ParameterizedTest - @ValueSource(strings = ["test", "hi", "h", "tttttt"]) - fun `5글자가 아니면 IllegalArgumentException 예외가 발생한다`(value: String) { - //when - val errorResponse = assertThrows { Word.fromInput(value) } - - ///then - assertThat(errorResponse.message).isEqualTo("5글자여야 합니다.") - } - - @Test - fun `영문 대문자는 소문자로 치환된다`() { - assertThat(Word.fromInput("Hello")).isEqualTo(Word.fromInput("hello")) - } - - @ParameterizedTest - @ValueSource(strings = ["testa", "hiwww", "heeee", "tttaa"]) - fun `존재하지 않으면 IllegalArgumentException 예외가 발생한다`(value: String) { - val errorResponse = - assertThrows { Word.fromInput(value) { _ -> false } } - - //then - assertThat(errorResponse.message).isEqualTo("존재하지 않는 단어입니다.") - } -} - -data class Word(val value: String) { - companion object { - private val englishRegex = Regex("^[A-Za-z]*") - private fun validateInput(input: String) { - if (!input.matches(englishRegex)) { - throw IllegalArgumentException("영문만 입력해야합니다.") - } - if (input.length != 5) { - throw IllegalArgumentException("5글자여야 합니다.") - } - } - - private fun checkInDict(isWordInDic: Boolean) { - if (!isWordInDic) { - throw IllegalArgumentException("존재하지 않는 단어입니다.") - } - } - - fun fromInput(input: String, dicPolicy: (s: String) -> Boolean = { _ -> true }): Word { - validateInput(input) - val word = input.lowercase() - checkInDict(dicPolicy(word)) - return Word(word) - } - } -} - diff --git a/src/test/kotlin/wordle/WordTest.kt b/src/test/kotlin/wordle/WordTest.kt new file mode 100644 index 0000000..b041ea4 --- /dev/null +++ b/src/test/kotlin/wordle/WordTest.kt @@ -0,0 +1,48 @@ +package wordle + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource + +internal class WordTest { + + private val alwaysInDic = { _: String -> true } + + @ParameterizedTest + @ValueSource(strings = ["-1", "1", " ", "w!", " ;fs"]) + fun `모두 영문으로 구성된다`(value: String) { + //when + val errorResponse = assertThrows { Word.fromInput(value, alwaysInDic) } + + ///then + assertThat(errorResponse.message).isEqualTo("영문만 입력해야합니다.") + } + + @ParameterizedTest + @ValueSource(strings = ["test", "hi", "h", "tttttt"]) + fun `5글자가 아니면 IllegalArgumentException 예외가 발생한다`(value: String) { + //when + val errorResponse = assertThrows { Word.fromInput(value, alwaysInDic) } + + ///then + assertThat(errorResponse.message).isEqualTo("5글자여야 합니다.") + } + + @Test + fun `영문 대문자는 소문자로 치환된다`() { + assertThat(Word.fromInput("Hello", alwaysInDic)).isEqualTo(Word.fromInput("hello", alwaysInDic)) + } + + @ParameterizedTest + @ValueSource(strings = ["testa", "hiwww", "heeee", "tttaa"]) + fun `사전에 존재하지 않으면 IllegalArgumentException 예외가 발생한다`(value: String) { + val errorResponse = + assertThrows { Word.fromInput(value) { _ -> false } } + + //then + assertThat(errorResponse.message).isEqualTo("존재하지 않는 단어입니다.") + } +} + From 73296da005ff78c2fda638bf37a67c592ae8c1b5 Mon Sep 17 00:00:00 2001 From: woo-yu Date: Sun, 16 Jun 2024 22:45:31 +0900 Subject: [PATCH 17/28] =?UTF-8?q?fix:=20Step=20=ED=8C=A8=ED=82=A4=EC=A7=80?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20answer,=20word=20=ED=8F=AC?= =?UTF-8?q?=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/stage/step/Step.kt | 58 --------- src/main/kotlin/wordle/Step.kt | 48 +++++++ src/test/kotlin/stage/step/StepTest.kt | 165 ------------------------- src/test/kotlin/wordle/StepTest.kt | 72 +++++++++++ 4 files changed, 120 insertions(+), 223 deletions(-) delete mode 100644 src/main/kotlin/stage/step/Step.kt create mode 100644 src/main/kotlin/wordle/Step.kt delete mode 100644 src/test/kotlin/stage/step/StepTest.kt create mode 100644 src/test/kotlin/wordle/StepTest.kt diff --git a/src/main/kotlin/stage/step/Step.kt b/src/main/kotlin/stage/step/Step.kt deleted file mode 100644 index 9496273..0000000 --- a/src/main/kotlin/stage/step/Step.kt +++ /dev/null @@ -1,58 +0,0 @@ -package stage.step - -data class Step(val code: List) { - - val isCorrect = code.all { it == Result.CORRECT } - - enum class Result { - CORRECT, - MISMATCH, - WRONG; - } - - companion object { - fun create(answer: String, word: String): Step { - if (answer == word) { - return Step(Array(5) { Result.CORRECT }.toList()) - } - val answerBuilder = StringBuilder(answer) - - val correctResult = correct(answerBuilder = answerBuilder, word = word) - mismatch(answerBuilder = answerBuilder, word = word, codes = correctResult) - return Step(correctResult.toList()) - } - - fun correct(answerBuilder: StringBuilder, word: String): Array { - val codes = Array(5) { Result.WRONG } - - word.forEachIndexed { index, c -> - if (answerBuilder[index] == c) { - codes[index] = Result.CORRECT - } - } - - (4 downTo 0).forEach { - if (codes[it] == Result.CORRECT) { - answerBuilder.deleteAt(it) - } - } - return codes - } - - fun mismatch( - answerBuilder: StringBuilder, - word: String, - codes: Array - ): Array { - - word.forEachIndexed { index, c -> - if (codes[index] != Result.CORRECT && answerBuilder.contains(c)) { - codes[index] = Result.MISMATCH - val indexOf = answerBuilder.indexOf(c) - answerBuilder.deleteAt(indexOf) - } - } - return codes - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/wordle/Step.kt b/src/main/kotlin/wordle/Step.kt new file mode 100644 index 0000000..7889252 --- /dev/null +++ b/src/main/kotlin/wordle/Step.kt @@ -0,0 +1,48 @@ +package wordle + +/** + * 사용자가 [Word]를 입력하여 정답과 비교한 결과를 가진 한 번의 단계 + */ +data class Step(val answer: String, val word: Word) { + + enum class Result { + CORRECT, + MISMATCH, + WRONG; + } + + val result: List + + init { + result = if (answer == word.value) { + List(5) { Result.CORRECT }.toList() + } else { + MutableList(5) { Result.WRONG }.apply { + fillCorrect(this) + fillMismatch(this) + } + } + } + + val isCorrect = result.all { it == Result.CORRECT } + + private fun fillCorrect(initResult: MutableList) { + word.value.forEachIndexed { index, c -> + if (answer[index] == c) { + initResult[index] = Result.CORRECT + } + } + } + + private fun fillMismatch(correctedResult: MutableList) { + val calculatedAnswer = StringBuilder(answer.filterIndexed { index, _ -> correctedResult[index] != Result.CORRECT }) + + word.value.forEachIndexed { index, c -> + if (correctedResult[index] != Result.CORRECT && calculatedAnswer.contains(c)) { + correctedResult[index] = Result.MISMATCH + val foundIndex = calculatedAnswer.indexOf(c) + calculatedAnswer.deleteAt(foundIndex) + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/stage/step/StepTest.kt b/src/test/kotlin/stage/step/StepTest.kt deleted file mode 100644 index 6ec62f2..0000000 --- a/src/test/kotlin/stage/step/StepTest.kt +++ /dev/null @@ -1,165 +0,0 @@ -package stage.step - -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test - -/** - * 남은 기능 - * - 결과 값 담기 - Stage - * - 출력하기 (UI) - * - * */ - - -class StepTest { - @Nested - inner class ResultTest { - @Test - fun `완전히 동일한 단어일 경우 전부 CORRECT`() { - val answer = "hello" - val word = "hello" - - assertThat( - Step.create( - answer, - word - ).code - ).isEqualTo(Array(5) { Step.Result.CORRECT }.toList()) - } - - @Test - fun `단어는 포함되어 있고 위치가 일치하면 CORRECT`() { - val answer = "hello" - val word = "helol" - - val resultCode = Step.create(answer, word).code - - // then - assertThat(resultCode[0]).isEqualTo(Step.Result.CORRECT) - assertThat(resultCode[2]).isEqualTo(Step.Result.CORRECT) - } - - @Test - fun `단어는 포함되어 있으나 위치가 일치하지 않을경우 MISMATCH`() { - val answer = "hello" - val word = "helol" - - val resultCode = Step.create(answer, word).code - - // then - assertThat(resultCode[3]).isEqualTo(Step.Result.MISMATCH) - assertThat(resultCode[4]).isEqualTo(Step.Result.MISMATCH) - } - - @Test - fun `단어는 포함되어 있으나 위치가 일치하지 않고 정답개수의 단어보다 입력 갯수가 더 많을 경우 일치한 횟수(왼쪽에서 오른쪽으로 계산)가 적으면 MISMATCH`() { - val answer = "hello" - val word = "heool" - - val resultCode = Step.create(answer, word).code - - // then - assertThat(resultCode[2]).isEqualTo(Step.Result.MISMATCH) - } - - @Test - fun `단어는 포함되어 있으나 위치가 일치하지 않고 정답개수의 단어보다 입력 갯수가 더 많을 경우 일치한 횟수(왼쪽에서 오른쪽으로 계산)가 많으면 WRONG`() { - val answer = "hello" - val word = "heool" - - val resultCode = Step.create(answer, word).code - - // then - assertThat(resultCode[3]).isEqualTo(Step.Result.WRONG) - } - - @Test - fun `단어는 포함조차 안되어있으면 WRONG`() { - val answer = "hello" - val word = "helok" - - val resultCode = Step.create(answer, word).code - - // then - assertThat(resultCode[4]).isEqualTo(Step.Result.WRONG) - } - } - - @Test - fun `Result가 전부 CORRECT면 맞춤(isCorrect가 true)`() { - assertThat(Step(listOf(Step.Result.CORRECT, Step.Result.CORRECT)).isCorrect).isTrue() - } - - @Test - fun `Result가 전부 CORRECT가 아닐 경우에는 틀림(isCorrect가 false)`() { - assertThat( - Step( - listOf( - Step.Result.CORRECT, - Step.Result.MISMATCH, - Step.Result.WRONG - ) - ).isCorrect - ).isFalse() - } - -} - -data class Step(val code: List) { - - val isCorrect = code.all { it == Result.CORRECT } - - enum class Result { - CORRECT, - MISMATCH, - WRONG; - } - - companion object { - fun create(answer: String, word: String): Step { - if (answer == word) { - return Step(Array(5) { Result.CORRECT }.toList()) - } - val answerBuilder = StringBuilder(answer) - - val correctResult = correct(answerBuilder = answerBuilder, word = word) - mismatch(answerBuilder = answerBuilder, word = word, codes = correctResult) - return Step(correctResult.toList()) - } - - fun correct(answerBuilder: StringBuilder, word: String): Array { - val codes = Array(5) { Result.WRONG } - - word.forEachIndexed { index, c -> - if (answerBuilder[index] == c) { - codes[index] = Result.CORRECT - } - } - - (4 downTo 0).forEach { - if (codes[it] == Result.CORRECT) { - answerBuilder.deleteAt(it) - } - } - return codes - } - - - fun mismatch( - answerBuilder: StringBuilder, - word: String, - codes: Array - ): Array { - - word.forEachIndexed { index, c -> - if (codes[index] != Result.CORRECT && answerBuilder.contains(c)) { - codes[index] = Result.MISMATCH - val indexOf = answerBuilder.indexOf(c) - answerBuilder.deleteAt(indexOf) - } - } - return codes - } - } -} diff --git a/src/test/kotlin/wordle/StepTest.kt b/src/test/kotlin/wordle/StepTest.kt new file mode 100644 index 0000000..f20ac79 --- /dev/null +++ b/src/test/kotlin/wordle/StepTest.kt @@ -0,0 +1,72 @@ +package wordle + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class StepTest { + + @Test + fun `완전히 동일한 단어일 경우 전부 CORRECT`() { + val answer = "hello" + val word = Word("hello") + + assertThat(Step(answer, word).result).isEqualTo(Array(5) { Step.Result.CORRECT }.toList()) + } + + @Test + fun `단어는 포함되어 있고 위치가 일치하면 CORRECT`() { + val answer = "hello" + val word = Word("helol") + + val resultCode = Step(answer, word).result + + // then + assertThat(resultCode[0]).isEqualTo(Step.Result.CORRECT) + assertThat(resultCode[2]).isEqualTo(Step.Result.CORRECT) + } + + @Test + fun `단어는 포함되어 있으나 위치가 일치하지 않을경우 MISMATCH`() { + val answer = "hello" + val word = Word("helol") + + val resultCode = Step(answer, word).result + + // then + assertThat(resultCode[3]).isEqualTo(Step.Result.MISMATCH) + assertThat(resultCode[4]).isEqualTo(Step.Result.MISMATCH) + } + + @Test + fun `단어는 포함되어 있으나 위치가 일치하지 않고 정답개수의 단어보다 입력 갯수가 더 많을 경우 일치한 횟수(왼쪽에서 오른쪽으로 계산)가 적으면 MISMATCH`() { + val answer = "hello" + val word = Word("heool") + + val resultCode = Step(answer, word).result + + // then + assertThat(resultCode[2]).isEqualTo(Step.Result.MISMATCH) + } + + @Test + fun `단어는 포함되어 있으나 위치가 일치하지 않고 정답개수의 단어보다 입력 갯수가 더 많을 경우 일치한 횟수(왼쪽에서 오른쪽으로 계산)가 많으면 WRONG`() { + val answer = "hello" + val word = Word("heool") + + val resultCode = Step(answer, word).result + + // then + assertThat(resultCode[3]).isEqualTo(Step.Result.WRONG) + } + + @Test + fun `단어는 포함조차 안되어있으면 WRONG`() { + val answer = "hello" + val word = Word("helok") + + val resultCode = Step(answer, word).result + + // then + assertThat(resultCode[4]).isEqualTo(Step.Result.WRONG) + } +} From 4c43baac5e3365b1816cafb949364f3910874bdf Mon Sep 17 00:00:00 2001 From: woo-yu Date: Sun, 16 Jun 2024 23:22:43 +0900 Subject: [PATCH 18/28] =?UTF-8?q?fix:=20Stage=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20state=20=EC=97=B0?= =?UTF-8?q?=EC=82=B0=EC=9D=84=20=EC=B4=88=EA=B8=B0=ED=99=94=EC=8B=9C=20?= =?UTF-8?q?=EC=A7=84=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/stage/Stage.kt | 30 ---------- src/main/kotlin/wordle/Stage.kt | 32 ++++++++++ src/test/kotlin/stage/StageTest.kt | 92 ----------------------------- src/test/kotlin/wordle/StageTest.kt | 62 +++++++++++++++++++ 4 files changed, 94 insertions(+), 122 deletions(-) delete mode 100644 src/main/kotlin/stage/Stage.kt create mode 100644 src/main/kotlin/wordle/Stage.kt delete mode 100644 src/test/kotlin/stage/StageTest.kt create mode 100644 src/test/kotlin/wordle/StageTest.kt diff --git a/src/main/kotlin/stage/Stage.kt b/src/main/kotlin/stage/Stage.kt deleted file mode 100644 index 93c12a8..0000000 --- a/src/main/kotlin/stage/Stage.kt +++ /dev/null @@ -1,30 +0,0 @@ -package stage - -import stage.step.Step - -data class Stage(val answer: String, var state: State = State.PROGRESS) { - - val steps = mutableListOf() - - enum class State { - PROGRESS, COMPLETE, FAIL - } - - fun play(word: String) { - if (!canPlay()) return - val step = Step.create(answer, word) - steps.add(step) - - if (step.isCorrect) { - state = State.COMPLETE - } - - if (steps.size == 6 && state != State.COMPLETE) { - state = State.FAIL - } - } - - fun canPlay(): Boolean { - return state != State.COMPLETE && state != State.FAIL - } -} diff --git a/src/main/kotlin/wordle/Stage.kt b/src/main/kotlin/wordle/Stage.kt new file mode 100644 index 0000000..a377b49 --- /dev/null +++ b/src/main/kotlin/wordle/Stage.kt @@ -0,0 +1,32 @@ +package wordle + +/** + * 사용자가 하나의 정답을 가지고 진행하는 한 + */ +data class Stage(val answer: String, val steps: List = listOf()) { + + enum class State { + PROGRESS, COMPLETE, FAIL + } + + val state: State = when { + steps.any { step -> step.isCorrect } -> State.COMPLETE + steps.size == 6 -> State.FAIL + else -> State.PROGRESS + } + + val finished = state != State.PROGRESS + + fun play(word: Word): Stage { + if (finished) return this + val step = Step(answer, word) + val newSteps = steps.toMutableList().apply { + add(step) + } + + return Stage( + answer = answer, + steps = newSteps, + ) + } +} diff --git a/src/test/kotlin/stage/StageTest.kt b/src/test/kotlin/stage/StageTest.kt deleted file mode 100644 index f62c4cf..0000000 --- a/src/test/kotlin/stage/StageTest.kt +++ /dev/null @@ -1,92 +0,0 @@ -package stage - -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import stage.step.Step - -/** - * stage는 answer을 입력받아서 생성된다 - * stage를 play 할 때 마다 step이 만들어짐 - * play를 할 때 step이 isCorrect -> true이면 맞춘 종료 - * play를 6번 했는데 모든 step이 isCorrect -> false면 틀린 종료 - * - * stage 상태: 진행중(Progress)/맞춘 종료(Complete)/틀린 종료(Fail) -> - * 워들 -> 정답: xxx (틀린여부) - * */ -class StageTest { - - @Test - fun `스테이지가 Progress일 때 play할 수 있음`() { - val stage = Stage("answer", Stage.State.PROGRESS) - assertThat(stage.canPlay()).isTrue() - } - - @Test - fun `스테이지가 Complete일 때 더이상 play를 할 수 없음`() { - val stage = Stage("answer", Stage.State.COMPLETE) - assertThat(stage.canPlay()).isFalse() - } - - - @Test - fun `스테이지가 Fail일 때 더이상 play를 할 수 없음`() { - val stage = Stage("answer", Stage.State.FAIL) - assertThat(stage.canPlay()).isFalse() - } - - @Test - fun `스테이지가 play시 정답을 맞추면(step이 isCorrect) Complete`() { - // given - val stage = Stage("answer", Stage.State.PROGRESS) - - // when - stage.play("answer") - - // then - assertThat(stage.state).isEqualTo(Stage.State.COMPLETE) - } - - @Test - fun `스테이지가 play시 6번 모두 정답을 맞추지 못하면(step이 모두 isCorrect false) FAIL`() { - // given - val stage = Stage("answer", Stage.State.PROGRESS) - - // when - stage.play("world") - stage.play("world") - stage.play("world") - stage.play("world") - stage.play("world") - stage.play("world") - - // then - assertThat(stage.state).isEqualTo(Stage.State.FAIL) - } -} - -data class Stage(val answer: String, var state: State = State.PROGRESS) { - - val steps = mutableListOf() - - enum class State { - PROGRESS, COMPLETE, FAIL - } - - fun play(word: String) { - if (!canPlay()) return - val step = Step.create(answer, word) - steps.add(step) - - if (step.isCorrect) { - state = State.COMPLETE - } - - if (steps.size == 6 && state != State.COMPLETE) { - state = State.FAIL - } - } - - fun canPlay(): Boolean { - return state != State.COMPLETE && state != State.FAIL - } -} \ No newline at end of file diff --git a/src/test/kotlin/wordle/StageTest.kt b/src/test/kotlin/wordle/StageTest.kt new file mode 100644 index 0000000..c1d20d1 --- /dev/null +++ b/src/test/kotlin/wordle/StageTest.kt @@ -0,0 +1,62 @@ +package wordle + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import wordle.Stage +import wordle.Word + +internal class StageTest { + + @Test + fun `스테이지를 만들면 PROGRESS`() { + val stage = Stage("hello") + + assertThat(stage.state).isEqualTo(Stage.State.PROGRESS) + assertThat(stage.finished).isFalse() + } + + @Test + fun `스테이지가 play시 정답을 맞추면 Complete`() { + // given + val stage = Stage("hello") + + // when + val newStage = stage.play(Word("hello")) + + // then + assertThat(newStage.state).isEqualTo(Stage.State.COMPLETE) + assertThat(newStage.finished).isTrue() + } + + @Test + fun `스테이지가 play시 정답을 맞추지 못하면 Complete`() { + // given + val stage = Stage("hello") + + // when + val newStage = stage.play(Word("wrong")) + + // then + assertThat(newStage.state).isEqualTo(Stage.State.PROGRESS) + assertThat(newStage.finished).isFalse() + } + + @Test + fun `스테이지가 play시 6번 모두 정답을 맞추지 못하면 FAIL`() { + // given + val stage = Stage("hello") + + //when + val failedStage = stage + .play(Word("wrong")) + .play(Word("wrong")) + .play(Word("wrong")) + .play(Word("wrong")) + .play(Word("wrong")) + .play(Word("wrong")) + + // then + assertThat(failedStage.state).isEqualTo(Stage.State.FAIL) + assertThat(failedStage.finished).isTrue() + } +} \ No newline at end of file From 7980f692ce6878252eef74c343561b815fa4e15d Mon Sep 17 00:00:00 2001 From: woo-yu Date: Mon, 17 Jun 2024 00:08:20 +0900 Subject: [PATCH 19/28] =?UTF-8?q?fix:=20Game,=20Application=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20view/domain=20=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=96=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/Computer.kt | 5 - src/main/kotlin/Game.kt | 27 ----- src/main/kotlin/dictionary/Dictionary.kt | 25 ----- src/main/kotlin/view/Input.kt | 13 --- src/main/kotlin/view/Print.kt | 57 ---------- src/main/kotlin/wordle/Application.kt | 7 ++ src/main/kotlin/wordle/Dictionary.kt | 21 ++++ src/main/kotlin/wordle/Game.kt | 33 ++++++ src/main/kotlin/wordle/{ => domain}/Stage.kt | 4 +- src/main/kotlin/wordle/{ => domain}/Step.kt | 7 +- src/main/kotlin/wordle/{ => domain}/Word.kt | 5 +- src/main/kotlin/wordle/view/Input.kt | 12 +++ src/main/kotlin/wordle/view/Output.kt | 49 +++++++++ src/test/kotlin/DictionaryTest.kt | 58 ---------- src/test/kotlin/GameTest.kt | 108 ------------------- src/test/kotlin/wordle/DictionaryTest.kt | 40 +++++++ src/test/kotlin/wordle/StageTest.kt | 4 +- src/test/kotlin/wordle/StepTest.kt | 2 + src/test/kotlin/wordle/WordTest.kt | 19 +--- 19 files changed, 178 insertions(+), 318 deletions(-) delete mode 100644 src/main/kotlin/Computer.kt delete mode 100644 src/main/kotlin/Game.kt delete mode 100644 src/main/kotlin/dictionary/Dictionary.kt delete mode 100644 src/main/kotlin/view/Input.kt delete mode 100644 src/main/kotlin/view/Print.kt create mode 100644 src/main/kotlin/wordle/Application.kt create mode 100644 src/main/kotlin/wordle/Dictionary.kt create mode 100644 src/main/kotlin/wordle/Game.kt rename src/main/kotlin/wordle/{ => domain}/Stage.kt (87%) rename src/main/kotlin/wordle/{ => domain}/Step.kt (85%) rename src/main/kotlin/wordle/{ => domain}/Word.kt (69%) create mode 100644 src/main/kotlin/wordle/view/Input.kt create mode 100644 src/main/kotlin/wordle/view/Output.kt delete mode 100644 src/test/kotlin/DictionaryTest.kt delete mode 100644 src/test/kotlin/GameTest.kt create mode 100644 src/test/kotlin/wordle/DictionaryTest.kt diff --git a/src/main/kotlin/Computer.kt b/src/main/kotlin/Computer.kt deleted file mode 100644 index 1e4f1f6..0000000 --- a/src/main/kotlin/Computer.kt +++ /dev/null @@ -1,5 +0,0 @@ -import java.time.LocalDate - -fun main(args: Array) { - Game.start(LocalDate.now()) -} \ No newline at end of file diff --git a/src/main/kotlin/Game.kt b/src/main/kotlin/Game.kt deleted file mode 100644 index a554868..0000000 --- a/src/main/kotlin/Game.kt +++ /dev/null @@ -1,27 +0,0 @@ -import dictionary.Dictionary -import stage.Stage -import view.Input -import view.Print -import wordle.Word -import java.time.LocalDate - -class Game { - companion object { - fun start(now: LocalDate) { - val answer = Dictionary.findTodayWord(now) - val stage = Stage(answer = answer) - Print.start() - while (stage.state == Stage.State.PROGRESS) { - try { - Print.requestInput() - val inputValue = Input.write() - val value = Word.fromInput(inputValue) { Dictionary.hasWord(inputValue) }.value - stage.play(value) - Print.resultStage(stage, answer) - } catch (e: Exception) { - println(e.message) - } - } - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/dictionary/Dictionary.kt b/src/main/kotlin/dictionary/Dictionary.kt deleted file mode 100644 index 6df56ac..0000000 --- a/src/main/kotlin/dictionary/Dictionary.kt +++ /dev/null @@ -1,25 +0,0 @@ -package dictionary - -import java.io.File -import java.time.LocalDate - -object Dictionary { - private const val PATH = "./src/main/resources" - private const val FILE_NAME = "words.txt" - private val words: List = File(PATH, FILE_NAME).readLines() - private val BASE_DATE = LocalDate.of(2021, 6, 19) - - fun hasWord(word: String): Boolean { - return words.contains(word) - } - - operator fun get(index: Int): String { - return words[index] - } - - fun findTodayWord(nowDate: LocalDate): String { - val calcDate = nowDate.toEpochDay().minus(BASE_DATE.toEpochDay()) - val index: Int = (calcDate % words.size).toInt() - return words[index] - } -} \ No newline at end of file diff --git a/src/main/kotlin/view/Input.kt b/src/main/kotlin/view/Input.kt deleted file mode 100644 index c756b27..0000000 --- a/src/main/kotlin/view/Input.kt +++ /dev/null @@ -1,13 +0,0 @@ -package view - -import java.util.* - -class Input { - companion object { - val scanner = Scanner(System.`in`) - - fun write(): String { - return scanner.nextLine() - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/view/Print.kt b/src/main/kotlin/view/Print.kt deleted file mode 100644 index a8056ec..0000000 --- a/src/main/kotlin/view/Print.kt +++ /dev/null @@ -1,57 +0,0 @@ -package view - -import stage.Stage -import stage.step.Step - -class Print { - - companion object { - fun start() { - println( - """ - WORDLE을 6번 만에 맞춰 보세요. - 시도의 결과는 타일의 색 변화로 나타납니다. - """.trimIndent() - ) - } - - fun requestInput() { - println("정답을 입력해 주세요.") - } - - - fun resultStage(stage: Stage, answer: String) { - when (stage.state) { - Stage.State.FAIL -> { - resultAllStep(stage) - println("answer = $answer") - } - - Stage.State.COMPLETE -> { - println("${stage.steps.size}/6") - resultAllStep(stage) - } - - Stage.State.PROGRESS -> { - resultAllStep(stage) - } - } - } - - fun resultAllStep(stage: Stage) { - stage.steps.forEach { resultStep(it) } - } - - fun resultStep(step: Step) { - val str: StringBuilder = StringBuilder() - step.code.forEach { - when (it) { - Step.Result.CORRECT -> str.append("\uD83D\uDFE9") - Step.Result.MISMATCH -> str.append("\uD83D\uDFE8") - Step.Result.WRONG -> str.append("⬜") - } - } - println(str) - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/wordle/Application.kt b/src/main/kotlin/wordle/Application.kt new file mode 100644 index 0000000..6f5d9e7 --- /dev/null +++ b/src/main/kotlin/wordle/Application.kt @@ -0,0 +1,7 @@ +package wordle + +import java.time.LocalDate + +fun main() { + Game(LocalDate.now()).start() +} diff --git a/src/main/kotlin/wordle/Dictionary.kt b/src/main/kotlin/wordle/Dictionary.kt new file mode 100644 index 0000000..a4cf8da --- /dev/null +++ b/src/main/kotlin/wordle/Dictionary.kt @@ -0,0 +1,21 @@ +package wordle + +import wordle.domain.Word +import java.io.File +import java.time.LocalDate + +object Dictionary { + private const val PATH = "./src/main/resources" + private const val FILE_NAME = "words.txt" + private val words: List = File(PATH, FILE_NAME).readLines() + private val BASE_DATE = LocalDate.of(2021, 6, 19) + + fun hasWord(word: Word): Boolean { + return words.contains(word.value) + } + + fun findTodayWord(nowDate: LocalDate): String { + val index = nowDate.toEpochDay().minus(BASE_DATE.toEpochDay()).toInt() % words.size + return words[index] + } +} \ No newline at end of file diff --git a/src/main/kotlin/wordle/Game.kt b/src/main/kotlin/wordle/Game.kt new file mode 100644 index 0000000..210c008 --- /dev/null +++ b/src/main/kotlin/wordle/Game.kt @@ -0,0 +1,33 @@ +package wordle + +import wordle.domain.Stage +import wordle.domain.Word +import wordle.view.Input +import wordle.view.Output +import java.time.LocalDate + +class Game(today: LocalDate) { + private val answer = Dictionary.findTodayWord(today) + private var stage = Stage(answer = answer) + + fun start() { + Output.start() + while (!stage.finished) { + val word = readWord() + stage = stage.play(word) + Output.show(stage) + } + } + + private fun readWord(): Word { + while (true) { + try { + val word = Input.guess() + require(Dictionary.hasWord(word)) { "존재하지 않는 단어입니다." } + return word + } catch (e: Exception) { + println(e.message) + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/wordle/Stage.kt b/src/main/kotlin/wordle/domain/Stage.kt similarity index 87% rename from src/main/kotlin/wordle/Stage.kt rename to src/main/kotlin/wordle/domain/Stage.kt index a377b49..6051e73 100644 --- a/src/main/kotlin/wordle/Stage.kt +++ b/src/main/kotlin/wordle/domain/Stage.kt @@ -1,7 +1,7 @@ -package wordle +package wordle.domain /** - * 사용자가 하나의 정답을 가지고 진행하는 한 + * 사용자가 하나의 정답을 가지고 진행하는 워들 한 판 */ data class Stage(val answer: String, val steps: List = listOf()) { diff --git a/src/main/kotlin/wordle/Step.kt b/src/main/kotlin/wordle/domain/Step.kt similarity index 85% rename from src/main/kotlin/wordle/Step.kt rename to src/main/kotlin/wordle/domain/Step.kt index 7889252..2b85ee4 100644 --- a/src/main/kotlin/wordle/Step.kt +++ b/src/main/kotlin/wordle/domain/Step.kt @@ -1,4 +1,4 @@ -package wordle +package wordle.domain /** * 사용자가 [Word]를 입력하여 정답과 비교한 결과를 가진 한 번의 단계 @@ -15,7 +15,7 @@ data class Step(val answer: String, val word: Word) { init { result = if (answer == word.value) { - List(5) { Result.CORRECT }.toList() + List(5) { Result.CORRECT }.toList() } else { MutableList(5) { Result.WRONG }.apply { fillCorrect(this) @@ -35,7 +35,8 @@ data class Step(val answer: String, val word: Word) { } private fun fillMismatch(correctedResult: MutableList) { - val calculatedAnswer = StringBuilder(answer.filterIndexed { index, _ -> correctedResult[index] != Result.CORRECT }) + val calculatedAnswer = + StringBuilder(answer.filterIndexed { index, _ -> correctedResult[index] != Result.CORRECT }) word.value.forEachIndexed { index, c -> if (correctedResult[index] != Result.CORRECT && calculatedAnswer.contains(c)) { diff --git a/src/main/kotlin/wordle/Word.kt b/src/main/kotlin/wordle/domain/Word.kt similarity index 69% rename from src/main/kotlin/wordle/Word.kt rename to src/main/kotlin/wordle/domain/Word.kt index bf4dd13..d3bea57 100644 --- a/src/main/kotlin/wordle/Word.kt +++ b/src/main/kotlin/wordle/domain/Word.kt @@ -1,4 +1,4 @@ -package wordle +package wordle.domain /** * 사용자의 검증된 입력 단어 @@ -9,10 +9,9 @@ value class Word(val value: String) { companion object { private val englishRegex = Regex("^[A-Za-z]*") - fun fromInput(input: String, checkInDic: (s: String) -> Boolean): Word { + fun fromInput(input: String): Word { require(input.matches(englishRegex)) { "영문만 입력해야합니다." } require(input.length == 5) { "5글자여야 합니다." } - require(checkInDic(input)) { "존재하지 않는 단어입니다." } return Word(input.lowercase()) } } diff --git a/src/main/kotlin/wordle/view/Input.kt b/src/main/kotlin/wordle/view/Input.kt new file mode 100644 index 0000000..4f246af --- /dev/null +++ b/src/main/kotlin/wordle/view/Input.kt @@ -0,0 +1,12 @@ +package wordle.view + +import wordle.domain.Word + +object Input { + + fun guess(): Word { + println("정답을 입력해 주세요.") + return Word.fromInput(readln().trim()) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/wordle/view/Output.kt b/src/main/kotlin/wordle/view/Output.kt new file mode 100644 index 0000000..fba70cc --- /dev/null +++ b/src/main/kotlin/wordle/view/Output.kt @@ -0,0 +1,49 @@ +package wordle.view + +import wordle.domain.Stage +import wordle.domain.Step + +object Output { + + fun start() { + println( + """ + WORDLE을 6번 만에 맞춰 보세요. + 시도의 결과는 타일의 색 변화로 나타납니다. + """.trimIndent() + ) + } + + fun show(stage: Stage) { + when (stage.state) { + Stage.State.FAIL -> { + showAllSteps(stage) + println("answer = ${stage.answer}") + } + + Stage.State.COMPLETE -> { + println("${stage.steps.size}/6") + showAllSteps(stage) + } + + Stage.State.PROGRESS -> { + showAllSteps(stage) + } + } + } + + private fun showAllSteps(stage: Stage) { + stage.steps.forEach { showStep(it) } + } + + private fun showStep(step: Step) { + println(step.result.joinToString("") { + when (it) { + Step.Result.CORRECT -> "\uD83D\uDFE9" + Step.Result.MISMATCH -> "\uD83D\uDFE8" + Step.Result.WRONG -> "⬜" + } + }) + } + +} \ No newline at end of file diff --git a/src/test/kotlin/DictionaryTest.kt b/src/test/kotlin/DictionaryTest.kt deleted file mode 100644 index b5bf93c..0000000 --- a/src/test/kotlin/DictionaryTest.kt +++ /dev/null @@ -1,58 +0,0 @@ -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.ValueSource -import java.io.File -import java.time.LocalDate -import java.time.Period - -class DictionaryTest { - - @ParameterizedTest - @ValueSource(strings = ["asder", "wrfdx", "bljwq"]) - fun `단어가 존재하지 않으면 false`(word: String) { - //when - val result = Dictionary.hasWord(word) - - //then - assertThat(result).isFalse() - } - - @ParameterizedTest - @ValueSource(strings = ["hello", "organ", "mercy"]) - fun `단어가 존재하면 true`(word: String) { - //when - val result = Dictionary.hasWord(word) - - //then - assertThat(result).isTrue() - } - - @Test - fun `현재날짜 기준으로 단어를 추출한다`() { - val now = LocalDate.now() - val findTodayWord = Dictionary.findTodayWord(now) - //then - assertThat(findTodayWord).isNotNull() - } -} - -object Dictionary { - private const val PATH = "./src/main/resources" - private const val FILE_NAME = "words.txt" - private val words: List = File(PATH, FILE_NAME).readLines() - private val BASE_DATE = LocalDate.of(2021, 6, 19) - fun hasWord(word: String): Boolean { - return words.contains(word) - } - - operator fun get(index: Int): String { - return words[index] - } - - fun findTodayWord(nowDate: LocalDate): String { - val calcDate = nowDate.toEpochDay().minus(BASE_DATE.toEpochDay()) - val index: Int = (calcDate % words.size).toInt() - return words[index] - } -} \ No newline at end of file diff --git a/src/test/kotlin/GameTest.kt b/src/test/kotlin/GameTest.kt deleted file mode 100644 index 8cf3148..0000000 --- a/src/test/kotlin/GameTest.kt +++ /dev/null @@ -1,108 +0,0 @@ -import stage.Stage -import stage.step.Step -import wordle.Word -import java.time.LocalDate -import java.util.Scanner - - -class GameTest { - - /** - * todo - * 테스트를 해야할까? - * */ - -} - - -/** - * 게임 시작시 - * - 시작문구 출력 - * - 정답만들기 - * 게임 진행시 - * - 문구 입력받기 - * - 결과 출력 - * - 스테이지 끝나면(COMPLETE/FAIL) COMPLETE: 몇 스텝만에 맞췄는지 / FAIL: 정답이 뭔지 - * - * 1. Dictionary 날짜 계산 - * 2. - * */ -class Game { - - companion object { - fun start(now: LocalDate) { - val answer = Dictionary.findTodayWord(now) - val stage = Stage(answer = answer) - Print.start() - while (stage.state == Stage.State.PROGRESS) { - try { - val scanner = Scanner(System.`in`) // input 역할 나누기 - Print.requestInput() - val inputValue = scanner.nextLine() - val value = Word.fromInput(inputValue) { Dictionary.hasWord(inputValue) }.value - stage.play(value) - Print.resultStage(stage, answer) - } catch (e: Exception) { - println(e.message) - } - } - } - } -} - -class Print { - - companion object { - fun start() { - println( - """ - WORDLE을 6번 만에 맞춰 보세요. - 시도의 결과는 타일의 색 변화로 나타납니다. - """.trimIndent() - ) - } - - fun requestInput() { - println("정답을 입력해 주세요.") - } - - - fun resultStage(stage: Stage, answer: String) { - when (stage.state) { - Stage.State.FAIL -> { - resultAllStep(stage) - println("answer = $answer") - } - - Stage.State.COMPLETE -> { - println("${stage.steps.size}/6") - resultAllStep(stage) - } - - Stage.State.PROGRESS -> { - resultAllStep(stage) - } - } - } - - fun resultAllStep(stage: Stage) { - stage.steps.forEach { resultStep(it) } - } - - fun resultStep(step: Step) { - val str: StringBuilder = StringBuilder() - step.code.forEach { - when (it) { - Step.Result.CORRECT -> str.append("\uD83D\uDFE9") - Step.Result.MISMATCH -> str.append("\uD83D\uDFE8") - Step.Result.WRONG -> str.append("⬜") - } - } - println(str) - } - } -} - -fun main(args: Array) { - Game.start(LocalDate.now()) -} \ No newline at end of file diff --git a/src/test/kotlin/wordle/DictionaryTest.kt b/src/test/kotlin/wordle/DictionaryTest.kt new file mode 100644 index 0000000..4f211fb --- /dev/null +++ b/src/test/kotlin/wordle/DictionaryTest.kt @@ -0,0 +1,40 @@ +package wordle + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import wordle.domain.Word +import java.time.LocalDate + +class DictionaryTest { + + @ParameterizedTest + @ValueSource(strings = ["asder", "wrfdx", "bljwq"]) + fun `단어가 존재하지 않으면 false`(word: String) { + //when + val result = Dictionary.hasWord(Word(word)) + + //then + assertThat(result).isFalse() + } + + @ParameterizedTest + @ValueSource(strings = ["hello", "organ", "mercy"]) + fun `단어가 존재하면 true`(word: String) { + //when + val result = Dictionary.hasWord(Word(word)) + + //then + assertThat(result).isTrue() + } + + @Test + fun `날짜 기준으로 단어를 추출한다`() { + val now = LocalDate.of(2024, 6, 16) + + val findTodayWord = Dictionary.findTodayWord(now) + + assertThat(findTodayWord).isEqualTo("worry") + } +} \ No newline at end of file diff --git a/src/test/kotlin/wordle/StageTest.kt b/src/test/kotlin/wordle/StageTest.kt index c1d20d1..1db38fd 100644 --- a/src/test/kotlin/wordle/StageTest.kt +++ b/src/test/kotlin/wordle/StageTest.kt @@ -2,8 +2,8 @@ package wordle import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test -import wordle.Stage -import wordle.Word +import wordle.domain.Stage +import wordle.domain.Word internal class StageTest { diff --git a/src/test/kotlin/wordle/StepTest.kt b/src/test/kotlin/wordle/StepTest.kt index f20ac79..0af0e2a 100644 --- a/src/test/kotlin/wordle/StepTest.kt +++ b/src/test/kotlin/wordle/StepTest.kt @@ -2,6 +2,8 @@ package wordle import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import wordle.domain.Step +import wordle.domain.Word internal class StepTest { diff --git a/src/test/kotlin/wordle/WordTest.kt b/src/test/kotlin/wordle/WordTest.kt index b041ea4..a7d8c5d 100644 --- a/src/test/kotlin/wordle/WordTest.kt +++ b/src/test/kotlin/wordle/WordTest.kt @@ -5,16 +5,15 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource +import wordle.domain.Word internal class WordTest { - private val alwaysInDic = { _: String -> true } - @ParameterizedTest @ValueSource(strings = ["-1", "1", " ", "w!", " ;fs"]) fun `모두 영문으로 구성된다`(value: String) { //when - val errorResponse = assertThrows { Word.fromInput(value, alwaysInDic) } + val errorResponse = assertThrows { Word.fromInput(value) } ///then assertThat(errorResponse.message).isEqualTo("영문만 입력해야합니다.") @@ -24,7 +23,7 @@ internal class WordTest { @ValueSource(strings = ["test", "hi", "h", "tttttt"]) fun `5글자가 아니면 IllegalArgumentException 예외가 발생한다`(value: String) { //when - val errorResponse = assertThrows { Word.fromInput(value, alwaysInDic) } + val errorResponse = assertThrows { Word.fromInput(value) } ///then assertThat(errorResponse.message).isEqualTo("5글자여야 합니다.") @@ -32,17 +31,7 @@ internal class WordTest { @Test fun `영문 대문자는 소문자로 치환된다`() { - assertThat(Word.fromInput("Hello", alwaysInDic)).isEqualTo(Word.fromInput("hello", alwaysInDic)) - } - - @ParameterizedTest - @ValueSource(strings = ["testa", "hiwww", "heeee", "tttaa"]) - fun `사전에 존재하지 않으면 IllegalArgumentException 예외가 발생한다`(value: String) { - val errorResponse = - assertThrows { Word.fromInput(value) { _ -> false } } - - //then - assertThat(errorResponse.message).isEqualTo("존재하지 않는 단어입니다.") + assertThat(Word.fromInput("Hello")).isEqualTo(Word.fromInput("hello")) } } From d24df44570268ec840db6cb5e95d2496135f4ea2 Mon Sep 17 00:00:00 2001 From: woo-yu Date: Sat, 22 Jun 2024 16:22:55 +0900 Subject: [PATCH 20/28] =?UTF-8?q?fix:=20fail=EC=8B=9C=20X/6=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EB=85=B8=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/view/Output.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/wordle/view/Output.kt b/src/main/kotlin/wordle/view/Output.kt index fba70cc..5f6bee9 100644 --- a/src/main/kotlin/wordle/view/Output.kt +++ b/src/main/kotlin/wordle/view/Output.kt @@ -17,6 +17,7 @@ object Output { fun show(stage: Stage) { when (stage.state) { Stage.State.FAIL -> { + println("X/6") showAllSteps(stage) println("answer = ${stage.answer}") } From 1699aea3002e95102ed8bcea58ca5a829c154387 Mon Sep 17 00:00:00 2001 From: woo-yu Date: Sun, 30 Jun 2024 18:05:04 +0900 Subject: [PATCH 21/28] =?UTF-8?q?fix:=20dictionary=20=EB=B6=84=EB=A6=AC=20?= =?UTF-8?q?-=20AnswerSelector=20=EA=B0=9C=EB=85=90=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?-=20Dictionary=20=ED=99=95=EC=9E=A5=ED=95=98=EC=97=AC=20file=20?= =?UTF-8?q?=EA=B8=B0=EB=B0=98=EC=9C=BC=EB=A1=9C=20=EB=AA=85=EB=AA=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/Dictionary.kt | 21 ------------------- src/main/kotlin/wordle/Game.kt | 8 ++++--- src/main/kotlin/wordle/TodayAnswerSelector.kt | 12 +++++++++++ .../kotlin/wordle/domain/AnswerSelector.kt | 5 +++++ src/main/kotlin/wordle/domain/Dictionary.kt | 13 ++++++++++++ .../kotlin/wordle/infra/FileDictionary.kt | 11 ++++++++++ src/test/kotlin/wordle/DictionaryTest.kt | 21 +++++++++++-------- 7 files changed, 58 insertions(+), 33 deletions(-) delete mode 100644 src/main/kotlin/wordle/Dictionary.kt create mode 100644 src/main/kotlin/wordle/TodayAnswerSelector.kt create mode 100644 src/main/kotlin/wordle/domain/AnswerSelector.kt create mode 100644 src/main/kotlin/wordle/domain/Dictionary.kt create mode 100644 src/main/kotlin/wordle/infra/FileDictionary.kt diff --git a/src/main/kotlin/wordle/Dictionary.kt b/src/main/kotlin/wordle/Dictionary.kt deleted file mode 100644 index a4cf8da..0000000 --- a/src/main/kotlin/wordle/Dictionary.kt +++ /dev/null @@ -1,21 +0,0 @@ -package wordle - -import wordle.domain.Word -import java.io.File -import java.time.LocalDate - -object Dictionary { - private const val PATH = "./src/main/resources" - private const val FILE_NAME = "words.txt" - private val words: List = File(PATH, FILE_NAME).readLines() - private val BASE_DATE = LocalDate.of(2021, 6, 19) - - fun hasWord(word: Word): Boolean { - return words.contains(word.value) - } - - fun findTodayWord(nowDate: LocalDate): String { - val index = nowDate.toEpochDay().minus(BASE_DATE.toEpochDay()).toInt() % words.size - return words[index] - } -} \ No newline at end of file diff --git a/src/main/kotlin/wordle/Game.kt b/src/main/kotlin/wordle/Game.kt index 210c008..11f1032 100644 --- a/src/main/kotlin/wordle/Game.kt +++ b/src/main/kotlin/wordle/Game.kt @@ -2,13 +2,15 @@ package wordle import wordle.domain.Stage import wordle.domain.Word +import wordle.infra.FileDictionary import wordle.view.Input import wordle.view.Output import java.time.LocalDate class Game(today: LocalDate) { - private val answer = Dictionary.findTodayWord(today) - private var stage = Stage(answer = answer) + private val dictionary = FileDictionary() + private val answerSelector = TodayAnswerSelector(today) + private var stage = Stage(answer = dictionary.findAnswer(answerSelector)) fun start() { Output.start() @@ -23,7 +25,7 @@ class Game(today: LocalDate) { while (true) { try { val word = Input.guess() - require(Dictionary.hasWord(word)) { "존재하지 않는 단어입니다." } + require(dictionary.hasWord(word)) { "존재하지 않는 단어입니다." } return word } catch (e: Exception) { println(e.message) diff --git a/src/main/kotlin/wordle/TodayAnswerSelector.kt b/src/main/kotlin/wordle/TodayAnswerSelector.kt new file mode 100644 index 0000000..453f3f7 --- /dev/null +++ b/src/main/kotlin/wordle/TodayAnswerSelector.kt @@ -0,0 +1,12 @@ +package wordle + +import wordle.domain.AnswerSelector +import java.time.LocalDate + +class TodayAnswerSelector(private val today: LocalDate): AnswerSelector { + private val baseDate = LocalDate.of(2021, 6, 19) + + override fun findIndex(maxSize: Int): Int { + return today.toEpochDay().minus(baseDate.toEpochDay()).toInt() % maxSize + } +} \ No newline at end of file diff --git a/src/main/kotlin/wordle/domain/AnswerSelector.kt b/src/main/kotlin/wordle/domain/AnswerSelector.kt new file mode 100644 index 0000000..61f514e --- /dev/null +++ b/src/main/kotlin/wordle/domain/AnswerSelector.kt @@ -0,0 +1,5 @@ +package wordle.domain + +fun interface AnswerSelector { + fun findIndex(maxSize: Int): Int +} \ No newline at end of file diff --git a/src/main/kotlin/wordle/domain/Dictionary.kt b/src/main/kotlin/wordle/domain/Dictionary.kt new file mode 100644 index 0000000..3cdcac6 --- /dev/null +++ b/src/main/kotlin/wordle/domain/Dictionary.kt @@ -0,0 +1,13 @@ +package wordle.domain + +interface Dictionary { + val words: List + + fun hasWord(word: Word): Boolean { + return words.contains(word.value) + } + + fun findAnswer(answerSelector: AnswerSelector): String { + return words[answerSelector.findIndex(words.size)] + } +} \ No newline at end of file diff --git a/src/main/kotlin/wordle/infra/FileDictionary.kt b/src/main/kotlin/wordle/infra/FileDictionary.kt new file mode 100644 index 0000000..45f5c45 --- /dev/null +++ b/src/main/kotlin/wordle/infra/FileDictionary.kt @@ -0,0 +1,11 @@ +package wordle.infra + +import wordle.domain.Dictionary +import java.io.File + +class FileDictionary: Dictionary { + private val PATH = "./src/main/resources" + private val FILE_NAME = "words.txt" + + override val words: List = File(PATH, FILE_NAME).readLines() +} \ No newline at end of file diff --git a/src/test/kotlin/wordle/DictionaryTest.kt b/src/test/kotlin/wordle/DictionaryTest.kt index 4f211fb..a4af841 100644 --- a/src/test/kotlin/wordle/DictionaryTest.kt +++ b/src/test/kotlin/wordle/DictionaryTest.kt @@ -4,37 +4,40 @@ import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource +import wordle.domain.Dictionary import wordle.domain.Word -import java.time.LocalDate class DictionaryTest { + class MockDictionary(override val words: List) : Dictionary + + private val dictionary = MockDictionary(listOf("world", "angry", "hello")) + private val answerSelector = { _: Int -> 1 } + @ParameterizedTest @ValueSource(strings = ["asder", "wrfdx", "bljwq"]) fun `단어가 존재하지 않으면 false`(word: String) { //when - val result = Dictionary.hasWord(Word(word)) + val result = dictionary.hasWord(Word(word)) //then assertThat(result).isFalse() } @ParameterizedTest - @ValueSource(strings = ["hello", "organ", "mercy"]) + @ValueSource(strings = ["hello", "world", "angry"]) fun `단어가 존재하면 true`(word: String) { //when - val result = Dictionary.hasWord(Word(word)) + val result = dictionary.hasWord(Word(word)) //then assertThat(result).isTrue() } @Test - fun `날짜 기준으로 단어를 추출한다`() { - val now = LocalDate.of(2024, 6, 16) - - val findTodayWord = Dictionary.findTodayWord(now) + fun `answerSelector 기준으로 단어를 추출한다`() { + val findTodayWord = dictionary.findAnswer(answerSelector) - assertThat(findTodayWord).isEqualTo("worry") + assertThat(findTodayWord).isEqualTo("answer") } } \ No newline at end of file From 397a5c7462181557cf122554919c853eeb0b5036 Mon Sep 17 00:00:00 2001 From: woo-yu Date: Sun, 30 Jun 2024 18:06:52 +0900 Subject: [PATCH 22/28] =?UTF-8?q?fix:=20error=20=EC=B6=9C=EB=A0=A5?= =?UTF-8?q?=EB=8F=84=20ouput=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/Game.kt | 2 +- src/main/kotlin/wordle/view/Output.kt | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/wordle/Game.kt b/src/main/kotlin/wordle/Game.kt index 11f1032..9b04b94 100644 --- a/src/main/kotlin/wordle/Game.kt +++ b/src/main/kotlin/wordle/Game.kt @@ -28,7 +28,7 @@ class Game(today: LocalDate) { require(dictionary.hasWord(word)) { "존재하지 않는 단어입니다." } return word } catch (e: Exception) { - println(e.message) + Output.error(e) } } } diff --git a/src/main/kotlin/wordle/view/Output.kt b/src/main/kotlin/wordle/view/Output.kt index 5f6bee9..5d30cfc 100644 --- a/src/main/kotlin/wordle/view/Output.kt +++ b/src/main/kotlin/wordle/view/Output.kt @@ -33,6 +33,10 @@ object Output { } } + fun error(e: Exception) { + println(e.message) + } + private fun showAllSteps(stage: Stage) { stage.steps.forEach { showStep(it) } } From 54bcaf4a57c0a9e2353bb99facc7eb2eed39afc1 Mon Sep 17 00:00:00 2001 From: woo-yu Date: Sun, 30 Jun 2024 18:14:52 +0900 Subject: [PATCH 23/28] =?UTF-8?q?fix:=20Word=EC=9D=98=20validation?= =?UTF-8?q?=EC=9D=B4=20constructor=EB=A5=BC=20=ED=99=9C=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/domain/Word.kt | 17 ++++++++++------- src/main/kotlin/wordle/view/Input.kt | 2 +- src/test/kotlin/wordle/WordTest.kt | 6 +++--- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/wordle/domain/Word.kt b/src/main/kotlin/wordle/domain/Word.kt index d3bea57..8cb9b74 100644 --- a/src/main/kotlin/wordle/domain/Word.kt +++ b/src/main/kotlin/wordle/domain/Word.kt @@ -4,16 +4,19 @@ package wordle.domain * 사용자의 검증된 입력 단어 */ @JvmInline -value class Word(val value: String) { +value class Word private constructor(val value: String) { + + init { + require(value.matches(englishRegex)) { "영문만 입력해야합니다." } + require(value.length == 5) { "5글자여야 합니다." } + } + + constructor(value: String, isLowercase: Boolean = false) : + this(if (isLowercase) value else value.lowercase()) + companion object { private val englishRegex = Regex("^[A-Za-z]*") - - fun fromInput(input: String): Word { - require(input.matches(englishRegex)) { "영문만 입력해야합니다." } - require(input.length == 5) { "5글자여야 합니다." } - return Word(input.lowercase()) - } } } diff --git a/src/main/kotlin/wordle/view/Input.kt b/src/main/kotlin/wordle/view/Input.kt index 4f246af..d5b4d0f 100644 --- a/src/main/kotlin/wordle/view/Input.kt +++ b/src/main/kotlin/wordle/view/Input.kt @@ -6,7 +6,7 @@ object Input { fun guess(): Word { println("정답을 입력해 주세요.") - return Word.fromInput(readln().trim()) + return Word(readln().trim()) } } \ No newline at end of file diff --git a/src/test/kotlin/wordle/WordTest.kt b/src/test/kotlin/wordle/WordTest.kt index a7d8c5d..3c8e05a 100644 --- a/src/test/kotlin/wordle/WordTest.kt +++ b/src/test/kotlin/wordle/WordTest.kt @@ -13,7 +13,7 @@ internal class WordTest { @ValueSource(strings = ["-1", "1", " ", "w!", " ;fs"]) fun `모두 영문으로 구성된다`(value: String) { //when - val errorResponse = assertThrows { Word.fromInput(value) } + val errorResponse = assertThrows { Word(value) } ///then assertThat(errorResponse.message).isEqualTo("영문만 입력해야합니다.") @@ -23,7 +23,7 @@ internal class WordTest { @ValueSource(strings = ["test", "hi", "h", "tttttt"]) fun `5글자가 아니면 IllegalArgumentException 예외가 발생한다`(value: String) { //when - val errorResponse = assertThrows { Word.fromInput(value) } + val errorResponse = assertThrows { Word(value) } ///then assertThat(errorResponse.message).isEqualTo("5글자여야 합니다.") @@ -31,7 +31,7 @@ internal class WordTest { @Test fun `영문 대문자는 소문자로 치환된다`() { - assertThat(Word.fromInput("Hello")).isEqualTo(Word.fromInput("hello")) + assertThat(Word("Hello")).isEqualTo(Word("hello")) } } From 977b25e305310e67d9db2d2e1ef34e00586232d1 Mon Sep 17 00:00:00 2001 From: woo-yu Date: Sun, 30 Jun 2024 18:16:06 +0900 Subject: [PATCH 24/28] =?UTF-8?q?fix:=20!=20=EC=82=AC=EC=9A=A9=20->=20not(?= =?UTF-8?q?)=EC=9C=BC=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/Game.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/wordle/Game.kt b/src/main/kotlin/wordle/Game.kt index 9b04b94..64811c8 100644 --- a/src/main/kotlin/wordle/Game.kt +++ b/src/main/kotlin/wordle/Game.kt @@ -14,7 +14,7 @@ class Game(today: LocalDate) { fun start() { Output.start() - while (!stage.finished) { + while (stage.finished.not()) { val word = readWord() stage = stage.play(word) Output.show(stage) From 8a2da069d64f0ca6c78f0515d0672030fb26ab9d Mon Sep 17 00:00:00 2001 From: woo-yu Date: Sun, 30 Jun 2024 18:17:20 +0900 Subject: [PATCH 25/28] fix: EOF --- src/main/kotlin/wordle/Game.kt | 2 +- src/main/kotlin/wordle/TodayAnswerSelector.kt | 2 +- src/main/kotlin/wordle/domain/AnswerSelector.kt | 2 +- src/main/kotlin/wordle/domain/Dictionary.kt | 2 +- src/main/kotlin/wordle/domain/Step.kt | 2 +- src/main/kotlin/wordle/infra/FileDictionary.kt | 2 +- src/main/kotlin/wordle/view/Input.kt | 2 +- src/main/kotlin/wordle/view/Output.kt | 2 +- src/test/kotlin/wordle/DictionaryTest.kt | 2 +- src/test/kotlin/wordle/StageTest.kt | 2 +- src/test/kotlin/wordle/WordTest.kt | 1 - 11 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/wordle/Game.kt b/src/main/kotlin/wordle/Game.kt index 64811c8..79cf128 100644 --- a/src/main/kotlin/wordle/Game.kt +++ b/src/main/kotlin/wordle/Game.kt @@ -32,4 +32,4 @@ class Game(today: LocalDate) { } } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/wordle/TodayAnswerSelector.kt b/src/main/kotlin/wordle/TodayAnswerSelector.kt index 453f3f7..2e99477 100644 --- a/src/main/kotlin/wordle/TodayAnswerSelector.kt +++ b/src/main/kotlin/wordle/TodayAnswerSelector.kt @@ -9,4 +9,4 @@ class TodayAnswerSelector(private val today: LocalDate): AnswerSelector { override fun findIndex(maxSize: Int): Int { return today.toEpochDay().minus(baseDate.toEpochDay()).toInt() % maxSize } -} \ No newline at end of file +} diff --git a/src/main/kotlin/wordle/domain/AnswerSelector.kt b/src/main/kotlin/wordle/domain/AnswerSelector.kt index 61f514e..3511b14 100644 --- a/src/main/kotlin/wordle/domain/AnswerSelector.kt +++ b/src/main/kotlin/wordle/domain/AnswerSelector.kt @@ -2,4 +2,4 @@ package wordle.domain fun interface AnswerSelector { fun findIndex(maxSize: Int): Int -} \ No newline at end of file +} diff --git a/src/main/kotlin/wordle/domain/Dictionary.kt b/src/main/kotlin/wordle/domain/Dictionary.kt index 3cdcac6..2daa863 100644 --- a/src/main/kotlin/wordle/domain/Dictionary.kt +++ b/src/main/kotlin/wordle/domain/Dictionary.kt @@ -10,4 +10,4 @@ interface Dictionary { fun findAnswer(answerSelector: AnswerSelector): String { return words[answerSelector.findIndex(words.size)] } -} \ No newline at end of file +} diff --git a/src/main/kotlin/wordle/domain/Step.kt b/src/main/kotlin/wordle/domain/Step.kt index 2b85ee4..398b26f 100644 --- a/src/main/kotlin/wordle/domain/Step.kt +++ b/src/main/kotlin/wordle/domain/Step.kt @@ -46,4 +46,4 @@ data class Step(val answer: String, val word: Word) { } } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/wordle/infra/FileDictionary.kt b/src/main/kotlin/wordle/infra/FileDictionary.kt index 45f5c45..0faaadc 100644 --- a/src/main/kotlin/wordle/infra/FileDictionary.kt +++ b/src/main/kotlin/wordle/infra/FileDictionary.kt @@ -8,4 +8,4 @@ class FileDictionary: Dictionary { private val FILE_NAME = "words.txt" override val words: List = File(PATH, FILE_NAME).readLines() -} \ No newline at end of file +} diff --git a/src/main/kotlin/wordle/view/Input.kt b/src/main/kotlin/wordle/view/Input.kt index d5b4d0f..fbe90cc 100644 --- a/src/main/kotlin/wordle/view/Input.kt +++ b/src/main/kotlin/wordle/view/Input.kt @@ -9,4 +9,4 @@ object Input { return Word(readln().trim()) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/wordle/view/Output.kt b/src/main/kotlin/wordle/view/Output.kt index 5d30cfc..9ce3f23 100644 --- a/src/main/kotlin/wordle/view/Output.kt +++ b/src/main/kotlin/wordle/view/Output.kt @@ -51,4 +51,4 @@ object Output { }) } -} \ No newline at end of file +} diff --git a/src/test/kotlin/wordle/DictionaryTest.kt b/src/test/kotlin/wordle/DictionaryTest.kt index a4af841..e624538 100644 --- a/src/test/kotlin/wordle/DictionaryTest.kt +++ b/src/test/kotlin/wordle/DictionaryTest.kt @@ -40,4 +40,4 @@ class DictionaryTest { assertThat(findTodayWord).isEqualTo("answer") } -} \ No newline at end of file +} diff --git a/src/test/kotlin/wordle/StageTest.kt b/src/test/kotlin/wordle/StageTest.kt index 1db38fd..62eabde 100644 --- a/src/test/kotlin/wordle/StageTest.kt +++ b/src/test/kotlin/wordle/StageTest.kt @@ -59,4 +59,4 @@ internal class StageTest { assertThat(failedStage.state).isEqualTo(Stage.State.FAIL) assertThat(failedStage.finished).isTrue() } -} \ No newline at end of file +} diff --git a/src/test/kotlin/wordle/WordTest.kt b/src/test/kotlin/wordle/WordTest.kt index 3c8e05a..4bf0446 100644 --- a/src/test/kotlin/wordle/WordTest.kt +++ b/src/test/kotlin/wordle/WordTest.kt @@ -34,4 +34,3 @@ internal class WordTest { assertThat(Word("Hello")).isEqualTo(Word("hello")) } } - From 218134470d891b2ceb0e3fe11821c875e24ce721 Mon Sep 17 00:00:00 2001 From: woo-yu Date: Sun, 30 Jun 2024 18:20:38 +0900 Subject: [PATCH 26/28] =?UTF-8?q?fix:=20Dictionary=20=EC=9E=98=EB=AA=BB?= =?UTF-8?q?=EB=90=9C=20=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/test/kotlin/wordle/DictionaryTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/kotlin/wordle/DictionaryTest.kt b/src/test/kotlin/wordle/DictionaryTest.kt index e624538..61d54c7 100644 --- a/src/test/kotlin/wordle/DictionaryTest.kt +++ b/src/test/kotlin/wordle/DictionaryTest.kt @@ -38,6 +38,6 @@ class DictionaryTest { fun `answerSelector 기준으로 단어를 추출한다`() { val findTodayWord = dictionary.findAnswer(answerSelector) - assertThat(findTodayWord).isEqualTo("answer") + assertThat(findTodayWord).isEqualTo("angry") } } From 925ea1fca85e98c8e9343f36b03e7d508509f3e0 Mon Sep 17 00:00:00 2001 From: woo-yu Date: Sun, 30 Jun 2024 18:20:58 +0900 Subject: [PATCH 27/28] =?UTF-8?q?test:=20test=20assertAll=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/kotlin/wordle/StageTest.kt | 25 +++++++++++++++++-------- src/test/kotlin/wordle/StepTest.kt | 13 +++++++++---- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/test/kotlin/wordle/StageTest.kt b/src/test/kotlin/wordle/StageTest.kt index 62eabde..7b8a709 100644 --- a/src/test/kotlin/wordle/StageTest.kt +++ b/src/test/kotlin/wordle/StageTest.kt @@ -2,6 +2,7 @@ package wordle import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertAll import wordle.domain.Stage import wordle.domain.Word @@ -11,8 +12,10 @@ internal class StageTest { fun `스테이지를 만들면 PROGRESS`() { val stage = Stage("hello") - assertThat(stage.state).isEqualTo(Stage.State.PROGRESS) - assertThat(stage.finished).isFalse() + assertAll( + { assertThat(stage.state).isEqualTo(Stage.State.PROGRESS) }, + { assertThat(stage.finished).isFalse() } + ) } @Test @@ -24,8 +27,10 @@ internal class StageTest { val newStage = stage.play(Word("hello")) // then - assertThat(newStage.state).isEqualTo(Stage.State.COMPLETE) - assertThat(newStage.finished).isTrue() + assertAll( + { assertThat(newStage.state).isEqualTo(Stage.State.COMPLETE) }, + { assertThat(newStage.finished).isTrue() } + ) } @Test @@ -37,8 +42,10 @@ internal class StageTest { val newStage = stage.play(Word("wrong")) // then - assertThat(newStage.state).isEqualTo(Stage.State.PROGRESS) - assertThat(newStage.finished).isFalse() + assertAll( + { assertThat(newStage.state).isEqualTo(Stage.State.PROGRESS) }, + { assertThat(newStage.finished).isFalse() } + ) } @Test @@ -56,7 +63,9 @@ internal class StageTest { .play(Word("wrong")) // then - assertThat(failedStage.state).isEqualTo(Stage.State.FAIL) - assertThat(failedStage.finished).isTrue() + assertAll( + { assertThat(failedStage.state).isEqualTo(Stage.State.FAIL) }, + { assertThat(failedStage.finished).isTrue() } + ) } } diff --git a/src/test/kotlin/wordle/StepTest.kt b/src/test/kotlin/wordle/StepTest.kt index 0af0e2a..ebcadf5 100644 --- a/src/test/kotlin/wordle/StepTest.kt +++ b/src/test/kotlin/wordle/StepTest.kt @@ -2,6 +2,7 @@ package wordle import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertAll import wordle.domain.Step import wordle.domain.Word @@ -23,8 +24,10 @@ internal class StepTest { val resultCode = Step(answer, word).result // then - assertThat(resultCode[0]).isEqualTo(Step.Result.CORRECT) - assertThat(resultCode[2]).isEqualTo(Step.Result.CORRECT) + assertAll( + { assertThat(resultCode[0]).isEqualTo(Step.Result.CORRECT) }, + { assertThat(resultCode[2]).isEqualTo(Step.Result.CORRECT) }, + ) } @Test @@ -35,8 +38,10 @@ internal class StepTest { val resultCode = Step(answer, word).result // then - assertThat(resultCode[3]).isEqualTo(Step.Result.MISMATCH) - assertThat(resultCode[4]).isEqualTo(Step.Result.MISMATCH) + assertAll( + { assertThat(resultCode[3]).isEqualTo(Step.Result.MISMATCH) }, + { assertThat(resultCode[4]).isEqualTo(Step.Result.MISMATCH) }, + ) } @Test From 059569400a33e8a2d219dd806c6fb9df4bcb8fc0 Mon Sep 17 00:00:00 2001 From: woo-yu Date: Sun, 30 Jun 2024 18:21:29 +0900 Subject: [PATCH 28/28] =?UTF-8?q?fix:=20c=20->=20letter=EB=A1=9C=20?= =?UTF-8?q?=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/Step.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/wordle/domain/Step.kt b/src/main/kotlin/wordle/domain/Step.kt index 398b26f..142e95e 100644 --- a/src/main/kotlin/wordle/domain/Step.kt +++ b/src/main/kotlin/wordle/domain/Step.kt @@ -27,8 +27,8 @@ data class Step(val answer: String, val word: Word) { val isCorrect = result.all { it == Result.CORRECT } private fun fillCorrect(initResult: MutableList) { - word.value.forEachIndexed { index, c -> - if (answer[index] == c) { + word.value.forEachIndexed { index, letter -> + if (answer[index] == letter) { initResult[index] = Result.CORRECT } } @@ -38,10 +38,10 @@ data class Step(val answer: String, val word: Word) { val calculatedAnswer = StringBuilder(answer.filterIndexed { index, _ -> correctedResult[index] != Result.CORRECT }) - word.value.forEachIndexed { index, c -> - if (correctedResult[index] != Result.CORRECT && calculatedAnswer.contains(c)) { + word.value.forEachIndexed { index, letter -> + if (correctedResult[index] != Result.CORRECT && calculatedAnswer.contains(letter)) { correctedResult[index] = Result.MISMATCH - val foundIndex = calculatedAnswer.indexOf(c) + val foundIndex = calculatedAnswer.indexOf(letter) calculatedAnswer.deleteAt(foundIndex) } }