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

[Wordle] 돌쓰팀(yooth) 미션 제출합니다. #20

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0ba1760
docs: README 정리
djawnstj Jun 17, 2024
3c19d5c
feat: word.txt 파일 로더
djawnstj Jun 17, 2024
12e9b47
feat: 예외 메시지 이넘
djawnstj Jun 17, 2024
b4e4aea
feat: 단어의 글자를 담는 Letter 클래스
djawnstj Jun 17, 2024
233336e
feat: Letter 테스트
djawnstj Jun 17, 2024
833ce1c
feat: 단어 클래스
djawnstj Jun 17, 2024
da3a4e9
feat: Word 클래스 테스트
djawnstj Jun 17, 2024
719819f
feat: 오늘의 단어 유틸
djawnstj Jun 17, 2024
66e1c1c
feat: TodayWord 테스트
djawnstj Jun 17, 2024
4cb13b8
feat: 단어 비교 결과 이넘
djawnstj Jun 17, 2024
e5dfcdd
feat: 시도 가능 횟수 클래스
djawnstj Jun 17, 2024
5ed2326
feat: TryCount 테스트
djawnstj Jun 17, 2024
c660fc4
feat: 단어 비교 결과 값 객체
djawnstj Jun 17, 2024
1617e24
feat: WordResult 테스트
djawnstj Jun 17, 2024
9b5a258
feat: 단어 비교 결과 일급 컬렉션
djawnstj Jun 17, 2024
548f705
feat: WordResults 테스트
djawnstj Jun 17, 2024
1ee237b
feat: 답안 단어 정답 비교 클래스
djawnstj Jun 17, 2024
ae2fb60
feat: WordleGameLogic 테스트
djawnstj Jun 17, 2024
ee98432
feat: 답안 단어 비교 결과 생성 클래스
djawnstj Jun 17, 2024
4c4dc67
feat: WordComparator 테스트
djawnstj Jun 17, 2024
203704e
feat: 답안 단어 비교 결과 표출 이넘
djawnstj Jun 17, 2024
8a78dcd
feat: UI 유틸
djawnstj Jun 17, 2024
4ca3984
feat: 게임 실행기 클래스
djawnstj Jun 17, 2024
cf35dc8
feat: WordleGame 테스트
djawnstj Jun 17, 2024
92169e9
feat: 게임 진입점(Controller)
djawnstj Jun 17, 2024
00d39c2
feat: 게임 진입점(Controller)
djawnstj Jun 17, 2024
3c9fde1
fix: kotlin, gradle 버전 변경
djawnstj Jun 17, 2024
90de547
fix: 오타 수정
djawnstj Jun 17, 2024
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
216 changes: 175 additions & 41 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
plugins {
kotlin("jvm") version "1.9.0"
kotlin("jvm") version "1.9.23"
id("org.jlleitschuh.gradle.ktlint") version "12.1.0"
}

Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
7 changes: 7 additions & 0 deletions src/main/kotlin/wordle/Application.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package wordle

import wordle.controller.WordleGameController

fun main() {
WordleGameController().run()
}
13 changes: 13 additions & 0 deletions src/main/kotlin/wordle/controller/WordleGameController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package wordle.controller

import wordle.domain.TodayWord
import wordle.domain.WordleGame
import wordle.view.printWelcome
import java.time.LocalDate

class WordleGameController {
fun run() {
printWelcome()

Choose a reason for hiding this comment

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

얘만 여기 홀로있는 이유가 있을까요? 🧐

Copy link
Author

Choose a reason for hiding this comment

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

시작 전 안내 메시지 출력과 게임 런타임(?) 은 다르다고 생각했습니다!

WordleGame().play(TodayWord(LocalDate.now()))
}
}
27 changes: 27 additions & 0 deletions src/main/kotlin/wordle/domain/Letter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package wordle.domain

import wordle.exception.ExceptionMessage.INVALID_LETTER

