diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..65629b5d --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,13 @@ +## 작업 요약 + +- 요구사항에 맞게 작업할 내용을 간략하게 정리합니다. + +## 작업 내용 + +- [ ] 작업 1 +- [ ] 작업 2 + +
+ +## 참고 자료 +- 기술 문서나 테스트 결과 등 작업에 관련된 참고 자료를 정리합니다. diff --git a/README.md b/README.md index b722ac00..656aac1b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,17 @@ # 미션 - 워들 +## 👥 페어 멤버 +| ALEX |HOLDEN | +| --- | --- | +[](https://github.com/giibeom)| [](https://github.com/seung-00) | + +* [Alex 님의 repository](https://github.com/giibeom/study-java-wordle) 에서 페어 프로그래밍을 진행했습니다. + * 이 repository 는 해당 repository 를 미러링했습니다. +* 진행 방식은 해당 [repository wiki](https://github.com/giibeom/study-java-wordle/wiki) 에 정리되어 있습니다. + + +--- + ## 🔍 진행 방식 - 미션은 **기능 요구 사항, 프로그래밍 요구 사항, 과제 진행 요구 사항** 세 가지로 구성되어 있다. diff --git a/src/main/java/woowaapplication/pair/game/Application.java b/src/main/java/woowaapplication/pair/game/Application.java new file mode 100644 index 00000000..3415cdf0 --- /dev/null +++ b/src/main/java/woowaapplication/pair/game/Application.java @@ -0,0 +1,11 @@ +package woowaapplication.pair.game; + +import woowaapplication.pair.game.wordle.WordleGame; + +public class Application { + + public static void main(String[] args) { + WordleGame wordleGame = new WordleGame(); + wordleGame.start(); + } +} diff --git a/src/main/java/woowaapplication/pair/game/util/FileReader.java b/src/main/java/woowaapplication/pair/game/util/FileReader.java new file mode 100644 index 00000000..d2798493 --- /dev/null +++ b/src/main/java/woowaapplication/pair/game/util/FileReader.java @@ -0,0 +1,20 @@ +package woowaapplication.pair.game.util; + +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; +import woowaapplication.pair.game.wordle.exception.ReadFileException; + +public class FileReader { + + public static List readLinesFromFile(URL fileUrl) { + try { + return Files.readAllLines(Paths.get(fileUrl.toURI())); + } catch (IOException | URISyntaxException e) { + throw new ReadFileException(); + } + } +} diff --git a/src/main/java/woowaapplication/pair/game/util/KeywordValidator.java b/src/main/java/woowaapplication/pair/game/util/KeywordValidator.java new file mode 100644 index 00000000..7b230a2f --- /dev/null +++ b/src/main/java/woowaapplication/pair/game/util/KeywordValidator.java @@ -0,0 +1,28 @@ +package woowaapplication.pair.game.util; + +import woowaapplication.pair.game.wordle.exception.InvalidInputKeywordException; + +public class KeywordValidator { + + public static void validate(String keyword, int lengthLimit) { + if (keyword == null) { + throw new InvalidInputKeywordException(); + } + + if (!isAlphabetic(keyword)) { + throw new InvalidInputKeywordException(); + } + + if (!isLengthValid(keyword, lengthLimit)) { + throw new InvalidInputKeywordException(); + } + } + + private static boolean isAlphabetic(String keyword) { + return keyword.matches("[a-zA-Z]+"); + } + + private static boolean isLengthValid(String keyword, int length) { + return keyword.length() == length; + } +} diff --git a/src/main/java/woowaapplication/pair/game/wordle/WordleGame.java b/src/main/java/woowaapplication/pair/game/wordle/WordleGame.java new file mode 100644 index 00000000..647f64c5 --- /dev/null +++ b/src/main/java/woowaapplication/pair/game/wordle/WordleGame.java @@ -0,0 +1,88 @@ +package woowaapplication.pair.game.wordle; + +import woowaapplication.pair.game.util.KeywordValidator; +import woowaapplication.pair.game.wordle.domain.Coin; +import woowaapplication.pair.game.wordle.domain.WordleBlock; +import woowaapplication.pair.game.wordle.dto.GameResultDto; +import woowaapplication.pair.game.wordle.exception.InvalidAnswerKeywordException; +import woowaapplication.pair.game.wordle.exception.InvalidInputKeywordException; +import woowaapplication.pair.game.wordle.exception.OutOfChanceException; +import woowaapplication.pair.game.wordle.exception.ReadFileException; + +public class WordleGame { + public static final int TOTAL_CHANCE = 6; + + public static final int KEYWORD_LENGTH = 5; + + private String answerKeyword; + +private final WordleGameStorage wordleGameStorage; + + private final WordleGameIO wordleGameIO; + + public WordleGame() { + Coin coin = Coin.of(this.TOTAL_CHANCE); + this.wordleGameStorage = WordleGameStorage.of(coin); + this.wordleGameIO = WordleGameIO.of(); + } + + public static WordleGame of() { + return new WordleGame(); + } + + public void start() { + ready(); + + while (!wordleGameStorage.isGameEnd()) { + try { + run(); + } catch (InvalidInputKeywordException e) { + System.out.println(e.getMessage()); + } catch (InvalidAnswerKeywordException e) { + System.out.println(e.getMessage()); + break; + } catch (ReadFileException e) { + System.out.println(e.getMessage()); + break; + } catch (OutOfChanceException e) { + System.out.println(e.getMessage()); + break; + } + } + + terminate(); + } + + private void run() { + String inputKeyword = WordleGameIO.printInputKeyword(); + + KeywordValidator.validate(inputKeyword, KEYWORD_LENGTH); + + GameResultDto gameResultDto = playRound(inputKeyword, answerKeyword); + + wordleGameIO.printResult(gameResultDto); + } + + public GameResultDto playRound(String inputKeyword, String answerKeyword) { + WordleBlock[] wordleBlocks = WordleBlock.from(inputKeyword, answerKeyword); + + wordleGameStorage.submit(wordleBlocks); + + return GameResultDto.of( + wordleGameStorage.convertHistoryToEmoji(), + TOTAL_CHANCE, + wordleGameStorage.getRestChance(), + wordleGameStorage.isGameClear() + ); + } + + private void ready() { + WordleGameIO.printReady(); + + answerKeyword = WordleGameAnswerGenerator.getAnswerKeyword(); + } + + private void terminate() { + WordleGameIO.printTerminate(); + } +} diff --git a/src/main/java/woowaapplication/pair/game/wordle/WordleGameAnswerGenerator.java b/src/main/java/woowaapplication/pair/game/wordle/WordleGameAnswerGenerator.java new file mode 100644 index 00000000..8441e39f --- /dev/null +++ b/src/main/java/woowaapplication/pair/game/wordle/WordleGameAnswerGenerator.java @@ -0,0 +1,52 @@ +package woowaapplication.pair.game.wordle; + +import java.net.URL; +import java.time.LocalDate; +import java.time.Period; +import java.util.List; + +import woowaapplication.pair.game.util.FileReader; +import woowaapplication.pair.game.wordle.exception.InvalidAnswerKeywordException; + +public class WordleGameAnswerGenerator { + + public static final String WORDS_FILE_NAME = "words.txt"; + + private static final LocalDate standardDate = LocalDate.of(2021, 6, 19); + + public static String getAnswerKeyword(LocalDate comparisonDate) { + List keywords = readKeywordsFromFile(); + + if (keywords.isEmpty()) { + throw new InvalidAnswerKeywordException(); + } + + int index = findAnswerKeywordIndex(keywords, comparisonDate); + + return keywords.get(index); + } + + public static String getAnswerKeyword() { + List keywords = readKeywordsFromFile(); + LocalDate comparisonDate = LocalDate.now(); + + if (keywords.isEmpty()) { + throw new InvalidAnswerKeywordException(); + } + + int index = findAnswerKeywordIndex(keywords, comparisonDate); + + return keywords.get(index); + } + + public static List readKeywordsFromFile() { + URL resource = WordleGameAnswerGenerator.class.getClassLoader().getResource(WORDS_FILE_NAME); + return FileReader.readLinesFromFile(resource); + } + + public static int findAnswerKeywordIndex(List keywords, LocalDate comparisonDate) { + Period period = Period.between(standardDate, comparisonDate); + + return (period.getDays() % keywords.size()) - 1; + } +} diff --git a/src/main/java/woowaapplication/pair/game/wordle/WordleGameIO.java b/src/main/java/woowaapplication/pair/game/wordle/WordleGameIO.java new file mode 100644 index 00000000..d892d4a6 --- /dev/null +++ b/src/main/java/woowaapplication/pair/game/wordle/WordleGameIO.java @@ -0,0 +1,41 @@ +package woowaapplication.pair.game.wordle; + +import java.util.Scanner; + +import woowaapplication.pair.game.wordle.dto.GameResultDto; + +public class WordleGameIO { + + + public WordleGameIO() { + } + + public static void printReady() { + System.out.println("WORDLE을 6번 만에 맞춰 보세요."); + System.out.println("시도의 결과는 타일의 색 변화로 나타납니다."); + + } + + public static String printInputKeyword() { + System.out.println("5글자의 단어를 입력해주세요."); + + Scanner sc = new Scanner(System.in); + return sc.nextLine(); + } + + public static void printTerminate() { + System.out.println("게임이 종료되었습니다."); + } + + public void printResult(GameResultDto gameResultDto) { + System.out.println(gameResultDto.getHistory()); + + if (gameResultDto.isClear()) { + System.out.println(gameResultDto.getChance()); + } + } + + public static WordleGameIO of() { + return new WordleGameIO(); + } +} diff --git a/src/main/java/woowaapplication/pair/game/wordle/WordleGameStorage.java b/src/main/java/woowaapplication/pair/game/wordle/WordleGameStorage.java new file mode 100644 index 00000000..e0fc45f9 --- /dev/null +++ b/src/main/java/woowaapplication/pair/game/wordle/WordleGameStorage.java @@ -0,0 +1,53 @@ +package woowaapplication.pair.game.wordle; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import woowaapplication.pair.game.wordle.domain.Coin; +import woowaapplication.pair.game.wordle.domain.WordleBlock; + +public class WordleGameStorage { + + private final List wordleBlocksHistory = new ArrayList<>(); + private final Coin coin; + + public WordleGameStorage(Coin coin) { + this.coin = coin; + } + + public int getRestChance() { + return coin.getRestChance(); + } + + private boolean isGameOver() { + return coin.isOutOfChance(); + } + + public boolean isGameClear() { + if (wordleBlocksHistory.isEmpty()){ + return false; + } + return WordleBlock.isAllCorrect(wordleBlocksHistory.get(wordleBlocksHistory.size() - 1)); + } + + public boolean isGameEnd() { + return isGameOver() || isGameClear(); + } + + public void submit(WordleBlock[] wordleBlocks) { + wordleBlocksHistory.add(wordleBlocks); + + coin.decreaseChance(); + } + + public List convertHistoryToEmoji() { + return wordleBlocksHistory.stream() + .map(WordleBlock::toEmojiList) + .collect(Collectors.toList()); + } + + public static WordleGameStorage of(Coin coin) { + return new WordleGameStorage(coin); + } +} diff --git a/src/main/java/woowaapplication/pair/game/wordle/domain/Coin.java b/src/main/java/woowaapplication/pair/game/wordle/domain/Coin.java new file mode 100644 index 00000000..f42bbc8f --- /dev/null +++ b/src/main/java/woowaapplication/pair/game/wordle/domain/Coin.java @@ -0,0 +1,32 @@ +package woowaapplication.pair.game.wordle.domain; + +import woowaapplication.pair.game.wordle.exception.OutOfChanceException; + +public class Coin { + + private int restChance; + + public Coin(int restChance) { + this.restChance = restChance; + } + + public void decreaseChance() { + if (restChance < 1) { + throw new OutOfChanceException(); + } + + restChance -= 1; + } + + public int getRestChance() { + return restChance; + } + + public boolean isOutOfChance() { + return restChance < 1; + } + + public static Coin of(int restChance) { + return new Coin(restChance); + } +} diff --git a/src/main/java/woowaapplication/pair/game/wordle/domain/WordleBlock.java b/src/main/java/woowaapplication/pair/game/wordle/domain/WordleBlock.java new file mode 100644 index 00000000..866aa35e --- /dev/null +++ b/src/main/java/woowaapplication/pair/game/wordle/domain/WordleBlock.java @@ -0,0 +1,70 @@ +package woowaapplication.pair.game.wordle.domain; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import woowaapplication.pair.game.wordle.WordleGame; + +public enum WordleBlock { + CORRECT( "🟩"), + EXIST_BUT_WRONG_SPOT( "🟨"), + WRONG( "⬜"), + ; + + private final String emoji; + + WordleBlock(String emoji) { + this.emoji = emoji; + } + + public String getEmoji() { + return emoji; + } + + public static boolean isAllCorrect(WordleBlock[] wordleBlocks) { + return Arrays.stream(wordleBlocks) + .allMatch(block -> block == WordleBlock.CORRECT); + } + + public static WordleBlock[] from(String inputKeyword, String answerKeyword) { + WordleBlock[] resultBlocks = new WordleBlock[WordleGame.KEYWORD_LENGTH]; + Set answerLetters = createAnswerLetters(answerKeyword); + + for (int index = 0; index < inputKeyword.length(); index++) { + char inputLetter = inputKeyword.charAt(index); + char answerLetter = answerKeyword.charAt(index); + + WordleBlock block = compareLetters(inputLetter, answerLetter, answerLetters); + + resultBlocks[index] = block; + } + + return resultBlocks; + } + + private static HashSet createAnswerLetters(String answerKeyword) { + return answerKeyword.chars() + .mapToObj(letter -> (char) letter) + .collect(Collectors.toCollection(HashSet::new)); + } + + private static WordleBlock compareLetters(char inputLetter, char answerLetter, Set answerLetters) { + if (answerLetter == inputLetter) { + return WordleBlock.CORRECT; + } + + if (answerLetters.contains(inputLetter)) { + return WordleBlock.EXIST_BUT_WRONG_SPOT; + } + + return WordleBlock.WRONG; + } + + public static String[] toEmojiList(WordleBlock[] wordleBlocks) { + return Arrays.stream(wordleBlocks) + .map(WordleBlock::getEmoji) + .toArray(String[]::new); + } +} diff --git a/src/main/java/woowaapplication/pair/game/wordle/dto/GameResultDto.java b/src/main/java/woowaapplication/pair/game/wordle/dto/GameResultDto.java new file mode 100644 index 00000000..8e5ae76c --- /dev/null +++ b/src/main/java/woowaapplication/pair/game/wordle/dto/GameResultDto.java @@ -0,0 +1,39 @@ +package woowaapplication.pair.game.wordle.dto; + +import java.util.List; +import java.util.stream.Collectors; + +public class GameResultDto { + private final String history; + private final String chance; + + private final boolean isClear; + + public GameResultDto(String history, String chance, boolean isClear) { + this.history = history; + this.chance = chance; + this.isClear = isClear; + } + + public static GameResultDto of(List scores, int totalChance, int restChance, boolean isClear) { + String chance = isClear? (totalChance - restChance) + "/" + totalChance : null; + + String history = scores.stream() + .map(array -> java.lang.String.join(" ", array)) + .collect(Collectors.joining("\n")); + + return new GameResultDto(history, chance, isClear); + } + + public String getHistory() { + return history; + } + + public String getChance() { + return chance; + } + + public boolean isClear() { + return isClear; + } +} diff --git a/src/main/java/woowaapplication/pair/game/wordle/exception/InvalidAnswerKeywordException.java b/src/main/java/woowaapplication/pair/game/wordle/exception/InvalidAnswerKeywordException.java new file mode 100644 index 00000000..94b1f833 --- /dev/null +++ b/src/main/java/woowaapplication/pair/game/wordle/exception/InvalidAnswerKeywordException.java @@ -0,0 +1,10 @@ +package woowaapplication.pair.game.wordle.exception; + +public class InvalidAnswerKeywordException extends RuntimeException { + + private static final String MESSAGE = "정답 키워드가 유효하지 않습니다."; + + public InvalidAnswerKeywordException() { + super(MESSAGE); + } +} diff --git a/src/main/java/woowaapplication/pair/game/wordle/exception/InvalidInputKeywordException.java b/src/main/java/woowaapplication/pair/game/wordle/exception/InvalidInputKeywordException.java new file mode 100644 index 00000000..2208bc38 --- /dev/null +++ b/src/main/java/woowaapplication/pair/game/wordle/exception/InvalidInputKeywordException.java @@ -0,0 +1,10 @@ +package woowaapplication.pair.game.wordle.exception; + +public class InvalidInputKeywordException extends RuntimeException { + + private static final String MESSAGE = "입력한 키워드가 유효하지 않습니다."; + + public InvalidInputKeywordException() { + super(MESSAGE); + } +} diff --git a/src/main/java/woowaapplication/pair/game/wordle/exception/OutOfChanceException.java b/src/main/java/woowaapplication/pair/game/wordle/exception/OutOfChanceException.java new file mode 100644 index 00000000..a2b2b06e --- /dev/null +++ b/src/main/java/woowaapplication/pair/game/wordle/exception/OutOfChanceException.java @@ -0,0 +1,10 @@ +package woowaapplication.pair.game.wordle.exception; + +public class OutOfChanceException extends RuntimeException { + + private static final String MESSAGE = "남은 기회가 없습니다."; + + public OutOfChanceException() { + super(MESSAGE); + } +} diff --git a/src/main/java/woowaapplication/pair/game/wordle/exception/ReadFileException.java b/src/main/java/woowaapplication/pair/game/wordle/exception/ReadFileException.java new file mode 100644 index 00000000..0e9250e8 --- /dev/null +++ b/src/main/java/woowaapplication/pair/game/wordle/exception/ReadFileException.java @@ -0,0 +1,9 @@ +package woowaapplication.pair.game.wordle.exception; + +public class ReadFileException extends RuntimeException { + private static final String MESSAGE = "파일 데이터 읽기를 실패하였습니다."; + + public ReadFileException() { + super(MESSAGE); + } +} diff --git a/src/test/java/acceptance/WordleGameAcceptanceTest.java b/src/test/java/acceptance/WordleGameAcceptanceTest.java new file mode 100644 index 00000000..56b72319 --- /dev/null +++ b/src/test/java/acceptance/WordleGameAcceptanceTest.java @@ -0,0 +1,102 @@ +package acceptance; + +import static acceptance.support.WordGameSupporter.*; +import static org.junit.jupiter.api.Assertions.*; +import static woowaapplication.pair.game.wordle.WordleGame.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import woowaapplication.pair.game.wordle.WordleGame; +import woowaapplication.pair.game.wordle.domain.Coin; +import woowaapplication.pair.game.wordle.dto.GameResultDto; + +@DisplayName("워들 인수 테스트") +public class WordleGameAcceptanceTest { + private String 정답_키워드 = "jason"; + + private final String 오답_키워드 = "jxosn"; + + private final String 정답_기록 = "🟩 🟩 🟩 🟩 🟩"; + + private final String 오답_기록 = "🟩 ⬜ 🟨 🟨 🟩"; + + private Coin 코인; + private WordleGame 워들_게임; + + @BeforeEach + void setUp() { + 코인 = Coin.of(TOTAL_CHANCE); + 워들_게임 = of(); + } + + + @Nested + @DisplayNameGeneration(ReplaceUnderscores.class) + class 정답_케이스 { + + @Nested + @DisplayName("정답 키워드를 입력하면") + class Context_with_corrected_keyword { + + @Test + @DisplayName("남은 시도 횟수가 그대로 반환되고, 5개의 네모칸이 모두 초록색으로 반환된다") + void it_returns_remaining_chance_and_answer() { + GameResultDto 게임_결과 = 워들_게임.playRound(정답_키워드, 정답_키워드); + + assertAll( + () -> 정답_기록이_올바르다(게임_결과), + () -> 시도_횟수가_올바르다(게임_결과, TOTAL_CHANCE - 1) + ); + } + } + } + + @Nested + @DisplayNameGeneration(ReplaceUnderscores.class) + class 오답_케이스 { + + @Nested + @DisplayName("오답 키워드를 입력하면") + class Context_with_input_incorrect_keyword { + @Test + @DisplayName("올바른 알파벳인 자리는 초록색, 위치가 틀린 자리는 노란색, 오답은 흰색으로 표시한다") + void it_returns_answer_and_decrease_rest_chance() { + int 기존_남은_시도_횟수 = 코인.getRestChance(); + GameResultDto 게임_결과 = 워들_게임.playRound(오답_키워드, 정답_키워드); + + assertAll( + () -> 게임_기록이_올바르다(게임_결과, 오답_기록) + ); + } + } + } + + @Nested + @DisplayNameGeneration(ReplaceUnderscores.class) + class 오답_후_정답_케이스 { + + @Nested + @DisplayName("오답 키워드 입력 후, 정답 키워드를 입력하면") + class Context_with_input_incorrect_keyword { + @Test + @DisplayName("남은 시도 횟수가 2 감소되고, 오답과 정답을 포함한 게임 기록이 반환된다") + void it_returns_answer_and_decrease_rest_chance() { + int 기존_남은_시도_횟수 = 코인.getRestChance(); + + 워들_게임.playRound(오답_키워드, 정답_키워드); + + GameResultDto 게임_결과 = 워들_게임.playRound(정답_키워드, 정답_키워드); + + assertAll( + () -> 게임_기록이_올바르다(게임_결과, 오답_기록+"\n"+정답_기록), + () -> 시도_횟수가_올바르다(게임_결과, 기존_남은_시도_횟수 - 2) + ); + } + } + } +} diff --git a/src/test/java/acceptance/support/WordGameSupporter.java b/src/test/java/acceptance/support/WordGameSupporter.java new file mode 100644 index 00000000..7192be74 --- /dev/null +++ b/src/test/java/acceptance/support/WordGameSupporter.java @@ -0,0 +1,27 @@ +package acceptance.support; + +import static org.assertj.core.api.Assertions.assertThat; + +import woowaapplication.pair.game.wordle.WordleGame; +import woowaapplication.pair.game.wordle.dto.GameResultDto; + +public class WordGameSupporter { + + private static String 정답_기록 = "🟩 🟩 🟩 🟩 🟩"; + + private static int 전체_시도_횟수 = WordleGame.TOTAL_CHANCE; + + public static void 정답_기록이_올바르다(GameResultDto 게임_결과) { + assertThat(게임_결과.getHistory()).isEqualTo(정답_기록); + } + + public static void 게임_기록이_올바르다(GameResultDto 게임_결과, String 예상_결과) { + assertThat(게임_결과.getHistory()).isEqualTo(예상_결과); + } + + public static void 시도_횟수가_올바르다(GameResultDto 게임_결과, int 예상_시도_횟수) { + String 올바른_시도_횟수 = (전체_시도_횟수 - 예상_시도_횟수) + "/" + 전체_시도_횟수; + + assertThat(게임_결과.getChance()).isEqualTo(올바른_시도_횟수); + } +} diff --git a/src/test/java/unit/WordleGameAnswerGeneratorTest.java b/src/test/java/unit/WordleGameAnswerGeneratorTest.java new file mode 100644 index 00000000..264eaa46 --- /dev/null +++ b/src/test/java/unit/WordleGameAnswerGeneratorTest.java @@ -0,0 +1,37 @@ +package unit; + +import java.time.LocalDate; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import woowaapplication.pair.game.wordle.WordleGameAnswerGenerator; + +public class WordleGameAnswerGeneratorTest { + private String 오늘의_정답_키워드 = "jason"; + + private LocalDate 비교_날짜 = LocalDate.of(2021, 6, 24); + + @Nested + @DisplayNameGeneration(ReplaceUnderscores.class) + class 정답_생성_테스트 { + + @Nested + @DisplayName("오늘의 날짜가 2021년 6월 24일이면") + class Context_with_corrected_keyword { + @Test + @DisplayName("오늘의 정답 키워드는 jason이다") + void it_returns_remaining_chance_and_answer() { + String actual오늘의_정답_키워드 = WordleGameAnswerGenerator.getAnswerKeyword(비교_날짜); + + assertThat(오늘의_정답_키워드).isEqualTo(actual오늘의_정답_키워드); + } + } + } + +} diff --git a/src/test/java/unit/WordleGameStorageTest.java b/src/test/java/unit/WordleGameStorageTest.java new file mode 100644 index 00000000..cc0c43d7 --- /dev/null +++ b/src/test/java/unit/WordleGameStorageTest.java @@ -0,0 +1,63 @@ +package unit; + +import static org.assertj.core.api.Assertions.assertThat; +import static woowaapplication.pair.game.wordle.domain.WordleBlock.CORRECT; +import static woowaapplication.pair.game.wordle.domain.WordleBlock.EXIST_BUT_WRONG_SPOT; +import static woowaapplication.pair.game.wordle.domain.WordleBlock.WRONG; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import woowaapplication.pair.game.wordle.WordleGame; +import woowaapplication.pair.game.wordle.domain.Coin; +import woowaapplication.pair.game.wordle.domain.WordleBlock; +import woowaapplication.pair.game.wordle.WordleGameStorage; + +@DisplayName("워들 게임 저장소 테스트") +class WordleGameStorageTest { + private WordleGameStorage wordleGameStorage; + + @BeforeEach + void setUp() { + Coin coin = Coin.of(WordleGame.TOTAL_CHANCE); + wordleGameStorage = WordleGameStorage.of(coin); + } + + @Nested + @DisplayNameGeneration(ReplaceUnderscores.class) + class 정답_확인_기능 { + + @Nested + @DisplayName("정답으로 구성된 워들 블럭들이 주어진다면") + class Context_with_clear_wordle_blocks { + private WordleBlock[] 워들_블럭들 = {CORRECT, CORRECT, CORRECT, CORRECT, CORRECT}; + + @Test + @DisplayName("게임 엔드 판단한다") + void it_clear_game() { + wordleGameStorage.submit(워들_블럭들); + + assertThat(wordleGameStorage.isGameEnd()).isTrue(); + } + } + + @Nested + @DisplayName("정답이 아닌 것이 존재하는 워들 블럭들이 주어진다면") + class Context_with_not_clear_wordle_blocks { + private WordleBlock[] 워들_블럭들 = {CORRECT, WRONG, EXIST_BUT_WRONG_SPOT, CORRECT, CORRECT}; + + @Test + @DisplayName("게임 클리어 실패로 판단한다") + void it_clear_game() { + wordleGameStorage.submit(워들_블럭들); + + assertThat(wordleGameStorage.isGameEnd()).isFalse(); + } + } + } + +} diff --git a/src/test/java/unit/domain/WordleBlockTest.java b/src/test/java/unit/domain/WordleBlockTest.java new file mode 100644 index 00000000..2f91cdc9 --- /dev/null +++ b/src/test/java/unit/domain/WordleBlockTest.java @@ -0,0 +1,67 @@ +package unit.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static woowaapplication.pair.game.wordle.domain.WordleBlock.CORRECT; +import static woowaapplication.pair.game.wordle.domain.WordleBlock.EXIST_BUT_WRONG_SPOT; +import static woowaapplication.pair.game.wordle.domain.WordleBlock.WRONG; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import woowaapplication.pair.game.wordle.domain.WordleBlock; + +class WordleBlockTest { + + @Nested + @DisplayNameGeneration(ReplaceUnderscores.class) + class 입력_키워드와_정답_키워드로_워들블럭_리스트를_만드는_기능 { + private String 정답_키워드 = "jason"; + + @Nested + @DisplayName("입력 키워드가 정답일 경우") + class Context_with_correct_input_keyword { + private String 입력_키워드 = "jason"; + + @Test + @DisplayName("CORRECT로 구성된 워들 블럭들을 반환환다") + void it_returns_correct_wordle_blocks() { + WordleBlock[] 워들_블럭들 = WordleBlock.from(입력_키워드, 정답_키워드); + + assertThat(워들_블럭들).containsOnly(CORRECT); + } + } + + @Nested + @DisplayName("입력 키워드가 한 글자도 못맞춘 경우") + class Context_with_wrong_input_keyword { + private String 입력_키워드 = "xxxxx"; + + @Test + @DisplayName("WRONG으로 구성된 워들 블럭들을 반환환다") + void it_returns_correct_wordle_blocks() { + WordleBlock[] 워들_블럭들 = WordleBlock.from(입력_키워드, 정답_키워드); + + assertThat(워들_블럭들).containsOnly(WRONG); + } + } + + @Nested + @DisplayName("입력 키워드의 첫번째 글자는 정답이고," + + "나머지 글자들은 정답 키워드에 존재하는 글자이지만 다른 위치에 있는 경우") + class Context_with_exist_but_wrong_spot_input_keyword { + + private String 입력_키워드 = "jjjjj"; + + @Test + @DisplayName("첫번째는 CORRECT, 나머지는 EXIST_BUT_WRONG_SPOT으로 구성된 워들 블럭들을 반환환다") + void it_returns_correct_wordle_blocks() { + WordleBlock[] 워들_블럭들 = WordleBlock.from(입력_키워드, 정답_키워드); + + assertThat(워들_블럭들).containsExactly(CORRECT, EXIST_BUT_WRONG_SPOT, EXIST_BUT_WRONG_SPOT, + EXIST_BUT_WRONG_SPOT, EXIST_BUT_WRONG_SPOT); + } + } + } +} diff --git a/src/test/java/unit/util/KeywordValidatorTest.java b/src/test/java/unit/util/KeywordValidatorTest.java new file mode 100644 index 00000000..d6aee3d1 --- /dev/null +++ b/src/test/java/unit/util/KeywordValidatorTest.java @@ -0,0 +1,49 @@ +package unit.util; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import woowaapplication.pair.game.util.KeywordValidator; +import woowaapplication.pair.game.wordle.exception.InvalidInputKeywordException; + +class KeywordValidatorTest { + + @Nested + @DisplayNameGeneration(ReplaceUnderscores.class) + class 입력_검증_예외_케이스 { + private final int LENGTH_LIMIT = 5; + + @Nested + @DisplayName("알파벳이 아닌 입력이 들어오면") + class Context_with_not_string_input { + + private final String 알파벳이_아닌_키워드 = "12345"; + + @Test + @DisplayName("시도가 무효된다") + void it_makes_chance_canceled() { + assertThatThrownBy(() -> KeywordValidator.validate(알파벳이_아닌_키워드, LENGTH_LIMIT)).isInstanceOf( + InvalidInputKeywordException.class); + } + } + + @Nested + @DisplayName("글자수 제한에 맞지 않는 입력이 들어오면") + class Context_with_over_limit_input { + + private final String 글자수_제한에_맞지_않는_키워드 = "alexholden"; + + @Test + @DisplayName("시도가 무효된다") + void it_makes_chance_canceled() { + assertThatThrownBy(() -> KeywordValidator.validate(글자수_제한에_맞지_않는_키워드, LENGTH_LIMIT)).isInstanceOf( + InvalidInputKeywordException.class); + } + } + } +} diff --git a/src/test/resources/words.txt b/src/test/resources/words.txt new file mode 100644 index 00000000..e67708f4 --- /dev/null +++ b/src/test/resources/words.txt @@ -0,0 +1,7 @@ +alexz +holde +mutoz +bunso +jason +johnz +bobzz