diff --git a/README.md b/README.md index d3179421..c964a220 100644 --- a/README.md +++ b/README.md @@ -1,89 +1,31 @@ # λ―Έμ…˜ - μ›Œλ“€ - -## πŸ” 진행 방식 - -- λ―Έμ…˜μ€ **과제 진행 μš”κ΅¬ 사항**, **κΈ°λŠ₯ μš”κ΅¬ 사항**, **ν”„λ‘œκ·Έλž˜λ° μš”κ΅¬ 사항** μ„Έ κ°€μ§€λ‘œ κ΅¬μ„±λ˜μ–΄ μžˆλ‹€. -- μ„Έ 개의 μš”κ΅¬ 사항을 λ§Œμ‘±ν•˜κΈ° μœ„ν•΄ λ…Έλ ₯ν•œλ‹€. 특히 κΈ°λŠ₯을 κ΅¬ν˜„ν•˜κΈ° 전에 κΈ°λŠ₯ λͺ©λ‘μ„ λ§Œλ“€κ³ , κΈ°λŠ₯ λ‹¨μœ„λ‘œ 컀밋 ν•˜λŠ” λ°©μ‹μœΌλ‘œ μ§„ν–‰ν•œλ‹€. -- **κΈ°λŠ₯ μš”κ΅¬ 사항에 κΈ°μž¬λ˜μ§€ μ•Šμ€ λ‚΄μš©μ€ 슀슀둜 νŒλ‹¨ν•˜μ—¬ κ΅¬ν˜„ν•œλ‹€.** - ---- - -## πŸš€ κΈ°λŠ₯ μš”κ΅¬ 사항 - -선풍적인 인기λ₯Ό λŒμ—ˆλ˜ μ˜μ–΄ 단어 λ§žμΆ”κΈ° κ²Œμž„μ΄λ‹€. - -- 6x5 격자λ₯Ό ν†΅ν•΄μ„œ 5κΈ€μž 단어λ₯Ό 6번 λ§Œμ— μΆ”μΈ‘ν•œλ‹€. -- ν”Œλ ˆμ΄μ–΄κ°€ λ‹΅μ•ˆμ„ μ œμΆœν•˜λ©΄ ν”„λ‘œκ·Έλž¨μ΄ μ •λ‹΅κ³Ό 제좜된 λ‹¨μ–΄μ˜ 각 μ•ŒνŒŒλ²³ μ’…λ₯˜μ™€ μœ„μΉ˜λ₯Ό 비ꡐ해 νŒλ³„ν•œλ‹€. -- νŒλ³„ κ²°κ³ΌλŠ” ν°μƒ‰μ˜ 타일이 μ„Έ 가지 색(μ΄ˆλ‘μƒ‰/λ…Έλž€μƒ‰/νšŒμƒ‰) 쀑 ν•˜λ‚˜λ‘œ λ°”λ€Œλ©΄μ„œ ν‘œν˜„λœλ‹€. - - λ§žλŠ” κΈ€μžλŠ” μ΄ˆλ‘μƒ‰, μœ„μΉ˜κ°€ 틀리면 λ…Έλž€μƒ‰, μ—†μœΌλ©΄ νšŒμƒ‰ - - 두 개의 λ™μΌν•œ 문자λ₯Ό μž…λ ₯ν•˜κ³  그쀑 ν•˜λ‚˜κ°€ νšŒμƒ‰μœΌλ‘œ ν‘œμ‹œλ˜λ©΄ ν•΄λ‹Ή 문자 쀑 ν•˜λ‚˜λ§Œ μ΅œμ’… 단어에 λ‚˜νƒ€λ‚œλ‹€. -- μ •λ‹΅κ³Ό λ‹΅μ•ˆμ€ `words.txt`에 μ‘΄μž¬ν•˜λŠ” 단어여야 ν•œλ‹€. -- 정닡은 맀일 λ°”λ€Œλ©° ((ν˜„μž¬ λ‚ μ§œ - 2021λ…„ 6μ›” 19일) % λ°°μ—΄μ˜ 크기) 번째의 단어이닀. - -### μž…μΆœλ ₯ μš”κ΅¬ 사항 - -#### μ‹€ν–‰ κ²°κ³Ό μ˜ˆμ‹œ - -``` -WORDLE을 6번 λ§Œμ— 맞좰 λ³΄μ„Έμš”. -μ‹œλ„μ˜ κ²°κ³ΌλŠ” νƒ€μΌμ˜ 색 λ³€ν™”λ‘œ λ‚˜νƒ€λ‚©λ‹ˆλ‹€. -정닡을 μž…λ ₯ν•΄ μ£Όμ„Έμš”. -hello - -⬜⬜🟨🟩⬜ - -정닡을 μž…λ ₯ν•΄ μ£Όμ„Έμš”. -label - -⬜⬜🟨🟩⬜ -🟨⬜⬜⬜🟩 - -정닡을 μž…λ ₯ν•΄ μ£Όμ„Έμš”. -spell - -⬜⬜🟨🟩⬜ -🟨⬜⬜⬜🟩 -🟩🟩⬜🟩🟩 - -정닡을 μž…λ ₯ν•΄ μ£Όμ„Έμš”. -spill - -4/6 - -⬜⬜🟨🟩⬜ -🟨⬜⬜⬜🟩 -🟩🟩⬜🟩🟩 -🟩🟩🟩🟩🟩 -``` - ---- - -## 🎯 ν”„λ‘œκ·Έλž˜λ° μš”κ΅¬ 사항 - -- JDK 21 λ²„μ „μ—μ„œ μ‹€ν–‰ κ°€λŠ₯ν•΄μ•Ό ν•œλ‹€. -- ν”„λ‘œκ·Έλž¨ μ‹€ν–‰μ˜ μ‹œμž‘μ μ€ `Application`의 `main()`이닀. -- `build.gradle` νŒŒμΌμ€ λ³€κ²½ν•  수 μ—†μœΌλ©°, **제곡된 라이브러리 μ΄μ™Έμ˜ μ™ΈλΆ€ λΌμ΄λΈŒλŸ¬λ¦¬λŠ” μ‚¬μš©ν•˜μ§€ μ•ŠλŠ”λ‹€.** -- ν”„λ‘œκ·Έλž¨ μ’…λ£Œ μ‹œ `System.exit()`λ₯Ό ν˜ΈμΆœν•˜μ§€ μ•ŠλŠ”λ‹€. -- ν”„λ‘œκ·Έλž˜λ° μš”κ΅¬ μ‚¬ν•­μ—μ„œ 달리 λͺ…μ‹œν•˜μ§€ μ•ŠλŠ” ν•œ 파일, νŒ¨ν‚€μ§€ λ“±μ˜ 이름을 λ°”κΎΈκ±°λ‚˜ μ΄λ™ν•˜μ§€ μ•ŠλŠ”λ‹€. -- μžλ°” μ½”λ“œ μ»¨λ²€μ…˜μ„ μ§€ν‚€λ©΄μ„œ ν”„λ‘œκ·Έλž˜λ°ν•œλ‹€. - - 기본적으둜 [Google Java Style Guide](https://google.github.io/styleguide/javaguide.html)λ₯Ό μ›μΉ™μœΌλ‘œ ν•œλ‹€. - - 단, λ“€μ—¬μ“°κΈ°λŠ” '2 spaces'κ°€ μ•„λ‹Œ '4 spaces'둜 ν•œλ‹€. -- indent(인덴트, λ“€μ—¬μ“°κΈ°) depthλ₯Ό 3이 λ„˜μ§€ μ•Šλ„λ‘ κ΅¬ν˜„ν•œλ‹€. 2κΉŒμ§€λ§Œ ν—ˆμš©ν•œλ‹€. - - 예λ₯Ό λ“€μ–΄ whileλ¬Έ μ•ˆμ— if문이 있으면 λ“€μ—¬μ“°κΈ°λŠ” 2이닀. - - 힌트: indent(인덴트, λ“€μ—¬μ“°κΈ°) depthλ₯Ό μ€„μ΄λŠ” 쒋은 방법은 ν•¨μˆ˜(λ˜λŠ” λ©”μ„œλ“œ)λ₯Ό λΆ„λ¦¬ν•˜λ©΄ λœλ‹€. -- 3ν•­ μ—°μ‚°μžλ₯Ό 쓰지 μ•ŠλŠ”λ‹€. -- ν•¨μˆ˜(λ˜λŠ” λ©”μ„œλ“œ)κ°€ ν•œ 가지 일만 ν•˜λ„λ‘ μ΅œλŒ€ν•œ μž‘κ²Œ λ§Œλ“€μ–΄λΌ. -- 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라인을 λ„˜μ–΄κ°€μ§€ μ•Šλ„λ‘ κ΅¬ν˜„ν•œλ‹€. - - ν•¨μˆ˜(λ˜λŠ” λ©”μ„œλ“œ)κ°€ ν•œ 가지 일만 잘 ν•˜λ„λ‘ κ΅¬ν˜„ν•œλ‹€. -- else μ˜ˆμ•½μ–΄λ₯Ό 쓰지 μ•ŠλŠ”λ‹€. - - elseλ₯Ό 쓰지 말라고 ν•˜λ‹ˆ switch/case둜 κ΅¬ν˜„ν•˜λŠ” κ²½μš°κ°€ μžˆλŠ”λ° switch/case도 ν—ˆμš©ν•˜μ§€ μ•ŠλŠ”λ‹€. - - 힌트: if μ‘°κ±΄μ ˆμ—μ„œ 값을 returnν•˜λŠ” λ°©μ‹μœΌλ‘œ κ΅¬ν˜„ν•˜λ©΄ elseλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šμ•„λ„ λœλ‹€. -- 도메인 λ‘œμ§μ— λ‹¨μœ„ ν…ŒμŠ€νŠΈλ₯Ό κ΅¬ν˜„ν•΄μ•Ό ν•œλ‹€. 단, UI(System.out, System.in, Scanner) λ‘œμ§μ€ μ œμ™Έν•œλ‹€. - - 핡심 λ‘œμ§μ„ κ΅¬ν˜„ν•˜λŠ” μ½”λ“œμ™€ UIλ₯Ό λ‹΄λ‹Ήν•˜λŠ” λ‘œμ§μ„ 뢄리해 κ΅¬ν˜„ν•œλ‹€. - - 힌트: MVC νŒ¨ν„΄ 기반으둜 κ΅¬ν˜„ν•œ ν›„, View와 Controllerλ₯Ό μ œμ™Έν•œ Model에 λŒ€ν•œ λ‹¨μœ„ ν…ŒμŠ€νŠΈ 좔가에 μ§‘μ€‘ν•œλ‹€. +## νŽ˜μ–΄ +- 치즈 +- kr + +## κΈ°λŠ₯ λͺ©λ‘ +### μž…μΆœλ ₯ +- [X] κ²Œμž„ μ‹œμž‘ 메세지λ₯Ό μ½˜μ†”μ— 좜λ ₯ν•œλ‹€. +- [X] ν”Œλ ˆμ΄μ–΄λŠ” ν•œ 번의 μž…λ ₯λ§ˆλ‹€ 5자의 μ˜λ¬Έμ„ μž…λ ₯ν•œλ‹€. +- [X] ν”Œλ ˆμ΄μ–΄κ°€ μž…λ ₯ν•œ 결과에 따라 λ¬Έμžλ³„λ‘œ 5개의 μƒ‰μƒμœΌλ‘œ ν‘œν˜„ν•œλ‹€. + - ⬜️ νšŒμƒ‰: 정닡에 μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” 문자 + - 🟨 λ…Έλž‘μƒ‰: μ •λ‹΅ 내에 μ‘΄μž¬ν•˜λŠ” λ¬Έμžμ΄λ‚˜, μœ„μΉ˜κ°€ 닀름 + - 🟩 μ΄ˆλ‘μƒ‰: 정닡에 ν•΄λ‹Ήν•˜λŠ” 문자 +- [X] ν‘œν˜„ν•œ 색상을 직전 결과에 μŠ€νƒ ν˜•μ‹μœΌλ‘œ μŒ“λŠ”λ‹€. +### κ²Œμž„ 진행 +- [X] ν”Œλ ˆμ΄μ–΄μ˜ ν„΄(μž…λ ₯) νšŸμˆ˜λŠ” 6회둜 μ œν•œν•œλ‹€. +- [X] 정닡을 μƒμ„±ν•œλ‹€. +- [X] λ‹΅μ•ˆκ³Ό 정닡이 같은지 λΉ„κ΅ν•œλ‹€. +- [X] 주어진 μž…λ ₯ 횟수 내에 정닡을 λ§žμΆ”λ©΄ κ²Œμž„μ„ μ’…λ£Œν•œλ‹€. +- [X] 정닡을 λ§žμΆ”μ§€ λͺ»ν•˜λ©΄ νšŸμˆ˜μ™€ μ½˜μ†” λ‚΄μš©μ„ μ΄ˆκΈ°ν™”ν•˜κ³  μž¬μ‹œμž‘ν•œλ‹€. +### 파일 +- [X] 정닡은 words.txt μ—μ„œ ((ν˜„μž¬ λ‚ μ§œ - 2021λ…„ 6μ›” 19일) % λ°°μ—΄μ˜ 크기) 번째의 단어λ₯Ό κ°€μ Έμ˜¨λ‹€. +- [X] ν”Œλ ˆμ΄μ–΄μ˜ λ‹΅μ•ˆμ΄ words.txt 에 μ‘΄μž¬ν•˜λŠ”μ§€ ν™•μΈν•œλ‹€. + +## ν…ŒμŠ€νŠΈ μ‹œλ‚˜λ¦¬μ˜€ +- [X] μ‚¬μš©μžμ˜ μž…λ ₯이 5자의 영문이 μ•„λ‹ˆλ©΄ μ˜ˆμ™Έκ°€ λ°œμƒν•œλ‹€. +- [X] 였늘의 μ •λ‹΅ 단어 μΈλ±μŠ€λŠ” ((ν˜„μž¬ λ‚ μ§œ - 2021λ…„ 6μ›” 19일) % λ°°μ—΄μ˜ 크기) 이닀. +- [X] μ‚¬μš©μžμ˜ μž…λ ₯이 words.txt 에 μ‘΄μž¬ν•˜μ§€ μ•ŠμœΌλ©΄ μ˜ˆμ™Έκ°€ λ°œμƒν•œλ‹€. +- [X] λ¬Έμžμ™€ μœ„μΉ˜κ°€ μΌμΉ˜ν•˜λ©΄ GREEN μƒνƒœλ₯Ό λ°˜ν™˜ν•œλ‹€. +- [X] λ¬ΈμžλŠ” μΌμΉ˜ν•˜μ§€λ§Œ μœ„μΉ˜κ°€ λ‹€λ₯΄λ©΄ YELLOW μƒνƒœλ₯Ό λ°˜ν™˜ν•œλ‹€. +- [X] λ¬Έμžκ°€ μΌμΉ˜ν•˜μ§€ μ•ŠμœΌλ©΄ GRAY μƒνƒœλ₯Ό λ°˜ν™˜ν•œλ‹€. diff --git a/src/main/java/kr/co/wordle/Application.java b/src/main/java/kr/co/wordle/Application.java new file mode 100644 index 00000000..340b2048 --- /dev/null +++ b/src/main/java/kr/co/wordle/Application.java @@ -0,0 +1,8 @@ +package kr.co.wordle; + +public class Application { + public static void main(String[] args) { + WordleGame wordleGame = new WordleGame(); + wordleGame.start(); + } +} diff --git a/src/main/java/kr/co/wordle/WordleGame.java b/src/main/java/kr/co/wordle/WordleGame.java new file mode 100644 index 00000000..11d79a87 --- /dev/null +++ b/src/main/java/kr/co/wordle/WordleGame.java @@ -0,0 +1,46 @@ +package kr.co.wordle; + +import kr.co.wordle.domain.Answer; +import kr.co.wordle.domain.Round; +import kr.co.wordle.view.Console; + +import static kr.co.wordle.config.WordleGameConfig.MAX_ROUND; + +public class WordleGame { + private final Console console; + private final Answer answer; + private final StringBuilder roundResults; + + private int currentRound; + + public WordleGame() { + this.console = new Console(); + this.answer = new Answer(); + this.roundResults = new StringBuilder(); + this.currentRound = 0; + } + + public void start() { + console.init(); + Round round = null; + while (isRoundInProgress(round, roundResults)) { + currentRound++; + String input = console.userInput(); + round = new Round(input); + roundResults.append(round.roundResult(answer)).append("\n"); + console.printRoundResult(roundResults); + } + } + + private boolean isRoundInProgress(Round round, StringBuilder roundResults) { + if (round == null) { + return true; + } + if (round.isFinished()) { + console.printRound(currentRound, MAX_ROUND); + console.printRoundResult(roundResults); + return false; + } + return currentRound <= MAX_ROUND; + } +} diff --git a/src/main/java/kr/co/wordle/config/WordleGameConfig.java b/src/main/java/kr/co/wordle/config/WordleGameConfig.java new file mode 100644 index 00000000..fce55fce --- /dev/null +++ b/src/main/java/kr/co/wordle/config/WordleGameConfig.java @@ -0,0 +1,10 @@ +package kr.co.wordle.config; + +public class WordleGameConfig { + public static final String WORDS_FILE_PATH = "src/main/resources/words.txt"; + public static final int WORD_LENGTH = 5; + public static final int MAX_ROUND = 6; + + private WordleGameConfig() { + } +} diff --git a/src/main/java/kr/co/wordle/domain/Answer.java b/src/main/java/kr/co/wordle/domain/Answer.java new file mode 100644 index 00000000..e7f2b9db --- /dev/null +++ b/src/main/java/kr/co/wordle/domain/Answer.java @@ -0,0 +1,35 @@ +package kr.co.wordle.domain; + +import kr.co.wordle.domain.provider.AnswerProvider; + +public class Answer { + + private final String value; + private final int[] countPerCharacter = new int[26]; + + public Answer() { + this.value = AnswerProvider.todayAnswer(); + countPerCharacter(); + } + + protected Answer(String value) { + this.value = value; + countPerCharacter(); + } + + private void countPerCharacter() { + char[] chars = value.toCharArray(); + for (char ch : chars) { + countPerCharacter[ch - 'a']++; + } + } + + public int[] getCountPerCharacter() { + return countPerCharacter; + } + + public char charAt(int index) { + return value.charAt(index); + } + +} diff --git a/src/main/java/kr/co/wordle/domain/Round.java b/src/main/java/kr/co/wordle/domain/Round.java new file mode 100644 index 00000000..7f6c1a32 --- /dev/null +++ b/src/main/java/kr/co/wordle/domain/Round.java @@ -0,0 +1,45 @@ +package kr.co.wordle.domain; + +import kr.co.wordle.support.InputValidator; +import java.util.Arrays; + +import static kr.co.wordle.config.WordleGameConfig.WORD_LENGTH; + +public class Round { + + private final String input; + private final RoundResult roundResult; + + public Round(String input) { + if (InputValidator.isNotValid(input)) { + throw new IllegalArgumentException(); + } + this.input = input; + this.roundResult = new RoundResult(); + } + + public String roundResult(Answer answer) { + char[] inputChars = input.toCharArray(); + int[] countPerCharacter = Arrays.copyOf(answer.getCountPerCharacter(), 26); + for (int i = 0; i< WORD_LENGTH; i++) { + Tile key = getTile(countPerCharacter, answer.charAt(i), inputChars[i]); + roundResult.update(key); + } + return roundResult.toString(); + } + + public Tile getTile(int[] counts, char source, char target) { + if(counts[target - 'a'] == 0) { + return Tile.GRAY; + } + if(source != target) { + return Tile.YELLOW; + } + counts[target - 'a']--; + return Tile.GREEN; + } + + public boolean isFinished() { + return roundResult.isAllGreen(); + } +} diff --git a/src/main/java/kr/co/wordle/domain/RoundResult.java b/src/main/java/kr/co/wordle/domain/RoundResult.java new file mode 100644 index 00000000..dc935fbb --- /dev/null +++ b/src/main/java/kr/co/wordle/domain/RoundResult.java @@ -0,0 +1,30 @@ +package kr.co.wordle.domain; + +import java.util.EnumMap; +import java.util.Map; + +import static kr.co.wordle.config.WordleGameConfig.WORD_LENGTH; + +public class RoundResult { + private final Map countPerTile; + private final StringBuilder result; + + public RoundResult() { + this.countPerTile = new EnumMap<>(Tile.class); + result = new StringBuilder(); + } + + public void update(Tile tile) { + countPerTile.compute(tile, (k, v) -> (v == null) ? 1 : v + 1); + result.append(tile); + } + + public boolean isAllGreen() { + return countPerTile.getOrDefault(Tile.GREEN, 0) == WORD_LENGTH; + } + + @Override + public String toString() { + return result.toString(); + } +} diff --git a/src/main/java/kr/co/wordle/domain/Tile.java b/src/main/java/kr/co/wordle/domain/Tile.java new file mode 100644 index 00000000..655a9bb1 --- /dev/null +++ b/src/main/java/kr/co/wordle/domain/Tile.java @@ -0,0 +1,18 @@ +package kr.co.wordle.domain; + +public enum Tile { + GREEN("🟩"), + YELLOW("🟨"), + GRAY("⬜️"); + + private final String text; + + Tile(String text) { + this.text = text; + } + + @Override + public String toString() { + return text; + } +} diff --git a/src/main/java/kr/co/wordle/domain/provider/AnswerProvider.java b/src/main/java/kr/co/wordle/domain/provider/AnswerProvider.java new file mode 100644 index 00000000..7c70088f --- /dev/null +++ b/src/main/java/kr/co/wordle/domain/provider/AnswerProvider.java @@ -0,0 +1,27 @@ +package kr.co.wordle.domain.provider; + +import kr.co.wordle.support.WordFileReader; + +import java.time.LocalDate; +import java.time.Period; +import java.util.List; + +public class AnswerProvider { + + private static final LocalDate REFERENCE_DATE = LocalDate.of(2021, 6, 19); + + private AnswerProvider() { + } + + public static String todayAnswer() { + List words = WordFileReader.readWordsInFile(); + int dayDiff = dayDiff(); + return words.get(dayDiff % words.size()); + } + + private static int dayDiff() { + LocalDate currentDate = LocalDate.now(); + Period period = Period.between(REFERENCE_DATE, currentDate); + return period.getDays(); + } +} diff --git a/src/main/java/kr/co/wordle/support/InputValidator.java b/src/main/java/kr/co/wordle/support/InputValidator.java new file mode 100644 index 00000000..a800a1e1 --- /dev/null +++ b/src/main/java/kr/co/wordle/support/InputValidator.java @@ -0,0 +1,34 @@ +package kr.co.wordle.support; + +import java.util.List; + +import static kr.co.wordle.config.WordleGameConfig.WORD_LENGTH; + +public class InputValidator { + + private static final List words = WordFileReader.readWordsInFile(); + + private InputValidator() { + } + + public static boolean isNotValid(String input) { + if (input == null) { + return true; + } + if (input.length() != WORD_LENGTH) { + return true; + } + if (isNotAllAlphabet(input)) { + return true; + } + return isNotInWords(input); + } + + private static boolean isNotAllAlphabet(String input) { + return input.chars().anyMatch(ch -> ch < 'a' || ch > 'z'); + } + + private static boolean isNotInWords(String input) { + return !words.contains(input); + } +} diff --git a/src/main/java/kr/co/wordle/support/WordFileReader.java b/src/main/java/kr/co/wordle/support/WordFileReader.java new file mode 100644 index 00000000..0e01ed6c --- /dev/null +++ b/src/main/java/kr/co/wordle/support/WordFileReader.java @@ -0,0 +1,23 @@ +package kr.co.wordle.support; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import static kr.co.wordle.config.WordleGameConfig.WORDS_FILE_PATH; + +public class WordFileReader { + + private WordFileReader() { + } + + public static List readWordsInFile() { + Path wordsFilePath = Path.of(WORDS_FILE_PATH); + try { + return Files.readAllLines(wordsFilePath); + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + } +} diff --git a/src/main/java/kr/co/wordle/view/Console.java b/src/main/java/kr/co/wordle/view/Console.java new file mode 100644 index 00000000..83219f80 --- /dev/null +++ b/src/main/java/kr/co/wordle/view/Console.java @@ -0,0 +1,32 @@ +package kr.co.wordle.view; + +import kr.co.wordle.support.InputValidator; +import java.util.Scanner; + +import static kr.co.wordle.config.WordleGameConfig.MAX_ROUND; + +public class Console { + + public void init() { + System.out.println("WORDLE을 " + MAX_ROUND + "번 λ§Œμ— 맞좰 λ³΄μ„Έμš”.\nμ‹œλ„μ˜ κ²°κ³ΌλŠ” νƒ€μΌμ˜ 색 λ³€ν™”λ‘œ λ‚˜νƒ€λ‚©λ‹ˆλ‹€.\n"); + } + + public String userInput() { + System.out.println("정닡을 μž…λ ₯ν•΄ μ£Όμ„Έμš”."); + Scanner sc = new Scanner(System.in); + String input = sc.nextLine(); + while (InputValidator.isNotValid(input)) { + System.out.println("λ‹€μ‹œ μž…λ ₯ν•΄μ£Όμ„Έμš”."); + input = sc.nextLine(); + } + return input; + } + + public void printRoundResult(StringBuilder roundResults) { + System.out.println(roundResults); + } + + public void printRound(int currentRound, int maxRound) { + System.out.printf("%d/%d%n", currentRound, maxRound); + } +} diff --git a/src/test/java/kr/co/wordle/domain/RoundResultTest.java b/src/test/java/kr/co/wordle/domain/RoundResultTest.java new file mode 100644 index 00000000..8f1ee069 --- /dev/null +++ b/src/test/java/kr/co/wordle/domain/RoundResultTest.java @@ -0,0 +1,18 @@ +package kr.co.wordle.domain; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class RoundResultTest { + + @Test + void 정닡인지_확인() { + RoundResult roundResult = new RoundResult(); + roundResult.update(Tile.GREEN); + roundResult.update(Tile.GREEN); + roundResult.update(Tile.GREEN); + roundResult.update(Tile.GREEN); + roundResult.update(Tile.GREEN); + Assertions.assertTrue(roundResult.isAllGreen()); + } +} diff --git a/src/test/java/kr/co/wordle/domain/RoundTest.java b/src/test/java/kr/co/wordle/domain/RoundTest.java new file mode 100644 index 00000000..5a3cb668 --- /dev/null +++ b/src/test/java/kr/co/wordle/domain/RoundTest.java @@ -0,0 +1,47 @@ +package kr.co.wordle.domain; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; + +class RoundTest { + + @ParameterizedTest + @ValueSource(strings = {"abcdef", "asdf", "a", "133ke", "μ•„ddsz", ""}) + void μž…λ ₯κ°’_μœ νš¨ν•˜μ§€_μ•ŠμŒ(String input) { + Assertions.assertThrows(IllegalArgumentException.class, () -> new Round(input)); + } + + @ParameterizedTest + @ValueSource(strings = {"cigar", "rebut", "sissy", "humph"}) + void μž…λ ₯κ°’_유효(String input) { + Assertions.assertDoesNotThrow(() -> new Round(input)); + } + + @Test + void λΌμš΄λ“œ_μ’…λ£Œ_μ—¬λΆ€_확인() { + String input = "cigar"; + Round round = new Round(input); + Assertions.assertFalse(round.isFinished()); + } + + @ParameterizedTest + @CsvSource(value = {"cigar:cigar:GREEN", "stink:marry:GRAY", "marry:karma:YELLOW"}, delimiter = ':') + void μ •λ‹΅κ³Ό_비ꡐ(String input, String strAnswer, String tile) { + Round round = new Round(input); + Answer answer = new Answer(strAnswer); + Tile expected = Tile.valueOf(tile); + Tile result = round.getTile(answer.getCountPerCharacter(), answer.charAt(0), input.charAt(0)); + Assertions.assertEquals(expected, result); + } + + @ParameterizedTest + @CsvSource(value = {"cigar:cigar:🟩🟩🟩🟩🟩", "marry:karma:🟨🟩🟩⬜️⬜️", "major:marry:🟩🟩⬜️⬜️🟨"}, delimiter = ':') + void λΌμš΄λ“œ_κ²°κ³Ό_확인(String input, String answer, String expected) { + Round round = new Round(input); + String result = round.roundResult(new Answer(answer)); + Assertions.assertEquals(expected, result); + } +} diff --git a/src/test/java/kr/co/wordle/support/InputValidatorTest.java b/src/test/java/kr/co/wordle/support/InputValidatorTest.java new file mode 100644 index 00000000..4fac3769 --- /dev/null +++ b/src/test/java/kr/co/wordle/support/InputValidatorTest.java @@ -0,0 +1,38 @@ +package kr.co.wordle.support; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class InputValidatorTest { + + @Test + void μž…λ ₯_null() { + Assertions.assertTrue(InputValidator.isNotValid(null)); + } + + @ParameterizedTest + @ValueSource(strings = {"abcdef", "ant", "a"}) + void μž…λ ₯_5자_μ•„λ‹˜(String input) { + Assertions.assertTrue(InputValidator.isNotValid(input)); + } + + @ParameterizedTest + @ValueSource(strings = {"133ke", "μ•„ddsz", "치즈&kr"}) + void μž…λ ₯_μ•ŒνŒŒλ²³_μ•„λ‹˜(String input) { + Assertions.assertTrue(InputValidator.isNotValid(input)); + } + + @ParameterizedTest + @ValueSource(strings = {"woowa", "qwery", "zxcvb"}) + void μž…λ ₯_λ‹¨μ–΄νŒŒμΌμ—_μ—†μŒ(String input) { + Assertions.assertTrue(InputValidator.isNotValid(input)); + } + + @ParameterizedTest + @ValueSource(strings = {"feign", "major", "death", "fresh"}) + void μž…λ ₯_유효(String input) { + Assertions.assertFalse(InputValidator.isNotValid(input)); + } +} diff --git a/src/test/java/kr/co/wordle/support/WordFileReaderTest.java b/src/test/java/kr/co/wordle/support/WordFileReaderTest.java new file mode 100644 index 00000000..19e29225 --- /dev/null +++ b/src/test/java/kr/co/wordle/support/WordFileReaderTest.java @@ -0,0 +1,15 @@ +package kr.co.wordle.support; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; + +class WordFileReaderTest { + + @Test + void 파일읽기() { + List words = WordFileReader.readWordsInFile(); + Assertions.assertFalse(words.isEmpty()); + } +}