data class Letter(private val value: Char) {
init {
check(isAlphabetOrMatchMarker()) { INVALID_LETTER.message }
}
Comment on lines +6 to +8

Choose a reason for hiding this comment

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

초기화 블록 사용 좋네요~


fun changeMatchMarker(): Letter = MATCH_MARKER_LETTER

Choose a reason for hiding this comment

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

change 여서 내부 상태를 바꾸는걸 예상하고 봤는데 객체를 리턴하는 함수인게 네이밍이 약간 어색한것같아요

Copy link
Author

Choose a reason for hiding this comment

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

앗 메서드 이름이 적절하지 않았네요ㅠㅠ
처음 만들었던 메서드에서 로직을 변경했는데 그때 같이 변경해주지 못한 것 같아요.


fun value(): String {
return value.toString()
}

private fun isAlphabetOrMatchMarker(): Boolean = isAlphabet() || isMatchMarker()

private fun isAlphabet(): Boolean = (value in ALPHABET)

private fun isMatchMarker(): Boolean = (value == MATCH_MARKER)

companion object {
private const val MATCH_MARKER = '#'
private val ALPHABET = ('a'..'z').toSet()
private val MATCH_MARKER_LETTER = Letter(MATCH_MARKER)
}
}
7 changes: 7 additions & 0 deletions src/main/kotlin/wordle/domain/LetterMatch.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package wordle.domain

enum class LetterMatch {
CORRECT,
PRESENT,
ABSENT,
}
15 changes: 15 additions & 0 deletions src/main/kotlin/wordle/domain/TodayWord.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package wordle.domain

import wordle.loader.dictionaryWordsSize
import wordle.loader.indexOfDictionaryWord
import java.time.LocalDate
import java.time.temporal.ChronoUnit

private val CRITERION_DATE: LocalDate = LocalDate.of(2021, 6, 19)

fun extractWordleWord(date: LocalDate): String {
val between = ChronoUnit.DAYS.between(CRITERION_DATE, date)
val index = (between % dictionaryWordsSize).toInt()

return indexOfDictionaryWord(index)
}
16 changes: 16 additions & 0 deletions src/main/kotlin/wordle/domain/TryCount.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package wordle.domain

import wordle.exception.ExceptionMessage

class TryCount(private var count: Int = MAX_TRY_COUNT) {
val attempts get() = MAX_TRY_COUNT - count

Choose a reason for hiding this comment

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

오 계산식에도 프로퍼티를 활용하시는군요~
저는 추후에 매개변수가 필요해질때 바꾸는게 불편해서 처음부터 함수로 만드는편인데 어떻게 생각하시나요
아니면 변경하는 꿀팁이 있을까요ㅎㅎ

Copy link
Author

Choose a reason for hiding this comment

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

저도 확실한 게터 역할이 아니라면 메서드로 만드는 편입니다!
커스텀 게터를 사용하는 경우는 클래스 내부에서만 호출하거나 앞으로도 변하지 않겠다는 (90% 정도...?)확신이 있다면 public 커스텀 게터를 사용하는 편입니다.


fun isRemainder(): Boolean = count > 0

fun minus() {
check(isRemainder()) { ExceptionMessage.TRY_COUNT_HAS_NOT_REMAINDER.message }
--count
}
Comment on lines +10 to +13

Choose a reason for hiding this comment

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

꼼꼼한 예외체크네요 👍

}

const val MAX_TRY_COUNT = 6
40 changes: 40 additions & 0 deletions src/main/kotlin/wordle/domain/Word.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package wordle.domain

import wordle.exception.ExceptionMessage
import wordle.loader.isContainsDictionaryWord
Comment on lines +3 to +4

Choose a reason for hiding this comment

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

요기를 보시면 도메인인데 외부를 의존하고 있네요
저는 보통 도메인은 다른것에 의존성이 없게 짜려고 노력하는편인데 어떻게 생각하시는지 궁금합니다~

Copy link
Author

Choose a reason for hiding this comment

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

제가 계층 간 의존에 대한 개념이 조금 부족한데 어떻게 풀어야 할지 고민해 보겠습니다!

import java.time.LocalDate

typealias TodayWord = Word

Choose a reason for hiding this comment

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

정답단어과 입력단어에서 클래스분리 대신 중도를 찾으셨군요ㅋㅋ
적절한 활용인것 같습니다~ 💯


