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

Pako 워들제출합니다. #4

Open
wants to merge 55 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
5d8479e
doc: 기능 명세서 추가
SGKIM94 Apr 12, 2022
eb31a9d
feat: Word에 대한 기능명세서의 테스트 코드 추가 및 클래스 추가
SGKIM94 Apr 12, 2022
f2afbd3
refactor: 불필요한 gitkeep 파일 제거
SGKIM94 Apr 12, 2022
5bab906
doc: 구현한 기능에 대한 체크 추가
SGKIM94 Apr 12, 2022
58b435b
refactor: klint에 대한 컨벤션 적용
SGKIM94 Apr 12, 2022
35852c3
feat: 타일에 대한 기능 명세서 구현 및 테스트 코드 추가
SGKIM94 Apr 12, 2022
6fde073
doc: 구현한 기능에 대한 체크 추가
SGKIM94 Apr 12, 2022
3bb8164
refactor: klint 에 대한 컨벤션 적용
SGKIM94 Apr 12, 2022
6fc2288
refactor: 단어를 비교하여 타일을 전달하는 WordMatcher 추가 및 테스트코드 추가
SGKIM94 Apr 12, 2022
991380a
refactor: Word 내 인덱스 위치에 단어가 존재하는지 찾는 foundAlphabet 함수 및 테스트코드 추가
SGKIM94 Apr 12, 2022
7369f19
refactor: 매직넘버에 대한 상수 추출
SGKIM94 Apr 12, 2022
51876df
doc: 기능 명세를 새로 추가함에 따른 수정
SGKIM94 Apr 13, 2022
7bba0b9
feat: 매칭에 대한 노란색과 회색이 되는 케이스에 대한 로직 및 테스트코드 추가
SGKIM94 Apr 13, 2022
3d994bf
feat: Word 에 알파벳이 포함되는 지에 대한 함수 및 테스트코드 추가
SGKIM94 Apr 13, 2022
050f619
doc: 매칭에 대해 구현한 기능 체크리스트 추가
SGKIM94 Apr 13, 2022
de730f8
feat: 게임을 승리하는 규칙과 실패하는 규칙에 대한 기능 및 테스트코드 추가
SGKIM94 Apr 14, 2022
d9546f3
feat: 시도횟수에 대한 value object 추출 진행
SGKIM94 Apr 14, 2022
3bb172b
refactor: 불필요한 예외 명시 제거 및 private 접근제어자 추가
SGKIM94 Apr 14, 2022
ced063d
doc: 구현된 기능에 대한 명세서 체크리스트 추가
SGKIM94 Apr 14, 2022
759013e
refactor: foundAlphabet -> findAlphabet 변경 및 메서드 추출 진행
SGKIM94 Apr 14, 2022
1d39fa9
refactor: private 접근제어자 추가
SGKIM94 Apr 14, 2022
eb71087
refactor: 가독성을 위한 until로 변경
SGKIM94 Apr 14, 2022
cbf8f74
feat: junit test import 추가
SGKIM94 Apr 14, 2022
8e52fd5
refactor: answer 에 대한 중복된 파라미터로 인해 멤버변수로 전환
SGKIM94 Apr 14, 2022
0a32d12
refactor: 게임 결과에 대해서 타일들을 전달하기 위한 요구사항 구현 및 테스트 코드 추가
SGKIM94 Apr 14, 2022
b06d616
feat: Tiles value object 추가 및 테스트코드 추가
SGKIM94 Apr 14, 2022
79f9e49
refactor: Tiles 추가에 따른 수정
SGKIM94 Apr 14, 2022
c4263dd
refactor: 불필요한 tryCount 제거
SGKIM94 Apr 14, 2022
21a5791
feat: words.txt 의 정답들을 가지는 Answers 추가
SGKIM94 Apr 14, 2022
1d526cd
feat: 입력을 받기 위한 InputView 추가
SGKIM94 Apr 14, 2022
1f4a332
refactor: 결과를 출력하기 위한 resultView 추가
SGKIM94 Apr 14, 2022
ad3d1b5
feat: 게임을 플레이하기 위한 기능 구현
SGKIM94 Apr 14, 2022
6783141
doc: 구현된 기능에 대한 체크리스트 추가
SGKIM94 Apr 14, 2022
5319a1a
refactor: 불필요한 함수 제거
SGKIM94 Apr 14, 2022
6d6d054
refactor: data 를 value class 로 변경
SGKIM94 Apr 14, 2022
e4dfdf0
refactor: 불필요한 선언 제거
SGKIM94 Apr 14, 2022
4ee303d
refactor: MAX_TRY_COUNT 에 대한 상수 추출
SGKIM94 Apr 14, 2022
6cc86be
refactor: List 로 사용할 수 있도록 선언
SGKIM94 Apr 14, 2022
beb1b18
refactor: 타일을 뷰와 분리하기 위한 viewTiles 추가
SGKIM94 Apr 14, 2022
2494155
refactor: 결과에 대한 멘트 수저
SGKIM94 Apr 14, 2022
605b476
refactor: Words 일급 컬렉션 추가
SGKIM94 Apr 14, 2022
dd9930b
refactor: 코틀린 함수 사용하도록 수정
SGKIM94 Apr 14, 2022
828fb34
refactor: 조건문을 간결하게 하도록 수정 및 불필요한 delicate 제거
SGKIM94 Apr 15, 2022
155f16a
refactor: delicate 제거에 따른 수정
SGKIM94 Apr 15, 2022
72ebdb8
feat: 결과물을 만들어내기 위한 Results 클래스 추가 및 테스트 코드 추가
SGKIM94 Apr 15, 2022
f57b1e5
refactor: word 를 매번 생성가능하도록 수정 및 2021.6.19에 대한 상수 추출
SGKIM94 Apr 15, 2022
64f24a9
refactor: klint
SGKIM94 Apr 15, 2022
5bce301
refactor: readlin() 으로 변경
SGKIM94 Apr 15, 2022
e544c0f
refactor: results를 ResultView 에서 가져다 사용하도록 수정
SGKIM94 Apr 15, 2022
c0633eb
refactor: mutableList 제거하기 위한 불변형태로 수정
SGKIM94 Apr 15, 2022
7e67435
refactor: Words 일급컬렉션 추가에 따른 수정 및 테스트코드 추가
SGKIM94 Apr 15, 2022
842c92e
refactor: kotlin extension 함수로 변겨
SGKIM94 Apr 15, 2022
0fcd81e
refactor: 불필요한 println 제거
SGKIM94 Apr 15, 2022
293c4b6
refactor: 코드 리뷰 반영 및 변경 필요한 부분 추가
SGKIM94 Apr 15, 2022
c40742d
refactor: 주석 삭제
etff Apr 16, 2022
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
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,43 @@
- 정답과 답안은 `words.txt`에 존재하는 단어여야 한다.
- 정답은 매일 바뀌며 ((현재 날짜 - 2021년 6월 19일) % 배열의 크기) 번째의 단어이다.


