-
Notifications
You must be signed in to change notification settings - Fork 35
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
base: main
Are you sure you want to change the base?
Changes from all commits
0ba1760
3c19d5c
12e9b47
b4e4aea
233336e
833ce1c
da3a4e9
719819f
66e1c1c
4cb13b8
e5dfcdd
5ed2326
c660fc4
1617e24
9b5a258
548f705
1ee237b
ae2fb60
ee98432
4c4dc67
203704e
8a78dcd
4ca3984
cf35dc8
92169e9
00d39c2
3c9fde1
90de547
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package wordle | ||
|
||
import wordle.controller.WordleGameController | ||
|
||
fun main() { | ||
WordleGameController().run() | ||
} |
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() | ||
WordleGame().play(TodayWord(LocalDate.now())) | ||
} | ||
} |
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 초기화 블록 사용 좋네요~ |
||
|
||
fun changeMatchMarker(): Letter = MATCH_MARKER_LETTER | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. change 여서 내부 상태를 바꾸는걸 예상하고 봤는데 객체를 리턴하는 함수인게 네이밍이 약간 어색한것같아요 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package wordle.domain | ||
|
||
enum class LetterMatch { | ||
CORRECT, | ||
PRESENT, | ||
ABSENT, | ||
} |
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) | ||
} |
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 계산식에도 프로퍼티를 활용하시는군요~ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저도 확실한 게터 역할이 아니라면 메서드로 만드는 편입니다! |
||
|
||
fun isRemainder(): Boolean = count > 0 | ||
|
||
fun minus() { | ||
check(isRemainder()) { ExceptionMessage.TRY_COUNT_HAS_NOT_REMAINDER.message } | ||
--count | ||
} | ||
Comment on lines
+10
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 꼼꼼한 예외체크네요 👍 |
||
} | ||
|
||
const val MAX_TRY_COUNT = 6 |
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 요기를 보시면 도메인인데 외부를 의존하고 있네요 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 제가 계층 간 의존에 대한 개념이 조금 부족한데 어떻게 풀어야 할지 고민해 보겠습니다! |
||
import java.time.LocalDate | ||
|
||
typealias TodayWord = Word | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 외부에서 주생성자를 private 으로 막고 동반객체에 팩터리메서드를 만들어볼수 있을것같아요! (그러고보니까 저도 빼먹었네요ㅋㅋ) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 주 생성자에 접근 제어자를 추가 해야 했는데 급하게 제출하느라 놓쳤네요ㅠㅠ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 궁금해서 해봤는데 주생성자를 private으로 막으면 가짜 생성자를 사용할수가 없네요 |
||
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)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저는 네임스페이스처럼 나오는게 보기편해서 확장함수가 아니면 탑레벨에 선언하지 않고 클래스안에 넣어두는 편인데요 탑레벨에 선언하는걸 선택하는데는 어떤 장점들이 있어서 결정하셨는지 궁금합니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저는 개인적으로 코틀린의 가짜 생성자 패턴을 좋아하는데 이번 미션 하면서 이 내용을 녹여보고 싶었습니다! 작업 하면서도 '파일을 분리하는 것이 좋나?' 에 대한 고민은 있었지만 파일이 너무 많아질 것 같아서 같은 파일 안에 함수를 두었습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아하 저는 |
||
} |
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() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 같은 객체를 가져오는 건데 꼭 letters 리스트의 특정 원소의 메서드로 함수이름도 changeLetter 보단 이미 매칭됨을 마킹한다는 맥락이 들어가면 더 읽기 쉬웠을것 같아요 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 위에 남긴 코멘트와 같은 이유로 코드가 이상한 형태가 됐네요ㅠㅠ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
좋은 리뷰 감사합니다.👍 |
||
} | ||
|
||
private fun changeLetters(letter: Letter) { | ||
changeLetter(letters.indexOf(letter)) | ||
} | ||
|
||
Comment on lines
+57
to
+60
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저는 이 함수가 계속 letter가 반복되니까 뭐가 입력단어가 인지 뭐가 오늘의단어인지 파악하기 쉽지 않아서 읽기 힘들었던것같아요 😂 추가로 얘도 한글자만 변경하는데 위랑 다르게 이름이 changeLetters인 이유가 있을까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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()) | ||
} |
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() | ||
} |
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
} |
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) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package wordle.domain | ||
|
||
class WordleGameLogic(private val todayWord: Word) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 클래스의 역할이 약한것같아요 compare 메서드가 WordComparator에 있어도 괜찮지 않을까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저희도 리팩토링을 하고 보니 결국 WordleGameLogic 은 WordComparator 를 호출하는 역할 밖에 없다고 느껴졌습니다! 그래서 게임 실행 시 한번 WordleGameLogic 을 생성해주고, 사용자가 입력을 하면 compare 메서드를 호출하는 방법으로 비교하도록 구현했습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WordComparator도 이미 TodayWord의 Letter를 상태로가지고 있어서 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오! 저도 따로 리팩토링 하면서 고민이 되었던 부분이에요. TodayWord는 변경하지 않은 상태에서 저희도 이부분 테스트코드를 짜면서, 뭔가 느낌은 좋은 피드백 정말 감사드립니다. 👍 |
||
fun compare(answerWord: Word): WordResult = | ||
todayWord.comparator() | ||
.matchCorrect(answerWord) | ||
.matchPresent((answerWord)) | ||
Comment on lines
+6
to
+7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 약간 빌더패턴같은 느낌이나는데 어떤이유로 이런 방식으로 구성하셨는지 궁금하네요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 로직 변경 WordComparator 는 List로 구현하고 있었습니다! 그래서 코틀린 컬렉션 메서드처럼 |
||
.result() | ||
|
||
private fun Word.comparator(): WordComparator = WordComparator(this) | ||
} |
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)와 일치해야 합니다."), | ||
} |
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() } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 코틀린 문법을 잘 활용하시네요~ 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package wordle.view | ||
|
||
fun inputAnswer(): String { | ||
println("🚀 정답을 입력하세요. : ") | ||
return readln() | ||
} |
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 | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OutputView 에 출력 코드들이 다 모여있는데 용도별로 각자의 View로 나눠주는건 어떨까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 개인적으로는 아직 OutputView 가 크지 않기 때문에 이 정도는 괜찮다고 생각하는데 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저는 리액트 생각하면서 각자 하나의 컴포넌트라 생각해서 다 따로 만들었는데요 |
||
fun printWelcome() { | ||
println("🥳 WORDLE을 6번 만에 맞춰 보세요.\n시도의 결과는 타일의 색 변화로 나타납니다.\n") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
요부분은 함수로 분리가되면 쬐끔더 가독성이 높지않았을까 조심스레 의견내봅니다 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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") | ||
} |
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오호 이런식으로도 매핑하는군요 읽는 흐름이 좋네요 Tile.of(LetterMatch.CORRECT) |
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
얘만 여기 홀로있는 이유가 있을까요? 🧐
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
시작 전 안내 메시지 출력과 게임 런타임(?) 은 다르다고 생각했습니다!