class Word(private val word: List<Letter>) : List<Letter> by word {
fun letters(): String {
return word.joinToString("", transform = Letter::value)
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as Word

return word == other.word
}

override fun hashCode(): Int {
return word.hashCode()
}
}

const val WORD_LENGTH = 5

fun Word(word: String): Word {

Choose a reason for hiding this comment

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

외부에서 Word(listOf<Letter>()) 처럼 유효성체크없이 생성이 가능하겠네요

주생성자를 private 으로 막고 동반객체에 팩터리메서드를 만들어볼수 있을것같아요! (그러고보니까 저도 빼먹었네요ㅋㅋ)

Copy link
Author

Choose a reason for hiding this comment

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

주 생성자에 접근 제어자를 추가 해야 했는데 급하게 제출하느라 놓쳤네요ㅠㅠ

Choose a reason for hiding this comment

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

궁금해서 해봤는데 주생성자를 private으로 막으면 가짜 생성자를 사용할수가 없네요
internal 이 최선인것같아요

check(word.isNotBlank()) { ExceptionMessage.WORD_NOT_BLANK.message }
check(word.length == WORD_LENGTH) { ExceptionMessage.INVALID_WORD_LENGTH.message }
check(isContainsDictionaryWord(word)) { ExceptionMessage.WORD_NOT_FOUND.message }

return Word(word.toCharArray().map { Letter(it) })
}

