Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions docs/기능_목록표.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# 기능목록표

## MVP
- [x] **[domain] 오늘의 정답 단어를 하드코딩한다**
- 예: `"APPLE"`
- 게임 로직에 집중하기 위한 임시 처리

- [x] **[domain] Word 객체 구현 (길이만 검증)**
- 문자열 래핑
- 5글자가 아니면 예외
- 알파벳 검증

- [x] **[test] Word 길이 검증 테스트**
- 정상 케이스
- 예외 케이스

- [x] **[domain] 문자 판정 결과 enum 추가**
- GREEN
- YELLOW/GRAY

- [x] **[domain] 입력 단어와 정답 비교 로직 구현**
- 같은 위치 + 같은 문자 → GREEN
- 그 외 → YELLOW/GRAY

- [x] **[test] 기본 판정 로직 테스트**
- 완전 일치
- 완전 불일치

- [x] **[domain] Game 클래스 구현**
- 정답 보관
- 시도 횟수 관리
- 종료 여부 상태

- [x] **[domain] 게임은 최대 6번 입력받고 종료된다**
- 정답을 맞히면 즉시 종료
- 6번 시도 후 종료

- [x] **[test] Game 종료 조건 테스트**
- 6턴 초과 불가
- 정답 성공 시 종료

- [x] **[ui] 콘솔 입력으로 단어를 입력받는다**
- [x] **[ui] 판정 결과를 콘솔에 출력한다**
- GREEN / NOT_GREEN 문자열 출력

- [x] **[app] main에서 Game과 UI를 연결한다**
- while 루프 기반
- `System.exit()` 사용 ❌
- 자연 종료

## 리팩토링
규칙 정확도
- [x] YELLOW / GRAY 분리
- [x] 중복 문자 처리

입력 검증 강화
- [x] 알파벳 소문자 검증
- [x] words.txt 기반 사전 검증
- [x] 예외처리 콘솔로그 출력
- [x] 테스트코드 작성

정답 선정 고도화
- [x] AnswerSelector 도입
- [x] 날짜 기반 index
- [x] 관련 테스트 추가

구조 리팩토링
- [ ] Domain / Application / UI 분리
- [ ] Port / Adapter 도입
- [ ] 책임 재배치 및 네이밍 정리
166 changes: 166 additions & 0 deletions docs/요구사항_분석.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# Wordle 요구사항 분석

## 1. 개요

본 문서는 콘솔 기반 Wordle 게임의 기능 요구사항을 정의한다.
플레이어는 5글자 영어 단어를 입력하여 정답을 맞히며, 최대 6번의 시도가 허용된다.

---

## 2. 규칙

### 2.1 게임 기본 규칙

- 정답은 **5글자 영어 단어**이다.
- 플레이어는 **최대 6회**까지 단어를 입력할 수 있다.
- 각 입력은 즉시 정답과 비교되어 결과가 출력된다.
- 정답을 맞히면 즉시 게임이 종료된다.
- 6회를 모두 사용하면 게임은 실패로 종료된다.

### 2.2 개발 기본 규칙
- 프로그램 종료시 System.exit()을 호출하지 않는다.
- 삼항 연산자를 사용하지 않는다.
- 함수가 한가지일만 하도록 최대한 작게 만든다.
- 기능은 테스트로 확인한다.
- 메소드 길이는 15라인을 넘어가지 않게 구현한다.
- else를 사용하지 않는다.
- 도메인 클래스에 단위테스트가 작성되어야한다.
- Java 21 이상
- 외부 라이브러리 사용 금지
- 콘솔 기반 입출력
- 테스트 코드 작성 필수
- 객체지향 설계 원칙 준수
- 클린 코드 지향
---

### 2.2 판정 규칙 (타일 상태)

각 입력 단어의 문자들은 정답과 비교되어 아래 3가지 상태 중 하나로 판정된다.

| 상태 | 의미 |
|----|----|
| 초록색 | 해당 문자가 정답에 존재하며 위치도 동일 |
| 노란색 | 해당 문자가 정답에 존재하지만 위치는 다름 |
| 회색 | 해당 문자가 정답에 존재하지 않음 |

#### 중복 문자 처리 규칙

- 동일한 문자를 여러 번 입력한 경우, **정답에 존재하는 개수만큼만 초록/노란색으로 표시**된다.
- 초과된 동일 문자는 회색으로 판정된다.
- 예시.
- 정답 : hello
- 입력 : lllll
- 출력 : ⬜⬜🟩🟩⬜

---

## 3. 단어 유효성 규칙

### 3.1 단어 길이

- 입력 단어는 반드시 **5글자**여야 한다.

### 3.2 단어 사전
- 정답과 사용자 입력 단어는 모두 `words.txt` 파일에 포함된 단어여야 한다.
- 사전에 없는 단어는 유효하지 않은 입력으로 처리한다.
- ex.
- 사전에 있는 단어이자 정답 mercy
- 사전에 없는 사용자 입력 nurse
- 출력
1. ⬜⬜🟩⬜🟨 보여주지 않고 횟수를 차감하지 않는다.
2. 유효하지 않은 입력 (채택)
---

## 4. 오늘의 정답 선정 규칙

정답 단어는 날짜 기반으로 결정된다.

