Skip to content
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

[4기][3주차] Wordle 과제 제출 - 와제 #20

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
49 changes: 42 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
- 6x5 격자를 통해서 5글자 단어를 6번 만에 추측한다.
- 플레이어가 답안을 제출하면 프로그램이 정답과 제출된 단어의 각 알파벳 종류와 위치를 비교해 판별한다.
- 판별 결과는 흰색의 타일이 세 가지 색(초록색/노란색/회색) 중 하나로 바뀌면서 표현된다.
- 맞는 글자는 초록색, 위치가 틀리면 노란색, 없으면 회색
- 두 개의 동일한 문자를 입력하고 그중 하나가 회색으로 표시되면 해당 문자 중 하나만 최종 단어에 나타난다.
- 맞는 글자는 초록색, 위치가 틀리면 노란색, 없으면 회색
- 두 개의 동일한 문자를 입력하고 그중 하나가 회색으로 표시되면 해당 문자 중 하나만 최종 단어에 나타난다.
- 정답과 답안은 `words.txt`에 존재하는 단어여야 한다.
- 정답은 매일 바뀌며 ((현재 날짜 - 2021년 6월 19일) % 배열의 크기) 번째의 단어이다.

Expand Down Expand Up @@ -65,11 +65,46 @@ spill
- [Java 코드 컨벤션](https://github.com/woowacourse/woowacourse-docs/tree/master/styleguide/java) 가이드를 준수하며 프로그래밍한다.
- 프로그래밍 요구 사항에서 별도의 변경 불가 안내가 없는 한 자유롭게 파일을 수정하고 패키지를 이동할 수 있다.
- indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다.
- 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
- 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다.
- 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
- 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다.
- 3항 연산자를 쓰지 않는다.
- 함수(또는 메서드)의 길이가 15라인을 넘어가지 않도록 구현한다.
- 함수(또는 메서드)가 한 가지 일만 잘 하도록 구현한다.
- 함수(또는 메서드)가 한 가지 일만 잘 하도록 구현한다.
- else 예약어를 쓰지 않는다.
- 힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다.
- else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다.
- 힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다.
- else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다.

---

# 업무 방식 - 페어프로그래밍

* Intellij Code With me 를 사용하여 페어 프로그래밍을 진행 합니다.
* 이거 재밌네요
* Start role
* navigator: jimbae
* driver: yj
* Role 스위치 방식
* 각각의 기능 혹은 메소드 단위로 드라이버와 네비게이터를 지정하여 진행한다.
* Repository
* fork 'woowahan-pjs/java-wordle' repository
* Requirements analysis method
* In -> Out

# 기능 정의

* 정답 : s p i l l
* X X Y G X

* 입력 : h e l l o
*
* hello 와 label 비교하여 단어 맞추기
* enum : YELLOW, GREEN, GREY

* 위치와 단어가 동일하면 GREEN
* 단어만 존재하면 YELLOW
* 없으면 GREY
* words.txt 파일에서 오늘의 단어를 선택한다.
* 단어는 ((현재 날짜 - 2021년 6월 19일) % 배열의 크기)
* 결과를 저장하는 객체 - ArrayList
* 입력을 담당하는 객체
* 다섯글자만 입력 가능하도록 validation
Empty file removed src/main/java/.gitkeep
Empty file.
31 changes: 31 additions & 0 deletions src/main/java/controller/WordleController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package controller;

import domain.Color;
import domain.TryResult;
import domain.Word;
import domain.Words;
import ui.IOUtils;
import ui.InputView;
import ui.ResultView;

import java.time.LocalDate;
import java.util.List;

public class WordleController {

private static final int PLAY_ROUND = 6;
akayj820 marked this conversation as resolved.
Show resolved Hide resolved

public static void main(String[] args) {
Words words = new Words(IOUtils.readFromResource("words.txt"));
akayj820 marked this conversation as resolved.
Show resolved Hide resolved

ResultView.startComent();

TryResult tryResult = new TryResult();
int round = 0;
while (round++ < PLAY_ROUND && !tryResult.isFinished()) {
List<Color> colors = words.matchingAnswer(InputView.inputComment());
tryResult.addTry(colors);
ResultView.results(tryResult);
}
}
}
18 changes: 18 additions & 0 deletions src/main/java/domain/Color.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package domain;

public enum Color {
GREY ("⬜"),
YELLOW ("\uD83D\uDFE8"),
GREEN ("\uD83D\uDFE9");

private String icon;

Color(String icon) {
this.icon = icon;
}

@Override
public String toString() {
return icon;
}
}
28 changes: 28 additions & 0 deletions src/main/java/domain/TryResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package domain;

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

public class TryResult {
private static final List<Color> ALL_GREEN = List.of(Color.GREEN, Color.GREEN, Color.GREEN, Color.GREEN, Color.GREEN);
private List<List<Color>> results = new ArrayList<>();
private boolean finished;

public void addTry (List<Color> colors) {
if (colors.equals(ALL_GREEN)) {
finished = true;
}
results.add(colors);
}

public int count() {
return results.size();
}
public List<List<Color>> getResults() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r: 코드 포맷팅이 필요해 보입니다 😂😂

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요런 디테일 감사합니닷👍

return results;
}

public boolean isFinished() {
return finished;
}
}
56 changes: 56 additions & 0 deletions src/main/java/domain/Word.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package domain;

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