fun TodayWord(today: LocalDate): TodayWord {
return Word(extractWordleWord(today))

Choose a reason for hiding this comment

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

저는 네임스페이스처럼 나오는게 보기편해서 확장함수가 아니면 탑레벨에 선언하지 않고 클래스안에 넣어두는 편인데요
IDE 나 깃헙콘솔로 들어오지 않으면 extractWordleWord 가 어딨는지 찾기 힘든 단점도 있네요

탑레벨에 선언하는걸 선택하는데는 어떤 장점들이 있어서 결정하셨는지 궁금합니다!

Copy link
Author

Choose a reason for hiding this comment

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

저는 개인적으로 코틀린의 가짜 생성자 패턴을 좋아하는데 이번 미션 하면서 이 내용을 녹여보고 싶었습니다!

작업 하면서도 '파일을 분리하는 것이 좋나?' 에 대한 고민은 있었지만 파일이 너무 많아질 것 같아서 같은 파일 안에 함수를 두었습니다.

Choose a reason for hiding this comment

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

아하 저는 extractWordleWord 이 함수가 클래스없이 탑레벨에 선언된거에 대해서
제가 적은 단점에 비해 어떤 장점이 있는지 궁금해서 코멘트를 단거였는데요
위치가 부적절해서 오해여지가 있었군요 죄송합니다ㅎ

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

class WordComparator(
private val letters: MutableList<Letter>,
private val wordResult: WordResult = WordResult(),
) {
fun matchCorrect(answerWord: Word): WordComparator =
apply {
letters.forEachIndexed { index, _ ->
changeCorrectMatch(index, answerWord[index])
}
}

fun matchPresent(answerWord: Word): WordComparator =
apply {
letters.forEachIndexed { index, _ ->
changePresentMatch(index, answerWord[index])
}
}

fun result(): WordResult = wordResult

private fun changeCorrectMatch(
index: Int,
letter: Letter,
) {
if (isCorrectLetter(index, letter)) {
wordResult.changeMatchType(index, LetterMatch.CORRECT)
changeLetter(index)
}
}

private fun changePresentMatch(
index: Int,
letter: Letter,
) {
if (isPresentLatter(index, letter)) {
wordResult.changeMatchType(index, LetterMatch.PRESENT)
changeLetters(letter)
}
}

private fun isCorrectLetter(
index: Int,
letter: Letter,
) = letters[index] == letter

private fun isPresentLatter(
index: Int,
letter: Letter,
) = !isCorrectLetter(index, letter) && contains(letter)

private fun changeLetter(index: Int) {
letters[index] = letters[index].changeMatchMarker()

Choose a reason for hiding this comment

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

같은 객체를 가져오는 건데 꼭 letters 리스트의 특정 원소의 메서드로 MATCH_MARKER_LETTER를 가져와야할까요?
Letter.MATCH_MARKER_LETTER 혹은 Letter.matchMarkerLetter() 등이 더 이해하기 쉬운것 같은데 어떻게 생각하시나요 😃

함수이름도 changeLetter 보단 이미 매칭됨을 마킹한다는 맥락이 들어가면 더 읽기 쉬웠을것 같아요

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.

함수이름도 changeLetter 보단 이미 매칭됨을 마킹한다는 맥락이 들어가면 더 읽기 쉬웠을것 같아요

좋은 리뷰 감사합니다.👍

}

private fun changeLetters(letter: Letter) {
changeLetter(letters.indexOf(letter))
}

Comment on lines +57 to +60

Choose a reason for hiding this comment

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

저는 이 함수가 계속 letter가 반복되니까 뭐가 입력단어가 인지 뭐가 오늘의단어인지 파악하기 쉽지 않아서 읽기 힘들었던것같아요 😂
이건 지극히 개인적인 의견이라 흘러들으셔도 좋습니다!

추가로 얘도 한글자만 변경하는데 위랑 다르게 이름이 changeLetters인 이유가 있을까요?

Copy link
Author

Choose a reason for hiding this comment

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

changeLetters 도 처음엔 역할이 달랐다가 변경 되면서 메서드 이름이 같이 따라오지 못한 것 같아요!ㅠㅠ

입력 단어와 오늘의 단어를 구분하지 않다 보니 생기는 문제라고 생각하는데요 메서드 이름이라도 확실하게 구분이 필요한것 같습니다!

private fun contains(letter: Letter) = letter in letters
}

fun WordComparator(todayWord: Word): WordComparator {
return WordComparator(todayWord.map(Letter::copy).toMutableList())
}
20 changes: 20 additions & 0 deletions src/main/kotlin/wordle/domain/WordResult.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package wordle.domain

import wordle.exception.ExceptionMessage

data class WordResult(private val result: MutableList<LetterMatch> = MutableList(WORD_LENGTH) { LetterMatch.ABSENT }) {
init {
check(result.size == WORD_LENGTH) { ExceptionMessage.INVALID_WORD_RESULT_LENGTH.message }
}

fun isSuccessGame() = result.all { matchType -> matchType == LetterMatch.CORRECT }

fun changeMatchType(
index: Int,
matchType: LetterMatch,
) {
result[index] = matchType
}

fun matches(): List<LetterMatch> = result.toList()
}
19 changes: 19 additions & 0 deletions src/main/kotlin/wordle/domain/WordResults.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package wordle.domain

class WordResults(
private val results: MutableList<WordResult> = mutableListOf(),
private val tryCount: TryCount = TryCount(),
) {
Comment on lines +3 to +6

Choose a reason for hiding this comment

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

오 저는 controller에서 results 와 tryCount를 가지고 거기서 게임 종료를 판단하게했는데
어차피 같이 바뀌는거 이렇게하니까 정말 객체끼리 메시지를 주고받게 되네요 하나 배워갑니다~ 👍

val attemptCount get() = tryCount.attempts

fun addResults(result: WordResult) {
tryCount.minus()
results.add(result)
}

fun isContinuousGame(): Boolean = !isSuccessGame() && tryCount.isRemainder()

fun isSuccessGame(): Boolean = results.any(WordResult::isSuccessGame)

fun wordResults(): List<List<LetterMatch>> = results.map(WordResult::matches)
}
36 changes: 36 additions & 0 deletions src/main/kotlin/wordle/domain/WordleGame.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package wordle.domain

import wordle.view.inputAnswer
import wordle.view.printFail
import wordle.view.printResult
import wordle.view.printRetry
import wordle.view.printSuccess

class WordleGame {
private val results = WordResults()

fun play(todayWord: TodayWord) {
val wordleGameLogic = WordleGameLogic(todayWord)

while (results.isContinuousGame()) {
val answer = inputAnswer()
try {
val answerWord = Word(answer)
val result = wordleGameLogic.compare(answerWord)
results.addResults(result)
printResult(results)
} catch (e: IllegalStateException) {
printRetry(e.message)
}
}
gameResult(todayWord)
}

private fun gameResult(todayWord: Word) {
if (results.isSuccessGame()) {
printSuccess(results.attemptCount)
return
}
printFail(todayWord)
}
}
11 changes: 11 additions & 0 deletions src/main/kotlin/wordle/domain/WordleGameLogic.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package wordle.domain

class WordleGameLogic(private val todayWord: Word) {

Choose a reason for hiding this comment

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

이 클래스의 역할이 약한것같아요 compare 메서드가 WordComparator에 있어도 괜찮지 않을까요?
추가로 WordComparator 밖에서 완전일치와 부분일치 순서를 맘대로 바꿀수있는 단점도 있는것같아요
유스님 의견도 궁금하네요~

Copy link
Author

Choose a reason for hiding this comment

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

저희도 리팩토링을 하고 보니 결국 WordleGameLogic 은 WordComparator 를 호출하는 역할 밖에 없다고 느껴졌습니다!
이렇게 남겨둔 이유는 WordleGameLogic 은 todayWord 를 상태로 갖고 있는데, 사용자가 답을 입력 할때마다 TodayWord 를 넘겨주면서 생성하는 것은 비효율 적이다 생각했습니다.

그래서 게임 실행 시 한번 WordleGameLogic 을 생성해주고, 사용자가 입력을 하면 compare 메서드를 호출하는 방법으로 비교하도록 구현했습니다.

Choose a reason for hiding this comment

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

WordComparator도 이미 TodayWord의 Letter를 상태로가지고 있어서
WordComparator를 매번 사용자 입력마다 새로 생성하지않고 WordGameLogic 처럼 한번만 생성하면되지 않을까요?

Copy link

Choose a reason for hiding this comment

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

오! 저도 따로 리팩토링 하면서 고민이 되었던 부분이에요.

TodayWord는 변경하지 않은 상태에서
TodayWord를 copy하여 MatchMarckerLetter를 넣어줄 수 있는 tempTodayLetters가 필요하게 되어
WordComparator를 만들게 되었는데요.

저희도 이부분 테스트코드를 짜면서, WordleGameLogicWordComparator 둘 차이를 많이 느끼지 못했습니다.
그래서 더 리팩토링 해보려고 하였는데 뇌의 한계로 리팩토링 하지 못하게 되었어요.

뭔가 느낌은
WordleGameLogic -> Result
WordComparator -> ResultLetter
이런 느낌으로 했으면 좋았을것 같아요. 그럼 TodayWord 한번만 생성되었을 텐데요.
이건 시간을 갖고 리팩토링 해보겠습니다!

좋은 피드백 정말 감사드립니다. 👍

fun compare(answerWord: Word): WordResult =
todayWord.comparator()
.matchCorrect(answerWord)
.matchPresent((answerWord))
Comment on lines +6 to +7

Choose a reason for hiding this comment

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

약간 빌더패턴같은 느낌이나는데 어떤이유로 이런 방식으로 구성하셨는지 궁금하네요!

Copy link
Author

@djawnstj djawnstj Jun 25, 2024

Choose a reason for hiding this comment

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

로직 변경 WordComparator 는 List로 구현하고 있었습니다!

그래서 코틀린 컬렉션 메서드처럼 onEach 를 이용한 메서드 체이닝으로 사용이 가능하도록 구현했었는데요,
WordComparator 클래스를 생성하면서 코드 변화를 적게 가져가려고 메서드 체이닝으 형태를 그대로 두었습니다!

.result()

private fun Word.comparator(): WordComparator = WordComparator(this)
}
12 changes: 12 additions & 0 deletions src/main/kotlin/wordle/exception/ExceptionMessage.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package wordle.exception

import wordle.domain.WORD_LENGTH

enum class ExceptionMessage(val message: String) {
INVALID_LETTER("유효하지 않은 글자입니다."),
WORD_NOT_BLANK("단어는 공백을 허용하지 않습니다."),
INVALID_WORD_LENGTH("단어는 ${WORD_LENGTH}자 이여야만 합니다."),
WORD_NOT_FOUND("단어사전에 없는 단어입니다."),
TRY_COUNT_HAS_NOT_REMAINDER("시행 횟수는 0보다 작을 수 없습니다."),
INVALID_WORD_RESULT_LENGTH("단어 비교 결과는 단어 길이($WORD_LENGTH)와 일치해야 합니다."),
}
16 changes: 16 additions & 0 deletions src/main/kotlin/wordle/loader/DictionaryFileLoader.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package wordle.loader

import java.io.File

private const val CARRIAGE_RETURN = "\n"
private const val WORDS_FILE_PATH = "src/main/resources/words.txt"
private val dictionaryWords: List<String> by lazy { load() }

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.

유스버스님의 갓틀린 능력 덕분임다.👍

val dictionaryWordsSize: Int get() = dictionaryWords.size

fun isContainsDictionaryWord(word: String): Boolean = dictionaryWords.contains(word)

fun indexOfDictionaryWord(index: Int): String = dictionaryWords[index]

private fun load(): List<String> =
File(WORDS_FILE_PATH).readText()
.split(CARRIAGE_RETURN)
6 changes: 6 additions & 0 deletions src/main/kotlin/wordle/view/InputView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package wordle.view

fun inputAnswer(): String {
println("🚀 정답을 입력하세요. : ")
return readln()
}
25 changes: 25 additions & 0 deletions src/main/kotlin/wordle/view/OutputView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package wordle.view

import wordle.domain.MAX_TRY_COUNT
import wordle.domain.Word
import wordle.domain.WordResults

Choose a reason for hiding this comment

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

OutputView 에 출력 코드들이 다 모여있는데 용도별로 각자의 View로 나눠주는건 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

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

개인적으로는 아직 OutputView 가 크지 않기 때문에 이 정도는 괜찮다고 생각하는데
점프님은 확실한 구분이 더 좋다고 생각하시나요??

Choose a reason for hiding this comment

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

저는 리액트 생각하면서 각자 하나의 컴포넌트라 생각해서 다 따로 만들었는데요
유스님 판단하시기에 나중에 변경하는데 큰 어려움이 없을것같으면 모아둔것도 좋은것 같습니다!

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

Choose a reason for hiding this comment

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

MAX_TRY_COUNT 빼먹은것 같습니다! 아래에 다른함수에는 잘 사용되고있네요 👍

Copy link
Author

Choose a reason for hiding this comment

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

그렇군요 ㅠㅠㅠ
중간중간 빠진 부분이 많을 것 같습니다.

}

