-
Notifications
You must be signed in to change notification settings - Fork 43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[5기] 3주차 Wordle 과제 제출 - 호아 #39
base: main
Are you sure you want to change the base?
Changes from 47 commits
f41c990
ecce462
68be191
9664220
d225a84
b8ece65
76fdedf
d97c0c2
846ec02
7fdb063
d03c4b4
34c2333
60ca6a9
e3a595d
0a66559
aa72923
20cf464
151e2ad
e14a085
b201c59
f161544
5c08e85
bb6eb4f
e52073e
b647035
74acae5
7c56c0a
ee4a197
db4f022
553fefc
808df83
65b95af
a9e9e76
e848b10
6ca7944
4e50c35
bad65f2
f861ed0
1158a12
cc4afbf
8e397f0
5f37a11
609a398
7e58797
a678cd3
d170631
261dbd5
3d52104
f09f3c2
55f0609
f8f42c4
690cae5
fc871a1
31cf9d0
5764cde
0f04861
8c59d10
9593935
19e3484
cdb969d
8b9b2b9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
# 미션 - 워들 | ||
|
||
## 게임 진행 순서 | ||
|
||
- 단어장(`words.txt`)에 있는 단어를 읽어들인다. | ||
- 읽어들인 단어들에서 정답인 단어를 정한다. | ||
- 정답은 매일 바뀌며 ((현재 날짜 - 2021년 6월 19일) % 배열의 크기) 번째의 단어이다. | ||
- 문자 5개를 입력한다. | ||
- 5개가 아닌 경우 재입력을 받는다. | ||
- 단어장에 존재하지 않는 단어인 경우 재입력을 받는다. | ||
- 알파벳이 아닌 경우 재입력을 받는다. | ||
- 입력받은 문자와 정답을 비교한다. | ||
- 비교 결과는 타일이 초록색/노란색/회색 중 하나로 바뀌면서 표현된다. | ||
- 맞는 글자는 초록색, 위치가 틀리면 노란색, 없으면 회색 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 리뷰를 다하고 돌아보니... 여기에 맞는글자는 초록색이라고 아예 설명을 정해두고 가셨던거였군요! ㅋㅋㅋㅋㅋ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아이쿠 ㅎㅎㅎㅎ 뷰에 대한 책임을 대부분 콘솔에 몰아 놓고 메서드명에 박아버렸네요 수정해야겠어요!! |
||
- 같은 문자가 2개 입력되었을 때, 해당 문자가 정답에 하나만 존재하지만 위치가 틀린 경우 첫번 째 문자만 노란색으로 표시된다. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 같은 문자가 3개 입력되었을 때, 해당 문자가 정답에 하나만 존재하면 어떻게 되나요? 😄 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 같은 문자가 n개 입력되었을 때, 하나만 존재하면 첫번 째 문자만 노란색으로 표시됩니다! 문서 수정할게요~ |
||
- 정답: lurid, 입력: hello, 결과: ⬜⬜🟨⬜⬜ | ||
- 6번 안에 맞추면 게임을 종료한다. | ||
- 6번 안에 맞추지 못하면 그래도 종료한다. | ||
|
||
## 용어 사전 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 크 역시 ddd 영재 호아님 👍 정리하신거 멋집니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 감사합니당!! |
||
|
||
| 한글명 | 영문명 | 설명 | | ||
|--------|----------------|------------------------------------------------| | ||
| 워들 | Wordle | 5글자 영어 단어 맞추기 게임 | | ||
| 단어장 | Word Book | 이 게임에서 사용될 수 있는 단어 모음 | | ||
| 입력 단어 | Input Word | 플레이어가 입력하는 5글자 단어 | | ||
| 정답 단어 | Answer Word | 오늘 게임의 정답인 5글자 단어 | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 클래스명중에 Word가 있는데 이 Word는 입력단어와 정답 단어를 포괄하는 개념인가요? 😄 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 초반에는 InputWord와 AnswerWord 클래스를 분리하려고 했으나, 굳이 그럴 필요가 없겠더라구요~ 개발하면서 모델링이 계속 변경되었는데 지속적 업데이트의 중요성을 느낍니다 ㅠㅠ |
||
| 글자 | Letter | 단어를 구성하는 알파벳 | | ||
| 영문 | Alphabet | 글자를 구성하는 최소단위 | | ||
| 위치 | Position | 단어를 구성하는 글자의 위치 | | ||
| 플레이어 | Player | 게임에 참여하는 사용자 | | ||
| 결과 | Result | 입력단어와 정답단어를 비교해서 표현되는 타일모음 | | ||
| 비교 | Compare | 입력단어와 정답단어의 글자와 위치를 비교하는 행위 | | ||
| 초록색 타일 | Green Tile | 글자와 위치가 동일한 경우 표현되는 타일 | | ||
| 노란색 타일 | Yellow Tile | 글자는 포함되지만 위치가 다른 경우 표현되는 타일 | | ||
| 회색 타일 | Gray Tile | 글자와 위치가 모두 다른 경우 표현되는 타일 | | ||
| 결과모음 | Results | 라운드가 진행될 때 마다 누적된 결과모음 | | ||
| 기록모음 | Record | 누적된 결과모음의 기록 | | ||
| 기준일 | Base Date | 오늘의 정답 단어를 계산하는 기준일(2021년 6월 19일) | | ||
| 정답 공식 | Answer Formula | 오늘의 정답 단어를 계산하는 공식 `(현재 날짜 - 기준일) % 단어장의 단어 수` | | ||
| 시작 | Start | 플레이어가 워들을 시작하는 행위 | | ||
| 종료 | End | 워들이 종료되는 행위(라운드가 전부 끝났거나, 그 전에 정답을 맞추면 종료된다) | | ||
|
||
## 모델링 | ||
|
||
### 클래스 다이어그램 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 클래스 다이어그램까지 👍👍👍👍👍 |
||
|
||
```mermaid | ||
--- | ||
title: Wordle | ||
--- | ||
classDiagram | ||
class Word { | ||
-List<Letter> letters | ||
+compare(Word inputWord) Results | ||
+equals(Object o) boolean | ||
} | ||
|
||
class WordComparator { | ||
-List<Letter> pendingLetters | ||
-Results results | ||
+WordComparator(letters: List<Letter>) | ||
+compare(inputWord: Word): Results | ||
-process(targetLetter: Letter, predicate: Predicate<Letter>, tile: Tile) | ||
-fillEmptyToGray(targetLetter: Letter) | ||
} | ||
|
||
Word --|> WordComparator | ||
|
||
class Letter { | ||
-Alphabet alphabet | ||
-Position position | ||
+isSameAlphabet(Letter letter) boolean | ||
+getPosition() Position | ||
+equals(Object o) boolean | ||
} | ||
|
||
class Alphabet { | ||
-char alphabet | ||
+equals(Object o) boolean | ||
} | ||
|
||
class Position { | ||
-char position | ||
+compareTo(Position position) boolean | ||
+equals(Object o) boolean | ||
} | ||
|
||
class Record { | ||
-List<Results> record | ||
+add(Results results) void | ||
+isEnd() boolean | ||
+isCountOver() boolean | ||
+existAllGreen() boolean | ||
+iterator() Iterator<Results> | ||
+size() int | ||
} | ||
class Results { | ||
-SortedSet<Result> results | ||
+add(Result result) void | ||
+isCheckedPosition(Position position) boolean | ||
+isAllGreen() boolean | ||
} | ||
class Result { | ||
-Tile tile | ||
-Position poistion | ||
+isSamePosition(Position position) boolean | ||
+equals(Object o) boolean | ||
+compareTo(Result result) int | ||
+isGreen() boolean | ||
+isYellow() boolean | ||
+isGray() boolean | ||
} | ||
class Tile { | ||
<<enumeration>> | ||
+GREEN | ||
+YELLOW | ||
+GRAY | ||
} | ||
class AnswerFormula { | ||
<<interface>> | ||
+calculate(int wordCount) int | ||
} | ||
class BaseAnswerFormula { | ||
+calculate(int wordCount) int | ||
} | ||
BaseAnswerFormula ..|> AnswerFormula | ||
class Wordle { | ||
-WordBook wordBook | ||
-InputView inputView | ||
-OutputView outputView | ||
-Record record | ||
-AnswerFormula answerFormula | ||
+startGame() void | ||
-runGame(Word answerWord) void | ||
-processTurn(Word answerWord) void | ||
-concludeGame() void | ||
-handleWrongAnswer(Runnable runnable) void | ||
} | ||
class WordBook { | ||
<<interface>> | ||
+pick(AnswerFormula formula) Word | ||
+exist(Word word) boolean | ||
+find(String target) Word | ||
} | ||
class FileWordBook { | ||
-List<Word> words | ||
+pick(AnswerFormula formula) Word | ||
+exist(Word word) boolean | ||
+find(String target) Word | ||
} | ||
FileWordBook ..|> WordBook | ||
``` | ||
|
||
## 🚀 세부 요구 사항 | ||
|
||
- 6x5 격자를 통해서 5글자 단어를 6번 만에 추측한다. | ||
- 플레이어가 답안을 제출하면 프로그램이 정답과 제출된 단어의 각 알파벳 종류와 위치를 비교해 판별한다. | ||
- 판별 결과는 흰색의 타일이 세 가지 색(초록색/노란색/회색) 중 하나로 바뀌면서 표현된다. | ||
- 맞는 글자는 초록색, 위치가 틀리면 노란색, 없으면 회색 | ||
- 두 개의 동일한 문자를 입력하고 그중 하나가 회색으로 표시되면 해당 문자 중 하나만 최종 단어에 나타난다. | ||
- 정답과 답안은 `words.txt`에 존재하는 단어여야 한다. | ||
- 정답은 매일 바뀌며 ((현재 날짜 - 2021년 6월 19일) % 배열의 크기) 번째의 단어이다. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package wordle; | ||
|
||
import wordle.application.Wordle; | ||
import wordle.domain.FileWordBook; | ||
import wordle.domain.WordBook; | ||
import wordle.infra.FileReader; | ||
import wordle.ui.ConsoleInputView; | ||
import wordle.ui.ConsoleOutputView; | ||
import wordle.ui.InputView; | ||
import wordle.ui.OutputView; | ||
|
||
public class Application { | ||
|
||
public static void main(String[] args) { | ||
WordBook wordBook = new FileWordBook(new FileReader()); | ||
InputView inputView = new ConsoleInputView(); | ||
OutputView outputView = new ConsoleOutputView(); | ||
Wordle wordle = new Wordle(wordBook, inputView, outputView); | ||
wordle.startGame(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package wordle.application; | ||
|
||
import wordle.domain.AnswerFormula; | ||
import wordle.domain.BaseAnswerFormula; | ||
import wordle.domain.Record; | ||
import wordle.domain.Results; | ||
import wordle.domain.Word; | ||
import wordle.domain.WordBook; | ||
import wordle.exception.WordleException; | ||
import wordle.exception.WordleInvalidInputException; | ||
import wordle.ui.InputView; | ||
import wordle.ui.OutputView; | ||
|
||
public class Wordle { | ||
|
||
private final WordBook wordBook; | ||
|
||
private final InputView inputView; | ||
|
||
private final OutputView outputView; | ||
|
||
private final Record record; | ||
|
||
private final AnswerFormula answerFormula; | ||
|
||
public Wordle(WordBook wordBook, InputView inputView, OutputView outputView) { | ||
this.wordBook = wordBook; | ||
this.inputView = inputView; | ||
this.outputView = outputView; | ||
this.record = new Record(); | ||
this.answerFormula = new BaseAnswerFormula(); | ||
} | ||
|
||
public void startGame() { | ||
Word answerWord = wordBook.pick(answerFormula); | ||
outputView.welcome(); | ||
runGame(answerWord); | ||
concludeGame(); | ||
} | ||
|
||
private void runGame(Word answerWord) { | ||
while (!record.isEnd()) { | ||
outputView.showRecord(record); | ||
handleWrongAnswer(() -> processTurn(answerWord)); | ||
} | ||
} | ||
|
||
private void processTurn(Word answerWord) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 각 단계의 명칭이 Turn이군요! 😄 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ㅎㅎㅎㅎㅎ 메서드명을 processRound로 변경해야겠네요 |
||
outputView.askAnswer(); | ||
Word inputWord = wordBook.find(inputView.input()); | ||
Results results = answerWord.compare(inputWord); | ||
record.add(results); | ||
} | ||
|
||
private void concludeGame() { | ||
if (record.existAllGreen()) { | ||
outputView.successEnd(record); | ||
return; | ||
} | ||
outputView.failEnd(record); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. record에는 종료되었는지 여부를 isEnd() 조건을 잘 만들어주셨는데요 👍 |
||
|
||
private void handleWrongAnswer(Runnable runnable) { | ||
try { | ||
runnable.run(); | ||
} catch (WordleInvalidInputException e) { | ||
outputView.wrongAnswer(); | ||
} catch (WordleException e) { | ||
outputView.unexpectedEnd(); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 크 exception과 각각의 화면출력요소를 남기는 센스 👍👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아예 예외메세지를 담아서 outputView에 넘겨주는 방법도 있겠네요~ |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package wordle.domain; | ||
|
||
import java.util.Objects; | ||
import wordle.exception.InvalidAlphabetException; | ||
|
||
public class Alphabet { | ||
|
||
private static final char MIN_ALPHABET = 'a'; | ||
private static final char MAX_ALPHABET = 'z'; | ||
private final char alphabet; | ||
|
||
public Alphabet(char alphabet) { | ||
char lowerAlphabet = Character.toLowerCase(alphabet); | ||
if (lowerAlphabet < MIN_ALPHABET || lowerAlphabet > MAX_ALPHABET) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 알파벳에 min과 max라는 표현을 사용할수도 있군요! 뭔가 숫자 표기만 보다가 문자표기는 신선하네요 👍 |
||
throw new InvalidAlphabetException(); | ||
} | ||
|
||
this.alphabet = lowerAlphabet; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (o == null || getClass() != o.getClass()) { | ||
return false; | ||
} | ||
Alphabet alphabet1 = (Alphabet) o; | ||
return alphabet == alphabet1.alphabet; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(alphabet); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package wordle.domain; | ||
|
||
@FunctionalInterface | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 functional interace까지 👍 |
||
public interface AnswerFormula { | ||
|
||
int calculate(int wordCount); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package wordle.domain; | ||
|
||
import java.time.LocalDate; | ||
import java.time.temporal.ChronoUnit; | ||
import wordle.exception.AnswerFormulaException; | ||
|
||
public class BaseAnswerFormula implements AnswerFormula { | ||
|
||
private static final LocalDate BASE = LocalDate.of(2021, 6, 19); | ||
private static final int MIN_WORD_COUNT = 1; | ||
|
||
public int calculate(int wordCount) { | ||
if (wordCount < MIN_WORD_COUNT) { | ||
throw new AnswerFormulaException(); | ||
} | ||
|
||
return (int) ChronoUnit.DAYS.between(BASE, LocalDate.now()) % wordCount; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package wordle.domain; | ||
|
||
import java.util.List; | ||
import wordle.exception.WordNotExistException; | ||
import wordle.infra.FileReader; | ||
|
||
public class FileWordBook implements WordBook { | ||
|
||
public static final String FILE_PATH = "words.txt"; | ||
private final List<Word> words; | ||
|
||
public FileWordBook(FileReader fileReader) { | ||
this.words = fileReader.readByLine(FILE_PATH) | ||
.stream() | ||
.map(Word::new) | ||
.toList(); | ||
} | ||
|
||
@Override | ||
public Word pick(AnswerFormula answerFormula) { | ||
int index = answerFormula.calculate(words.size()); | ||
return words.get(index); | ||
} | ||
|
||
@Override | ||
public boolean exist(Word word) { | ||
return words.contains(word); | ||
} | ||
|
||
@Override | ||
public Word find(String target) { | ||
Word targetWord = new Word(target); | ||
return words.stream() | ||
.filter(targetWord::equals) | ||
.findFirst() | ||
.orElseThrow(WordNotExistException::new); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오... 단어가 없을 때 대한 예외처리도 WordBook이 직접 하는군요? ㅋㅋㅋㅋ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 헉...... WordBook이 레포라는 관점으로 보면 어플리케이션 서비스에서 던지는게 맞겠네요.. 역시 👍 |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
테스트하실때 mock을 활용하셨군요! ㅇ_ㅇ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
네 맞습니다! staticMock을 사용하려고 의존성을 추가했습니다~~