public class Word {
private final String word;

public Word(String word) {
if (word.length() != 5) {
throw new IllegalArgumentException("단어는 다섯글자로만 입력 가능합니다");
}
this.word = word;
}

public List<Color> compareWith(Word input) {
List<Color> result = new ArrayList<>();
char[] answerArray = word.toCharArray();
char[] inputArray = input.word.toCharArray();

for (int i = 0; i < answerArray.length; i++) {
result.add(mapped(answerArray[i], inputArray[i]));
}

return result;
}

private Color mapped(char answer, char input) {
if (answer == input) {
return Color.GREEN;
}
if (isContains2(input)) {
return Color.YELLOW;
}
return Color.GREY;
}

private boolean isContains2(char c) {
return this.word.contains(String.valueOf(c));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r: 메서드명에 있는 숫자 2는 제거해도 될 것 같습니다 🙃

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

중간에 메서드 리팩터링 하느라고 잠깐 2붙여놨었는데 저걸 그냥 두고 있었네요... 바로 제거하도록 하겠습니다:)


@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Word word1 = (Word) o;
return Objects.equals(word, word1.word);
}

@Override
public int hashCode() {
return Objects.hash(word);
}

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

import java.time.LocalDate;
import java.time.Period;
import java.util.Arrays;
import java.util.Collection;
akayj820 marked this conversation as resolved.
Show resolved Hide resolved
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class Words {
private final List<Word> words;

public Words(String... words){
this(Arrays.stream(words)
.map(Word::new)
.collect(Collectors.toList()));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a: 한가지 궁금한 점이 있습니다!

IOUtils#readFromResource에서 List<String>String[]로 변환하신 이유가 혹시 무엇인지 궁금합니다!
IOUtils#readFromResource에서 리스트를 그대로 반환받아도 해당 생성자에서 List<String>을 인자로 받는다면 동일하게 처리할 수 있을 것 같은데, 제가 모르는 무언가의 꿀팁이 있는건가요!? 😮😮

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기본 생성자 하나만 두고 쓰기 위해서 매개변수 타입을 다르게 하려고 배열로 받았었습니다:) 말씀 주신 피드백대로 한 번 반영해보려고 정적 팩터리 메서드로 수정해봤습니다!!! 흠 어떤게 더 좋은 방법인지에 대해 고민해보고 싶은데 알렉스님 생각도 궁금합니다🤔

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 저는 기존 기본 생성자의 인자를 String []이 아닌 List<String>로 받아도 되지 않나? 라는 생각이었습니다!!
또한 개인적인 생각으로는 필요하다면 생성자는 여러개가 있어도 괜찮지 않을까라는 의견입니다 :)


public Words(List<Word> words) {
this.words = Collections.unmodifiableList(words);
}

public boolean contains(Word word){
return words.contains(word);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a: 만약 테스트 코드가 불필요하다고 판단되신다면, 해당 코드 또한 테스트에서만 사용되는 메서드이므로 삭제되어도 될 것 같네요 😊😊

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 말씀주신대로 확인하다보니 불필요한 코드였더라구요! 해당 코드 삭제해두었습니다
감사합니다😙


public Word answer(LocalDate from){
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a: 혹시 answer를 가져오는 행위를 명명하지 않으신 이유가 따로 있으신가요? (ex. getAnswer())

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메서드명 앞에 get을 붙이면 뭔가 getter 처럼 보여서 Words의 멤버변수값을 가져오는 것처럼 느껴져서
get을 붙이기보다는 리턴값이 '정답'이다라는 걸 조금 더 명시적으로 표현하기 위해 명사형으로 명명했던 것 같습니다

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹시 해당 내용에 관련된 레퍼런스를 공유해주실 수 있으신가요?
저는 처음 보는 방법이라 매우 궁금하네요 😊😊

Period period = Period.between(LocalDate.of(2021, 6, 19), from);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a: 테스트하기 어려운 영역을 외부에서 인자로 받아서 테스트 하기 쉬운 코드로 만들어주셨네요 👍🏻👍🏻

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

c: LocalDate.of(2021, 6, 19)는 상수로 추출한다면 가독성이 올라갈 것 같습니다 😊

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

피드백 반영해보려고 상수로 추출하는 도중에 변수명이 많이 고민되더라구욬ㅋㅋㅋㅋㅋㅋ

int range = period.getDays()%words.size();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r: 코드 포맷팅이 필요해 보입니다 😂😂

Copy link
Author

@akayj820 akayj820 Apr 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵:)

return words.get(range);
}

