From e71587853b14ce27a8bc9409967b14ea27543fc6 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 03:10:15 +0900 Subject: [PATCH 01/40] =?UTF-8?q?docs(README.md):=20=EC=A7=84=ED=96=89=20?= =?UTF-8?q?=EC=9A=94=EA=B5=AC=20=EC=82=AC=ED=95=AD=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 세 가지 요구사항에 대해 간략하게 작성 2. 진행사항 중 페어프로그래밍 내용 작성 --- README.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3150c08..3ebc513 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,23 @@ # 미션 - 워들 -## 🔍 진행 방식 - -- 미션은 **과제 진행 요구 사항**, **기능 요구 사항**, **프로그래밍 요구 사항** 세 가지로 구성되어 있다. -- 세 개의 요구 사항을 만족하기 위해 노력한다. 특히 기능을 구현하기 전에 기능 목록을 만들고, 기능 단위로 커밋 하는 방식으로 진행한다. +## 🔍 진행 요구 사항 +- 미션은 아래의 세 가지 요구 사항으로 구성되어 있고, 각 요구 사항들을 만족하기 위해 노력한다. + +### [**1. 진행 요구 사항**](#-진행-방식) + +- 미션은 `페어프로그래밍`으로 진행한다. + > 유스방 5기 - Team 돌쓰 + > - boradol + > - yooth + +### [**2. 프로그래밍 요구 사항**](#-프로그래밍-요구-사항) +- 구현 전 환경구성이 프로그래밍 요구 사항을 확인하고 프로그래밍 요구 사항을 목록화한다. +- 구현 중 필요한 프로그래밍 요구사항들을 만족하기 위해 노력한다. +- 제출 전 프로그래밍 요구 사항을 지켰는지 확인한다. + +### [**3. 기능 요구 사항**](#-기능-요구-사항) +- 기능을 구현하기 전에 **기능 목록**을 만든다. +- **기능 단위로 커밋** 하는 방식으로 진행한다. - **기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다.** --- From 50d1cf41b4193937064ead47fe4db16252a4c8f9 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 03:14:23 +0900 Subject: [PATCH 02/40] =?UTF-8?q?docs(README.md):=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EB=9E=98=EB=B0=8D=20=EC=9A=94=EA=B5=AC=20=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 0. 기존 프로그래밍 요구 사항에서 수정 1. 환경 2. 구현 전 확인 사항 3. 구현 중 필요 사항 4. 제출 전 확인 사항 --- README.md | 52 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 3ebc513..1f68108 100644 --- a/README.md +++ b/README.md @@ -74,26 +74,34 @@ spill ## 🎯 프로그래밍 요구 사항 +### 환경 - Kotlin 1.9.0에서 실행 가능해야 한다. -- **Java 코드가 아닌 Kotlin 코드로만 구현해야 한다.** -- 프로그램 실행의 시작점은 `Application`의 `main()`이다. -- `build.gradle.kts` 파일은 변경할 수 없으며, **제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다.** -- 프로그램 종료 시 `System.exit()` 또는 `exitProcess()`를 호출하지 않는다. -- 프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다. -- 코틀린 코드 컨벤션을 지키면서 프로그래밍한다. - - 기본적으로 [Kotlin Coding conventions](https://kotlinlang.org/docs/coding-conventions.html)를 원칙으로 한다. -- indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다. - - 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다. - - 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다. -- 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라. -- JUnit 5와 AssertJ를 이용하여 정리한 기능 목록이 정상적으로 작동하는지 테스트 코드로 확인한다. - - 테스트 도구 사용법이 익숙하지 않다면 아래 문서를 참고하여 학습한 후 테스트를 구현한다. - - [JUnit 5 User Guide](https://junit.org/junit5/docs/current/user-guide) - - [AssertJ User Guide](https://assertj.github.io/doc) - - [AssertJ Exception Assertions](https://www.baeldung.com/assertj-exception-assertion) - - [Guide to JUnit 5 Parameterized Tests](https://www.baeldung.com/parameterized-tests-junit-5) -- 함수(또는 메서드)의 길이가 15라인을 넘어가지 않도록 구현한다. - - 함수(또는 메서드)가 한 가지 일만 잘 하도록 구현한다. -- 도메인 로직에 단위 테스트를 구현해야 한다. 단, UI(System.out, System.in, Scanner) 로직은 제외한다. - - 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 분리해 구현한다. - - 힌트: MVC 패턴 기반으로 구현한 후, View와 Controller를 제외한 Model에 대한 단위 테스트 추가에 집중한다. + - **Java 코드가 아닌 Kotlin 코드로만 구현해야 한다.** + +### 구현 전 확인 사항 +- [ ] 프로그램 실행의 시작점은 `Application`의 `main()`이다. +- [ ] `build.gradle.kts` 파일은 변경할 수 없으며, **제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다.** +- [ ] 프로그램 종료 시 `System.exit()` 또는 `exitProcess()`를 호출하지 않는다. +- [ ] 프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다. + +### 구현 중 필요 사항 +- [ ] 코틀린 코드 컨벤션[(Kotlin Coding conventions)](https://kotlinlang.org/docs/coding-conventions.html)을 지키면서 프로그래밍한다. + - Commit 전에 `ktlint`를 습관적으로 확인한다. + - ```./gradlew ktlintCheck``` + - ```./gradlew addKtlintCheckGitPreCommitHook``` +- [ ] indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다. +- [ ] 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라. +- [ ] 함수(또는 메서드)의 길이가 15라인을 넘어가지 않도록 구현한다. +- [ ] 도메인 로직에 단위 테스트를 구현해야 한다. + - `JUnit 5`와 `AssertJ`를 이용하여 정리한 기능 목록이 정상적으로 작동하는지 테스트 코드로 확인한다. + - [JUnit 5 User Guide](https://junit.org/junit5/docs/current/user-guide) + - [AssertJ User Guide](https://assertj.github.io/doc) + - [AssertJ Exception Assertions](https://www.baeldung.com/assertj-exception-assertion) + - [Guide to JUnit 5 Parameterized Tests](https://www.baeldung.com/parameterized-tests-junit-5) + - 단, UI(System.out, System.in, Scanner) 로직은 제외한다. +- [ ] 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 분리해 구현한다. + - 힌트: MVC 패턴 기반으로 구현한 후, View와 Controller를 제외한 Model에 대한 단위 테스트 추가에 집중한다. + +### 제출 전 확인 사항 +- [ ] 위의 프로그래밍 요구 사항을 준수했는지 확인한다. + - 체크박스를 모두 [x]로 만드는 것을 목표로 한다. From 59f7a4e9037e484bfe71c03a3f15e70f6c170219 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 03:35:55 +0900 Subject: [PATCH 03/40] =?UTF-8?q?docs(README.md):=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EB=9E=98=EB=B0=8D=20=EC=9A=94=EA=B5=AC=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=9C=84=EC=B9=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/README.md b/README.md index 1f68108..4bb5610 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,41 @@ - 기능을 구현하기 전에 **기능 목록**을 만든다. - **기능 단위로 커밋** 하는 방식으로 진행한다. - **기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다.** +--- + +## 🎯 프로그래밍 요구 사항 + +### 환경 +- Kotlin 1.9.0에서 실행 가능해야 한다. + - **Java 코드가 아닌 Kotlin 코드로만 구현해야 한다.** + +### 구현 전 확인 사항 +- [ ] 프로그램 실행의 시작점은 `Application`의 `main()`이다. +- [ ] `build.gradle.kts` 파일은 변경할 수 없으며, **제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다.** +- [ ] 프로그램 종료 시 `System.exit()` 또는 `exitProcess()`를 호출하지 않는다. +- [ ] 프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다. + +### 구현 중 필요 사항 +- [ ] 코틀린 코드 컨벤션[(Kotlin Coding conventions)](https://kotlinlang.org/docs/coding-conventions.html)을 지키면서 프로그래밍한다. + - Commit 전에 `ktlint`를 습관적으로 확인한다. + - ```./gradlew ktlintCheck``` + - ```./gradlew addKtlintCheckGitPreCommitHook``` +- [ ] indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다. +- [ ] 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라. +- [ ] 함수(또는 메서드)의 길이가 15라인을 넘어가지 않도록 구현한다. +- [ ] 도메인 로직에 단위 테스트를 구현해야 한다. + - `JUnit 5`와 `AssertJ`를 이용하여 정리한 기능 목록이 정상적으로 작동하는지 테스트 코드로 확인한다. + - [JUnit 5 User Guide](https://junit.org/junit5/docs/current/user-guide) + - [AssertJ User Guide](https://assertj.github.io/doc) + - [AssertJ Exception Assertions](https://www.baeldung.com/assertj-exception-assertion) + - [Guide to JUnit 5 Parameterized Tests](https://www.baeldung.com/parameterized-tests-junit-5) + - 단, UI(System.out, System.in, Scanner) 로직은 제외한다. +- [ ] 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 분리해 구현한다. + - 힌트: MVC 패턴 기반으로 구현한 후, View와 Controller를 제외한 Model에 대한 단위 테스트 추가에 집중한다. + +### 제출 전 확인 사항 +- [ ] 위의 프로그래밍 요구 사항을 준수했는지 확인한다. + - 체크박스를 모두 [x]로 만드는 것을 목표로 한다. --- From 8013a33b93fcac83932cd28c7acf0e58c8f375db Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 03:40:21 +0900 Subject: [PATCH 04/40] =?UTF-8?q?docs(README.md):=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=9A=94=EA=B5=AC=20=EC=82=AC=ED=95=AD=EC=97=90=20=EC=9A=A9?= =?UTF-8?q?=EC=96=B4=20=EC=A0=95=EB=A6=AC=EC=99=80=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=ED=95=AD=EB=AA=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 63 ++++++++++++++++++++----------------------------------- 1 file changed, 23 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 4bb5610..6ca872a 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ## 🔍 진행 요구 사항 - 미션은 아래의 세 가지 요구 사항으로 구성되어 있고, 각 요구 사항들을 만족하기 위해 노력한다. -### [**1. 진행 요구 사항**](#-진행-방식) +### [**1. 진행 요구 사항**](#-기능-요구-사항) - 미션은 `페어프로그래밍`으로 진행한다. > 유스방 5기 - Team 돌쓰 @@ -16,7 +16,9 @@ - 제출 전 프로그래밍 요구 사항을 지켰는지 확인한다. ### [**3. 기능 요구 사항**](#-기능-요구-사항) -- 기능을 구현하기 전에 **기능 목록**을 만든다. +- 기능을 구현하기 전에 기능 요구 사항을 참고하여 **기능 목록**을 만든다. + - 페어프로그래밍을 위해 **용어를 정리**한다. + - [**용어 정리**](#-용어-정리)와 [**기능 목록**](#-기능-목록)은 계속 수정될 수 있다. - **기능 단위로 커밋** 하는 방식으로 진행한다. - **기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다.** --- @@ -59,15 +61,16 @@ ## 🚀 기능 요구 사항 -선풍적인 인기를 끌었던 영어 단어 맞추기 게임이다. +### 워들 게임 규칙 -- 6x5 격자를 통해서 5글자 단어를 6번 만에 추측한다. -- 플레이어가 답안을 제출하면 프로그램이 정답과 제출된 단어의 각 알파벳 종류와 위치를 비교해 판별한다. -- 판별 결과는 흰색의 타일이 세 가지 색(초록색/노란색/회색) 중 하나로 바뀌면서 표현된다. - - 맞는 글자는 초록색, 위치가 틀리면 노란색, 없으면 회색 - - 두 개의 동일한 문자를 입력하고 그중 하나가 회색으로 표시되면 해당 문자 중 하나만 최종 단어에 나타난다. -- 정답과 답안은 `words.txt`에 존재하는 단어여야 한다. -- 정답은 매일 바뀌며 ((현재 날짜 - 2021년 6월 19일) % 배열의 크기) 번째의 단어이다. +>선풍적인 인기를 끌었던 영어 단어 맞추기 게임이다. +>- 6x5 격자를 통해서 5글자 단어를 6번 만에 추측한다. +>- 플레이어가 답안을 제출하면 프로그램이 정답과 제출된 단어의 각 알파벳 종류와 위치를 비교해 판별한다. +>- 판별 결과는 흰색의 타일이 세 가지 색(초록색/노란색/회색) 중 하나로 바뀌면서 표현된다. +> - 맞는 글자는 초록색, 위치가 틀리면 노란색, 없으면 회색 +> - 두 개의 동일한 문자를 입력하고 그중 하나가 회색으로 표시되면 해당 문자 중 하나만 최종 단어에 나타난다. +>- 정답과 답안은 `words.txt`에 존재하는 단어여야 한다. +>- 정답은 매일 바뀌며 ((현재 날짜 - 2021년 6월 19일) % 배열의 크기) 번째의 단어이다. ### 입출력 요구 사항 @@ -105,38 +108,18 @@ spill 🟩🟩🟩🟩🟩 ``` ---- +### ✏️ 용어 정리 -## 🎯 프로그래밍 요구 사항 +| 국문 | 영문 | 설명 | +|--|---------|----------------------------| +| 워들 게임 | Wordle Game | 5글자 영어 단어를 6번 만에 맞추는 게임이다. | +| | | | -### 환경 -- Kotlin 1.9.0에서 실행 가능해야 한다. - - **Java 코드가 아닌 Kotlin 코드로만 구현해야 한다.** -### 구현 전 확인 사항 -- [ ] 프로그램 실행의 시작점은 `Application`의 `main()`이다. -- [ ] `build.gradle.kts` 파일은 변경할 수 없으며, **제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다.** -- [ ] 프로그램 종료 시 `System.exit()` 또는 `exitProcess()`를 호출하지 않는다. -- [ ] 프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다. +### 💻 기능 목록 -### 구현 중 필요 사항 -- [ ] 코틀린 코드 컨벤션[(Kotlin Coding conventions)](https://kotlinlang.org/docs/coding-conventions.html)을 지키면서 프로그래밍한다. - - Commit 전에 `ktlint`를 습관적으로 확인한다. - - ```./gradlew ktlintCheck``` - - ```./gradlew addKtlintCheckGitPreCommitHook``` -- [ ] indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다. -- [ ] 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라. -- [ ] 함수(또는 메서드)의 길이가 15라인을 넘어가지 않도록 구현한다. -- [ ] 도메인 로직에 단위 테스트를 구현해야 한다. - - `JUnit 5`와 `AssertJ`를 이용하여 정리한 기능 목록이 정상적으로 작동하는지 테스트 코드로 확인한다. - - [JUnit 5 User Guide](https://junit.org/junit5/docs/current/user-guide) - - [AssertJ User Guide](https://assertj.github.io/doc) - - [AssertJ Exception Assertions](https://www.baeldung.com/assertj-exception-assertion) - - [Guide to JUnit 5 Parameterized Tests](https://www.baeldung.com/parameterized-tests-junit-5) - - 단, UI(System.out, System.in, Scanner) 로직은 제외한다. -- [ ] 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 분리해 구현한다. - - 힌트: MVC 패턴 기반으로 구현한 후, View와 Controller를 제외한 Model에 대한 단위 테스트 추가에 집중한다. +#### 입력(Input View) -### 제출 전 확인 사항 -- [ ] 위의 프로그래밍 요구 사항을 준수했는지 확인한다. - - 체크박스를 모두 [x]로 만드는 것을 목표로 한다. +#### 출력(Output View) + +#### 워들 게임(Wordle Game) From c5b1d44ca30a234b2ba6e9689e31ce3121b4a84e Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 05:25:03 +0900 Subject: [PATCH 05/40] =?UTF-8?q?docs(README.md):=20=EC=9A=A9=EC=96=B4?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6ca872a..9180cd2 100644 --- a/README.md +++ b/README.md @@ -110,10 +110,36 @@ spill ### ✏️ 용어 정리 -| 국문 | 영문 | 설명 | -|--|---------|----------------------------| -| 워들 게임 | Wordle Game | 5글자 영어 단어를 6번 만에 맞추는 게임이다. | -| | | | +| 국문 | 영문 | 설명 | +|-------------|-------------------|--------------------------------------------------------------------------------------------------------------| +| 워들 게임 | Wordle Game | 알파벳 소문자로만 이루어진 5글자 영어 단어를 6번 만에 맞추는 게임이다. | +| 단어 | Word | 워들 게임에서 플레이어가 입력해야 하는 최소 단위이다. | +| 사전 | Dictionary | 유효한 단어들의 목록이다. 이 목록에서 정답 단어를 가져오고 답안 단어의 유효함을 확인한다. | +| 기준 날짜 | Criterion Date | 단어 사전에서 한 단어를 가져오는 계산에 필요한 기준 날짜이다. | +| 답안 단어 | Answer Word | 플레이어가 정답이 되는 단어를 맞추기 위해 입력하는 단어이다. | +| 오늘의 단어 | Today Word | 정답이 되는 단어이다. 기준 날짜와 오늘의 날짜를 계산하여 단어 사전에서 단어 하나를 가져온다. 이 단어는 매일 바뀐다. | +| 글자 | Letter | 답안 단어와 오늘의 단어가 일치하는지 확인하는 최소 단위이다. 글자 여러 개가 모여 단어를 구성한다. 예) "hello"는 'h', 'e', 'l', 'o'의 글자로 이루어져 있다. | +| 워들 게임 로직 | Wordle Game Logic | 답안 단어와 오늘의 단어를 비교한 단어 결과를 받아 온다. | +| 단어 비교기 | Word Comparator | 답안 단어와 오늘의 단어를 각 글자의 일치를 검증하는 기능을 한다. | +| 시도 가능 횟수 | Try Count | 답안 단어를 입력 가능한 횟수이다. 유효한 단어를 입력할 때마다 1회씩 차감되며, 0회가 되면 게임은 무조건 종료된다. | +| 최대 시도 가능 횟수 | Max Try Count | 유효한 답안을 입력할 수 있는 최대 횟수이다. | +| 시도한 횟수 | Attempt Count | 유효한 답안 단어를 입력한 횟수이다. ([최대 시도 가능 횟수] - [시도 가능 횟수]) | +| 단어 결과 | Word Result | 유효한 답안 단어를 입력 후 해당 답안 단어에 대한 비교를 거친 후 각 글자 일치 상태 목록을 가지고 있다. | +| 단어 결과 목록 | Word Results | 글자 일치 상태 목록과 시도 가능 횟수를 가지고 있는 목록이다. | +| 글자 일치 상태 | Letter Match | 답안 단어와 오늘의 단어를 이루는 각 글자들의 일치하는 상태를 나타내는 유형이다. | +| 완전 일치 글자 | Correct Letter | 글자 일치 상태 중, 같은 위치에 있는 답안 단어와 오늘의 단어의 글자가 같을 때의 상태이다. | +| 부분 일치 글자 | Present Letter | 글자 일치 상태 중, 다른 위치에 있는 글자가 오늘의 단어에 존재하는 상태이다. 이 때, 오늘의 단어에서 한번 비교한 부분 일치 글자는 다음 위치에 같은 부분 일치 글자가 나와도 비교하지 않는다. | +| 불일치 글자 | Absent Letter | 글자 일치 상태 중, 오늘의 단어에 해당 글자가 없는 상태이다. | +| 일치 표시 기호 | Match Marker | 오늘의 단어 중 답안의 단어와 불일치 상태가 아닌 글자를 표시하는 기호이다. | +| 타일 | Tile | 단어 비교의 각 글자별 결과를 타일의 색깔로 나타낸다. 글자 일치 상태와 대응 된다. | +| 초록색 타일 | Green Tile | 글자 완전 일치 상태의 결과를 나타낸 색상의 타일이다. | +| 노란색 타일 | Yellow Tile | 글자 부분 일치 상태의 결과를 나타낸 색상의 타일이다. | +| 회색 타일 | Grey Tile | 글자 불일치 상태의 결과를 나타낸 색상의 타일이다. | +| 성공 | Success | 워들 게임에서 답안의 단어가 오늘의 단어의 모든 글자가 완전 일치한 상태이다. 시도한 횟수를 알려주며 게임이 종료된다. | +| 실패 | Fail | 워들 게임을 성공하지 못한 상태에서 시도 가능 횟수가 0회 이하가 되면 워들 게임에 실패한다. 오늘의 단어를 알려주며 게임이 종료된다. | +| 계속 | Continuous | 워들 게임을 성공하지 못한 상태에서 시도 가능 횟수가 0회보다 크면 게임을 계속 시도해볼 수 있다. | +| 다시 시도 | Retry | 유효한 단어를 입력하지 못할 때, 시도 가능 횟수가 차감되지 않는다. 이 때, 유효하지 않은 이유를 안내하며 다시 답안을 입력하게 한다. | +| | | | ### 💻 기능 목록 From 54fe090715e31adf343398310f89bcbdb9bb5ec9 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 08:01:15 +0900 Subject: [PATCH 06/40] =?UTF-8?q?docs(README.md):=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9180cd2..350bc07 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ ``` WORDLE을 6번 만에 맞춰 보세요. 시도의 결과는 타일의 색 변화로 나타납니다. + 정답을 입력해 주세요. hello @@ -100,12 +101,12 @@ spell 정답을 입력해 주세요. spill -4/6 - ⬜⬜🟨🟩⬜ 🟨⬜⬜⬜🟩 🟩🟩⬜🟩🟩 🟩🟩🟩🟩🟩 + +성공!! 정답입니다. 4/6 ``` ### ✏️ 용어 정리 @@ -144,8 +145,75 @@ spill ### 💻 기능 목록 -#### 입력(Input View) - #### 출력(Output View) +- [ ] 게임 시작 메시지를 출력한다. + - `WORDLE을 6번 만에 맞춰 보세요.\n시도의 결과는 타일의 색 변화로 나타납니다.` +- [ ] 타일 결과를 출력한다. + - [ ] 플레이어가 입력한 답안 단어와 비교한 답안 결과에 따라 각 글자의 일치 상태에 맞게 타일을 출력한다. + - `🟩` 초록색: 오늘의 단어와 비교하여 답안 단어의 글자가 '완전 일치'인 경우 + - `🟨` 노란색: 오늘의 단어와 비교하여 답안 단어의 글자가 '부분 일치'인 경우 + - `⬜️` 회색: 오늘의 단어와 비교하여 답안 단어의 글자가 '불일치'인 경우 + - [ ] 지금까지 입력한 답안 단어에 따른 단어 결과 목록을 출력한다. +- [ ] 다시 시도 할 때, 다시 시도의 메시지와 그 이유를 함께 출력한다. +- [ ] 게임의 결과에 따라 메시지를 출력한다 + - [ ] 성공 메시지를 출력한다. `{시도한 횟수} / {최대 시도 가능 횟수}`도 함께 출력한다. + - [ ] 실패 메시지를 출력한다. `{오늘의 단어}`를 함께 출력한다. + +#### 입력(Input View) +- [ ] 답안 단어를 입력받는다. 입력받기 전에 답안 입력을 위한 메시지를 먼저 출력한다. + - `정답을 입력해 주세요.` + +#### 글자(Letter) +- [ ] 글자는 하나의 문자를 가진다. +- [ ] 글자는 소문자 알파벳이나 일치 표시 기호('#')만 유효하다. +- [ ] 일치 표시 기호가 들어간 글자로 변경할 수 있다. + +#### 단어(Word) +- [ ] 단어는 여러 개의 글자로 이루어진다. +- [ ] 단어는 공백을 입력할 수 없습니다. +- [ ] 단어는 5개의 글자를 가진다. +- [ ] 단어는 사전에 있는 단어이어야 한다. + +#### 오늘의 단어(Today Word) +- [ ] 오늘의 단어는 현재 날짜를 입력하여 사전에서 한 단어를 불러온다. 이때, 오늘의 단어는 매일 바뀐다. + - [ ] `DictionaryFileLoader`를 이용하여 `words.txt`파일에서 사전의 단어 목록을 불러온다. + - [ ] 사전 단어 목록의 ((현재 날짜 - 2021년 6월 19일) % 배열의 크기) 번째의 단어이다. + +#### 단어 비교기(Word Comparator) +- [ ] 각 단어를 이루고 있는 글자들의 일치 상태를 비교한다. + - [ ] 완전 일치 글자를 비교한다. 완전 일치 글자의 상태는 ABSENT -> CORRECT 로 변경된다. + - [ ] 부분 일치 글자를 비교한다. 부분 일치 글자의 상태는 ABSENT -> PRESENT 로 변경된다. +- [ ] 완전일치하거나 부분일치한 글자이면 그 글자는 일치 표시 기호로 변경된다. + +#### 워들 게임 로직(Wordle Game Logic) +- [ ] 오늘의 단어와 답안 단어를 비교하여 단어 결과를 얻는다. + - [ ] 단어 결과가 모두 불일치인 경우 + - [ ] 단어 결과에 예상되는 일치상태가 포함된 경우 + - [ ] 단어 결과가 모두 완전 일치인 경우 #### 워들 게임(Wordle Game) +- [ ] 답안 단어를 최대 6번 입력받아 게임을 한다. +- [ ] 워들게임에서 유효하지 않은 단어를 입력하면 시행 가능 횟수가 줄어들지 않는다. +- [ ] 예상되는 단어 결과 목록을 확인한다. + - [ ] 답안 단어 1번 입력하는 경우 + - [ ] 답안 단어를 6번 입력하고 게임에 실패하는 경우 +- [ ] 게임에 성공하였을 때, 시도한 횟수를 알 수 있다. + +#### 시도 가능 횟수(Try Count) +- [ ] 시도 가능 회수는 횟수를 가진다. 최초 생성할 때에는 6회이다. +- [ ] 시도 가능 횟수는 0회보다 커야 한다. +- [ ] 시도 가능 횟수를 1회씩 차감할 수 있다. +- [ ] 시도 가능 횟수가 남았는지 알 수 있다. +- [ ] 시도한 횟수를 구할 수 있다. + +#### 단어 결과(Word Result) +- [ ] 5개의 글자 일치 상태로 이루어져 있다. +- [ ] 처음에 생성할 때, 글자 일치 상태는 불일치 상태이다. +- [ ] 모든 글자 일치 상태가 CORRECT이면, 해당 단어는 오늘의 단어와 같다. +- [ ] 해당 글자의 글자 일치 상태를 변경할 수 있다. + +#### 단어 결과 목록(Word Results) +- [ ] 단어결과와 시도횟수를 가진다. 이때, 단어결과 목록은 빈 목록이고, 시도한 횟수는 0이다. +- [ ] 단어 결과를 추가하면, 시도횟수는 1회 증가한다. +- [ ] 단어 결과 목록에서 단어 결과가 성공인 상태가 추가되면 게임은 성공한다. +- [ ] 단어 결과 목록에서 아직 게임 성공하지 못하고 시도 가능 횟수가 1 이상이면 게임을 계속할 수 있다. From 770346bca5a8ac82370018be9e9dfd7f4b326774 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 13:42:22 +0900 Subject: [PATCH 07/40] =?UTF-8?q?chore(.gitkeep):=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20gitkeep=20=ED=8C=8C=EC=9D=BC=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/.gitkeep | 0 src/test/kotlin/.gitkeep | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/main/kotlin/.gitkeep delete mode 100644 src/test/kotlin/.gitkeep diff --git a/src/main/kotlin/.gitkeep b/src/main/kotlin/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/test/kotlin/.gitkeep b/src/test/kotlin/.gitkeep deleted file mode 100644 index e69de29..0000000 From 5c26467f4c64d57d232bbd80b18a5aa091d4d9c1 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 15:09:15 +0900 Subject: [PATCH 08/40] =?UTF-8?q?build:=20=EC=BD=94=ED=8B=80=EB=A6=B0=20?= =?UTF-8?q?=EB=B2=84=EC=A0=84=201.9.0=20->=201.9.23?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Solved Unknown Kotlin JVM target: 21 코틀린 1.9.0에서 JDK21을 지원하지 않아 생긴 문제 (https://github.com/gradle/gradle/issues/25574#issuecomment-1761314551) --- README.md | 4 +++- build.gradle.kts | 9 ++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 350bc07..460ca4d 100644 --- a/README.md +++ b/README.md @@ -26,12 +26,14 @@ ## 🎯 프로그래밍 요구 사항 ### 환경 -- Kotlin 1.9.0에서 실행 가능해야 한다. +- Kotlin 1.9.23에서 실행. - **Java 코드가 아닌 Kotlin 코드로만 구현해야 한다.** ### 구현 전 확인 사항 - [ ] 프로그램 실행의 시작점은 `Application`의 `main()`이다. - [ ] `build.gradle.kts` 파일은 변경할 수 없으며, **제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다.** + - `Unknown Kotlin JVM target: 21` 문제로 `kotlin 1.9.23`으로 올림 + - [문제해결](https://github.com/gradle/gradle/issues/25574#issuecomment-1761314551) - [ ] 프로그램 종료 시 `System.exit()` 또는 `exitProcess()`를 호출하지 않는다. - [ ] 프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다. diff --git a/build.gradle.kts b/build.gradle.kts index 19a215a..ab9c11f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("jvm") version "1.9.0" + kotlin("jvm") version "1.9.23" id("org.jlleitschuh.gradle.ktlint") version "12.1.0" } @@ -14,9 +14,12 @@ repositories { mavenCentral() } +val junitJupiterVersion = "5.10.2" +val assertJVersion = "3.25.3" + dependencies { - testImplementation("org.junit.jupiter", "junit-jupiter", "5.10.2") - testImplementation("org.assertj", "assertj-core", "3.25.3") + testImplementation("org.junit.jupiter", "junit-jupiter", junitJupiterVersion) + testImplementation("org.assertj", "assertj-core", assertJVersion) } tasks { From 4cc4c99f5a07facadb1cb4d66d49c6d9aae3c21f Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 15:37:01 +0900 Subject: [PATCH 09/40] =?UTF-8?q?feat(Word):=20=EB=AC=B8=EC=9E=90=EC=97=B4?= =?UTF-8?q?=EC=9D=84=20=EC=9E=85=EB=A0=A5=EB=B0=9B=EC=95=84=20=EB=8B=A8?= =?UTF-8?q?=EC=96=B4=EB=A5=BC=20=EB=A7=8C=EB=93=A4=20=EC=88=98=20=EC=9E=88?= =?UTF-8?q?=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 단어는 공백만 입력할 수 없다. 2. 단어의 길이는 5자 이어야 한다. --- README.md | 4 ++-- src/main/kotlin/wordle/domain/Word.kt | 10 +++++++++ src/test/kotlin/domain/WordTest.kt | 31 +++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/wordle/domain/Word.kt create mode 100644 src/test/kotlin/domain/WordTest.kt diff --git a/README.md b/README.md index 460ca4d..73a9f3a 100644 --- a/README.md +++ b/README.md @@ -172,8 +172,8 @@ spill #### 단어(Word) - [ ] 단어는 여러 개의 글자로 이루어진다. -- [ ] 단어는 공백을 입력할 수 없습니다. -- [ ] 단어는 5개의 글자를 가진다. +- [x] 단어는 공백만 입력할 수 없다. +- [x] 단어의 길이는 5자 이어야 한다. - [ ] 단어는 사전에 있는 단어이어야 한다. #### 오늘의 단어(Today Word) diff --git a/src/main/kotlin/wordle/domain/Word.kt b/src/main/kotlin/wordle/domain/Word.kt new file mode 100644 index 0000000..054949e --- /dev/null +++ b/src/main/kotlin/wordle/domain/Word.kt @@ -0,0 +1,10 @@ +package wordle.domain + +data class Word(val word: String) { + init { + check(word.isNotBlank()) { "단어는 공백만 입력할 수 없습니다" } + check(word.length == WORD_LENGTH) { "단어의 길이는 5자 입니다." } + } +} + +const val WORD_LENGTH = 5 diff --git a/src/test/kotlin/domain/WordTest.kt b/src/test/kotlin/domain/WordTest.kt new file mode 100644 index 0000000..0d195da --- /dev/null +++ b/src/test/kotlin/domain/WordTest.kt @@ -0,0 +1,31 @@ +package domain + +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EmptySource +import org.junit.jupiter.params.provider.ValueSource +import wordle.domain.Word + +class WordTest { + @Test + fun `(성공) 단어를 생성한다`() { + assertThat(Word("hello")).isEqualTo(Word("hello")) + } + + @EmptySource + @ValueSource(strings = [" ", " "]) + @ParameterizedTest + fun `(예외) 단어는 공백만 입력할 수 없다`(invalidWord: String) { + assertThatThrownBy { (Word(invalidWord)) } + .isInstanceOf(IllegalStateException::class.java) + } + + @ValueSource(strings = ["word", "aaaaaa"]) + @ParameterizedTest + fun `(예외) 단어의 길이는 5자 이어야 한다`(invalidWord: String) { + assertThatThrownBy { (Word(invalidWord)) } + .isInstanceOf(IllegalStateException::class.java) + } +} From a9312571301facbe2a4aa31846cfdb2eab3697e8 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 15:58:54 +0900 Subject: [PATCH 10/40] =?UTF-8?q?feat(Word):=20=EB=8B=A8=EC=96=B4=EB=8A=94?= =?UTF-8?q?=20=EC=82=AC=EC=A0=84=EC=97=90=20=EC=9E=88=EB=8A=94=20=EB=8B=A8?= =?UTF-8?q?=EC=96=B4=EC=9D=B4=EC=96=B4=EC=95=BC=20=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. DictionaryFileLoader에서 단어목록 불러옴. 2. 사전에서 단어목록 포함여부를 판별한다. --- README.md | 7 +++++-- src/main/kotlin/wordle/domain/Dictionary.kt | 5 +++++ src/main/kotlin/wordle/domain/Word.kt | 3 ++- src/main/kotlin/wordle/infra/DictionaryFileLoader.kt | 12 ++++++++++++ src/test/kotlin/domain/WordTest.kt | 7 +++++++ 5 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/wordle/domain/Dictionary.kt create mode 100644 src/main/kotlin/wordle/infra/DictionaryFileLoader.kt diff --git a/README.md b/README.md index 73a9f3a..ed950de 100644 --- a/README.md +++ b/README.md @@ -174,11 +174,14 @@ spill - [ ] 단어는 여러 개의 글자로 이루어진다. - [x] 단어는 공백만 입력할 수 없다. - [x] 단어의 길이는 5자 이어야 한다. -- [ ] 단어는 사전에 있는 단어이어야 한다. +- [x] 단어는 사전에 있는 단어이어야 한다. + +#### 사전(Dictionary) +- [x] `DictionaryFileLoader`를 이용하여 `words.txt`파일에서 사전의 단어 목록을 불러온다. +- [x] 사전에 단어가 포함되는지 판별한다. #### 오늘의 단어(Today Word) - [ ] 오늘의 단어는 현재 날짜를 입력하여 사전에서 한 단어를 불러온다. 이때, 오늘의 단어는 매일 바뀐다. - - [ ] `DictionaryFileLoader`를 이용하여 `words.txt`파일에서 사전의 단어 목록을 불러온다. - [ ] 사전 단어 목록의 ((현재 날짜 - 2021년 6월 19일) % 배열의 크기) 번째의 단어이다. #### 단어 비교기(Word Comparator) diff --git a/src/main/kotlin/wordle/domain/Dictionary.kt b/src/main/kotlin/wordle/domain/Dictionary.kt new file mode 100644 index 0000000..415cbda --- /dev/null +++ b/src/main/kotlin/wordle/domain/Dictionary.kt @@ -0,0 +1,5 @@ +package wordle.domain + +import wordle.infra.dictionaryWordSet + +fun isDictionaryWord(word: String): Boolean = dictionaryWordSet.contains(word) diff --git a/src/main/kotlin/wordle/domain/Word.kt b/src/main/kotlin/wordle/domain/Word.kt index 054949e..5b1f77e 100644 --- a/src/main/kotlin/wordle/domain/Word.kt +++ b/src/main/kotlin/wordle/domain/Word.kt @@ -3,7 +3,8 @@ package wordle.domain data class Word(val word: String) { init { check(word.isNotBlank()) { "단어는 공백만 입력할 수 없습니다" } - check(word.length == WORD_LENGTH) { "단어의 길이는 5자 입니다." } + check(word.length == WORD_LENGTH) { "단어의 길이는 ${WORD_LENGTH}자 입니다." } + check(isDictionaryWord(word)) { "워들 게임 사전에 있는 단어가 아닙니다" } } } diff --git a/src/main/kotlin/wordle/infra/DictionaryFileLoader.kt b/src/main/kotlin/wordle/infra/DictionaryFileLoader.kt new file mode 100644 index 0000000..abaa5f9 --- /dev/null +++ b/src/main/kotlin/wordle/infra/DictionaryFileLoader.kt @@ -0,0 +1,12 @@ +package wordle.infra + +import java.io.File + +private const val CARRIAGE_RETURN = "\n" +private const val WORDS_FILE_PATH = "src/main/resources/words.txt" +private val dictionaryWords: List by lazy { loadDictionaryWords() } +val dictionaryWordSet: Set by lazy { dictionaryWords.toSet() } + +private fun loadDictionaryWords(): List = + File(WORDS_FILE_PATH).readText() + .split(CARRIAGE_RETURN) diff --git a/src/test/kotlin/domain/WordTest.kt b/src/test/kotlin/domain/WordTest.kt index 0d195da..32d9bab 100644 --- a/src/test/kotlin/domain/WordTest.kt +++ b/src/test/kotlin/domain/WordTest.kt @@ -28,4 +28,11 @@ class WordTest { assertThatThrownBy { (Word(invalidWord)) } .isInstanceOf(IllegalStateException::class.java) } + + @ValueSource(strings = ["aaaaa", "abced"]) + @ParameterizedTest + fun `(예외) 단어는 사전에 있는 단어이어야 한다`(invalidWord: String) { + assertThatThrownBy { (Word(invalidWord)) } + .isInstanceOf(IllegalStateException::class.java) + } } From caeeff9caa7278d45d57742393dd45bef2e96895 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 16:17:00 +0900 Subject: [PATCH 11/40] =?UTF-8?q?feat(ExceptionCode):=20Wordle=EC=9D=98=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=EB=A9=94=EC=8B=9C=EC=A7=80=EB=A5=BC=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. WordleExceptionCode에서 메시지를 관리한다. 2. 이 에러 메시지는 게임에서 다시시도 이유 메시지로 활용될 수 있다. --- src/main/kotlin/wordle/domain/Word.kt | 12 +++++++++--- .../kotlin/wordle/exception/WordleExceptionCode.kt | 9 +++++++++ src/test/kotlin/domain/WordTest.kt | 4 ++++ 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/wordle/exception/WordleExceptionCode.kt diff --git a/src/main/kotlin/wordle/domain/Word.kt b/src/main/kotlin/wordle/domain/Word.kt index 5b1f77e..89fb9f7 100644 --- a/src/main/kotlin/wordle/domain/Word.kt +++ b/src/main/kotlin/wordle/domain/Word.kt @@ -1,11 +1,17 @@ package wordle.domain +import wordle.exception.WordleExceptionCode.WORD_INVALID_LENGTH +import wordle.exception.WordleExceptionCode.WORD_IS_NOT_IN_DICTIONARY +import wordle.exception.WordleExceptionCode.WORD_NOT_ALLOW_SPACE + data class Word(val word: String) { init { - check(word.isNotBlank()) { "단어는 공백만 입력할 수 없습니다" } - check(word.length == WORD_LENGTH) { "단어의 길이는 ${WORD_LENGTH}자 입니다." } - check(isDictionaryWord(word)) { "워들 게임 사전에 있는 단어가 아닙니다" } + check(word.isNotBlank()) { WORD_NOT_ALLOW_SPACE.message } + check(isValidLength()) { WORD_INVALID_LENGTH.message } + check(isDictionaryWord(word)) { WORD_IS_NOT_IN_DICTIONARY.message } } + + private fun isValidLength(): Boolean = word.length == WORD_LENGTH } const val WORD_LENGTH = 5 diff --git a/src/main/kotlin/wordle/exception/WordleExceptionCode.kt b/src/main/kotlin/wordle/exception/WordleExceptionCode.kt new file mode 100644 index 0000000..47c23bb --- /dev/null +++ b/src/main/kotlin/wordle/exception/WordleExceptionCode.kt @@ -0,0 +1,9 @@ +package wordle.exception + +import wordle.domain.WORD_LENGTH + +enum class WordleExceptionCode(val message: String) { + WORD_NOT_ALLOW_SPACE("단어는 공백만 입력할 수 없습니다."), + WORD_INVALID_LENGTH("단어의 길이는 ${WORD_LENGTH}자 입니다."), + WORD_IS_NOT_IN_DICTIONARY("Wordle Game에서 유효한 단어가 아닙니다."), +} diff --git a/src/test/kotlin/domain/WordTest.kt b/src/test/kotlin/domain/WordTest.kt index 32d9bab..d79e14b 100644 --- a/src/test/kotlin/domain/WordTest.kt +++ b/src/test/kotlin/domain/WordTest.kt @@ -6,6 +6,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.EmptySource import org.junit.jupiter.params.provider.ValueSource +import wordle.domain.WORD_LENGTH import wordle.domain.Word class WordTest { @@ -20,6 +21,7 @@ class WordTest { fun `(예외) 단어는 공백만 입력할 수 없다`(invalidWord: String) { assertThatThrownBy { (Word(invalidWord)) } .isInstanceOf(IllegalStateException::class.java) + .hasMessage("단어는 공백만 입력할 수 없습니다.") } @ValueSource(strings = ["word", "aaaaaa"]) @@ -27,6 +29,7 @@ class WordTest { fun `(예외) 단어의 길이는 5자 이어야 한다`(invalidWord: String) { assertThatThrownBy { (Word(invalidWord)) } .isInstanceOf(IllegalStateException::class.java) + .hasMessage("단어의 길이는 ${WORD_LENGTH}자 입니다.") } @ValueSource(strings = ["aaaaa", "abced"]) @@ -34,5 +37,6 @@ class WordTest { fun `(예외) 단어는 사전에 있는 단어이어야 한다`(invalidWord: String) { assertThatThrownBy { (Word(invalidWord)) } .isInstanceOf(IllegalStateException::class.java) + .hasMessage("Wordle Game에서 유효한 단어가 아닙니다.") } } From 1cb3148b5dab347a3d2cb98f1b52686313858ed5 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 16:28:37 +0900 Subject: [PATCH 12/40] =?UTF-8?q?feat(Letter):=20=EC=9C=A0=ED=9A=A8?= =?UTF-8?q?=ED=95=9C=20=EA=B8=80=EC=9E=90=EB=A5=BC=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 알파벳 소문자를 생성한다. 2. 일치 표시 기호를 생성한다. 3. 예외 케이스 테스트 작성한다. --- README.md | 4 +-- src/main/kotlin/wordle/domain/Letter.kt | 20 +++++++++++ .../wordle/exception/WordleExceptionCode.kt | 1 + src/test/kotlin/domain/LetterTest.kt | 34 +++++++++++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/wordle/domain/Letter.kt create mode 100644 src/test/kotlin/domain/LetterTest.kt diff --git a/README.md b/README.md index ed950de..f4c7673 100644 --- a/README.md +++ b/README.md @@ -166,8 +166,8 @@ spill - `정답을 입력해 주세요.` #### 글자(Letter) -- [ ] 글자는 하나의 문자를 가진다. -- [ ] 글자는 소문자 알파벳이나 일치 표시 기호('#')만 유효하다. +- [x] 글자는 하나의 문자를 가진다. +- [x] 글자는 소문자 알파벳이나 일치 표시 기호('#')만 유효하다. - [ ] 일치 표시 기호가 들어간 글자로 변경할 수 있다. #### 단어(Word) diff --git a/src/main/kotlin/wordle/domain/Letter.kt b/src/main/kotlin/wordle/domain/Letter.kt new file mode 100644 index 0000000..bdbc06c --- /dev/null +++ b/src/main/kotlin/wordle/domain/Letter.kt @@ -0,0 +1,20 @@ +package wordle.domain + +import wordle.exception.WordleExceptionCode + +data class Letter(private val value: Char) { + init { + check(isAlphabetOrMatchMarker()) { WordleExceptionCode.LETTER_INVALID_CHARACTER_TYPE.message } + } + + private fun isAlphabetOrMatchMarker(): Boolean = isAlphabet() || isMatchMarker() + + private fun isAlphabet(): Boolean = value in ALPHABET + + private fun isMatchMarker(): Boolean = value == MATCH_MARKER + + companion object { + private const val MATCH_MARKER = '#' + private val ALPHABET = ('a'..'z').toSet() + } +} diff --git a/src/main/kotlin/wordle/exception/WordleExceptionCode.kt b/src/main/kotlin/wordle/exception/WordleExceptionCode.kt index 47c23bb..e9dec50 100644 --- a/src/main/kotlin/wordle/exception/WordleExceptionCode.kt +++ b/src/main/kotlin/wordle/exception/WordleExceptionCode.kt @@ -6,4 +6,5 @@ enum class WordleExceptionCode(val message: String) { WORD_NOT_ALLOW_SPACE("단어는 공백만 입력할 수 없습니다."), WORD_INVALID_LENGTH("단어의 길이는 ${WORD_LENGTH}자 입니다."), WORD_IS_NOT_IN_DICTIONARY("Wordle Game에서 유효한 단어가 아닙니다."), + LETTER_INVALID_CHARACTER_TYPE("유효하지 않은 글자 형식입니다."), } diff --git a/src/test/kotlin/domain/LetterTest.kt b/src/test/kotlin/domain/LetterTest.kt new file mode 100644 index 0000000..2b9a583 --- /dev/null +++ b/src/test/kotlin/domain/LetterTest.kt @@ -0,0 +1,34 @@ +package domain + +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import wordle.domain.Letter + +class LetterTest { + @Test + fun `(성공) 알파벳 소문자 글자를 생성한다`() { + val actual = Letter('a') + + assertThat(actual).isEqualTo(Letter('a')) + } + + @Test + fun `(성공) 일치 표시 기호를 생성한다`() { + val actual = Letter(MATCH_MARKER) + + assertThat(actual).isEqualTo(Letter(MATCH_MARKER)) + } + + @ValueSource(strings = [" ", "1", "A", "나", "@"]) + @ParameterizedTest + fun `(예외) 유효하지 않은 글자를 생성하면 예외 발생한다`(letter: Char) { + assertThatThrownBy { (Letter(letter)) } + .isInstanceOf(IllegalStateException::class.java) + .hasMessage("유효하지 않은 글자 형식입니다.") + } +} + +private const val MATCH_MARKER = '#' From 2a3c133412ba21c1a04a8f8b3545c9f1b6ee165b Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 16:31:18 +0900 Subject: [PATCH 13/40] =?UTF-8?q?feat(Letter):=20=EC=9D=BC=EC=B9=98=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C=20=EA=B8=B0=ED=98=B8=EA=B0=80=20=EB=93=A4?= =?UTF-8?q?=EC=96=B4=EA=B0=84=20=EA=B8=80=EC=9E=90=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/main/kotlin/wordle/domain/Letter.kt | 3 +++ src/test/kotlin/domain/LetterTest.kt | 9 +++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f4c7673..3e4fe83 100644 --- a/README.md +++ b/README.md @@ -168,7 +168,7 @@ spill #### 글자(Letter) - [x] 글자는 하나의 문자를 가진다. - [x] 글자는 소문자 알파벳이나 일치 표시 기호('#')만 유효하다. -- [ ] 일치 표시 기호가 들어간 글자로 변경할 수 있다. +- [x] 일치 표시 기호가 들어간 글자로 변경할 수 있다. #### 단어(Word) - [ ] 단어는 여러 개의 글자로 이루어진다. diff --git a/src/main/kotlin/wordle/domain/Letter.kt b/src/main/kotlin/wordle/domain/Letter.kt index bdbc06c..27502a3 100644 --- a/src/main/kotlin/wordle/domain/Letter.kt +++ b/src/main/kotlin/wordle/domain/Letter.kt @@ -7,6 +7,8 @@ data class Letter(private val value: Char) { check(isAlphabetOrMatchMarker()) { WordleExceptionCode.LETTER_INVALID_CHARACTER_TYPE.message } } + fun changeMatchMarker(): Letter = MATCH_MARKER_LETTER + private fun isAlphabetOrMatchMarker(): Boolean = isAlphabet() || isMatchMarker() private fun isAlphabet(): Boolean = value in ALPHABET @@ -16,5 +18,6 @@ data class Letter(private val value: Char) { companion object { private const val MATCH_MARKER = '#' private val ALPHABET = ('a'..'z').toSet() + private val MATCH_MARKER_LETTER = Letter(MATCH_MARKER) } } diff --git a/src/test/kotlin/domain/LetterTest.kt b/src/test/kotlin/domain/LetterTest.kt index 2b9a583..9e7a1d2 100644 --- a/src/test/kotlin/domain/LetterTest.kt +++ b/src/test/kotlin/domain/LetterTest.kt @@ -29,6 +29,15 @@ class LetterTest { .isInstanceOf(IllegalStateException::class.java) .hasMessage("유효하지 않은 글자 형식입니다.") } + + @Test + fun `(성공) 체크 표시 글자로 변경한다`() { + val letter = Letter('b') + + val actual = letter.changeMatchMarker() + + assertThat(actual).isEqualTo(Letter(MATCH_MARKER)) + } } private const val MATCH_MARKER = '#' From 3e757649afca668b5192c634e8e822b3c672acd6 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 16:37:18 +0900 Subject: [PATCH 14/40] =?UTF-8?q?feat(Word):=20=EB=8B=A8=EC=96=B4=EB=8A=94?= =?UTF-8?q?=20=EC=97=AC=EB=9F=AC=20=EA=B0=9C=EC=9D=98=20=EA=B8=80=EC=9E=90?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=A3=A8=EC=96=B4=EC=A7=84=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/main/kotlin/wordle/domain/Word.kt | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 3e4fe83..a80c0ff 100644 --- a/README.md +++ b/README.md @@ -171,7 +171,7 @@ spill - [x] 일치 표시 기호가 들어간 글자로 변경할 수 있다. #### 단어(Word) -- [ ] 단어는 여러 개의 글자로 이루어진다. +- [x] 단어는 여러 개의 글자로 이루어진다. - [x] 단어는 공백만 입력할 수 없다. - [x] 단어의 길이는 5자 이어야 한다. - [x] 단어는 사전에 있는 단어이어야 한다. diff --git a/src/main/kotlin/wordle/domain/Word.kt b/src/main/kotlin/wordle/domain/Word.kt index 89fb9f7..b66f299 100644 --- a/src/main/kotlin/wordle/domain/Word.kt +++ b/src/main/kotlin/wordle/domain/Word.kt @@ -4,14 +4,16 @@ import wordle.exception.WordleExceptionCode.WORD_INVALID_LENGTH import wordle.exception.WordleExceptionCode.WORD_IS_NOT_IN_DICTIONARY import wordle.exception.WordleExceptionCode.WORD_NOT_ALLOW_SPACE -data class Word(val word: String) { - init { - check(word.isNotBlank()) { WORD_NOT_ALLOW_SPACE.message } - check(isValidLength()) { WORD_INVALID_LENGTH.message } - check(isDictionaryWord(word)) { WORD_IS_NOT_IN_DICTIONARY.message } - } +data class Word(private val word: List) - private fun isValidLength(): Boolean = word.length == WORD_LENGTH +const val WORD_LENGTH = 5 + +fun Word(word: String): Word { + check(word.isNotBlank()) { WORD_NOT_ALLOW_SPACE.message } + check(isValidLength(word)) { WORD_INVALID_LENGTH.message } + check(isDictionaryWord(word)) { WORD_IS_NOT_IN_DICTIONARY.message } + + return Word(word.toCharArray().map { Letter(it) }) } -const val WORD_LENGTH = 5 +private fun isValidLength(word: String) = word.length == WORD_LENGTH From f328f6538114c76ef65298b3b814bd1eb9cf877b Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 22:07:31 +0900 Subject: [PATCH 15/40] =?UTF-8?q?refactor(DictionaryFileLoader):=20ClassLo?= =?UTF-8?q?ader=20=EC=9D=B4=EC=9A=A9=ED=95=98=EC=97=AC=20words.txt=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=9D=BD=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. test/resources에도 words.txt파일 추가 --- .../wordle/infra/DictionaryFileLoader.kt | 14 +- src/test/resources/words.txt | 366 ++++++++++++++++++ 2 files changed, 375 insertions(+), 5 deletions(-) create mode 100644 src/test/resources/words.txt diff --git a/src/main/kotlin/wordle/infra/DictionaryFileLoader.kt b/src/main/kotlin/wordle/infra/DictionaryFileLoader.kt index abaa5f9..fcfaac4 100644 --- a/src/main/kotlin/wordle/infra/DictionaryFileLoader.kt +++ b/src/main/kotlin/wordle/infra/DictionaryFileLoader.kt @@ -1,12 +1,16 @@ package wordle.infra -import java.io.File +import java.nio.charset.StandardCharsets -private const val CARRIAGE_RETURN = "\n" -private const val WORDS_FILE_PATH = "src/main/resources/words.txt" +private const val NEW_LINE = "\n" +private const val WORDS_FILE_PATH = "words.txt" +private val classLoader: ClassLoader = Thread.currentThread().contextClassLoader private val dictionaryWords: List by lazy { loadDictionaryWords() } val dictionaryWordSet: Set by lazy { dictionaryWords.toSet() } private fun loadDictionaryWords(): List = - File(WORDS_FILE_PATH).readText() - .split(CARRIAGE_RETURN) + classLoader.getResourceAsStream(WORDS_FILE_PATH) + ?.bufferedReader(StandardCharsets.UTF_8) + ?.use { it.readText().split(NEW_LINE) } + ?.filter { it.isNotBlank() } + ?: throw IllegalArgumentException("Dictionary File not found: $WORDS_FILE_PATH") diff --git a/src/test/resources/words.txt b/src/test/resources/words.txt new file mode 100644 index 0000000..caebb49 --- /dev/null +++ b/src/test/resources/words.txt @@ -0,0 +1,366 @@ +hello +cigar +rebut +sissy +humph +awake +blush +focal +evade +naval +serve +heath +dwarf +model +karma +stink +grade +quiet +bench +abate +feign +major +death +fresh +crust +stool +colon +abase +marry +react +batty +pride +floss +helix +croak +staff +paper +unfed +whelp +trawl +outdo +adobe +crazy +sower +repay +digit +crate +cluck +spike +mimic +pound +maxim +linen +unmet +flesh +booby +forth +first +stand +belly +ivory +seedy +print +yearn +drain +bribe +stout +panel +crass +flume +offal +agree +error +swirl +argue +bleed +delta +flick +totem +wooer +front +shrub +parry +biome +lapel +start +greet +goner +golem +lusty +loopy +round +audit +lying +gamma +labor +islet +civic +forge +corny +moult +basic +salad +agate +spicy +spray +essay +fjord +spend +kebab +guild +aback +motor +alone +hatch +hyper +thumb +dowry +ought +belch +dutch +pilot +tweed +comet +jaunt +enema +steed +abyss +growl +fling +dozen +boozy +erode +world +gouge +click +briar +great +altar +pulpy +blurt +coast +duchy +groin +fixer +group +rogue +badly +smart +pithy +gaudy +chill +heron +vodka +finer +surer +radio +rouge +perch +retch +wrote +clock +tilde +store +prove +bring +solve +cheat +grime +exult +usher +epoch +triad +break +rhino +viral +conic +masse +sonic +vital +trace +using +peach +champ +baton +brake +pluck +craze +gripe +weary +picky +acute +ferry +aside +tapir +troll +unify +rebus +boost +truss +siege +tiger +banal +slump +crank +gorge +query +drink +favor +abbey +tangy +panic +solar +shire +proxy +point +robot +prick +wince +crimp +knoll +sugar +whack +mount +perky +could +wrung +light +those +moist +shard +pleat +aloft +skill +elder +frame +humor +pause +ulcer +ultra +robin +cynic +aroma +caulk +shake +dodge +swill +tacit +other +thorn +trove +bloke +vivid +spill +chant +choke +rupee +nasty +mourn +ahead +brine +cloth +hoard +sweet +month +lapse +watch +today +focus +smelt +tease +cater +movie +saute +allow +renew +their +slosh +purge +chest +depot +epoxy +nymph +found +shall +harry +stove +lowly +snout +trope +fewer +shawl +natal +comma +foray +scare +stair +black +squad +royal +chunk +mince +shame +cheek +ample +flair +foyer +cargo +oxide +plant +olive +inert +askew +heist +shown +zesty +hasty +trash +fella +larva +forgo +story +hairy +train +homer +badge +midst +canny +fetus +butch +farce +slung +tipsy +metal +yield +delve +being +scour +glass +gamer +scrap +money +hinge +album +vouch +asset +tiara +crept +bayou +atoll +manor +creak +showy +phase +froth +depth +gloom +flood +trait +girth +piety +payer +goose +float +donor +atone From 798afda405189400bb16fe2f1e176bd1c069dab6 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 22:17:29 +0900 Subject: [PATCH 16/40] =?UTF-8?q?refactor(Dictionary):=20=EC=82=AC?= =?UTF-8?q?=EC=A0=84=EC=97=90=20=ED=8F=AC=ED=95=A8=EB=90=9C=20=EB=AC=B8?= =?UTF-8?q?=EC=9E=90=EC=97=B4=EC=9D=B8=EC=A7=80=20=ED=99=95=EC=9D=B8?= =?UTF-8?q?=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 단어 목록에 관련된것은 DictionaryFileLoader에서만 관리하기 --- README.md | 2 ++ src/main/kotlin/wordle/domain/Dictionary.kt | 4 ++-- src/main/kotlin/wordle/infra/DictionaryFileLoader.kt | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a80c0ff..07c7862 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,8 @@ spill #### 사전(Dictionary) - [x] `DictionaryFileLoader`를 이용하여 `words.txt`파일에서 사전의 단어 목록을 불러온다. - [x] 사전에 단어가 포함되는지 판별한다. + - 'hello'는 사전에 포함된 단어이다 + - 'abced'는 사전에 포함되지 않은 단어이다. #### 오늘의 단어(Today Word) - [ ] 오늘의 단어는 현재 날짜를 입력하여 사전에서 한 단어를 불러온다. 이때, 오늘의 단어는 매일 바뀐다. diff --git a/src/main/kotlin/wordle/domain/Dictionary.kt b/src/main/kotlin/wordle/domain/Dictionary.kt index 415cbda..82290c1 100644 --- a/src/main/kotlin/wordle/domain/Dictionary.kt +++ b/src/main/kotlin/wordle/domain/Dictionary.kt @@ -1,5 +1,5 @@ package wordle.domain -import wordle.infra.dictionaryWordSet +import wordle.infra.contains -fun isDictionaryWord(word: String): Boolean = dictionaryWordSet.contains(word) +fun isDictionaryWord(word: String): Boolean = contains(word) diff --git a/src/main/kotlin/wordle/infra/DictionaryFileLoader.kt b/src/main/kotlin/wordle/infra/DictionaryFileLoader.kt index fcfaac4..a7c1f78 100644 --- a/src/main/kotlin/wordle/infra/DictionaryFileLoader.kt +++ b/src/main/kotlin/wordle/infra/DictionaryFileLoader.kt @@ -8,6 +8,8 @@ private val classLoader: ClassLoader = Thread.currentThread().contextClassLoader private val dictionaryWords: List by lazy { loadDictionaryWords() } val dictionaryWordSet: Set by lazy { dictionaryWords.toSet() } +fun contains(word: String): Boolean = dictionaryWordSet.contains(word) + private fun loadDictionaryWords(): List = classLoader.getResourceAsStream(WORDS_FILE_PATH) ?.bufferedReader(StandardCharsets.UTF_8) From add6622f59aedd8b7820a4b9f4130f078f7560ac Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 22:39:30 +0900 Subject: [PATCH 17/40] =?UTF-8?q?feat(Dictionary):=20=EC=82=AC=EC=A0=84=20?= =?UTF-8?q?=EB=8B=A8=EC=96=B4=20=EB=AA=A9=EB=A1=9D=EC=9D=98=20=EC=9D=B8?= =?UTF-8?q?=EB=8D=B1=EC=8A=A4=EB=A1=9C=20=ED=95=9C=20=EB=AC=B8=EC=9E=90?= =?UTF-8?q?=EC=97=B4=EC=9D=84=20=EB=B0=9B=EC=9D=84=20=EC=88=98=20=EC=9E=88?= =?UTF-8?q?=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/domain/Dictionary.kt | 3 ++ .../wordle/infra/DictionaryFileLoader.kt | 9 ++++- src/test/kotlin/domain/DictionaryKtTest.kt | 36 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/test/kotlin/domain/DictionaryKtTest.kt diff --git a/src/main/kotlin/wordle/domain/Dictionary.kt b/src/main/kotlin/wordle/domain/Dictionary.kt index 82290c1..a929d76 100644 --- a/src/main/kotlin/wordle/domain/Dictionary.kt +++ b/src/main/kotlin/wordle/domain/Dictionary.kt @@ -1,5 +1,8 @@ package wordle.domain import wordle.infra.contains +import wordle.infra.dictionaryWord fun isDictionaryWord(word: String): Boolean = contains(word) + +fun dictionaryElementAt(index: Int): String = dictionaryWord(index) diff --git a/src/main/kotlin/wordle/infra/DictionaryFileLoader.kt b/src/main/kotlin/wordle/infra/DictionaryFileLoader.kt index a7c1f78..8d4bcba 100644 --- a/src/main/kotlin/wordle/infra/DictionaryFileLoader.kt +++ b/src/main/kotlin/wordle/infra/DictionaryFileLoader.kt @@ -6,10 +6,17 @@ private const val NEW_LINE = "\n" private const val WORDS_FILE_PATH = "words.txt" private val classLoader: ClassLoader = Thread.currentThread().contextClassLoader private val dictionaryWords: List by lazy { loadDictionaryWords() } -val dictionaryWordSet: Set by lazy { dictionaryWords.toSet() } +private val dictionaryWordSet: Set by lazy { dictionaryWords.toSet() } +val dictionaryWordsSize = dictionaryWords.size fun contains(word: String): Boolean = dictionaryWordSet.contains(word) +fun dictionaryWord(index: Int): String { + check(dictionaryWords.isNotEmpty()) { NoSuchElementException("Dictionary File is Empty") } + + return dictionaryWords[index % dictionaryWordsSize] +} + private fun loadDictionaryWords(): List = classLoader.getResourceAsStream(WORDS_FILE_PATH) ?.bufferedReader(StandardCharsets.UTF_8) diff --git a/src/test/kotlin/domain/DictionaryKtTest.kt b/src/test/kotlin/domain/DictionaryKtTest.kt new file mode 100644 index 0000000..74533b1 --- /dev/null +++ b/src/test/kotlin/domain/DictionaryKtTest.kt @@ -0,0 +1,36 @@ +package domain + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Assertions.assertAll +import org.junit.jupiter.api.Test +import wordle.domain.dictionaryElementAt +import wordle.domain.isDictionaryWord +import wordle.infra.dictionaryWordsSize + +private const val CONTAINS_WORD = "hello" + +class DictionaryKtTest { + @Test + fun `(성공) 사전에 포함된 문자열인지 확인할 수 있다`() { + val notInWord = "abced" + + assertAll( + { assertThat(isDictionaryWord(CONTAINS_WORD)).isTrue() }, + { assertThat(isDictionaryWord(notInWord)).isFalse() }, + ) + } + + @Test + fun `(성공) 사전 안의 단어목록 중 인덱스에 해당하는 문자열을 가져온다`() { + val index = 0 + + assertThat(dictionaryElementAt(index)).isEqualTo(CONTAINS_WORD) + } + + @Test + fun `(성공) 단어 목록 인덱스의 최댓값보다 큰 인덱스를 입력하면 단어 목록의 인덱스는 다시 0부터 시작한다`() { + val index = dictionaryWordsSize + + assertThat(dictionaryElementAt(index)).isEqualTo(CONTAINS_WORD) + } +} From 7b2a40ee190d7094b2682b486b92df291abf6d94 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 22:42:35 +0900 Subject: [PATCH 18/40] =?UTF-8?q?chore=20:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EC=9D=B4=EB=8F=99=20->=20doma?= =?UTF-8?q?in=20->=20wordle.domain?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/kotlin/{ => wordle}/domain/DictionaryKtTest.kt | 4 +--- src/test/kotlin/{ => wordle}/domain/LetterTest.kt | 3 +-- src/test/kotlin/{ => wordle}/domain/WordTest.kt | 8 ++++---- 3 files changed, 6 insertions(+), 9 deletions(-) rename src/test/kotlin/{ => wordle}/domain/DictionaryKtTest.kt (91%) rename src/test/kotlin/{ => wordle}/domain/LetterTest.kt (96%) rename src/test/kotlin/{ => wordle}/domain/WordTest.kt (91%) diff --git a/src/test/kotlin/domain/DictionaryKtTest.kt b/src/test/kotlin/wordle/domain/DictionaryKtTest.kt similarity index 91% rename from src/test/kotlin/domain/DictionaryKtTest.kt rename to src/test/kotlin/wordle/domain/DictionaryKtTest.kt index 74533b1..bafc43e 100644 --- a/src/test/kotlin/domain/DictionaryKtTest.kt +++ b/src/test/kotlin/wordle/domain/DictionaryKtTest.kt @@ -1,10 +1,8 @@ -package domain +package wordle.domain import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions.assertAll import org.junit.jupiter.api.Test -import wordle.domain.dictionaryElementAt -import wordle.domain.isDictionaryWord import wordle.infra.dictionaryWordsSize private const val CONTAINS_WORD = "hello" diff --git a/src/test/kotlin/domain/LetterTest.kt b/src/test/kotlin/wordle/domain/LetterTest.kt similarity index 96% rename from src/test/kotlin/domain/LetterTest.kt rename to src/test/kotlin/wordle/domain/LetterTest.kt index 9e7a1d2..465b743 100644 --- a/src/test/kotlin/domain/LetterTest.kt +++ b/src/test/kotlin/wordle/domain/LetterTest.kt @@ -1,11 +1,10 @@ -package domain +package wordle.domain import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource -import wordle.domain.Letter class LetterTest { @Test diff --git a/src/test/kotlin/domain/WordTest.kt b/src/test/kotlin/wordle/domain/WordTest.kt similarity index 91% rename from src/test/kotlin/domain/WordTest.kt rename to src/test/kotlin/wordle/domain/WordTest.kt index d79e14b..b4b419c 100644 --- a/src/test/kotlin/domain/WordTest.kt +++ b/src/test/kotlin/wordle/domain/WordTest.kt @@ -1,4 +1,4 @@ -package domain +package wordle.domain import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy @@ -6,13 +6,13 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.EmptySource import org.junit.jupiter.params.provider.ValueSource -import wordle.domain.WORD_LENGTH -import wordle.domain.Word class WordTest { @Test fun `(성공) 단어를 생성한다`() { - assertThat(Word("hello")).isEqualTo(Word("hello")) + val actual = Word("hello") + + assertThat(actual).isEqualTo(Word("hello")) } @EmptySource From 44680bdc8ce8ac9eed8c7a9e9261730c73eaec6f Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 22:59:11 +0900 Subject: [PATCH 19/40] =?UTF-8?q?feat(TodayWord):=20=EC=98=A4=EB=8A=98?= =?UTF-8?q?=EC=9D=98=20=EB=8B=A8=EC=96=B4=EB=A5=BC=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 사전의 단어목록에서 오늘의 날짜에 맞게 가져온다. 2. 오늘의 단어는 매일 바뀐다. --- README.md | 7 +++-- src/main/kotlin/wordle/domain/TodayWord.kt | 19 ++++++++++++ .../kotlin/wordle/domain/TodayWordTest.kt | 30 +++++++++++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/wordle/domain/TodayWord.kt create mode 100644 src/test/kotlin/wordle/domain/TodayWordTest.kt diff --git a/README.md b/README.md index 07c7862..80c3b2f 100644 --- a/README.md +++ b/README.md @@ -177,14 +177,15 @@ spill - [x] 단어는 사전에 있는 단어이어야 한다. #### 사전(Dictionary) -- [x] `DictionaryFileLoader`를 이용하여 `words.txt`파일에서 사전의 단어 목록을 불러온다. +- [x] 사전 단어 목록에서 단어 하나를 가져온다. + - `DictionaryFileLoader`를 이용하여 `words.txt`파일에서 사전의 단어 목록을 불러온다. - [x] 사전에 단어가 포함되는지 판별한다. - 'hello'는 사전에 포함된 단어이다 - 'abced'는 사전에 포함되지 않은 단어이다. #### 오늘의 단어(Today Word) -- [ ] 오늘의 단어는 현재 날짜를 입력하여 사전에서 한 단어를 불러온다. 이때, 오늘의 단어는 매일 바뀐다. - - [ ] 사전 단어 목록의 ((현재 날짜 - 2021년 6월 19일) % 배열의 크기) 번째의 단어이다. +- [x] 오늘의 단어는 현재 날짜를 입력하여 사전에서 한 단어를 불러온다. 이때, 오늘의 단어는 매일 바뀐다. + - [x] 사전 단어 목록의 ((현재 날짜 - 2021년 6월 19일) % 배열의 크기) 번째의 단어이다. #### 단어 비교기(Word Comparator) - [ ] 각 단어를 이루고 있는 글자들의 일치 상태를 비교한다. diff --git a/src/main/kotlin/wordle/domain/TodayWord.kt b/src/main/kotlin/wordle/domain/TodayWord.kt new file mode 100644 index 0000000..2bbb60c --- /dev/null +++ b/src/main/kotlin/wordle/domain/TodayWord.kt @@ -0,0 +1,19 @@ +package wordle.domain + +import wordle.infra.dictionaryWord +import java.time.LocalDate +import java.time.temporal.ChronoUnit + +typealias TodayWord = Word + +private val CRITERION_DATE: LocalDate = LocalDate.of(2021, 6, 19) + +fun TodayWord(today: LocalDate): TodayWord { + return Word(extractDictionaryWord(today)) +} + +private fun extractDictionaryWord(date: LocalDate): String { + val calculatedIndex = ChronoUnit.DAYS.between(CRITERION_DATE, date).toInt() + + return dictionaryWord(calculatedIndex) +} diff --git a/src/test/kotlin/wordle/domain/TodayWordTest.kt b/src/test/kotlin/wordle/domain/TodayWordTest.kt new file mode 100644 index 0000000..41dc53b --- /dev/null +++ b/src/test/kotlin/wordle/domain/TodayWordTest.kt @@ -0,0 +1,30 @@ +package wordle.domain + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import java.time.LocalDate + +class TodayWordTest { + @Test + fun `(성공) 오늘의 단어는 오늘 날짜를 입력받아 단어를 생성한다`() { + val today = LocalDate.of(2021, 6, 19) + + val actual = TodayWord(today) + + assertThat(actual).isEqualTo(Word("hello")) + } + + @Test + fun `(성공) 오늘의 단어는 매일 바뀐다`() { + val criterionDate = LocalDate.of(2024, 6, 17) + val wordsSet = mutableSetOf() + + repeat(365) { + val currentDate = criterionDate.plusDays(it.toLong()) + val word = TodayWord(currentDate) + wordsSet.add(word) + } + + assertThat(wordsSet).hasSize(365) + } +} From 24412d94bc9e8bc9f16b6c31c5429b4a376947e9 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 23:04:44 +0900 Subject: [PATCH 20/40] =?UTF-8?q?feat(AnswerWord):=20=EB=8B=B5=EC=95=88=20?= =?UTF-8?q?=EB=8B=A8=EC=96=B4=EB=A5=BC=20=EC=83=9D=EC=84=B1=ED=95=9C?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/domain/AnswerWord.kt | 7 +++++++ src/test/kotlin/wordle/domain/AnswerWordTest.kt | 13 +++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/main/kotlin/wordle/domain/AnswerWord.kt create mode 100644 src/test/kotlin/wordle/domain/AnswerWordTest.kt diff --git a/src/main/kotlin/wordle/domain/AnswerWord.kt b/src/main/kotlin/wordle/domain/AnswerWord.kt new file mode 100644 index 0000000..ee35e0f --- /dev/null +++ b/src/main/kotlin/wordle/domain/AnswerWord.kt @@ -0,0 +1,7 @@ +package wordle.domain + +typealias AnswerWord = Word + +fun AnswerWord(answerWord: String): AnswerWord { + return Word(answerWord) +} diff --git a/src/test/kotlin/wordle/domain/AnswerWordTest.kt b/src/test/kotlin/wordle/domain/AnswerWordTest.kt new file mode 100644 index 0000000..87498fb --- /dev/null +++ b/src/test/kotlin/wordle/domain/AnswerWordTest.kt @@ -0,0 +1,13 @@ +package wordle.domain + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class AnswerWordTest { + @Test + fun `(성공) 답안 단어는 문자열을 입력받아 단어를 생성한다`() { + val actual = AnswerWord("hello") + + assertThat(actual).isEqualTo(Word("hello")) + } +} From 86967c232ff54519bd9136b9f842198cd1f0ed39 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Mon, 17 Jun 2024 23:41:29 +0900 Subject: [PATCH 21/40] =?UTF-8?q?feat(InputView,=20OutputView):=20?= =?UTF-8?q?=EC=B4=88=EA=B8=B0=20=EC=9E=85=EC=B6=9C=EB=A0=A5=EC=9D=84=20?= =?UTF-8?q?=EC=BD=98=EC=86=94=EC=97=90=20=EB=82=98=ED=83=80=EB=82=B8?= =?UTF-8?q?=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 게임 시작하는 메시지 출력 2. 답안 문자를 입력받는 문구 출력후 문자열 입력받기 --- README.md | 4 ++-- src/main/kotlin/wordle/view/InputView.kt | 6 ++++++ src/main/kotlin/wordle/view/OutputView.kt | 5 +++++ 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/wordle/view/InputView.kt create mode 100644 src/main/kotlin/wordle/view/OutputView.kt diff --git a/README.md b/README.md index 80c3b2f..fa089a8 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ spill ### 💻 기능 목록 #### 출력(Output View) -- [ ] 게임 시작 메시지를 출력한다. +- [x] 게임 시작 메시지를 출력한다. - `WORDLE을 6번 만에 맞춰 보세요.\n시도의 결과는 타일의 색 변화로 나타납니다.` - [ ] 타일 결과를 출력한다. - [ ] 플레이어가 입력한 답안 단어와 비교한 답안 결과에 따라 각 글자의 일치 상태에 맞게 타일을 출력한다. @@ -162,7 +162,7 @@ spill - [ ] 실패 메시지를 출력한다. `{오늘의 단어}`를 함께 출력한다. #### 입력(Input View) -- [ ] 답안 단어를 입력받는다. 입력받기 전에 답안 입력을 위한 메시지를 먼저 출력한다. +- [x] 답안 단어를 입력받는다. 입력받기 전에 답안 입력을 위한 메시지를 먼저 출력한다. - `정답을 입력해 주세요.` #### 글자(Letter) diff --git a/src/main/kotlin/wordle/view/InputView.kt b/src/main/kotlin/wordle/view/InputView.kt new file mode 100644 index 0000000..bc2cd40 --- /dev/null +++ b/src/main/kotlin/wordle/view/InputView.kt @@ -0,0 +1,6 @@ +package wordle.view + +fun inputAnswerWord(): String { + println("🚀 정답을 입력하세요. : ") + return readln() +} diff --git a/src/main/kotlin/wordle/view/OutputView.kt b/src/main/kotlin/wordle/view/OutputView.kt new file mode 100644 index 0000000..932443d --- /dev/null +++ b/src/main/kotlin/wordle/view/OutputView.kt @@ -0,0 +1,5 @@ +package wordle.view + +fun printStartingGameMessage() { + println("🎮 WORDLE을 6번 만에 맞춰 보세요.\n📌 시도의 결과는 타일의 색 변화로 나타납니다.🥳\n") +} From 075025bb35cbd94c937e8a1806e714d839d4ba28 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Tue, 18 Jun 2024 00:43:07 +0900 Subject: [PATCH 22/40] =?UTF-8?q?feat(Application,=20WordleGameController)?= =?UTF-8?q?:=20=ED=94=84=EB=A1=9C=EA=B7=B8=EB=9E=A8=20=EC=8B=A4=ED=96=89?= =?UTF-8?q?=20=EC=8B=9C=EC=9E=91=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Application : 프로그램 실행 2. WordleGameController : 워들 게임을 시작 할 수 있게 하는 시작 점 --- README.md | 4 ++-- src/main/kotlin/wordle/Application.kt | 7 +++++++ .../wordle/controller/WordleGameController.kt | 15 +++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/wordle/Application.kt create mode 100644 src/main/kotlin/wordle/controller/WordleGameController.kt diff --git a/README.md b/README.md index fa089a8..583eebd 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ - **Java 코드가 아닌 Kotlin 코드로만 구현해야 한다.** ### 구현 전 확인 사항 -- [ ] 프로그램 실행의 시작점은 `Application`의 `main()`이다. +- [x] 프로그램 실행의 시작점은 `Application`의 `main()`이다. - [ ] `build.gradle.kts` 파일은 변경할 수 없으며, **제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다.** - `Unknown Kotlin JVM target: 21` 문제로 `kotlin 1.9.23`으로 올림 - [문제해결](https://github.com/gradle/gradle/issues/25574#issuecomment-1761314551) @@ -52,7 +52,7 @@ - [AssertJ Exception Assertions](https://www.baeldung.com/assertj-exception-assertion) - [Guide to JUnit 5 Parameterized Tests](https://www.baeldung.com/parameterized-tests-junit-5) - 단, UI(System.out, System.in, Scanner) 로직은 제외한다. -- [ ] 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 분리해 구현한다. +- [x] 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 분리해 구현한다. - 힌트: MVC 패턴 기반으로 구현한 후, View와 Controller를 제외한 Model에 대한 단위 테스트 추가에 집중한다. ### 제출 전 확인 사항 diff --git a/src/main/kotlin/wordle/Application.kt b/src/main/kotlin/wordle/Application.kt new file mode 100644 index 0000000..7cdfcc2 --- /dev/null +++ b/src/main/kotlin/wordle/Application.kt @@ -0,0 +1,7 @@ +package wordle + +import wordle.controller.WordleGameController + +fun main() { + WordleGameController().run() +} diff --git a/src/main/kotlin/wordle/controller/WordleGameController.kt b/src/main/kotlin/wordle/controller/WordleGameController.kt new file mode 100644 index 0000000..aa05af0 --- /dev/null +++ b/src/main/kotlin/wordle/controller/WordleGameController.kt @@ -0,0 +1,15 @@ +package wordle.controller + +import wordle.application.WordleGame +import wordle.view.printStartingGameMessage +import java.time.LocalDate + +class WordleGameController { + private val gameStartDate: LocalDate = LocalDate.now() + private val wordleGame: WordleGame = WordleGame(gameStartDate) + + fun run() { + printStartingGameMessage() + wordleGame.play() + } +} From 64e58c2a98f059c8ad3a8fe9c3f25dfb8e848ecf Mon Sep 17 00:00:00 2001 From: boradol2 Date: Tue, 18 Jun 2024 01:12:21 +0900 Subject: [PATCH 23/40] =?UTF-8?q?feat(WordleGame):=20WordleGame=20?= =?UTF-8?q?=EB=B0=98=EB=B3=B5=20=EC=88=98=ED=96=89=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B4=88=EA=B8=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 29 +++++++++++++----- .../kotlin/wordle/application/WordleGame.kt | 30 +++++++++++++++++++ 2 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 src/main/kotlin/wordle/application/WordleGame.kt diff --git a/README.md b/README.md index 583eebd..2ff69a6 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,27 @@ spill - [x] 답안 단어를 입력받는다. 입력받기 전에 답안 입력을 위한 메시지를 먼저 출력한다. - `정답을 입력해 주세요.` +#### 워들 게임(Wordle Game) +- [ ] 단어 결과 목록을 가진다. +- [x] 유효한 답안 단어를 최대 6번 입력받아 게임을 한다. + - [x] 게임에 아직 성공하지 못한 상태에서 시도 가능 횟수가 0보다 크면 게임을 계속 할 수 있다. + - [ ] 단어를 입력받아 답안 단어를 만든다. + - 유효한 답안 단어를 입력 하였을 때, + - [ ] 게임 검증 로직을 수행한다. + - [ ] 타일 결과 목록을 출력한다. + - [ ] 시행 가능 횟수를 1회 차감한다. + - 유효하지 않은 답안 단어를 입력 하였을 때, + - [ ] 답안 단어 입력을 다시 시도해야 한다. 다시 시도 하는 이유를 출력한다. +- [ ] 단어 결과 목록과 대응하는 타일 결과 목록을 출력한다. + - [ ] 답안 단어 1번 입력하는 경우 + - [ ] 답안 단어를 6번 입력하고 게임에 실패하는 경우 +- [ ] 게임이 종료 된다. 게임의 성공과 실패 여부를 판별한다. + - 게임에 성공 하였을 때 + - [ ] 해당 답안 단어의 단어 결과가 모두 '완전 일치 글자'일 때이다. + - 관련 메시지 출력 + - 게임에 실패 하였을 때 + - [ ] 게임에 성공하지 않고, 시도횟수가 0회가 될 때이다. + #### 글자(Letter) - [x] 글자는 하나의 문자를 가진다. - [x] 글자는 소문자 알파벳이나 일치 표시 기호('#')만 유효하다. @@ -199,14 +220,6 @@ spill - [ ] 단어 결과에 예상되는 일치상태가 포함된 경우 - [ ] 단어 결과가 모두 완전 일치인 경우 -#### 워들 게임(Wordle Game) -- [ ] 답안 단어를 최대 6번 입력받아 게임을 한다. -- [ ] 워들게임에서 유효하지 않은 단어를 입력하면 시행 가능 횟수가 줄어들지 않는다. -- [ ] 예상되는 단어 결과 목록을 확인한다. - - [ ] 답안 단어 1번 입력하는 경우 - - [ ] 답안 단어를 6번 입력하고 게임에 실패하는 경우 -- [ ] 게임에 성공하였을 때, 시도한 횟수를 알 수 있다. - #### 시도 가능 횟수(Try Count) - [ ] 시도 가능 회수는 횟수를 가진다. 최초 생성할 때에는 6회이다. - [ ] 시도 가능 횟수는 0회보다 커야 한다. diff --git a/src/main/kotlin/wordle/application/WordleGame.kt b/src/main/kotlin/wordle/application/WordleGame.kt new file mode 100644 index 0000000..8dd789e --- /dev/null +++ b/src/main/kotlin/wordle/application/WordleGame.kt @@ -0,0 +1,30 @@ +package wordle.application + +import wordle.domain.AnswerWord +import wordle.domain.TodayWord +import wordle.domain.WordleGameLogic +import wordle.view.inputAnswerWord +import java.time.LocalDate + +class WordleGame(gameStartDate: LocalDate) { + private val todayWord = TodayWord(gameStartDate) + + fun play() { + var tryCount = 6 + while (isContinuousGame(tryCount)) { + try { + val answerWord = AnswerWord(inputAnswerWord()) + WordleGameLogic(todayWord, answerWord).compare() + tryCount-- + } catch (e: IllegalStateException) { + println("다시 시도 해주세요!") + } + } + } + + private fun isContinuousGame(tryCount: Int) = !isSuccessGame() && tryCount > 0 + + private fun isSuccessGame(): Boolean { + return false + } +} From a64c9657a8b4f63ec5d9140f470eed463b4a418e Mon Sep 17 00:00:00 2001 From: boradol2 Date: Tue, 18 Jun 2024 03:55:12 +0900 Subject: [PATCH 24/40] =?UTF-8?q?feat(WordleGameLogic):=20=EA=B2=8C?= =?UTF-8?q?=EC=9E=84=20=EB=A1=9C=EC=A7=81=EC=9D=84=20=ED=86=B5=ED=95=B4=20?= =?UTF-8?q?=EC=97=AC=EB=9F=AC=EA=B0=80=EC=A7=80=20=EC=83=81=ED=99=A9?= =?UTF-8?q?=EB=93=A4=EC=9D=84=20=EB=B9=84=EA=B5=90=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 글자 일치 상태 Enum 추가 2. Word클래스가 List의 인터페이스를 구현하도록 변경 3. 여러가지 상황들 테스트케이스 작성 4. 테스트에 필요한 단어 몇 개 추가 --- README.md | 19 +- src/main/kotlin/wordle/domain/LetterMatch.kt | 7 + src/main/kotlin/wordle/domain/Word.kt | 2 +- .../kotlin/wordle/domain/WordleGameLogic.kt | 36 ++++ .../wordle/domain/WordleGameLogicTest.kt | 188 ++++++++++++++++++ src/test/resources/words.txt | 5 + 6 files changed, 252 insertions(+), 5 deletions(-) create mode 100644 src/main/kotlin/wordle/domain/LetterMatch.kt create mode 100644 src/main/kotlin/wordle/domain/WordleGameLogic.kt create mode 100644 src/test/kotlin/wordle/domain/WordleGameLogicTest.kt diff --git a/README.md b/README.md index 2ff69a6..f7d0277 100644 --- a/README.md +++ b/README.md @@ -215,10 +215,21 @@ spill - [ ] 완전일치하거나 부분일치한 글자이면 그 글자는 일치 표시 기호로 변경된다. #### 워들 게임 로직(Wordle Game Logic) -- [ ] 오늘의 단어와 답안 단어를 비교하여 단어 결과를 얻는다. - - [ ] 단어 결과가 모두 불일치인 경우 - - [ ] 단어 결과에 예상되는 일치상태가 포함된 경우 - - [ ] 단어 결과가 모두 완전 일치인 경우 +- [x] 오늘의 단어와 답안 단어를 비교하여 단어 결과를 얻는다. + - 단어 결과 중 글자 일치 상태가 하나만 다른 경우 + - [x] 하나의 글자 결과만 '부분 일치 글자'일 때 비교한다. + - [x] 하나의 글자 결과만 '완전 일치 글자'일 때 비교한다. - '완전 일치 글자'부터 비교한다 + - 답안 단어에 같은 글자가 있는 경우 + - [x] 두 번 같은 글자가 연속되는 답안 단어에서, 오늘의 단어와 한 글자만 '부분 일치 글자'일 때 비교한다. + - [x] 두 번 같은 글자가 연속되는 답안 단어에서, 오늘의 단어와 두 글자 '부분 일치 글자'일 때 비교한다. + - [x] 두 번 같은 글자가 연속되는 답안 단어에서, '부분 일치 글자'와 '완전 일치 글자'를 가질 때 비교한다. + - [x] 같은 글자가 연속 하지 않는 답안 단어에서, '부분 일치 글자'와 '완전 일치 글자'를 가질 때 비교한다. + - [x] 세 개의 같은 글자를 가지는 답안 단어에서 하나의 글자 결과만 '부분 일치 글자'일 때 비교한다. + - [x] 네 개의 글자 결과가 '완전 일치 글자'일 때 비교한다. + - 단어 결과 중 글자 일치 상태가 모두 같은 경우 + - [x] 모든 글자 결과가 '불일치 상태'일 때 비교한다 + - [x] 모든 글자 결과가 '부분 일치 글자'일 때 비교한다 + - [x] 모든 글자 결과가 '완전 일치 글자'일 때 비교한다 #### 시도 가능 횟수(Try Count) - [ ] 시도 가능 회수는 횟수를 가진다. 최초 생성할 때에는 6회이다. diff --git a/src/main/kotlin/wordle/domain/LetterMatch.kt b/src/main/kotlin/wordle/domain/LetterMatch.kt new file mode 100644 index 0000000..bca68c0 --- /dev/null +++ b/src/main/kotlin/wordle/domain/LetterMatch.kt @@ -0,0 +1,7 @@ +package wordle.domain + +enum class LetterMatch { + CORRECT, + PRESENT, + ABSENT, +} diff --git a/src/main/kotlin/wordle/domain/Word.kt b/src/main/kotlin/wordle/domain/Word.kt index b66f299..4149511 100644 --- a/src/main/kotlin/wordle/domain/Word.kt +++ b/src/main/kotlin/wordle/domain/Word.kt @@ -4,7 +4,7 @@ import wordle.exception.WordleExceptionCode.WORD_INVALID_LENGTH import wordle.exception.WordleExceptionCode.WORD_IS_NOT_IN_DICTIONARY import wordle.exception.WordleExceptionCode.WORD_NOT_ALLOW_SPACE -data class Word(private val word: List) +data class Word(val word: List) : List by word const val WORD_LENGTH = 5 diff --git a/src/main/kotlin/wordle/domain/WordleGameLogic.kt b/src/main/kotlin/wordle/domain/WordleGameLogic.kt new file mode 100644 index 0000000..90ebbb5 --- /dev/null +++ b/src/main/kotlin/wordle/domain/WordleGameLogic.kt @@ -0,0 +1,36 @@ +package wordle.domain + +class WordleGameLogic(private val todayWord: Word) { + val tempList = todayWord.word.toMutableList() + val wordResult = MutableList(WORD_LENGTH) { LetterMatch.ABSENT } + + fun compare(answerWord: Word): MutableList = + answerWord.matchCorrect() + .matchPresent() + .toWordResult() + + private fun Word.matchCorrect(): Word { + return onEachIndexed { index, letter -> + if (isSame(index, letter)) { + wordResult[index] = LetterMatch.CORRECT + tempList[index] = tempList[index].changeMatchMarker() + } + } + } + + private fun Word.matchPresent(): Word { + return onEachIndexed { index, letter -> + if (!isSame(index, letter) && letter in tempList) { + wordResult[index] = LetterMatch.PRESENT + tempList[tempList.indexOf(letter)] = tempList[tempList.indexOf(letter)].changeMatchMarker() + } + } + } + + private fun Word.toWordResult() = wordResult + + private fun isSame( + index: Int, + letter: Letter, + ) = todayWord.word[index] == letter +} diff --git a/src/test/kotlin/wordle/domain/WordleGameLogicTest.kt b/src/test/kotlin/wordle/domain/WordleGameLogicTest.kt new file mode 100644 index 0000000..3e707a5 --- /dev/null +++ b/src/test/kotlin/wordle/domain/WordleGameLogicTest.kt @@ -0,0 +1,188 @@ +package wordle.domain + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test + +class WordleGameLogicTest { + private lateinit var wordleGameLogic: WordleGameLogic + + @BeforeEach + fun setUp() { + val todayWord = Word("spill") + wordleGameLogic = WordleGameLogic(todayWord) + } + + @Nested + inner class `단어 결과 중 글자 일치 상태가 하나만 다른 경우` { + @Test + fun `(성공) 하나의 글자 결과만 '부분 일치 글자'일 때 비교한다`() { + val answerWord = Word("proxy") + + val result = wordleGameLogic.compare(answerWord) + + assertThat(result).isEqualTo( + mutableListOf( + LetterMatch.PRESENT, + LetterMatch.ABSENT, + LetterMatch.ABSENT, + LetterMatch.ABSENT, + LetterMatch.ABSENT, + ), + ) + } + + @Test + fun `(성공) 하나의 글자 결과만 '완전 일치 글자'일 때 비교한다 - '완전 일치 글자'부터 비교한다`() { + val answerWord = Word("royal") + + val result = wordleGameLogic.compare(answerWord) + + assertThat(result).isEqualTo( + mutableListOf( + LetterMatch.ABSENT, + LetterMatch.ABSENT, + LetterMatch.ABSENT, + LetterMatch.ABSENT, + LetterMatch.CORRECT, + ), + ) + } + } + + @Nested + inner class `답안 단어에 같은 글자가 있는 경우` { + @Test + fun `(성공) 두 번 같은 글자가 연속되는 답안 단어에서, 오늘의 단어와 한 글자만 '부분 일치 글자'일 때 비교한다`() { + val answerWord = Word("truss") + + val result = wordleGameLogic.compare(answerWord) + + assertThat(result).isEqualTo( + mutableListOf( + LetterMatch.ABSENT, + LetterMatch.ABSENT, + LetterMatch.ABSENT, + LetterMatch.PRESENT, + LetterMatch.ABSENT, + ), + ) + } + + @Test + fun `(성공) 두 번 같은 글자가 연속되는 답안 단어에서, 오늘의 단어와 두 글자 '부분 일치 글자'일 때 비교한다`() { + val answerWord = Word("llama") + + val result = wordleGameLogic.compare(answerWord) + + assertThat(result).isEqualTo( + mutableListOf( + LetterMatch.PRESENT, + LetterMatch.PRESENT, + LetterMatch.ABSENT, + LetterMatch.ABSENT, + LetterMatch.ABSENT, + ), + ) + } + + @Test + fun `(성공) 두 번 같은 글자가 연속되는 답안 단어에서, '부분 일치 글자'와 '완전 일치 글자'를 가질 때 비교한다`() { + val answerWord = Word("hello") + + val result = wordleGameLogic.compare(answerWord) + + assertThat(result).isEqualTo( + mutableListOf( + LetterMatch.ABSENT, + LetterMatch.ABSENT, + LetterMatch.PRESENT, + LetterMatch.CORRECT, + LetterMatch.ABSENT, + ), + ) + } + + @Test + fun `(성공) 같은 글자가 연속 하지 않는 답안 단어에서, '부분 일치 글자'와 '완전 일치 글자'를 가질 때 비교한다`() { + val answerWord = Word("label") + + val result = wordleGameLogic.compare(answerWord) + + assertThat(result).isEqualTo( + mutableListOf( + LetterMatch.PRESENT, + LetterMatch.ABSENT, + LetterMatch.ABSENT, + LetterMatch.ABSENT, + LetterMatch.CORRECT, + ), + ) + } + + @Test + fun `(성공) 세 개의 같은 글자를 가지는 답안 단어에서 하나의 글자 결과만 '부분 일치 글자'일 때 비교한다`() { + val answerWord = Word("puppy") + + val result = wordleGameLogic.compare(answerWord) + + assertThat(result).isEqualTo( + mutableListOf( + LetterMatch.PRESENT, + LetterMatch.ABSENT, + LetterMatch.ABSENT, + LetterMatch.ABSENT, + LetterMatch.ABSENT, + ), + ) + } + + @Test + fun `(성공) 네 개의 글자 결과가 '완전 일치 글자'일 때 비교한다`() { + val answerWord = Word("spell") + + val result = wordleGameLogic.compare(answerWord) + + assertThat(result).isEqualTo( + mutableListOf( + LetterMatch.CORRECT, + LetterMatch.CORRECT, + LetterMatch.ABSENT, + LetterMatch.CORRECT, + LetterMatch.CORRECT, + ), + ) + } + } + + @Nested + inner class `단어 결과 중 글자 일치 상태가 모두 같은 경우` { + @Test + fun `(성공) 모든 글자 결과가 '불일치 상태'일 때 비교한다`() { + val answerWord = Word("major") + + val result = wordleGameLogic.compare(answerWord) + + assertThat(result).isEqualTo(MutableList(WORD_LENGTH) { LetterMatch.ABSENT }) + } + + @Test + fun `(성공) 모든 글자 결과가 '부분 일치 글자'일 때 비교한다`() { + val answerWord = Word("illps") + + val result = wordleGameLogic.compare(answerWord) + + assertThat(result).isEqualTo(MutableList(WORD_LENGTH) { LetterMatch.PRESENT }) + } + + @Test + fun `(성공) 모든 글자 결과가 '완전 일치 글자'일 때 비교한다`() { + val answerWord = Word("spill") + + val result = wordleGameLogic.compare(answerWord) + + assertThat(result).isEqualTo(MutableList(WORD_LENGTH) { LetterMatch.CORRECT }) + } + } +} diff --git a/src/test/resources/words.txt b/src/test/resources/words.txt index caebb49..3a06d50 100644 --- a/src/test/resources/words.txt +++ b/src/test/resources/words.txt @@ -364,3 +364,8 @@ goose float donor atone +label +spell +llama +puppy +illps From 8838156d9c3ed64fe3c30c7ac016f69fd7402830 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Tue, 18 Jun 2024 04:39:22 +0900 Subject: [PATCH 25/40] =?UTF-8?q?feat(WordResult):=20=EB=8B=A8=EC=96=B4=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=EB=8A=94=20=EA=B8=80=EC=9E=90=20=EC=9D=BC?= =?UTF-8?q?=EC=B9=98=20=EC=97=AC=EB=B6=80=20=EB=AA=A9=EB=A1=9D=EC=9D=84=20?= =?UTF-8?q?=EA=B0=80=EC=A7=80=EB=A9=B0,=20=EB=8B=A8=EC=96=B4=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=EB=A5=BC=20=EB=B0=98=ED=99=98=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 단어 결과는 각 글자의 글자 일치 여부 상태를 변경할 수 있다. 2. WordleGameLogic에서 비교한 단어들의 각 글자의 일치 상태를 관리한다. --- README.md | 9 ++--- src/main/kotlin/wordle/domain/WordResult.kt | 10 ++++++ .../kotlin/wordle/domain/WordleGameLogic.kt | 8 ++--- .../kotlin/wordle/domain/WordResultTest.kt | 34 +++++++++++++++++++ .../wordle/domain/WordleGameLogicTest.kt | 22 ++++++------ 5 files changed, 64 insertions(+), 19 deletions(-) create mode 100644 src/main/kotlin/wordle/domain/WordResult.kt create mode 100644 src/test/kotlin/wordle/domain/WordResultTest.kt diff --git a/README.md b/README.md index f7d0277..94ece22 100644 --- a/README.md +++ b/README.md @@ -239,10 +239,11 @@ spill - [ ] 시도한 횟수를 구할 수 있다. #### 단어 결과(Word Result) -- [ ] 5개의 글자 일치 상태로 이루어져 있다. -- [ ] 처음에 생성할 때, 글자 일치 상태는 불일치 상태이다. -- [ ] 모든 글자 일치 상태가 CORRECT이면, 해당 단어는 오늘의 단어와 같다. -- [ ] 해당 글자의 글자 일치 상태를 변경할 수 있다. +- [x] 5개의 글자 일치 상태 목록으로 이루어져 있다. + - 워들 게임 로직의 결과를 관리한다. +- [x] 처음에 생성할 때, 글자 일치 상태는 불일치 상태이다. +- [x] 해당 글자의 글자 일치 상태를 변경할 수 있다. +- [ ] 모든 글자 일치 상태가 '완전 일치 상태'이면, 해당 단어는 오늘의 단어와 같다.(게임 성공 여부가 된다) #### 단어 결과 목록(Word Results) - [ ] 단어결과와 시도횟수를 가진다. 이때, 단어결과 목록은 빈 목록이고, 시도한 횟수는 0이다. diff --git a/src/main/kotlin/wordle/domain/WordResult.kt b/src/main/kotlin/wordle/domain/WordResult.kt new file mode 100644 index 0000000..e6ec028 --- /dev/null +++ b/src/main/kotlin/wordle/domain/WordResult.kt @@ -0,0 +1,10 @@ +package wordle.domain + +data class WordResult(private val result: MutableList = MutableList(WORD_LENGTH) { LetterMatch.ABSENT }) { + fun changeMatchType( + index: Int, + matchType: LetterMatch, + ) { + result[index] = matchType + } +} diff --git a/src/main/kotlin/wordle/domain/WordleGameLogic.kt b/src/main/kotlin/wordle/domain/WordleGameLogic.kt index 90ebbb5..aa7b202 100644 --- a/src/main/kotlin/wordle/domain/WordleGameLogic.kt +++ b/src/main/kotlin/wordle/domain/WordleGameLogic.kt @@ -2,9 +2,9 @@ package wordle.domain class WordleGameLogic(private val todayWord: Word) { val tempList = todayWord.word.toMutableList() - val wordResult = MutableList(WORD_LENGTH) { LetterMatch.ABSENT } + val wordResult = WordResult() - fun compare(answerWord: Word): MutableList = + fun compare(answerWord: Word): WordResult = answerWord.matchCorrect() .matchPresent() .toWordResult() @@ -12,7 +12,7 @@ class WordleGameLogic(private val todayWord: Word) { private fun Word.matchCorrect(): Word { return onEachIndexed { index, letter -> if (isSame(index, letter)) { - wordResult[index] = LetterMatch.CORRECT + wordResult.changeMatchType(index, LetterMatch.CORRECT) tempList[index] = tempList[index].changeMatchMarker() } } @@ -21,7 +21,7 @@ class WordleGameLogic(private val todayWord: Word) { private fun Word.matchPresent(): Word { return onEachIndexed { index, letter -> if (!isSame(index, letter) && letter in tempList) { - wordResult[index] = LetterMatch.PRESENT + wordResult.changeMatchType(index, LetterMatch.PRESENT) tempList[tempList.indexOf(letter)] = tempList[tempList.indexOf(letter)].changeMatchMarker() } } diff --git a/src/test/kotlin/wordle/domain/WordResultTest.kt b/src/test/kotlin/wordle/domain/WordResultTest.kt new file mode 100644 index 0000000..1bc4e80 --- /dev/null +++ b/src/test/kotlin/wordle/domain/WordResultTest.kt @@ -0,0 +1,34 @@ +package wordle.domain + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class WordResultTest { + @Test + fun `단어 결과는 글자 일치 상태 목록 없이 생성할 수 있다 - 모두 '불일치 상태'를 가진다`() { + val wordResult = WordResult() + + assertThat(wordResult).isEqualTo(MutableList(WORD_LENGTH) { LetterMatch.ABSENT }.toWordResult()) + } + + @Test + fun `답안 단어의 일치 여부를 변경한다`() { + val wordResult = WordResult() + + wordResult.changeMatchType(0, LetterMatch.CORRECT) + wordResult.changeMatchType(2, LetterMatch.PRESENT) + wordResult.changeMatchType(4, LetterMatch.CORRECT) + + assertThat(wordResult).isEqualTo( + mutableListOf( + LetterMatch.CORRECT, + LetterMatch.ABSENT, + LetterMatch.PRESENT, + LetterMatch.ABSENT, + LetterMatch.CORRECT, + ).toWordResult(), + ) + } +} + +fun MutableList.toWordResult(): WordResult = WordResult(this) diff --git a/src/test/kotlin/wordle/domain/WordleGameLogicTest.kt b/src/test/kotlin/wordle/domain/WordleGameLogicTest.kt index 3e707a5..5fa5127 100644 --- a/src/test/kotlin/wordle/domain/WordleGameLogicTest.kt +++ b/src/test/kotlin/wordle/domain/WordleGameLogicTest.kt @@ -29,7 +29,7 @@ class WordleGameLogicTest { LetterMatch.ABSENT, LetterMatch.ABSENT, LetterMatch.ABSENT, - ), + ).toWordResult(), ) } @@ -46,7 +46,7 @@ class WordleGameLogicTest { LetterMatch.ABSENT, LetterMatch.ABSENT, LetterMatch.CORRECT, - ), + ).toWordResult(), ) } } @@ -66,7 +66,7 @@ class WordleGameLogicTest { LetterMatch.ABSENT, LetterMatch.PRESENT, LetterMatch.ABSENT, - ), + ).toWordResult(), ) } @@ -83,7 +83,7 @@ class WordleGameLogicTest { LetterMatch.ABSENT, LetterMatch.ABSENT, LetterMatch.ABSENT, - ), + ).toWordResult(), ) } @@ -100,7 +100,7 @@ class WordleGameLogicTest { LetterMatch.PRESENT, LetterMatch.CORRECT, LetterMatch.ABSENT, - ), + ).toWordResult(), ) } @@ -117,7 +117,7 @@ class WordleGameLogicTest { LetterMatch.ABSENT, LetterMatch.ABSENT, LetterMatch.CORRECT, - ), + ).toWordResult(), ) } @@ -134,7 +134,7 @@ class WordleGameLogicTest { LetterMatch.ABSENT, LetterMatch.ABSENT, LetterMatch.ABSENT, - ), + ).toWordResult(), ) } @@ -151,7 +151,7 @@ class WordleGameLogicTest { LetterMatch.ABSENT, LetterMatch.CORRECT, LetterMatch.CORRECT, - ), + ).toWordResult(), ) } } @@ -164,7 +164,7 @@ class WordleGameLogicTest { val result = wordleGameLogic.compare(answerWord) - assertThat(result).isEqualTo(MutableList(WORD_LENGTH) { LetterMatch.ABSENT }) + assertThat(result).isEqualTo(MutableList(WORD_LENGTH) { LetterMatch.ABSENT }.toWordResult()) } @Test @@ -173,7 +173,7 @@ class WordleGameLogicTest { val result = wordleGameLogic.compare(answerWord) - assertThat(result).isEqualTo(MutableList(WORD_LENGTH) { LetterMatch.PRESENT }) + assertThat(result).isEqualTo(MutableList(WORD_LENGTH) { LetterMatch.PRESENT }.toWordResult()) } @Test @@ -182,7 +182,7 @@ class WordleGameLogicTest { val result = wordleGameLogic.compare(answerWord) - assertThat(result).isEqualTo(MutableList(WORD_LENGTH) { LetterMatch.CORRECT }) + assertThat(result).isEqualTo(MutableList(WORD_LENGTH) { LetterMatch.CORRECT }.toWordResult()) } } } From 6293b08872222af5db4066ff52706b174f41247f Mon Sep 17 00:00:00 2001 From: boradol2 Date: Tue, 18 Jun 2024 06:20:28 +0900 Subject: [PATCH 26/40] =?UTF-8?q?feat(WordComparator):=20=EB=8B=A8?= =?UTF-8?q?=EC=96=B4=20=EB=B9=84=EA=B5=90=EA=B8=B0=EB=A5=BC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 테스트 코드는 WordGameLogic 테스트가 통과하는지 확인한다. 2. 단어 비교기는 오늘의 단어에 일치 표시 기호를 하여 일치 비교를 완료한 글자를 관리한다. --- README.md | 20 ++++-- src/main/kotlin/wordle/domain/Word.kt | 2 +- .../kotlin/wordle/domain/WordComparator.kt | 64 +++++++++++++++++++ src/main/kotlin/wordle/domain/WordResult.kt | 2 + .../kotlin/wordle/domain/WordleGameLogic.kt | 35 ++-------- .../kotlin/wordle/domain/WordResultTest.kt | 15 ++++- 6 files changed, 100 insertions(+), 38 deletions(-) create mode 100644 src/main/kotlin/wordle/domain/WordComparator.kt diff --git a/README.md b/README.md index 94ece22..d17b238 100644 --- a/README.md +++ b/README.md @@ -208,12 +208,6 @@ spill - [x] 오늘의 단어는 현재 날짜를 입력하여 사전에서 한 단어를 불러온다. 이때, 오늘의 단어는 매일 바뀐다. - [x] 사전 단어 목록의 ((현재 날짜 - 2021년 6월 19일) % 배열의 크기) 번째의 단어이다. -#### 단어 비교기(Word Comparator) -- [ ] 각 단어를 이루고 있는 글자들의 일치 상태를 비교한다. - - [ ] 완전 일치 글자를 비교한다. 완전 일치 글자의 상태는 ABSENT -> CORRECT 로 변경된다. - - [ ] 부분 일치 글자를 비교한다. 부분 일치 글자의 상태는 ABSENT -> PRESENT 로 변경된다. -- [ ] 완전일치하거나 부분일치한 글자이면 그 글자는 일치 표시 기호로 변경된다. - #### 워들 게임 로직(Wordle Game Logic) - [x] 오늘의 단어와 답안 단어를 비교하여 단어 결과를 얻는다. - 단어 결과 중 글자 일치 상태가 하나만 다른 경우 @@ -231,6 +225,19 @@ spill - [x] 모든 글자 결과가 '부분 일치 글자'일 때 비교한다 - [x] 모든 글자 결과가 '완전 일치 글자'일 때 비교한다 +#### 단어 비교기(Word Comparator) +- 단어 비교기는 글자 목록과 단어 결과를 상태로 가진다. +- '완전 일치 글자'로 매칭 시키기 + - [x] 완전 일치 글자를 비교한다. 완전 일치 글자의 상태는 ABSENT -> CORRECT 로 변경된다. + - [x] 위치와 글자가 같은 경우 체크한다. +- '부분 일치 글자'로 매칭 시키기 + - [x] '완전 일치 글자'매칭 후에 동작한다. + - [x] 부분 일치 글자를 비교한다. 부분 일치 글자의 상태는 ABSENT -> PRESENT 로 변경된다. + - [x] 해당 단어 비교기 글자는 '완전 일치 글자'가 아니여야 한다. + - [x] 각 글자는 다른 위치여야 한다. + - [x] 비교하려는 답안 단어의 글자는 비교기 글자 목록에 있는 글자여야 한다. +- [x] '완전 일치 글자'거나 '부분 일치 글자'이면 해당 단어 비교기 글자에 일치 표시 기호('#')로 변경된다. + #### 시도 가능 횟수(Try Count) - [ ] 시도 가능 회수는 횟수를 가진다. 최초 생성할 때에는 6회이다. - [ ] 시도 가능 횟수는 0회보다 커야 한다. @@ -243,6 +250,7 @@ spill - 워들 게임 로직의 결과를 관리한다. - [x] 처음에 생성할 때, 글자 일치 상태는 불일치 상태이다. - [x] 해당 글자의 글자 일치 상태를 변경할 수 있다. +- [x] 해당 글자가 '완전 일치 상태'인지 확인한다. - [ ] 모든 글자 일치 상태가 '완전 일치 상태'이면, 해당 단어는 오늘의 단어와 같다.(게임 성공 여부가 된다) #### 단어 결과 목록(Word Results) diff --git a/src/main/kotlin/wordle/domain/Word.kt b/src/main/kotlin/wordle/domain/Word.kt index 4149511..77127e7 100644 --- a/src/main/kotlin/wordle/domain/Word.kt +++ b/src/main/kotlin/wordle/domain/Word.kt @@ -4,7 +4,7 @@ import wordle.exception.WordleExceptionCode.WORD_INVALID_LENGTH import wordle.exception.WordleExceptionCode.WORD_IS_NOT_IN_DICTIONARY import wordle.exception.WordleExceptionCode.WORD_NOT_ALLOW_SPACE -data class Word(val word: List) : List by word +data class Word(private val word: List) : List by word const val WORD_LENGTH = 5 diff --git a/src/main/kotlin/wordle/domain/WordComparator.kt b/src/main/kotlin/wordle/domain/WordComparator.kt new file mode 100644 index 0000000..3b89970 --- /dev/null +++ b/src/main/kotlin/wordle/domain/WordComparator.kt @@ -0,0 +1,64 @@ +package wordle.domain + +class WordComparator( + private val letters: MutableList, + private val wordResult: WordResult = WordResult(), +) { + fun matchCorrect(answerWord: Word): WordComparator = + apply { + letters.forEachIndexed { index, _ -> + changeCorrectMatch(index, answerWord[index]) + } + } + + fun matchPresent(answerWord: Word): WordComparator = + apply { + letters.forEachIndexed { index, _ -> + changePresentMatch(index, answerWord[index]) + } + } + + fun result(): WordResult = wordResult + + private fun changeCorrectMatch( + index: Int, + letter: Letter, + ) { + if (isCorrectLetter(index, letter)) { + wordResult.changeMatchType(index, LetterMatch.CORRECT) + changeLetter(index) + } + } + + private fun changePresentMatch( + index: Int, + letter: Letter, + ) { + if (isPresentLatter(index, letter)) { + wordResult.changeMatchType(index, LetterMatch.PRESENT) + changeLetterIndexOf(letter) + } + } + + private fun isCorrectLetter( + index: Int, + letter: Letter, + ) = letters[index] == letter + + private fun isPresentLatter( + index: Int, + letter: Letter, + ) = !wordResult.isCorrectMatchIndex(index) && !isCorrectLetter(index, letter) && (letter in letters) + + private fun changeLetter(index: Int) { + letters[index] = letters[index].changeMatchMarker() + } + + private fun changeLetterIndexOf(letter: Letter) { + changeLetter(letters.indexOf(letter)) + } +} + +fun WordComparator(todayWord: Word): WordComparator { + return WordComparator(todayWord.map(Letter::copy).toMutableList()) +} diff --git a/src/main/kotlin/wordle/domain/WordResult.kt b/src/main/kotlin/wordle/domain/WordResult.kt index e6ec028..8ffa34c 100644 --- a/src/main/kotlin/wordle/domain/WordResult.kt +++ b/src/main/kotlin/wordle/domain/WordResult.kt @@ -7,4 +7,6 @@ data class WordResult(private val result: MutableList = MutableList ) { result[index] = matchType } + + fun isCorrectMatchIndex(index: Int): Boolean = result[index] == LetterMatch.CORRECT } diff --git a/src/main/kotlin/wordle/domain/WordleGameLogic.kt b/src/main/kotlin/wordle/domain/WordleGameLogic.kt index aa7b202..e2baf0b 100644 --- a/src/main/kotlin/wordle/domain/WordleGameLogic.kt +++ b/src/main/kotlin/wordle/domain/WordleGameLogic.kt @@ -1,36 +1,11 @@ package wordle.domain class WordleGameLogic(private val todayWord: Word) { - val tempList = todayWord.word.toMutableList() - val wordResult = WordResult() - fun compare(answerWord: Word): WordResult = - answerWord.matchCorrect() - .matchPresent() - .toWordResult() - - private fun Word.matchCorrect(): Word { - return onEachIndexed { index, letter -> - if (isSame(index, letter)) { - wordResult.changeMatchType(index, LetterMatch.CORRECT) - tempList[index] = tempList[index].changeMatchMarker() - } - } - } - - private fun Word.matchPresent(): Word { - return onEachIndexed { index, letter -> - if (!isSame(index, letter) && letter in tempList) { - wordResult.changeMatchType(index, LetterMatch.PRESENT) - tempList[tempList.indexOf(letter)] = tempList[tempList.indexOf(letter)].changeMatchMarker() - } - } - } - - private fun Word.toWordResult() = wordResult + todayWord.comparator() + .matchCorrect(answerWord) + .matchPresent(answerWord) + .result() - private fun isSame( - index: Int, - letter: Letter, - ) = todayWord.word[index] == letter + private fun Word.comparator(): WordComparator = WordComparator(this) } diff --git a/src/test/kotlin/wordle/domain/WordResultTest.kt b/src/test/kotlin/wordle/domain/WordResultTest.kt index 1bc4e80..14487c7 100644 --- a/src/test/kotlin/wordle/domain/WordResultTest.kt +++ b/src/test/kotlin/wordle/domain/WordResultTest.kt @@ -2,6 +2,7 @@ package wordle.domain import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertAll class WordResultTest { @Test @@ -12,7 +13,7 @@ class WordResultTest { } @Test - fun `답안 단어의 일치 여부를 변경한다`() { + fun `해당 글자의 글자 일치 상태를 변경할 수 있다`() { val wordResult = WordResult() wordResult.changeMatchType(0, LetterMatch.CORRECT) @@ -29,6 +30,18 @@ class WordResultTest { ).toWordResult(), ) } + + @Test + fun `해당 글자가 '완전 일치 상태'인지 확인한다`() { + val wordResult = WordResult() + + wordResult.changeMatchType(1, LetterMatch.CORRECT) + + assertAll( + { assertThat(wordResult.isCorrectMatchIndex(0)).isFalse() }, + { assertThat(wordResult.isCorrectMatchIndex(1)).isTrue() }, + ) + } } fun MutableList.toWordResult(): WordResult = WordResult(this) From bda98559650b23cb112c18b0f24ef4612be6a836 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Tue, 18 Jun 2024 06:49:33 +0900 Subject: [PATCH 27/40] =?UTF-8?q?feat(TryCount):=20=EC=9B=8C=EB=93=A4=20?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=EC=97=90=EC=84=9C=20=EC=8B=9C=EB=8F=84=20?= =?UTF-8?q?=EA=B0=80=EB=8A=A5=20=ED=9A=9F=EC=88=98=EB=A5=BC=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 +-- src/main/kotlin/wordle/domain/TryCount.kt | 16 +++++ .../wordle/exception/WordleExceptionCode.kt | 1 + src/test/kotlin/wordle/domain/TryCountTest.kt | 65 +++++++++++++++++++ 4 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 src/main/kotlin/wordle/domain/TryCount.kt create mode 100644 src/test/kotlin/wordle/domain/TryCountTest.kt diff --git a/README.md b/README.md index d17b238..9a54881 100644 --- a/README.md +++ b/README.md @@ -239,11 +239,11 @@ spill - [x] '완전 일치 글자'거나 '부분 일치 글자'이면 해당 단어 비교기 글자에 일치 표시 기호('#')로 변경된다. #### 시도 가능 횟수(Try Count) -- [ ] 시도 가능 회수는 횟수를 가진다. 최초 생성할 때에는 6회이다. -- [ ] 시도 가능 횟수는 0회보다 커야 한다. -- [ ] 시도 가능 횟수를 1회씩 차감할 수 있다. -- [ ] 시도 가능 횟수가 남았는지 알 수 있다. -- [ ] 시도한 횟수를 구할 수 있다. +- [x] 시도 가능 회수는 횟수를 가진다. 최초 생성할 때에는 6회이다. +- [x] 시도한 횟수를 구할 수 있다. +- [x] 시도 가능 횟수가 남았는지 알 수 있다. +- [x] 시도 가능 횟수를 1회씩 차감할 수 있다. +- [x] 시도 가능 횟수는 0회보다 커야 한다. #### 단어 결과(Word Result) - [x] 5개의 글자 일치 상태 목록으로 이루어져 있다. diff --git a/src/main/kotlin/wordle/domain/TryCount.kt b/src/main/kotlin/wordle/domain/TryCount.kt new file mode 100644 index 0000000..e82a62b --- /dev/null +++ b/src/main/kotlin/wordle/domain/TryCount.kt @@ -0,0 +1,16 @@ +package wordle.domain + +import wordle.exception.WordleExceptionCode.TRY_COUNT_HAS_NOT_REMAINDER + +data class TryCount(private var count: Int = MAX_TRY_COUNT) { + val attempts get() = MAX_TRY_COUNT - count + + fun isRemainder(): Boolean = count > 0 + + fun minus() { + check(isRemainder()) { TRY_COUNT_HAS_NOT_REMAINDER.message } + this.count -= 1 + } +} + +const val MAX_TRY_COUNT = 6 diff --git a/src/main/kotlin/wordle/exception/WordleExceptionCode.kt b/src/main/kotlin/wordle/exception/WordleExceptionCode.kt index e9dec50..5ad62b0 100644 --- a/src/main/kotlin/wordle/exception/WordleExceptionCode.kt +++ b/src/main/kotlin/wordle/exception/WordleExceptionCode.kt @@ -7,4 +7,5 @@ enum class WordleExceptionCode(val message: String) { WORD_INVALID_LENGTH("단어의 길이는 ${WORD_LENGTH}자 입니다."), WORD_IS_NOT_IN_DICTIONARY("Wordle Game에서 유효한 단어가 아닙니다."), LETTER_INVALID_CHARACTER_TYPE("유효하지 않은 글자 형식입니다."), + TRY_COUNT_HAS_NOT_REMAINDER("시행 횟수는 0보다 작을 수 없습니다."), } diff --git a/src/test/kotlin/wordle/domain/TryCountTest.kt b/src/test/kotlin/wordle/domain/TryCountTest.kt new file mode 100644 index 0000000..3bb8898 --- /dev/null +++ b/src/test/kotlin/wordle/domain/TryCountTest.kt @@ -0,0 +1,65 @@ +package wordle.domain + +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertAll +import org.junit.jupiter.api.assertDoesNotThrow + +class TryCountTest { + @Test + fun `시도 가능 횟수를 생성한다 - 최대 시도 가능 횟수를 가진다`() { + val actual = TryCount() + + assertThat(actual).isEqualTo(TryCount(MAX_TRY_COUNT)) + } + + @Test + fun `시도한 횟수를 반환한다`() { + val tryCount = TryCount() + + val actual = tryCount.attempts + + assertThat(actual).isZero() + } + + @Test + fun `시도 가능 횟수가 있다면 true 를 반환한다`() { + val tryCount = TryCount() + tryCount.minus() + + val actual = tryCount.isRemainder() + + assertThat(actual).isTrue() + } + + @Test + fun `시도 가능 횟수가 없다면 false 를 반환한다`() { + val tryCount = TryCount(0) + + val actual = tryCount.isRemainder() + + assertThat(actual).isFalse() + } + + @Test + fun `시도 가능 횟수를 1회 차감하면 시도한 횟수는 1회 증가한다`() { + val tryCount = TryCount(6) + + assertDoesNotThrow { tryCount.minus() } + + assertAll( + { assertThat(tryCount).isEqualTo(TryCount(5)) }, + { assertThat(tryCount.attempts).isEqualTo(1) }, + ) + } + + @Test + fun `시도 가능 횟수가 0 보다 작으면, 예외를 반환한다`() { + val tryCount = TryCount(0) + + assertThatThrownBy { tryCount.minus() } + .isInstanceOf(IllegalStateException::class.java) + .hasMessage("시행 횟수는 0보다 작을 수 없습니다.") + } +} From 9299ecae2bda4fab4624a770e6c2c9735e7454ee Mon Sep 17 00:00:00 2001 From: boradol2 Date: Tue, 18 Jun 2024 07:04:58 +0900 Subject: [PATCH 28/40] =?UTF-8?q?feat(WordResult):=20=EB=8B=A8=EC=96=B4=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=EB=8A=94=20=EB=8B=A8=EC=96=B4=20=EA=B8=B8?= =?UTF-8?q?=EC=9D=B4=EC=99=80=20=EC=9D=BC=EC=B9=98=ED=95=B4=EC=95=BC=20?= =?UTF-8?q?=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/domain/WordResult.kt | 6 ++++++ .../kotlin/wordle/exception/WordleExceptionCode.kt | 1 + src/test/kotlin/wordle/domain/WordResultTest.kt | 14 +++++++++++--- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/wordle/domain/WordResult.kt b/src/main/kotlin/wordle/domain/WordResult.kt index 8ffa34c..a0d9570 100644 --- a/src/main/kotlin/wordle/domain/WordResult.kt +++ b/src/main/kotlin/wordle/domain/WordResult.kt @@ -1,6 +1,12 @@ package wordle.domain +import wordle.exception.WordleExceptionCode.WORD_RESULT_INVALID_LENGTH + data class WordResult(private val result: MutableList = MutableList(WORD_LENGTH) { LetterMatch.ABSENT }) { + init { + check(result.size == WORD_LENGTH) { WORD_RESULT_INVALID_LENGTH.message } + } + fun changeMatchType( index: Int, matchType: LetterMatch, diff --git a/src/main/kotlin/wordle/exception/WordleExceptionCode.kt b/src/main/kotlin/wordle/exception/WordleExceptionCode.kt index 5ad62b0..cf1f51c 100644 --- a/src/main/kotlin/wordle/exception/WordleExceptionCode.kt +++ b/src/main/kotlin/wordle/exception/WordleExceptionCode.kt @@ -8,4 +8,5 @@ enum class WordleExceptionCode(val message: String) { WORD_IS_NOT_IN_DICTIONARY("Wordle Game에서 유효한 단어가 아닙니다."), LETTER_INVALID_CHARACTER_TYPE("유효하지 않은 글자 형식입니다."), TRY_COUNT_HAS_NOT_REMAINDER("시행 횟수는 0보다 작을 수 없습니다."), + WORD_RESULT_INVALID_LENGTH("단어 결과의 글자 일치 상태 목록들은 단어 길이인 ${WORD_LENGTH}와 일치해야 합니다."), } diff --git a/src/test/kotlin/wordle/domain/WordResultTest.kt b/src/test/kotlin/wordle/domain/WordResultTest.kt index 14487c7..ffa388c 100644 --- a/src/test/kotlin/wordle/domain/WordResultTest.kt +++ b/src/test/kotlin/wordle/domain/WordResultTest.kt @@ -1,19 +1,20 @@ package wordle.domain import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertAll class WordResultTest { @Test - fun `단어 결과는 글자 일치 상태 목록 없이 생성할 수 있다 - 모두 '불일치 상태'를 가진다`() { + fun `(성공) 단어 결과는 글자 일치 상태 목록 없이 생성할 수 있다 - 모두 '불일치 상태'를 가진다`() { val wordResult = WordResult() assertThat(wordResult).isEqualTo(MutableList(WORD_LENGTH) { LetterMatch.ABSENT }.toWordResult()) } @Test - fun `해당 글자의 글자 일치 상태를 변경할 수 있다`() { + fun `(성공) 해당 글자의 글자 일치 상태를 변경할 수 있다`() { val wordResult = WordResult() wordResult.changeMatchType(0, LetterMatch.CORRECT) @@ -32,7 +33,7 @@ class WordResultTest { } @Test - fun `해당 글자가 '완전 일치 상태'인지 확인한다`() { + fun `(성공) 해당 글자가 '완전 일치 상태'인지 확인한다`() { val wordResult = WordResult() wordResult.changeMatchType(1, LetterMatch.CORRECT) @@ -42,6 +43,13 @@ class WordResultTest { { assertThat(wordResult.isCorrectMatchIndex(1)).isTrue() }, ) } + + @Test + fun `(예외) 단어 결과 목록이 단어 길이와 일치헤야 한다`() { + assertThatThrownBy { WordResult(mutableListOf()) } + .isInstanceOf(IllegalStateException::class.java) + .hasMessage("단어 결과의 글자 일치 상태 목록들은 단어 길이인 ${WORD_LENGTH}와 일치해야 합니다.") + } } fun MutableList.toWordResult(): WordResult = WordResult(this) From 9a81038ba1048e08777a987692b5c0bcbd0e1457 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Tue, 18 Jun 2024 07:34:30 +0900 Subject: [PATCH 29/40] =?UTF-8?q?feat(WordResults):=20=EB=8B=A8=EC=96=B4?= =?UTF-8?q?=20=EA=B2=B0=EA=B3=BC=20=EB=AA=A9=EB=A1=9D=EC=9D=80=20=EB=8B=A8?= =?UTF-8?q?=EC=96=B4=20=EA=B2=B0=EA=B3=BC=EB=93=A4=EA=B3=BC=20=EC=8B=9C?= =?UTF-8?q?=EB=8F=84=ED=9A=9F=EC=88=98=EB=A5=BC=20=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 단어결과를 추가하여 게임의 진행여부와 게임 성공여부를 판단할 수 있다. --- README.md | 10 ++--- src/main/kotlin/wordle/domain/WordResult.kt | 4 ++ src/main/kotlin/wordle/domain/WordResults.kt | 19 ++++++++ .../kotlin/wordle/domain/WordResultsTest.kt | 45 +++++++++++++++++++ 4 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 src/main/kotlin/wordle/domain/WordResults.kt create mode 100644 src/test/kotlin/wordle/domain/WordResultsTest.kt diff --git a/README.md b/README.md index 9a54881..4a8848a 100644 --- a/README.md +++ b/README.md @@ -251,10 +251,10 @@ spill - [x] 처음에 생성할 때, 글자 일치 상태는 불일치 상태이다. - [x] 해당 글자의 글자 일치 상태를 변경할 수 있다. - [x] 해당 글자가 '완전 일치 상태'인지 확인한다. -- [ ] 모든 글자 일치 상태가 '완전 일치 상태'이면, 해당 단어는 오늘의 단어와 같다.(게임 성공 여부가 된다) +- [x] 모든 글자 일치 상태가 '완전 일치 상태'이면, 해당 단어는 오늘의 단어와 같다.(게임 성공 여부가 된다) #### 단어 결과 목록(Word Results) -- [ ] 단어결과와 시도횟수를 가진다. 이때, 단어결과 목록은 빈 목록이고, 시도한 횟수는 0이다. -- [ ] 단어 결과를 추가하면, 시도횟수는 1회 증가한다. -- [ ] 단어 결과 목록에서 단어 결과가 성공인 상태가 추가되면 게임은 성공한다. -- [ ] 단어 결과 목록에서 아직 게임 성공하지 못하고 시도 가능 횟수가 1 이상이면 게임을 계속할 수 있다. +- [x] 단어결과와 시도횟수를 가진다. 이때, 단어결과 목록은 빈 목록이고, 시도한 횟수는 0이다. +- [x] 단어 결과를 추가하면, 시도횟수는 1회 증가한다. +- [x] 단어 결과 목록에서 단어 결과가 성공인 상태가 추가되면 게임은 성공한다. +- [x] 단어 결과 목록에서 아직 게임 성공하지 못하고 시도 가능 횟수가 1 이상이면 게임을 계속할 수 있다. diff --git a/src/main/kotlin/wordle/domain/WordResult.kt b/src/main/kotlin/wordle/domain/WordResult.kt index a0d9570..eeae9f3 100644 --- a/src/main/kotlin/wordle/domain/WordResult.kt +++ b/src/main/kotlin/wordle/domain/WordResult.kt @@ -15,4 +15,8 @@ data class WordResult(private val result: MutableList = MutableList } fun isCorrectMatchIndex(index: Int): Boolean = result[index] == LetterMatch.CORRECT + + fun isSuccessGame() = result.all { matchType -> matchType == LetterMatch.CORRECT } + + fun matches(): List = result.toList() } diff --git a/src/main/kotlin/wordle/domain/WordResults.kt b/src/main/kotlin/wordle/domain/WordResults.kt new file mode 100644 index 0000000..ee2dcd3 --- /dev/null +++ b/src/main/kotlin/wordle/domain/WordResults.kt @@ -0,0 +1,19 @@ +package wordle.domain + +class WordResults( + private val results: MutableList = mutableListOf(), + private val tryCount: TryCount = TryCount(), +) { + val attemptCount get() = tryCount.attempts + + fun addResults(result: WordResult) { + tryCount.minus() + results.add(result) + } + + fun isContinuousGame(): Boolean = !isSuccessGame() && tryCount.isRemainder() + + fun isSuccessGame(): Boolean = results.any(WordResult::isSuccessGame) + + fun wordResults(): List> = results.map(WordResult::matches) +} diff --git a/src/test/kotlin/wordle/domain/WordResultsTest.kt b/src/test/kotlin/wordle/domain/WordResultsTest.kt new file mode 100644 index 0000000..812c952 --- /dev/null +++ b/src/test/kotlin/wordle/domain/WordResultsTest.kt @@ -0,0 +1,45 @@ +package wordle.domain + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Assertions.assertAll +import org.junit.jupiter.api.Test + +class WordResultsTest { + @Test + fun `(성공) 단어 결과 목록은 빈 단어 결과 목록과 시도한 횟수가 0이다`() { + val actual = WordResults() + + assertAll( + { assertThat(actual.wordResults()).hasSize(0) }, + { assertThat(actual.attemptCount).isZero() }, + ) + } + + @Test + fun `(성공) 단어 결과 목록은 단어 결과를 추가하고, 시도횟수가 1회 증가한다`() { + val wordResults = WordResults() + + wordResults.addResults(WordResult()) + + assertAll( + { assertThat(wordResults.wordResults()).hasSize(1) }, + { assertThat(wordResults.attemptCount).isEqualTo(1) }, + ) + } + + @Test + fun `(성공) 단어 결과 목록 상태가 게임을 계속 진행할 수 있는 상태인지 반환한다`() { + val wordResults = WordResults() + wordResults.addResults(WordResult()) + + assertThat(wordResults.isContinuousGame()).isTrue() + } + + @Test + fun `(성공) 단어 맞추기에 성공한 경우 true 를 반환한다`() { + val wordResults = WordResults() + wordResults.addResults(WordResult(MutableList(WORD_LENGTH) { LetterMatch.CORRECT })) + + assertThat(wordResults.isSuccessGame()).isTrue() + } +} From 30850a7d502a5da22183436c87e6bc9697b672a8 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Tue, 18 Jun 2024 07:35:23 +0900 Subject: [PATCH 30/40] =?UTF-8?q?feat(OutputView):=20=EB=8B=A8=EC=96=B4=20?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 게임 결과 출력 2. 타일 그리기 3. 다시시도 메시지 4. 성공 메시지 5. 실패 메시지, 오늘의 단어 안내 --- README.md | 14 +++++++------- src/main/kotlin/wordle/domain/Letter.kt | 2 ++ src/main/kotlin/wordle/domain/Word.kt | 4 +++- src/main/kotlin/wordle/view/OutputView.kt | 20 ++++++++++++++++++++ src/main/kotlin/wordle/view/Tile.kt | 16 ++++++++++++++++ 5 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 src/main/kotlin/wordle/view/Tile.kt diff --git a/README.md b/README.md index 4a8848a..2e55293 100644 --- a/README.md +++ b/README.md @@ -150,16 +150,16 @@ spill #### 출력(Output View) - [x] 게임 시작 메시지를 출력한다. - `WORDLE을 6번 만에 맞춰 보세요.\n시도의 결과는 타일의 색 변화로 나타납니다.` -- [ ] 타일 결과를 출력한다. - - [ ] 플레이어가 입력한 답안 단어와 비교한 답안 결과에 따라 각 글자의 일치 상태에 맞게 타일을 출력한다. +- [x] 타일 결과를 출력한다. + - [x] 플레이어가 입력한 답안 단어와 비교한 답안 결과에 따라 각 글자의 일치 상태에 맞게 타일을 출력한다. - `🟩` 초록색: 오늘의 단어와 비교하여 답안 단어의 글자가 '완전 일치'인 경우 - `🟨` 노란색: 오늘의 단어와 비교하여 답안 단어의 글자가 '부분 일치'인 경우 - `⬜️` 회색: 오늘의 단어와 비교하여 답안 단어의 글자가 '불일치'인 경우 - - [ ] 지금까지 입력한 답안 단어에 따른 단어 결과 목록을 출력한다. -- [ ] 다시 시도 할 때, 다시 시도의 메시지와 그 이유를 함께 출력한다. -- [ ] 게임의 결과에 따라 메시지를 출력한다 - - [ ] 성공 메시지를 출력한다. `{시도한 횟수} / {최대 시도 가능 횟수}`도 함께 출력한다. - - [ ] 실패 메시지를 출력한다. `{오늘의 단어}`를 함께 출력한다. + - [x] 지금까지 입력한 답안 단어에 따른 단어 결과 목록을 출력한다. +- [x] 다시 시도 할 때, 다시 시도의 메시지와 그 이유를 함께 출력한다. +- [x] 게임의 결과에 따라 메시지를 출력한다 + - [x] 성공 메시지를 출력한다. `{시도한 횟수} / {최대 시도 가능 횟수}`도 함께 출력한다. + - [x] 실패 메시지를 출력한다. `{오늘의 단어}`를 함께 출력한다. #### 입력(Input View) - [x] 답안 단어를 입력받는다. 입력받기 전에 답안 입력을 위한 메시지를 먼저 출력한다. diff --git a/src/main/kotlin/wordle/domain/Letter.kt b/src/main/kotlin/wordle/domain/Letter.kt index 27502a3..a808d64 100644 --- a/src/main/kotlin/wordle/domain/Letter.kt +++ b/src/main/kotlin/wordle/domain/Letter.kt @@ -9,6 +9,8 @@ data class Letter(private val value: Char) { fun changeMatchMarker(): Letter = MATCH_MARKER_LETTER + fun value(): String = value.toString() + private fun isAlphabetOrMatchMarker(): Boolean = isAlphabet() || isMatchMarker() private fun isAlphabet(): Boolean = value in ALPHABET diff --git a/src/main/kotlin/wordle/domain/Word.kt b/src/main/kotlin/wordle/domain/Word.kt index 77127e7..62a56d2 100644 --- a/src/main/kotlin/wordle/domain/Word.kt +++ b/src/main/kotlin/wordle/domain/Word.kt @@ -4,7 +4,9 @@ import wordle.exception.WordleExceptionCode.WORD_INVALID_LENGTH import wordle.exception.WordleExceptionCode.WORD_IS_NOT_IN_DICTIONARY import wordle.exception.WordleExceptionCode.WORD_NOT_ALLOW_SPACE -data class Word(private val word: List) : List by word +data class Word(private val word: List) : List by word { + fun letters(): String = word.joinToString("", transform = Letter::value) +} const val WORD_LENGTH = 5 diff --git a/src/main/kotlin/wordle/view/OutputView.kt b/src/main/kotlin/wordle/view/OutputView.kt index 932443d..4f720df 100644 --- a/src/main/kotlin/wordle/view/OutputView.kt +++ b/src/main/kotlin/wordle/view/OutputView.kt @@ -1,5 +1,25 @@ package wordle.view +import wordle.domain.MAX_TRY_COUNT +import wordle.domain.Word +import wordle.domain.WordResults + fun printStartingGameMessage() { println("🎮 WORDLE을 6번 만에 맞춰 보세요.\n📌 시도의 결과는 타일의 색 변화로 나타납니다.🥳\n") } + +fun printResult(results: WordResults) { + println(results.wordResults().joinToString("\n") { it.joinToString("") { Tile.of(it).color } } + "\n") +} + +fun printRetry(message: String?) { + println("🥲 다시 시도하세요! : ${message ?: ""}\n") +} + +fun printSuccess(attemptCount: Int) { + println("🎉 성공입니다. $attemptCount / $MAX_TRY_COUNT") +} + +fun printFail(todayWord: Word) { + println("👻 실패하였습니다. 오늘의 단어는 [ ${todayWord.letters()} ] 입니다.") +} diff --git a/src/main/kotlin/wordle/view/Tile.kt b/src/main/kotlin/wordle/view/Tile.kt new file mode 100644 index 0000000..aecc847 --- /dev/null +++ b/src/main/kotlin/wordle/view/Tile.kt @@ -0,0 +1,16 @@ +package wordle.view + +import wordle.domain.LetterMatch + +enum class Tile(val matchType: LetterMatch, val color: String) { + GREEN(LetterMatch.CORRECT, "🟩"), + YELLOW(LetterMatch.PRESENT, "🟨"), + GREY(LetterMatch.ABSENT, "⬜"), + ; + + companion object { + fun of(matchType: LetterMatch): Tile { + return entries.find { tile -> tile.matchType == matchType } ?: GREY + } + } +} From 51480af75d7f7a5af19111942985db346014cdac Mon Sep 17 00:00:00 2001 From: boradol2 Date: Tue, 18 Jun 2024 08:32:41 +0900 Subject: [PATCH 31/40] =?UTF-8?q?feat(WordleGame):=20WordleGame=EC=9D=84?= =?UTF-8?q?=20=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 테스트코드 콘솔 출력으로 작성 --- README.md | 24 +-- .../kotlin/wordle/application/WordleGame.kt | 32 +-- .../wordle/application/WordleGameTest.kt | 187 ++++++++++++++++++ src/test/resources/words.txt | 2 +- 4 files changed, 221 insertions(+), 24 deletions(-) create mode 100644 src/test/kotlin/wordle/application/WordleGameTest.kt diff --git a/README.md b/README.md index 2e55293..651fee5 100644 --- a/README.md +++ b/README.md @@ -166,25 +166,25 @@ spill - `정답을 입력해 주세요.` #### 워들 게임(Wordle Game) -- [ ] 단어 결과 목록을 가진다. +- [x] 단어 결과 목록을 가진다. - [x] 유효한 답안 단어를 최대 6번 입력받아 게임을 한다. - [x] 게임에 아직 성공하지 못한 상태에서 시도 가능 횟수가 0보다 크면 게임을 계속 할 수 있다. - - [ ] 단어를 입력받아 답안 단어를 만든다. + - [x] 단어를 입력받아 답안 단어를 만든다. - 유효한 답안 단어를 입력 하였을 때, - - [ ] 게임 검증 로직을 수행한다. - - [ ] 타일 결과 목록을 출력한다. - - [ ] 시행 가능 횟수를 1회 차감한다. + - [x] 게임 검증 로직을 수행한다. + - [x] 타일 결과 목록을 출력한다. + - [x] 시행 가능 횟수를 1회 차감한다. - 유효하지 않은 답안 단어를 입력 하였을 때, - - [ ] 답안 단어 입력을 다시 시도해야 한다. 다시 시도 하는 이유를 출력한다. -- [ ] 단어 결과 목록과 대응하는 타일 결과 목록을 출력한다. - - [ ] 답안 단어 1번 입력하는 경우 - - [ ] 답안 단어를 6번 입력하고 게임에 실패하는 경우 -- [ ] 게임이 종료 된다. 게임의 성공과 실패 여부를 판별한다. + - [x] 답안 단어 입력을 다시 시도해야 한다. 다시 시도 하는 이유를 출력한다. +- [x] 단어 결과 목록과 대응하는 타일 결과 목록을 출력한다. + - [x] 답안 단어 1번 입력하는 경우 + - [x] 답안 단어를 6번 입력하고 게임에 실패하는 경우 +- [x] 게임이 종료 된다. 게임의 성공과 실패 여부를 판별한다. - 게임에 성공 하였을 때 - - [ ] 해당 답안 단어의 단어 결과가 모두 '완전 일치 글자'일 때이다. + - [x] 해당 답안 단어의 단어 결과가 모두 '완전 일치 글자'일 때이다. - 관련 메시지 출력 - 게임에 실패 하였을 때 - - [ ] 게임에 성공하지 않고, 시도횟수가 0회가 될 때이다. + - [x] 게임에 성공하지 않고, 시도횟수가 0회가 될 때이다. #### 글자(Letter) - [x] 글자는 하나의 문자를 가진다. diff --git a/src/main/kotlin/wordle/application/WordleGame.kt b/src/main/kotlin/wordle/application/WordleGame.kt index 8dd789e..0413d6c 100644 --- a/src/main/kotlin/wordle/application/WordleGame.kt +++ b/src/main/kotlin/wordle/application/WordleGame.kt @@ -1,30 +1,40 @@ package wordle.application -import wordle.domain.AnswerWord import wordle.domain.TodayWord +import wordle.domain.Word +import wordle.domain.WordResults import wordle.domain.WordleGameLogic import wordle.view.inputAnswerWord +import wordle.view.printFail +import wordle.view.printResult +import wordle.view.printRetry +import wordle.view.printSuccess import java.time.LocalDate class WordleGame(gameStartDate: LocalDate) { private val todayWord = TodayWord(gameStartDate) + private val wordleGameLogic = WordleGameLogic(todayWord) + private val results = WordResults() fun play() { - var tryCount = 6 - while (isContinuousGame(tryCount)) { + while (results.isContinuousGame()) { try { - val answerWord = AnswerWord(inputAnswerWord()) - WordleGameLogic(todayWord, answerWord).compare() - tryCount-- + val answerWord = Word(inputAnswerWord()) + val result = wordleGameLogic.compare(answerWord) + results.addResults(result) + printResult(results) } catch (e: IllegalStateException) { - println("다시 시도 해주세요!") + printRetry(e.message) } } + gameResult(todayWord) } - private fun isContinuousGame(tryCount: Int) = !isSuccessGame() && tryCount > 0 - - private fun isSuccessGame(): Boolean { - return false + private fun gameResult(todayWord: Word) { + if (results.isSuccessGame()) { + printSuccess(results.attemptCount) + return + } + printFail(todayWord) } } diff --git a/src/test/kotlin/wordle/application/WordleGameTest.kt b/src/test/kotlin/wordle/application/WordleGameTest.kt new file mode 100644 index 0000000..9c522c6 --- /dev/null +++ b/src/test/kotlin/wordle/application/WordleGameTest.kt @@ -0,0 +1,187 @@ +package wordle.application + +import org.assertj.core.api.AssertionsForClassTypes.assertThat +import org.junit.jupiter.api.Test +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.PrintStream +import java.time.LocalDate + +private val GAME_START_DATE = LocalDate.of(2021, 6, 20) + +class WordleGameTest { + @Test + fun `6번 이내 오늘의 단어를 맞추면 성공 안내 문구를 출력한다`() { + val inputAnswerWords = "$answerWordHello$br$answerWordLabel$br$answerWordSpell$br$wordSpill" + val (printStream, outputStream) = printStreamByteArrayOutputStreamPair(inputAnswerWords) + + WordleGame(GAME_START_DATE).play() + printStream(printStream) + + assertThat(outputStream.toString().trimIndent()).isEqualTo(successConsolePrint.trimIndent()) + } + + @Test + fun `6번 이내 오늘의 단어를 맞추지 못하면 실패 안내 문구와 오늘의 단어를 출력한다`() { + val inputAnswerWords = + "$answerWordHello$br$answerWordLabel$br$answerWordSpell$br$answerWordHello$br$answerWordLabel$br$answerWordSpell" + val (printStream, outputStream) = printStreamByteArrayOutputStreamPair(inputAnswerWords) + + WordleGame(GAME_START_DATE).play() + printStream(printStream) + + assertThat(outputStream.toString().trimIndent()).isEqualTo(failConsolePrint.trimIndent()) + } + + @Test + fun `답안 입력 시 공백을 입력하면 다시 입력을 받는다`() { + val inputAnswerWords = "$blank$br$wordSpill" + val (printStream, outputStream) = printStreamByteArrayOutputStreamPair(inputAnswerWords) + + WordleGame(GAME_START_DATE).play() + printStream(printStream) + + assertThat(outputStream.toString().trimIndent()).isEqualTo(blankConsolePrint.trimIndent()) + } + + @Test + fun `답안 입력 시 정해진 단어의 길이와 다른 단어를 입력하면 다시 입력을 받는다`() { + val inputAnswerWords = "$invalidLengthWord$br$wordSpill" + val (printStream, outputStream) = printStreamByteArrayOutputStreamPair(inputAnswerWords) + + WordleGame(GAME_START_DATE).play() + printStream(printStream) + + assertThat(outputStream.toString().trimIndent()).isEqualTo(invalidLengthConsolePrint.trimIndent()) + } + + @Test + fun `답안 입력 시 단어 사전에 없는 단어를 입력하면 다시 입력을 받는다`() { + val inputAnswerWords = "$invalidWord$br$wordSpill" + val (printStream, outputStream) = printStreamByteArrayOutputStreamPair(inputAnswerWords) + + WordleGame(GAME_START_DATE).play() + printStream(printStream) + + assertThat(outputStream.toString().trimIndent()).isEqualTo(invalidWordConsolePrint.trimIndent()) + } + + private fun printStreamByteArrayOutputStreamPair(inputAnswerWords: String): Pair { + val printStream = System.out + val outputStream = ByteArrayOutputStream() + val inputStream = ByteArrayInputStream(inputAnswerWords.toByteArray()) + + System.setOut(PrintStream(outputStream)) + System.setIn(inputStream) + + return Pair(printStream, outputStream) + } + + private fun printStream(printStream: PrintStream) { + System.setOut(printStream) + System.setIn(System.`in`) + } + + private val answerWordHello = "hello" + private val answerWordLabel = "label" + private val answerWordSpell = "spell" + private val wordSpill = "spill" + private val br = "\n" + private val blank = "" + private val invalidLengthWord = "word" + private val invalidWord = "abcde" + + private val successConsolePrint = + """ +🚀 정답을 입력하세요. : +⬜⬜🟨🟩⬜ + +🚀 정답을 입력하세요. : +⬜⬜🟨🟩⬜ +🟨⬜⬜⬜🟩 + +🚀 정답을 입력하세요. : +⬜⬜🟨🟩⬜ +🟨⬜⬜⬜🟩 +🟩🟩⬜🟩🟩 + +🚀 정답을 입력하세요. : +⬜⬜🟨🟩⬜ +🟨⬜⬜⬜🟩 +🟩🟩⬜🟩🟩 +🟩🟩🟩🟩🟩 + +🎉 성공입니다. 4 / 6 +""" + + private val failConsolePrint = + """ + 🚀 정답을 입력하세요. : + ⬜⬜🟨🟩⬜ + + 🚀 정답을 입력하세요. : + ⬜⬜🟨🟩⬜ + 🟨⬜⬜⬜🟩 + + 🚀 정답을 입력하세요. : + ⬜⬜🟨🟩⬜ + 🟨⬜⬜⬜🟩 + 🟩🟩⬜🟩🟩 + + 🚀 정답을 입력하세요. : + ⬜⬜🟨🟩⬜ + 🟨⬜⬜⬜🟩 + 🟩🟩⬜🟩🟩 + ⬜⬜🟨🟩⬜ + + 🚀 정답을 입력하세요. : + ⬜⬜🟨🟩⬜ + 🟨⬜⬜⬜🟩 + 🟩🟩⬜🟩🟩 + ⬜⬜🟨🟩⬜ + 🟨⬜⬜⬜🟩 + + 🚀 정답을 입력하세요. : + ⬜⬜🟨🟩⬜ + 🟨⬜⬜⬜🟩 + 🟩🟩⬜🟩🟩 + ⬜⬜🟨🟩⬜ + 🟨⬜⬜⬜🟩 + 🟩🟩⬜🟩🟩 + + 👻 실패하였습니다. 오늘의 단어는 [ spill ] 입니다. +""" + + private val blankConsolePrint = + """ +🚀 정답을 입력하세요. : +🥲 다시 시도하세요! : 단어는 공백만 입력할 수 없습니다. + +🚀 정답을 입력하세요. : +🟩🟩🟩🟩🟩 + +🎉 성공입니다. 1 / 6 +""" + + private val invalidLengthConsolePrint = + """ +🚀 정답을 입력하세요. : +🥲 다시 시도하세요! : 단어의 길이는 5자 입니다. + +🚀 정답을 입력하세요. : +🟩🟩🟩🟩🟩 + +🎉 성공입니다. 1 / 6 +""" + + private val invalidWordConsolePrint = + """ +🚀 정답을 입력하세요. : +🥲 다시 시도하세요! : Wordle Game에서 유효한 단어가 아닙니다. + +🚀 정답을 입력하세요. : +🟩🟩🟩🟩🟩 + +🎉 성공입니다. 1 / 6 +""" +} diff --git a/src/test/resources/words.txt b/src/test/resources/words.txt index 3a06d50..2015d7d 100644 --- a/src/test/resources/words.txt +++ b/src/test/resources/words.txt @@ -1,4 +1,5 @@ hello +spill cigar rebut sissy @@ -251,7 +252,6 @@ thorn trove bloke vivid -spill chant choke rupee From 13225f6e5c9698ce231c3115f1deb0cfffaf430e Mon Sep 17 00:00:00 2001 From: boradol2 Date: Tue, 18 Jun 2024 08:40:17 +0900 Subject: [PATCH 32/40] =?UTF-8?q?refactor=20:=20PR=20=EC=98=AC=EB=A6=AC?= =?UTF-8?q?=EA=B8=B0=20=EC=A0=84=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/view/OutputView.kt | 2 +- src/main/kotlin/wordle/view/Tile.kt | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/wordle/view/OutputView.kt b/src/main/kotlin/wordle/view/OutputView.kt index 4f720df..3ccdd4c 100644 --- a/src/main/kotlin/wordle/view/OutputView.kt +++ b/src/main/kotlin/wordle/view/OutputView.kt @@ -5,7 +5,7 @@ import wordle.domain.Word import wordle.domain.WordResults fun printStartingGameMessage() { - println("🎮 WORDLE을 6번 만에 맞춰 보세요.\n📌 시도의 결과는 타일의 색 변화로 나타납니다.🥳\n") + println("🎮 WORDLE을 ${MAX_TRY_COUNT}번 만에 맞춰 보세요.\n📌 시도의 결과는 타일의 색 변화로 나타납니다.🥳\n") } fun printResult(results: WordResults) { diff --git a/src/main/kotlin/wordle/view/Tile.kt b/src/main/kotlin/wordle/view/Tile.kt index aecc847..3b25a34 100644 --- a/src/main/kotlin/wordle/view/Tile.kt +++ b/src/main/kotlin/wordle/view/Tile.kt @@ -9,8 +9,6 @@ enum class Tile(val matchType: LetterMatch, val color: String) { ; companion object { - fun of(matchType: LetterMatch): Tile { - return entries.find { tile -> tile.matchType == matchType } ?: GREY - } + fun of(matchType: LetterMatch): Tile = entries.find { tile -> tile.matchType == matchType } ?: GREY } } From b2c51db52430b874773900953c4da39389425b95 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Tue, 18 Jun 2024 08:40:39 +0900 Subject: [PATCH 33/40] =?UTF-8?q?docs(README.md):=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EB=9E=98=EB=B0=8D=20=EC=9A=94=EA=B5=AC=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=B2=B4=ED=81=AC=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 651fee5..c4a91c0 100644 --- a/README.md +++ b/README.md @@ -31,21 +31,21 @@ ### 구현 전 확인 사항 - [x] 프로그램 실행의 시작점은 `Application`의 `main()`이다. -- [ ] `build.gradle.kts` 파일은 변경할 수 없으며, **제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다.** +- [x] `build.gradle.kts` 파일은 변경할 수 없으며, **제공된 라이브러리 이외의 외부 라이브러리는 사용하지 않는다.** - `Unknown Kotlin JVM target: 21` 문제로 `kotlin 1.9.23`으로 올림 - [문제해결](https://github.com/gradle/gradle/issues/25574#issuecomment-1761314551) -- [ ] 프로그램 종료 시 `System.exit()` 또는 `exitProcess()`를 호출하지 않는다. -- [ ] 프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다. +- [x] 프로그램 종료 시 `System.exit()` 또는 `exitProcess()`를 호출하지 않는다. +- [x] 프로그래밍 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 등의 이름을 바꾸거나 이동하지 않는다. ### 구현 중 필요 사항 -- [ ] 코틀린 코드 컨벤션[(Kotlin Coding conventions)](https://kotlinlang.org/docs/coding-conventions.html)을 지키면서 프로그래밍한다. +- [x] 코틀린 코드 컨벤션[(Kotlin Coding conventions)](https://kotlinlang.org/docs/coding-conventions.html)을 지키면서 프로그래밍한다. - Commit 전에 `ktlint`를 습관적으로 확인한다. - ```./gradlew ktlintCheck``` - ```./gradlew addKtlintCheckGitPreCommitHook``` -- [ ] indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다. -- [ ] 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라. -- [ ] 함수(또는 메서드)의 길이가 15라인을 넘어가지 않도록 구현한다. -- [ ] 도메인 로직에 단위 테스트를 구현해야 한다. +- [x] indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다. +- [x] 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라. +- [x] 함수(또는 메서드)의 길이가 15라인을 넘어가지 않도록 구현한다. +- [x] 도메인 로직에 단위 테스트를 구현해야 한다. - `JUnit 5`와 `AssertJ`를 이용하여 정리한 기능 목록이 정상적으로 작동하는지 테스트 코드로 확인한다. - [JUnit 5 User Guide](https://junit.org/junit5/docs/current/user-guide) - [AssertJ User Guide](https://assertj.github.io/doc) @@ -56,7 +56,7 @@ - 힌트: MVC 패턴 기반으로 구현한 후, View와 Controller를 제외한 Model에 대한 단위 테스트 추가에 집중한다. ### 제출 전 확인 사항 -- [ ] 위의 프로그래밍 요구 사항을 준수했는지 확인한다. +- [x] 위의 프로그래밍 요구 사항을 준수했는지 확인한다. - 체크박스를 모두 [x]로 만드는 것을 목표로 한다. --- From 4a4381f94e75ce18cf086ac220bab304ab9b9305 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Wed, 19 Jun 2024 04:55:56 +0900 Subject: [PATCH 34/40] =?UTF-8?q?refactor(Letter):=20import=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/domain/Letter.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/wordle/domain/Letter.kt b/src/main/kotlin/wordle/domain/Letter.kt index a808d64..2cc7edf 100644 --- a/src/main/kotlin/wordle/domain/Letter.kt +++ b/src/main/kotlin/wordle/domain/Letter.kt @@ -1,10 +1,10 @@ package wordle.domain -import wordle.exception.WordleExceptionCode +import wordle.exception.WordleExceptionCode.LETTER_INVALID_CHARACTER_TYPE data class Letter(private val value: Char) { init { - check(isAlphabetOrMatchMarker()) { WordleExceptionCode.LETTER_INVALID_CHARACTER_TYPE.message } + check(isAlphabetOrMatchMarker()) { LETTER_INVALID_CHARACTER_TYPE.message } } fun changeMatchMarker(): Letter = MATCH_MARKER_LETTER From 8f462f71fe841bc73ae706a6262f0fcd57e767fa Mon Sep 17 00:00:00 2001 From: boradol2 Date: Wed, 19 Jun 2024 04:56:07 +0900 Subject: [PATCH 35/40] =?UTF-8?q?refactor(WordleGame):=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=ED=8C=8C=EB=9D=BC=EB=AF=B8?= =?UTF-8?q?=ED=84=B0=20=EC=A7=80=EC=9A=B0=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/application/WordleGame.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/wordle/application/WordleGame.kt b/src/main/kotlin/wordle/application/WordleGame.kt index 0413d6c..50c842e 100644 --- a/src/main/kotlin/wordle/application/WordleGame.kt +++ b/src/main/kotlin/wordle/application/WordleGame.kt @@ -27,11 +27,11 @@ class WordleGame(gameStartDate: LocalDate) { printRetry(e.message) } } - gameResult(todayWord) + printGameResult() } - private fun gameResult(todayWord: Word) { - if (results.isSuccessGame()) { + private fun printGameResult() { + if (results.isSuccessfulGame()) { printSuccess(results.attemptCount) return } From e8796943327c96de4b8cb67fb94cfc0dabef52a7 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Wed, 19 Jun 2024 04:57:33 +0900 Subject: [PATCH 36/40] =?UTF-8?q?refactor(WordComparator):=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C=ED=95=98=EB=8A=94=20=EB=AC=B8=EC=9E=90=EC=99=80=20?= =?UTF-8?q?=EB=8B=B5=EC=95=88=EB=AC=B8=EC=9E=90=EB=A5=BC=20=EA=B5=AC?= =?UTF-8?q?=EB=B6=84=ED=95=98=EB=8A=94=20=EC=9D=B4=EB=A6=84=20=EC=A7=93?= =?UTF-8?q?=EA=B8=B0,=20=ED=95=A8=EC=88=98=20=EC=9D=B4=EB=A6=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 --- .../kotlin/wordle/domain/WordComparator.kt | 38 +++++++++---------- src/main/kotlin/wordle/domain/WordResult.kt | 2 +- .../kotlin/wordle/domain/WordResultTest.kt | 4 +- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/main/kotlin/wordle/domain/WordComparator.kt b/src/main/kotlin/wordle/domain/WordComparator.kt index 3b89970..16e97d0 100644 --- a/src/main/kotlin/wordle/domain/WordComparator.kt +++ b/src/main/kotlin/wordle/domain/WordComparator.kt @@ -1,61 +1,57 @@ package wordle.domain class WordComparator( - private val letters: MutableList, + private val markerLetters: MutableList, private val wordResult: WordResult = WordResult(), ) { fun matchCorrect(answerWord: Word): WordComparator = apply { - letters.forEachIndexed { index, _ -> - changeCorrectMatch(index, answerWord[index]) - } + markerLetters.forEachIndexed { index, _ -> changeCorrectMatch(index, answerWord[index]) } } fun matchPresent(answerWord: Word): WordComparator = apply { - letters.forEachIndexed { index, _ -> - changePresentMatch(index, answerWord[index]) - } + markerLetters.forEachIndexed { index, _ -> changePresentMatch(index, answerWord[index]) } } fun result(): WordResult = wordResult private fun changeCorrectMatch( index: Int, - letter: Letter, + answerLetter: Letter, ) { - if (isCorrectLetter(index, letter)) { + if (isCorrectLetter(index, answerLetter)) { wordResult.changeMatchType(index, LetterMatch.CORRECT) - changeLetter(index) + changeMarkerLetter(index) } } private fun changePresentMatch( index: Int, - letter: Letter, + answerLetter: Letter, ) { - if (isPresentLatter(index, letter)) { + if (isPresentLatter(index, answerLetter)) { wordResult.changeMatchType(index, LetterMatch.PRESENT) - changeLetterIndexOf(letter) + changeMarkerLetterIndexOf(answerLetter) } } private fun isCorrectLetter( index: Int, - letter: Letter, - ) = letters[index] == letter + answerLetter: Letter, + ) = markerLetters[index] == answerLetter private fun isPresentLatter( index: Int, - letter: Letter, - ) = !wordResult.isCorrectMatchIndex(index) && !isCorrectLetter(index, letter) && (letter in letters) + answerLetter: Letter, + ) = !(wordResult.isCorrectLetterMatch(index) || isCorrectLetter(index, answerLetter)) && (answerLetter in markerLetters) - private fun changeLetter(index: Int) { - letters[index] = letters[index].changeMatchMarker() + private fun changeMarkerLetter(index: Int) { + markerLetters[index] = markerLetters[index].changeMatchMarker() } - private fun changeLetterIndexOf(letter: Letter) { - changeLetter(letters.indexOf(letter)) + private fun changeMarkerLetterIndexOf(letter: Letter) { + changeMarkerLetter(markerLetters.indexOf(letter)) } } diff --git a/src/main/kotlin/wordle/domain/WordResult.kt b/src/main/kotlin/wordle/domain/WordResult.kt index eeae9f3..239d086 100644 --- a/src/main/kotlin/wordle/domain/WordResult.kt +++ b/src/main/kotlin/wordle/domain/WordResult.kt @@ -14,7 +14,7 @@ data class WordResult(private val result: MutableList = MutableList result[index] = matchType } - fun isCorrectMatchIndex(index: Int): Boolean = result[index] == LetterMatch.CORRECT + fun isCorrectLetterMatch(index: Int): Boolean = result[index] == LetterMatch.CORRECT fun isSuccessGame() = result.all { matchType -> matchType == LetterMatch.CORRECT } diff --git a/src/test/kotlin/wordle/domain/WordResultTest.kt b/src/test/kotlin/wordle/domain/WordResultTest.kt index ffa388c..2be2609 100644 --- a/src/test/kotlin/wordle/domain/WordResultTest.kt +++ b/src/test/kotlin/wordle/domain/WordResultTest.kt @@ -39,8 +39,8 @@ class WordResultTest { wordResult.changeMatchType(1, LetterMatch.CORRECT) assertAll( - { assertThat(wordResult.isCorrectMatchIndex(0)).isFalse() }, - { assertThat(wordResult.isCorrectMatchIndex(1)).isTrue() }, + { assertThat(wordResult.isCorrectLetterMatch(0)).isFalse() }, + { assertThat(wordResult.isCorrectLetterMatch(1)).isTrue() }, ) } From 8a0dcb97da07af4de739b27778eb5b03a2623439 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Wed, 19 Jun 2024 05:00:04 +0900 Subject: [PATCH 37/40] =?UTF-8?q?test(WordResults):=20=EA=B2=8C=EC=9E=84?= =?UTF-8?q?=20=EA=B3=84=EC=86=8D=EC=97=AC=EB=B6=80=EC=99=80=20=EC=84=B1?= =?UTF-8?q?=EA=B3=B5=EC=97=AC=EB=B6=80=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=BD=94=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 추가로 가독성 좋은 이름으로 변경 2. TryCount 게임 횟수 범위 수정 --- src/main/kotlin/wordle/domain/TryCount.kt | 2 +- src/main/kotlin/wordle/domain/WordResult.kt | 2 +- src/main/kotlin/wordle/domain/WordResults.kt | 8 +-- .../kotlin/wordle/domain/WordResultsTest.kt | 72 +++++++++++++++++-- 4 files changed, 73 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/wordle/domain/TryCount.kt b/src/main/kotlin/wordle/domain/TryCount.kt index e82a62b..cd001db 100644 --- a/src/main/kotlin/wordle/domain/TryCount.kt +++ b/src/main/kotlin/wordle/domain/TryCount.kt @@ -5,7 +5,7 @@ import wordle.exception.WordleExceptionCode.TRY_COUNT_HAS_NOT_REMAINDER data class TryCount(private var count: Int = MAX_TRY_COUNT) { val attempts get() = MAX_TRY_COUNT - count - fun isRemainder(): Boolean = count > 0 + fun isRemainder(): Boolean = count in 1..MAX_TRY_COUNT fun minus() { check(isRemainder()) { TRY_COUNT_HAS_NOT_REMAINDER.message } diff --git a/src/main/kotlin/wordle/domain/WordResult.kt b/src/main/kotlin/wordle/domain/WordResult.kt index 239d086..f77a7d1 100644 --- a/src/main/kotlin/wordle/domain/WordResult.kt +++ b/src/main/kotlin/wordle/domain/WordResult.kt @@ -16,7 +16,7 @@ data class WordResult(private val result: MutableList = MutableList fun isCorrectLetterMatch(index: Int): Boolean = result[index] == LetterMatch.CORRECT - fun isSuccessGame() = result.all { matchType -> matchType == LetterMatch.CORRECT } + fun isSuccessfulWordResult() = result.all { matchType -> matchType == LetterMatch.CORRECT } fun matches(): List = result.toList() } diff --git a/src/main/kotlin/wordle/domain/WordResults.kt b/src/main/kotlin/wordle/domain/WordResults.kt index ee2dcd3..e98b7fb 100644 --- a/src/main/kotlin/wordle/domain/WordResults.kt +++ b/src/main/kotlin/wordle/domain/WordResults.kt @@ -4,16 +4,16 @@ class WordResults( private val results: MutableList = mutableListOf(), private val tryCount: TryCount = TryCount(), ) { - val attemptCount get() = tryCount.attempts + val attemptCount: Int get() = tryCount.attempts fun addResults(result: WordResult) { tryCount.minus() results.add(result) } - fun isContinuousGame(): Boolean = !isSuccessGame() && tryCount.isRemainder() + fun isContinuousGame(): Boolean = !isSuccessfulGame() && tryCount.isRemainder() - fun isSuccessGame(): Boolean = results.any(WordResult::isSuccessGame) + fun isSuccessfulGame(): Boolean = (tryCount.attempts != 0) && results.last().isSuccessfulWordResult() - fun wordResults(): List> = results.map(WordResult::matches) + fun wordResults(): List> = results.map { it.matches() } } diff --git a/src/test/kotlin/wordle/domain/WordResultsTest.kt b/src/test/kotlin/wordle/domain/WordResultsTest.kt index 812c952..bbeeacb 100644 --- a/src/test/kotlin/wordle/domain/WordResultsTest.kt +++ b/src/test/kotlin/wordle/domain/WordResultsTest.kt @@ -1,8 +1,11 @@ package wordle.domain import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.jupiter.api.Assertions.assertAll import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource class WordResultsTest { @Test @@ -27,19 +30,78 @@ class WordResultsTest { ) } + @ValueSource(ints = [1, 2, 3, 4, 5]) + @ParameterizedTest + fun `(성공) 단어 결과 목록을 추가할 때마다 게임을 계속 진행할 수 있는 상태인지 반환한다`(attemptCount: Int) { + val wordResults = WordResults() + + repeat(attemptCount) { + wordResults.addResults(WordResult()) + } + + assertThat(wordResults.isContinuousGame()).isTrue() + } + + @Test + fun `(성공) 단어 결과 목록이 최대로 추가되었을 때 게임을 계속할 수 없는 상태가 된다`() { + val wordResults = WordResults() + + repeat(MAX_TRY_COUNT) { + wordResults.addResults(WordResult()) + } + + assertThat(wordResults.isContinuousGame()).isFalse() + } + + @Test + fun `(예외) 단어 결과 목록을 더이상 추가할 수 없으면 예외가 발생한다`() { + val wordResults = WordResults() + + assertThatThrownBy { + repeat(MAX_TRY_COUNT + 1) { + wordResults.addResults(WordResult()) + } + }.isInstanceOf(IllegalStateException::class.java) + .hasMessage("시행 횟수는 0보다 작을 수 없습니다.") + } + @Test - fun `(성공) 단어 결과 목록 상태가 게임을 계속 진행할 수 있는 상태인지 반환한다`() { + fun `(성공) 단어 결과 목록 마지막에 모두 '완전 일치 상태'가 있어야 단어 맞추기 게임에 성공한다`() { val wordResults = WordResults() wordResults.addResults(WordResult()) + wordResults.addResults(oneAbsentWordResult) + wordResults.addResults(onePresentWordResult) + wordResults.addResults(MutableList(WORD_LENGTH) { LetterMatch.CORRECT }.toWordResult()) - assertThat(wordResults.isContinuousGame()).isTrue() + assertThat(wordResults.isSuccessfulGame()).isTrue() } @Test - fun `(성공) 단어 맞추기에 성공한 경우 true 를 반환한다`() { + fun `(성공) 단어 결과가 모두 '완전 일치 상태'가 아니면 단어 맞추기 게임에 성공여부가 False이다`() { val wordResults = WordResults() - wordResults.addResults(WordResult(MutableList(WORD_LENGTH) { LetterMatch.CORRECT })) + wordResults.addResults(oneAbsentWordResult) + wordResults.addResults(oneAbsentWordResult) + wordResults.addResults(oneAbsentWordResult) + wordResults.addResults(onePresentWordResult) - assertThat(wordResults.isSuccessGame()).isTrue() + assertThat(wordResults.isSuccessfulGame()).isFalse() } + + private val oneAbsentWordResult = + mutableListOf( + LetterMatch.ABSENT, + LetterMatch.CORRECT, + LetterMatch.CORRECT, + LetterMatch.CORRECT, + LetterMatch.CORRECT, + ).toWordResult() + + private val onePresentWordResult = + mutableListOf( + LetterMatch.PRESENT, + LetterMatch.CORRECT, + LetterMatch.CORRECT, + LetterMatch.CORRECT, + LetterMatch.CORRECT, + ).toWordResult() } From c15c88819922c0b8b636b06c6364480afed92220 Mon Sep 17 00:00:00 2001 From: boradol2 Date: Wed, 19 Jun 2024 05:20:05 +0900 Subject: [PATCH 38/40] =?UTF-8?q?refactor(OutputView):=20=ED=95=98?= =?UTF-8?q?=EB=93=9C=EC=BD=94=EB=94=A9=EB=90=9C=20=EB=B6=80=EB=B6=84=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=EB=A1=9C=20=EC=B6=94=EC=B6=9C=20=EB=B0=8F=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=EB=AA=85=20=EB=93=B1=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/application/WordleGame.kt | 4 ++-- src/main/kotlin/wordle/view/OutputView.kt | 14 ++++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/wordle/application/WordleGame.kt b/src/main/kotlin/wordle/application/WordleGame.kt index 50c842e..51cf861 100644 --- a/src/main/kotlin/wordle/application/WordleGame.kt +++ b/src/main/kotlin/wordle/application/WordleGame.kt @@ -6,9 +6,9 @@ import wordle.domain.WordResults import wordle.domain.WordleGameLogic import wordle.view.inputAnswerWord import wordle.view.printFail -import wordle.view.printResult import wordle.view.printRetry import wordle.view.printSuccess +import wordle.view.printWordResults import java.time.LocalDate class WordleGame(gameStartDate: LocalDate) { @@ -22,7 +22,7 @@ class WordleGame(gameStartDate: LocalDate) { val answerWord = Word(inputAnswerWord()) val result = wordleGameLogic.compare(answerWord) results.addResults(result) - printResult(results) + printWordResults(results) } catch (e: IllegalStateException) { printRetry(e.message) } diff --git a/src/main/kotlin/wordle/view/OutputView.kt b/src/main/kotlin/wordle/view/OutputView.kt index 3ccdd4c..fcdf2a4 100644 --- a/src/main/kotlin/wordle/view/OutputView.kt +++ b/src/main/kotlin/wordle/view/OutputView.kt @@ -1,19 +1,23 @@ package wordle.view +import wordle.domain.LetterMatch import wordle.domain.MAX_TRY_COUNT import wordle.domain.Word import wordle.domain.WordResults +private const val ENTER = "\n" +private const val SPACE = "" + fun printStartingGameMessage() { - println("🎮 WORDLE을 ${MAX_TRY_COUNT}번 만에 맞춰 보세요.\n📌 시도의 결과는 타일의 색 변화로 나타납니다.🥳\n") + println("🎮 WORDLE을 ${MAX_TRY_COUNT}번 만에 맞춰 보세요.$ENTER📌 시도의 결과는 타일의 색 변화로 나타납니다.🥳$ENTER") } -fun printResult(results: WordResults) { - println(results.wordResults().joinToString("\n") { it.joinToString("") { Tile.of(it).color } } + "\n") +fun printWordResults(results: WordResults) { + println(results.wordResults().joinToString(ENTER) { printTiles(it) } + ENTER) } fun printRetry(message: String?) { - println("🥲 다시 시도하세요! : ${message ?: ""}\n") + println("🥲 다시 시도하세요! : ${message ?: SPACE}$ENTER") } fun printSuccess(attemptCount: Int) { @@ -23,3 +27,5 @@ fun printSuccess(attemptCount: Int) { fun printFail(todayWord: Word) { println("👻 실패하였습니다. 오늘의 단어는 [ ${todayWord.letters()} ] 입니다.") } + +private fun printTiles(it: List) = it.joinToString(SPACE) { Tile.of(it).color } From 2aba7931320c03da3ba2097e761c4753c2f3b9df Mon Sep 17 00:00:00 2001 From: boradol2 Date: Wed, 19 Jun 2024 05:28:48 +0900 Subject: [PATCH 39/40] =?UTF-8?q?refactor(WordleGameTest):=20=EC=9D=B8?= =?UTF-8?q?=EB=8D=B4=ED=8A=B8=EC=99=80=20=EB=B3=80=EC=88=98=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wordle/application/WordleGameTest.kt | 166 +++++++++--------- 1 file changed, 84 insertions(+), 82 deletions(-) diff --git a/src/test/kotlin/wordle/application/WordleGameTest.kt b/src/test/kotlin/wordle/application/WordleGameTest.kt index 9c522c6..4435003 100644 --- a/src/test/kotlin/wordle/application/WordleGameTest.kt +++ b/src/test/kotlin/wordle/application/WordleGameTest.kt @@ -12,7 +12,7 @@ private val GAME_START_DATE = LocalDate.of(2021, 6, 20) class WordleGameTest { @Test fun `6번 이내 오늘의 단어를 맞추면 성공 안내 문구를 출력한다`() { - val inputAnswerWords = "$answerWordHello$br$answerWordLabel$br$answerWordSpell$br$wordSpill" + val inputAnswerWords = successConsoleInput val (printStream, outputStream) = printStreamByteArrayOutputStreamPair(inputAnswerWords) WordleGame(GAME_START_DATE).play() @@ -23,8 +23,7 @@ class WordleGameTest { @Test fun `6번 이내 오늘의 단어를 맞추지 못하면 실패 안내 문구와 오늘의 단어를 출력한다`() { - val inputAnswerWords = - "$answerWordHello$br$answerWordLabel$br$answerWordSpell$br$answerWordHello$br$answerWordLabel$br$answerWordSpell" + val inputAnswerWords = failConsoleInput val (printStream, outputStream) = printStreamByteArrayOutputStreamPair(inputAnswerWords) WordleGame(GAME_START_DATE).play() @@ -90,98 +89,101 @@ class WordleGameTest { private val blank = "" private val invalidLengthWord = "word" private val invalidWord = "abcde" + private val successConsoleInput = "$answerWordHello$br$answerWordLabel$br$answerWordSpell$br$wordSpill" + private val failConsoleInput = + "$answerWordHello$br$answerWordLabel$br$answerWordSpell$br$answerWordHello$br$answerWordLabel$br$answerWordSpell" private val successConsolePrint = """ -🚀 정답을 입력하세요. : -⬜⬜🟨🟩⬜ - -🚀 정답을 입력하세요. : -⬜⬜🟨🟩⬜ -🟨⬜⬜⬜🟩 - -🚀 정답을 입력하세요. : -⬜⬜🟨🟩⬜ -🟨⬜⬜⬜🟩 -🟩🟩⬜🟩🟩 - -🚀 정답을 입력하세요. : -⬜⬜🟨🟩⬜ -🟨⬜⬜⬜🟩 -🟩🟩⬜🟩🟩 -🟩🟩🟩🟩🟩 - -🎉 성공입니다. 4 / 6 -""" + 🚀 정답을 입력하세요. : + ⬜⬜🟨🟩⬜ + + 🚀 정답을 입력하세요. : + ⬜⬜🟨🟩⬜ + 🟨⬜⬜⬜🟩 + + 🚀 정답을 입력하세요. : + ⬜⬜🟨🟩⬜ + 🟨⬜⬜⬜🟩 + 🟩🟩⬜🟩🟩 + + 🚀 정답을 입력하세요. : + ⬜⬜🟨🟩⬜ + 🟨⬜⬜⬜🟩 + 🟩🟩⬜🟩🟩 + 🟩🟩🟩🟩🟩 + + 🎉 성공입니다. 4 / 6 + """ private val failConsolePrint = """ - 🚀 정답을 입력하세요. : - ⬜⬜🟨🟩⬜ - - 🚀 정답을 입력하세요. : - ⬜⬜🟨🟩⬜ - 🟨⬜⬜⬜🟩 - - 🚀 정답을 입력하세요. : - ⬜⬜🟨🟩⬜ - 🟨⬜⬜⬜🟩 - 🟩🟩⬜🟩🟩 - - 🚀 정답을 입력하세요. : - ⬜⬜🟨🟩⬜ - 🟨⬜⬜⬜🟩 - 🟩🟩⬜🟩🟩 - ⬜⬜🟨🟩⬜ - - 🚀 정답을 입력하세요. : - ⬜⬜🟨🟩⬜ - 🟨⬜⬜⬜🟩 - 🟩🟩⬜🟩🟩 - ⬜⬜🟨🟩⬜ - 🟨⬜⬜⬜🟩 - - 🚀 정답을 입력하세요. : - ⬜⬜🟨🟩⬜ - 🟨⬜⬜⬜🟩 - 🟩🟩⬜🟩🟩 - ⬜⬜🟨🟩⬜ - 🟨⬜⬜⬜🟩 - 🟩🟩⬜🟩🟩 - - 👻 실패하였습니다. 오늘의 단어는 [ spill ] 입니다. -""" + 🚀 정답을 입력하세요. : + ⬜⬜🟨🟩⬜ + + 🚀 정답을 입력하세요. : + ⬜⬜🟨🟩⬜ + 🟨⬜⬜⬜🟩 + + 🚀 정답을 입력하세요. : + ⬜⬜🟨🟩⬜ + 🟨⬜⬜⬜🟩 + 🟩🟩⬜🟩🟩 + + 🚀 정답을 입력하세요. : + ⬜⬜🟨🟩⬜ + 🟨⬜⬜⬜🟩 + 🟩🟩⬜🟩🟩 + ⬜⬜🟨🟩⬜ + + 🚀 정답을 입력하세요. : + ⬜⬜🟨🟩⬜ + 🟨⬜⬜⬜🟩 + 🟩🟩⬜🟩🟩 + ⬜⬜🟨🟩⬜ + 🟨⬜⬜⬜🟩 + + 🚀 정답을 입력하세요. : + ⬜⬜🟨🟩⬜ + 🟨⬜⬜⬜🟩 + 🟩🟩⬜🟩🟩 + ⬜⬜🟨🟩⬜ + 🟨⬜⬜⬜🟩 + 🟩🟩⬜🟩🟩 + + 👻 실패하였습니다. 오늘의 단어는 [ spill ] 입니다. + """ private val blankConsolePrint = """ -🚀 정답을 입력하세요. : -🥲 다시 시도하세요! : 단어는 공백만 입력할 수 없습니다. - -🚀 정답을 입력하세요. : -🟩🟩🟩🟩🟩 - -🎉 성공입니다. 1 / 6 -""" + 🚀 정답을 입력하세요. : + 🥲 다시 시도하세요! : 단어는 공백만 입력할 수 없습니다. + + 🚀 정답을 입력하세요. : + 🟩🟩🟩🟩🟩 + + 🎉 성공입니다. 1 / 6 + """ private val invalidLengthConsolePrint = """ -🚀 정답을 입력하세요. : -🥲 다시 시도하세요! : 단어의 길이는 5자 입니다. - -🚀 정답을 입력하세요. : -🟩🟩🟩🟩🟩 - -🎉 성공입니다. 1 / 6 -""" + 🚀 정답을 입력하세요. : + 🥲 다시 시도하세요! : 단어의 길이는 5자 입니다. + + 🚀 정답을 입력하세요. : + 🟩🟩🟩🟩🟩 + + 🎉 성공입니다. 1 / 6 + """ private val invalidWordConsolePrint = """ -🚀 정답을 입력하세요. : -🥲 다시 시도하세요! : Wordle Game에서 유효한 단어가 아닙니다. - -🚀 정답을 입력하세요. : -🟩🟩🟩🟩🟩 - -🎉 성공입니다. 1 / 6 -""" + 🚀 정답을 입력하세요. : + 🥲 다시 시도하세요! : Wordle Game에서 유효한 단어가 아닙니다. + + 🚀 정답을 입력하세요. : + 🟩🟩🟩🟩🟩 + + 🎉 성공입니다. 1 / 6 + """ } From 1c428d7ee0d1dfafe1e60e628460d71c671a86fb Mon Sep 17 00:00:00 2001 From: boradol2 Date: Wed, 26 Jun 2024 16:50:25 +0900 Subject: [PATCH 40/40] =?UTF-8?q?refactor(Letter):=20private=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/wordle/domain/Letter.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/kotlin/wordle/domain/Letter.kt b/src/main/kotlin/wordle/domain/Letter.kt index 2cc7edf..102e79f 100644 --- a/src/main/kotlin/wordle/domain/Letter.kt +++ b/src/main/kotlin/wordle/domain/Letter.kt @@ -4,15 +4,13 @@ import wordle.exception.WordleExceptionCode.LETTER_INVALID_CHARACTER_TYPE data class Letter(private val value: Char) { init { - check(isAlphabetOrMatchMarker()) { LETTER_INVALID_CHARACTER_TYPE.message } + check(isAlphabet() || isMatchMarker()) { LETTER_INVALID_CHARACTER_TYPE.message } } fun changeMatchMarker(): Letter = MATCH_MARKER_LETTER fun value(): String = value.toString() - private fun isAlphabetOrMatchMarker(): Boolean = isAlphabet() || isMatchMarker() - private fun isAlphabet(): Boolean = value in ALPHABET private fun isMatchMarker(): Boolean = value == MATCH_MARKER