### 기능 명세서
- [X] 단어
- [X] 단어를 만들 수 있다
- [X] 단어는 5글자가 아닌 경우 예외를 던진다
- [X] 단어는 알파벳이어야한다
- [X] 정답은 매일 바뀌며 ((현재 날짜 - 2021년 6월 19일) % 배열의 크기) 번째의 단어이다
- [X] 타일
- [X] 타일은 노란색, 초록색, 회색을 가진다
- [X] 회색 타일은 ⬜, 초록색 타일은 🟩, 노란색 타일은 🟨 값을 가진다
- [ ] 매칭
- [X] 답안과 정답의 한 단어가 위치와 스펠이 같은 경우 초록색이 된다
- [X] 답안과 정답의 한 단어가 위치는 같지만 스펠이 다른 경우 노란색이 된다
- [X] 답안과 정답의 한 단어가 위치도 다르고 스펠도 다른 경우 회색이 된다
- [ ] 답안과 정답의 단어 중 앞서 한 단어가 판별이 난 경우 뒤에 있는 동일한 단어는 판별되지 않는다
- (예 - 정답이 ROYAL인 경우 MOONY를 입력하면 회초회회노 가 결과로 나온다)
- hello가 답이고 llloo 라고 입력하면 GRAY, YELLO, GREEN, GRAY, GREEN 로 나와야한다
- 우선순위가 GREEN -> YELLO -> GRAY 순
- [X] 게임
- [X] 5글자의 모든 위치와 스펠을 6번안에 맞추는 경우 게임에 승리한다
- [X] 6번의 기회안에 5글자를 모두 맞추면 성공한다.
- [X] 6번의 기회동안 맞추지 못하면 패배한다
- [X] 5글자의 모든 위치와 스펠이 같지 않은 경우 시도 횟수가 더해진다
- [X] 정답과 답안을 비교하여 결과 타일을 담아 전달한다
- [X] 답안을 입력받는다
- [X] 정답과 답안(단어)는 words.txt 안에 있는 것이어야 한다
- [X] 정답이 words.txt 에 없는 것인 경우 재입력을 받는다
- [X] 정답은 매일 바뀌며 ((현재 날짜 - 2021년 6월 19일) % 배열의 크기) 번째의 단어이다
- [X] 결과를 출력한다
- [X] 결과를 누적해서 보여준다
- [X] 종료 시 시도한 횟수와 총 횟수를 출력한다.