public List<Color> matchingAnswer(Word input) {
validate(input);
Word answer = answer(LocalDate.now());
return answer.compareWith(input);
}

private void validate(Word input) {
if(!words.contains(input)){
throw new IllegalArgumentException("단어집에 없는 단어를 선택하였습니다.");
}
}
}
29 changes: 29 additions & 0 deletions src/main/java/ui/IOUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package ui;

import domain.Word;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

public class IOUtils {

private IOUtils(){}

public static String[] readFromResource(String resourceName){
URL txtUrl = IOUtils.class.getClassLoader().getResource(resourceName);
try {
List<String> words = Files.readAllLines(Paths.get(txtUrl.toURI()));
return words.toArray(new String[words.size()]);
} catch (IOException e) {
System.out.println("파일을 읽는도중 문제가 발생했습니다.");
throw new RuntimeException();
} catch (URISyntaxException e) {
System.out.println("존재하지 않는 경로 입니다.");
throw new RuntimeException();
}
}
}
15 changes: 15 additions & 0 deletions src/main/java/ui/InputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ui;

import domain.Word;

import java.util.Scanner;

public class InputView {
private InputView(){}

public static Word inputComment() {
Scanner scanner = new Scanner(System.in);
System.out.println("정답을 입력해 주세요.\n");
return new Word(scanner.nextLine());
}
}
26 changes: 26 additions & 0 deletions src/main/java/ui/ResultView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ui;

import domain.Color;
import domain.TryResult;

import java.util.List;

public class ResultView {
akayj820 marked this conversation as resolved.
Show resolved Hide resolved

private ResultView(){}

public static void startComent() {
System.out.println("WORDLE을 6번 만에 맞춰 보세요.\n시도의 결과는 타일의 색 변화로 나타납니다.\n");
}

public static void results(TryResult tryResult) {
List<List<Color>> results = tryResult.getResults();
akayj820 marked this conversation as resolved.
Show resolved Hide resolved
if (tryResult.isFinished()) {
System.out.println(String.format("%d/6", tryResult.count()));
}

for (List colors : results) {
System.out.println(colors);
}
}
}
Empty file removed src/test/java/.gitkeep
Empty file.
35 changes: 35 additions & 0 deletions src/test/java/WordleTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import domain.Color;
import domain.Word;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import ui.IOUtils;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.List;

public class WordleTest {
akayj820 marked this conversation as resolved.
Show resolved Hide resolved
@Test
void compare() {
Word answer = new Word("spill");
List<Color> colors = answer.compareWith(new Word("hello"));
Assertions.assertEquals(Arrays.asList(Color.GREY, Color.GREY, Color.YELLOW, Color.GREEN, Color.GREY), colors);
akayj820 marked this conversation as resolved.
Show resolved Hide resolved
}

@Test
void readFile() throws URISyntaxException, IOException {
akayj820 marked this conversation as resolved.
Show resolved Hide resolved
//when
String[] words = IOUtils.readFromResource("words.txt");

//then
Assertions.assertEquals(words[0], "cigar");
}

@Test
void sizeCheck() {
Assertions.assertThrows(IllegalArgumentException.class, () -> {
new Word("aaaa");
});
}
}
39 changes: 39 additions & 0 deletions src/test/java/WordsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import domain.Word;
import domain.Words;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.time.LocalDate;

public class WordsTest {

@Test
void containsWord(){
//given
Words words = new Words("aaaaa", "bbbbb", "ccccc");

//when
Assertions.assertTrue(words.contains(new Word("aaaaa")));
Assertions.assertFalse(words.contains(new Word("ddddd")));
}
akayj820 marked this conversation as resolved.
Show resolved Hide resolved

@Test
void getArrayLocation(){
//given
Words words = new Words("aaaaa", "bbbbb");

//when
Word answers = words.answer(LocalDate.of(2021, 6, 20));

//then
Assertions.assertEquals(answers, new Word("bbbbb"));
}

@Test
void validation() {
Words words = new Words("aaaaa");
Assertions.assertThrows(IllegalArgumentException.class, () -> {
words.matchingAnswer(new Word("bbbbb"));
});
}
}