- 기준 날짜: `2021-06-19`
- 단어 목록: `words.txt`



### 계산 식
```
배열의 크기 = x * y
index = ((현재 날짜 - 2021-06-19) % 배열의 크기)
오늘의 정답 = words[index]
0 ~ 29까지 출제된다
```
- 동일한 날짜에는 항상 동일한 정답이 선택된다.

---

## 5. 사용자 흐름

1. 게임 시작 메시지를 출력한다.
시작 메시지는 다음과 같다.
```
WORDLE을 6번 만에 맞춰 보세요.
시도의 결과는 타일의 색 변화로 나타납니다.
```

2. 사용자에게 단어 입력을 요청한다.
입력단어 길이를 제한하지 않는다

3. 사용자가 5글자 단어를 입력한다.
4. 입력 단어의 유효성을 검증한다.
- 길이 검사
- 사전 포함 여부 검사
- words.txt파일에 있는 단어만인데 배열크기-1 만큼 조회하면 더 빠르게 조회가 가능하다
5. 정답과 입력 단어를 비교하여 타일 결과를 생성한다
- ⬜ : 위치도 안맞고 스펠링도 안맞는 경우
- 🟨 : 스펠링은 맞지만 위치가 틀린 경우
- 🟩 : 스펠링도 맞고 위치도 맞는 경우
6. 결과를 누적 보드 형태로 출력한다.
- 계속 쌓아간걸로 표현되어야 한다
7. 정답을 맞히면 성공으로 게임을 종료한다.
- System.exit(0); // 강제 종료 - 이렇게 하지 말라는 것
- 종료처리에 대한 상태를 추가한다
8. 6회 시도를 모두 소진하면 실패로 게임을 종료한다.
- 종료처리에 대한 상태를 추가한다
---

## 6. 출력 명세

### 6.1 시작 메시지

- `WORDLE을 6번 만에 맞춰 보세요.`
- `시도의 결과는 타일의 색 변화로 나타납니다.`

### 6.2 입력 안내

- `정답을 입력해 주세요.`

### 6.3 결과 출력

- 각 시도 후, 이전 결과를 포함한 전체 보드를 출력한다.
- 성공 시 다음 형식의 결과 요약을 출력한다.

```
성공시도수 / 6
예: 4/6
```


---

## 7. 종료 조건
- 성공여부에 대한 상태를 추가한다
- 성공여부는 긍정형으로 작성한다

### 7.1 성공 종료
- 입력한 단어가 정답과 완전히 일치한 경우

### 7.2 실패 종료
- 6번의 시도 동안 정답을 맞히지 못한 경우

---

## 8. 용어 정의

| 용어 | 설명 |
|----|------------------------|
| 시도 | 사용자가 단어를 한 번 입력하는 행위 |
| 타일 | 문자 하나에 대한 판정 결과 |
| 보드 | 전체 시도 결과를 누적한 출력 영역(행) |
Empty file removed src/main/java/.gitkeep
Empty file.
20 changes: 20 additions & 0 deletions src/main/java/application/port/OutputPort.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package application.port;

public interface OutputPort {

String getWelcomeMessage();

String getInputInfoMessage();

String getBoards(StringBuilder[] boards);

String getHasNotWordRepository();

String getInvalidInputMessage();

String getLengthMismatchMessage();

String getNotAlphabetMessage();

String getTryCountExceededMessage();
}
39 changes: 39 additions & 0 deletions src/main/java/application/service/Client.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package application.service;

import application.port.OutputPort;
import domain.model.Result;
import domain.model.Word;
import domain.service.Game;

public class Client {

private final Word word;
private final Game game;
private final Result result;
private final OutputPort outputPort;

public Client(Word word, Game game, Result result, OutputPort outputPort) {
this.word = word;
this.game = game;
this.result = result;
this.outputPort = outputPort;
}

public String run() {
if (!game.checkedTryCount()) {
return "";
}

if (!word.valid()) {
return "";
}

word.compareAnswer();

game.updateFinished();

result.addBoard();

return outputPort.getBoards(result.getBoards());
}
}
19 changes: 19 additions & 0 deletions src/main/java/domain/model/Answer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package domain.model;

public class Answer {

private final String value;

// 기본 생성자는 apple로 생성한다.
public Answer(){
this.value = "apple";
}

public Answer(String value){
this.value = value;
}

public String getValue(){
return this.value;
}
}
22 changes: 22 additions & 0 deletions src/main/java/domain/model/Input.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package domain.model;

public class Input {

private String value;
private Result result;

public Input(String value, Result result) {
this.value = value;
this.result = result;
}

public void saveTile(String tile){
result.addTile(tile);
}

public String getValue() {
return this.value;
}


}
31 changes: 31 additions & 0 deletions src/main/java/domain/model/Result.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package domain.model;

import java.util.ArrayList;
import java.util.List;

public class Result {

private StringBuilder board = new StringBuilder();
private List<StringBuilder> boards = new ArrayList<>();

public void addTile(String tile) {
board.append(tile);
}

public void addBoard() {
boards.add(board);
board = new StringBuilder();
}

public String getBoard() {
return board.toString();
}

public int getCurrentTryCount() {
return boards.size() + 1;
}

public List<StringBuilder> getBoards() {
return boards;
}
}
Loading