### 입출력 요구 사항

#### 실행 결과 예시


```
WORDLE을 6번 만에 맞춰 보세요.
시도의 결과는 타일의 색 변화로 나타납니다.
Expand Down
Empty file removed src/main/kotlin/.gitkeep
Empty file.
38 changes: 38 additions & 0 deletions src/main/kotlin/wordle/controller/Application.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package wordle.controller

import wordle.domain.Answers
import wordle.domain.Game
import wordle.domain.Results
import wordle.domain.Word
import wordle.view.InputView
import wordle.view.ResultView

private const val LAST_PLAY_COUNT = 6

fun main() {
val game = Game(Answers().answer)

ResultView.printInit()

playGame(game)
}

private fun playGame(game: Game) {
val results = Results()

var tryCount = 0

while (tryCount < LAST_PLAY_COUNT) {
val inputWord = InputView.askWord(Answers.WORDS)
val resultTiles = game.play(Word(inputWord))

results.combine(resultTiles)
ResultView.printAllResults(results)
tryCount++

if (game.isWinner(resultTiles)) {
ResultView.printGamePlayCount(tryCount)
break
}
}
}
24 changes: 24 additions & 0 deletions src/main/kotlin/wordle/domain/Answers.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package wordle.domain

import java.io.File

class Answers {
val answer: Word
get() = createAnswer()

private fun createAnswer() = WORDS.findAnswer()

companion object {
private const val ANSWERS_TEXT_PATH = "./words.txt"
Copy link

Choose a reason for hiding this comment

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

답안은 도메인인데 외부 자원(resource)의 특정 위치를 알고 있어야 할 필요가 있을까요? 도메인 영역은 가능한 독립적으로 구현되어있는게 좋을것 같습니다.

val WORDS = createWords()

private fun createWords(): Words {
val wordsFile = getResourceText().readLines()
return Words(wordsFile.map { Word(it) })
}

private fun getResourceText(): File {
return File(ClassLoader.getSystemResource(ANSWERS_TEXT_PATH).file)
}
}
}
19 changes: 19 additions & 0 deletions src/main/kotlin/wordle/domain/Game.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package wordle.domain

class Game(private val answer: Word) {

fun play(input: Word): Tiles {
val inputChars = input.value.toCharArray()
val wordMatcher = WordMatcher(answer)

return createResultTiles(inputChars, wordMatcher)
}

private fun createResultTiles(inputChars: CharArray, wordMatcher: WordMatcher): Tiles {
return Tiles(inputChars.mapIndexed { index, it -> wordMatcher.match(it.toString(), index) })
}

fun isWinner(resultTiles: Tiles): Boolean {
return resultTiles.isWinner()
}
}
11 changes: 11 additions & 0 deletions src/main/kotlin/wordle/domain/Results.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package wordle.domain

class Results {
private val _results = mutableListOf<Tiles>()
val results: List<Tiles>
get() = _results

fun combine(newResults: Tiles) {
_results.add(newResults)
}
}
7 changes: 7 additions & 0 deletions src/main/kotlin/wordle/domain/Tile.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package wordle.domain

enum class Tile {
YELLOW,
GREEN,
GRAY
}
15 changes: 15 additions & 0 deletions src/main/kotlin/wordle/domain/Tiles.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package wordle.domain

@JvmInline
value class Tiles(val tiles: List<Tile>) {

fun isWinner(): Boolean {
val count = tiles.count { it == Tile.GREEN }

return count == TOTAL_TILE_COUNT
}

companion object {
private const val TOTAL_TILE_COUNT = 5
}
Comment on lines +6 to +14
Copy link

Choose a reason for hiding this comment

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

테스트에서는 모두 GREEN인지를 확인하고 있는데요 그렇다면 그에 맞게 코드도 변경해주는건 어떨까요? 특정 갯수보다 더 명확하게 의미를 나타내도록 코드 수정이 가능할것 같습니다.

}
33 changes: 33 additions & 0 deletions src/main/kotlin/wordle/domain/Word.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package wordle.domain

@JvmInline
value class Word(val value: String) {

init {
require(isWordSizeAndAlphabet()) {
WRONG_WORD_SIZE_MESSAGE
}
}

private fun isWordSizeAndAlphabet() = value.length == WORD_SIZE && ALPHABET_REGEX.matches(value)

fun findAlphabet(index: Int): String {
require(index >= FIRST_INDEX && index <= value.length) {
INDEX_OUT_RANGE_MESSAGE
}

return value[index].toString()
}

fun contains(compareValue: String): Boolean {
return value.contains(compareValue)
}

companion object {
private const val FIRST_INDEX = 0
private const val WORD_SIZE = 5
private const val WRONG_WORD_SIZE_MESSAGE = "5글자여야합니다"
private const val INDEX_OUT_RANGE_MESSAGE = "인덱스 범위를 초과했습니다."
private val ALPHABET_REGEX = Regex("^[a-zA-Z]*$")
}
}
16 changes: 16 additions & 0 deletions src/main/kotlin/wordle/domain/WordMatcher.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package wordle.domain

class WordMatcher(private val answer: Word) {

fun match(alphabet: String, index: Int): Tile {
if (alphabet == answer.findAlphabet(index)) {
return Tile.GREEN
}

if (answer.contains(alphabet)) {
return Tile.YELLOW
}

return Tile.GRAY
}
}
21 changes: 21 additions & 0 deletions src/main/kotlin/wordle/domain/Words.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package wordle.domain

import java.time.LocalDate
import java.time.temporal.ChronoUnit

class Words(private val words: List<Word>) {

fun findAnswer(): Word {
return words[(findAnswerPosition() % words.size).toInt()]
}

private fun findAnswerPosition() = ChronoUnit.DAYS.between(SUBTRACT_DATE_FOR_ANSWER, LocalDate.now())

fun contains(value: String): Boolean {
return words.contains(Word(value))
}

companion object {
private val SUBTRACT_DATE_FOR_ANSWER = LocalDate.of(2021, 6, 19)
}
}
22 changes: 22 additions & 0 deletions src/main/kotlin/wordle/view/InputView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package wordle.view

import wordle.domain.Words

object InputView {

fun askWord(words: Words): String {
while (true) {
val input = question()
if (words.contains(input)) {
return input
}

println("올바르지 않은 단어입니다.")
}
}

private fun question(): String {
println("정답을 입력해 주세요.")
return readln()
}
}
39 changes: 39 additions & 0 deletions src/main/kotlin/wordle/view/ResultView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package wordle.view

import wordle.domain.Results
import wordle.domain.Tile

private const val MAX_TRY_COUNT = 6
Copy link

Choose a reason for hiding this comment

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

ApplicationLAST_PLAY_COUNT와 동일한 의미이고 변경도 동일하게 이뤄질것같네요


object ResultView {

fun printInit() {
println("WORDLE을 ${MAX_TRY_COUNT}번 만에 맞춰 보세요.\n시도의 결과는 타일의 색 변화로 나타납니다.")
}

fun printGamePlayCount(index: Int) {
println("$index /$MAX_TRY_COUNT")
}

fun printAllResults(results: Results) {
results.results.forEach {
printAllTiles(it.tiles)
println()
}
}

private fun printAllTiles(tiles: List<Tile>) {
tiles.forEach { tile ->
print(tile.viewTile())
}
}

private val Tile.viewTile: () -> String
get() = {
when (this) {
Tile.GREEN -> "\uD83D\uDFE9"
Tile.YELLOW -> "\uD83D\uDFE8"
Tile.GRAY -> "⬜"
}
}
}
Empty file removed src/test/kotlin/.gitkeep
Empty file.
19 changes: 19 additions & 0 deletions src/test/kotlin/wordle/domain/GameTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package wordle.domain

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test

class GameTest {

@Test
fun `6번의 기회안에 5글자를 모두 맞추면 성공한다`() {
val givenAnswer = Word("words")
val game = Game(givenAnswer)
val givenInput = Word("words")

val result = game.play(givenInput)
val isWinner = game.isWinner(result)

assertThat(isWinner).isTrue
}
}
20 changes: 20 additions & 0 deletions src/test/kotlin/wordle/domain/ResultsTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package wordle.domain

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test

class ResultsTest {

@Test
fun `기존에_존재하는_결과타일에_타일결과를_추가한다`() {
val results = Results()

results.combine(Tiles(listOf(Tile.YELLOW, Tile.YELLOW, Tile.YELLOW, Tile.GRAY, Tile.YELLOW)))
results.combine(Tiles(listOf(Tile.GREEN, Tile.GRAY, Tile.GREEN, Tile.GRAY, Tile.YELLOW)))

assertThat(results.results).containsExactly(
Tiles(listOf(Tile.YELLOW, Tile.YELLOW, Tile.YELLOW, Tile.GRAY, Tile.YELLOW)),
Tiles(listOf(Tile.GREEN, Tile.GRAY, Tile.GREEN, Tile.GRAY, Tile.YELLOW))
)
}
}
12 changes: 12 additions & 0 deletions src/test/kotlin/wordle/domain/TileTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package wordle.domain

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test

class TileTest {

@Test
fun `타일은 노란색, 초록색, 회색을 가진다`() {
assertThat(Tile.values()).containsExactly(Tile.YELLOW, Tile.GREEN, Tile.GRAY)
}
}
24 changes: 24 additions & 0 deletions src/test/kotlin/wordle/domain/TilesTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package wordle.domain

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test

class TilesTest {
@Test
fun `모두 초록색인 경우 우승자이다`() {
val givenTiles = Tiles(listOf(Tile.GREEN, Tile.GREEN, Tile.GREEN, Tile.GREEN, Tile.GREEN))

val actual = givenTiles.isWinner()

assertThat(actual).isTrue
}

@Test
fun `모두 초록색이 아닌 경우 우승자가아니다`() {
val givenTiles = Tiles(listOf(Tile.GREEN, Tile.YELLOW, Tile.GREEN, Tile.GRAY, Tile.GREEN))

val actual = givenTiles.isWinner()

assertThat(actual).isFalse
}
}
Loading