fun printResult(results: WordResults) {
println(results.wordResults().joinToString("\n") { it.joinToString("") { Tile.of(it).color } } + "\n")

Choose a reason for hiding this comment

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

it.joinToString("") { Tile.of(it).color } } + "\n"

요부분은 함수로 분리가되면 쬐끔더 가독성이 높지않았을까 조심스레 의견내봅니다

Copy link
Author

Choose a reason for hiding this comment

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

앗 그렇네요ㅠㅠ 감사합니다!

}

fun printFail(todayWord: Word) {
println("오늘의 단어는 ${todayWord.letters()} 입니다.")
}

fun printSuccess(attemptCount: Int) {
println("성공입니다. $attemptCount / $MAX_TRY_COUNT")
}

fun printRetry(message: String?) {
println("🥲 다시 시도하세요! : ${message ?: ""}\n")
}
16 changes: 16 additions & 0 deletions src/main/kotlin/wordle/view/Tile.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package wordle.view

import wordle.domain.LetterMatch

enum class Tile(val matchType: LetterMatch, val color: String) {
GREEN(LetterMatch.CORRECT, "🟩"),
YELLOW(LetterMatch.PRESENT, "🟨"),
GREY(LetterMatch.ABSENT, "⬜"),
;

companion object {
fun of(matchType: LetterMatch): Tile {
return entries.find { tile -> tile.matchType == matchType } ?: GREY
}
Comment on lines +11 to +14

Choose a reason for hiding this comment

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

오호 이런식으로도 매핑하는군요 읽는 흐름이 좋네요 Tile.of(LetterMatch.CORRECT)

}
}
Loading