-
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] 페퍼(최수연) 미션 제출합니다. #10
base: main
Are you sure you want to change the base?
Changes from 11 commits
a07ca44
13a13cf
c1d7d33
778f9c1
0782fbf
58cbf75
551f64f
f0c74c9
d9a554d
8f1f5f0
5422144
92ee394
7fbf22a
4dc585c
91d964c
98a1af3
aaaad52
066b045
0350518
084a409
3c355c2
9dae55e
9e1193a
3e3219e
5e3965e
4f9251e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import wordle.controller.WordleController | ||
|
||
fun main() { | ||
WordleController().run() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package wordle.controller | ||
|
||
import wordle.domain.Answer | ||
import wordle.domain.Game | ||
import wordle.domain.Words | ||
import wordle.view.printInputMessage | ||
import wordle.view.printResults | ||
import wordle.view.printStartMessage | ||
import java.time.LocalDate | ||
|
||
class WordleController { | ||
|
||
fun run() { | ||
printStartMessage() | ||
val game = Game(Words.pick(LocalDate.now())) | ||
while (game.isPlaying) { | ||
val answer = Answer(printInputMessage()) | ||
game.playRound(answer) | ||
printResults(game.results, game.isPlaying, game.findTryCount()) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,58 @@ | ||||||
package wordle.domain | ||||||
|
||||||
class Answer(private val answer: String) { | ||||||
|
||||||
init { | ||||||
require(answer.length == WORD_SIZE) { "[ERROR] 부적절한 글자 길이입니다." } | ||||||
require(Words.contains(answer)) { "[ERROR] 목록에 존재하지 않는 단어입니다." } | ||||||
} | ||||||
|
||||||
fun compareToWord(word: String): MutableList<Mark> { | ||||||
val result = MutableList(WORD_SIZE) { Mark.NONE } | ||||||
val wordTable = createWordTable(word) | ||||||
matchExact(word, result, wordTable) | ||||||
matchExist(result, wordTable) | ||||||
return result | ||||||
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. matchExact, matchExist 함수를 안쓰고 아래 코드로 작성하는 방법도 있어보이는데 페퍼가 보기에는 어떤가요~?
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 createWordTable(word: String): HashMap<Char, Int> { | ||||||
val wordTable = HashMap<Char, Int>() | ||||||
for (char in word) { | ||||||
wordTable[char] = wordTable.getOrDefault(char, 0) + 1 | ||||||
} | ||||||
Comment on lines
+20
to
+22
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. 이 작업은 map으로도 똑같이 가능하더라고요! 그래서 map이랑 foreach랑 차이점이 뭘까 생각이 들어서 한번 검색해봤는데, 이러한 차이가 있었습니다. (Is there a difference between foreach and map?) 만약 HashMap이 아닌 List나 Set이었으면 map을 이용하는 것이 의미가 있었을 것 같은데, HashMap이어서 forEach가 더 좋아보이네요. 결론은 foreach 잘썼다 👍 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. 유용한 링크도 첨부해주셔서 저도 덕분에 함께 공부했습니다😊😊 감사합니다! |
||||||
return wordTable | ||||||
} | ||||||
|
||||||
private fun matchExact(word: String, result: MutableList<Mark>, wordTable: HashMap<Char, Int>) { | ||||||
for (i in 0 until WORD_SIZE) { | ||||||
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. 이렇게 map으로 처리하는 방법도 있겠네요! 꼭 이 방법이 더 좋다는건 아니고, 또 다른 방법도 존재한다는 내용의 코멘트입니다.
Suggested change
|
||||||
markExact(i, word, result, wordTable) | ||||||
} | ||||||
} | ||||||
|
||||||
private fun markExact(i: Int, word: String, result: MutableList<Mark>, wordTable: HashMap<Char, Int>) { | ||||||
if (word[i] == answer[i]) { | ||||||
result[i] = Mark.EXACT | ||||||
wordTable.computeIfPresent(word[i]) { _, v -> v - 1 } | ||||||
} | ||||||
} | ||||||
|
||||||
private fun matchExist(result: MutableList<Mark>, wordTable: HashMap<Char, Int>) { | ||||||
for (i in 0 until WORD_SIZE) { | ||||||
markExist(i, result, wordTable) | ||||||
} | ||||||
} | ||||||
|
||||||
private fun markExist(i: Int, result: MutableList<Mark>, wordTable: HashMap<Char, Int>) { | ||||||
if (isExist(i, result, wordTable, answer[i])) { | ||||||
result[i] = Mark.EXIST | ||||||
wordTable.computeIfPresent(answer[i]) { _, v -> v - 1 } | ||||||
} | ||||||
} | ||||||
|
||||||
private fun isExist( | ||||||
i: Int, | ||||||
result: MutableList<Mark>, | ||||||
wordTable: HashMap<Char, Int>, | ||||||
charOfAnswer: Char, | ||||||
) = result[i] == Mark.NONE && wordTable.containsKey(charOfAnswer) && wordTable[charOfAnswer] != 0 | ||||||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,23 @@ | ||||||
package wordle.domain | ||||||
|
||||||
class Game(private val word: String) { | ||||||
|
||||||
val results: Results = Results() | ||||||
var isPlaying: Boolean = true | ||||||
private set | ||||||
|
||||||
fun playRound(answer: Answer) { | ||||||
val result = answer.compareToWord(word) | ||||||
results.add(result) | ||||||
if (isOver(result)) { | ||||||
isPlaying = false | ||||||
} | ||||||
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 isOver(result: MutableList<Mark>) = | ||||||
results.isLimit() || result.all { mark -> mark == Mark.EXACT } | ||||||
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. it 키워드를 사용할 수 있겠네요~
Suggested change
|
||||||
|
||||||
fun findTryCount(): Int { | ||||||
return results.value.size | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package wordle.domain | ||
|
||
enum class Mark { | ||
|
||
NONE, | ||
EXIST, | ||
EXACT | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package wordle.domain | ||
|
||
private const val LIMIT_SIZE = 6 | ||
|
||
class Results { | ||
|
||
val value: MutableList<List<Mark>> = mutableListOf() | ||
|
||
fun add(result: List<Mark>) { | ||
value.add(result) | ||
} | ||
|
||
fun isLimit(): Boolean { | ||
return value.size >= LIMIT_SIZE | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,25 @@ | ||||||
package wordle.domain | ||||||
|
||||||
import java.nio.file.Files | ||||||
import java.nio.file.Paths | ||||||
import java.time.LocalDate | ||||||
import java.time.temporal.ChronoUnit | ||||||
|
||||||
const val WORD_SIZE = 5 | ||||||
|
||||||
class Words { | ||||||
|
||||||
companion object { | ||||||
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. companion object을 활용하여 캐싱한건가요? 👍 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. 현재 Words 클래스에 companion object밖에 없는데, Words 클래스 자체를 object 클래스로 만드는 건 어떤가요? 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 val VALUE: List<String> = Files.readAllLines(Paths.get("src/main/resources/words.txt")) | ||||||
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문을 제거할 수 있겠네요 😄
Suggested 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. 이런 방법이!!! 감사합니다 리뷰어님🙇♀️ |
||||||
private val BASIC_DATE = LocalDate.of(2021, 6, 19) | ||||||
|
||||||
fun contains(word: String): Boolean { | ||||||
return VALUE.contains(word) | ||||||
} | ||||||
|
||||||
fun pick(date: LocalDate): String { | ||||||
val index = ChronoUnit.DAYS.between(BASIC_DATE, date) % VALUE.size | ||||||
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. 케이님의 링크 덕분에 저도 다시한번 복습했네요😄😄 |
||||||
return VALUE[index.toInt()] | ||||||
} | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package wordle.view | ||
|
||
import wordle.domain.Mark | ||
import wordle.domain.Results | ||
|
||
fun printStartMessage() { | ||
println("WORDLE 을 6번 만에 맞춰 보세요.\n시도의 결과는 타일의 색 변화로 나타납니다.") | ||
} | ||
|
||
fun printInputMessage(): String { | ||
println("정답을 입력해 주세요.") | ||
return readln() | ||
} | ||
|
||
fun printResults(results: Results, isPlaying: Boolean, tryCount: Int) { | ||
println() | ||
if (!isPlaying) { | ||
printTryCount(tryCount) | ||
} | ||
results.value.forEach { | ||
printResult(it) | ||
} | ||
println() | ||
} | ||
|
||
fun printTryCount(tryCount: Int) { | ||
println("$tryCount/6\n") | ||
} | ||
|
||
private fun printResult(result: List<Mark>) { | ||
val stringBuilder = StringBuilder() | ||
result.forEach { | ||
when (it) { | ||
Mark.NONE -> stringBuilder.append("⬜") | ||
Mark.EXIST -> stringBuilder.append("\uD83D\uDFE8") | ||
Mark.EXACT -> stringBuilder.append("\uD83D\uDFE9") | ||
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. NONE은 도형을, EXACT, EXIST는 유니코드를 사용한 이유는 무엇인가요? 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. 예시 출력을 그대로 붙여넣었더니 통일성 있지 않게 삽입되었네요🥲 통일성을 위해 모두 도형으로 변경하였습니다⬜🟨🟩 매번 |
||
} | ||
} | ||
println(stringBuilder.toString()) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package wordle.domain | ||
|
||
import io.kotest.matchers.throwable.shouldHaveMessage | ||
import org.assertj.core.api.Assertions.assertThat | ||
import org.junit.jupiter.api.Test | ||
import org.junit.jupiter.api.assertThrows | ||
import wordle.domain.Mark.* | ||
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문에 와일드카드를 사용하지 않는 방법이 좋아보입니다 😄 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. 반영하였습니다! |
||
|
||
internal class AnswerTest { | ||
|
||
@Test | ||
fun 글자길이가_5가_아닌_경우_예외발생() { | ||
assertThrows<IllegalArgumentException> { Answer("abcdef") } | ||
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. forAll을 이용하여 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. assertThrow문 사용하셨군요! 👍 저는 kotest의 shouldThrow를 사용했는데 둘 다 좋네요 ㅎㅎ 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. 이전에는 아직 kotest가 익숙하지 않았어요. |
||
.shouldHaveMessage("[ERROR] 부적절한 글자 길이입니다.") | ||
} | ||
|
||
@Test | ||
fun 주어진_단어목록에_존재하지_않는_경우_예외발생() { | ||
assertThrows<IllegalArgumentException> { Answer("abcde") } | ||
.shouldHaveMessage("[ERROR] 목록에 존재하지 않는 단어입니다.") | ||
} | ||
|
||
@Test | ||
fun 답안과_정답을_비교_CASE_중복되는_문자_중_하나만_일치_할_때() { | ||
val answer = Answer("groom") | ||
|
||
assertThat(answer.compareToWord("goose")) | ||
.isEqualTo(listOf(EXACT, NONE, EXACT, EXIST, NONE)) | ||
} | ||
|
||
@Test | ||
fun 답안과_정답을_비교_CASE_중복되는_문자가_존재하지만_정답의_개수가_더_많을_때() { | ||
val answer = Answer("eerie") | ||
|
||
assertThat(answer.compareToWord("sheen")) | ||
.isEqualTo(listOf(EXIST, EXIST, NONE, NONE, NONE)) | ||
} | ||
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,49 @@ | ||
package wordle.domain | ||
|
||
import org.assertj.core.api.Assertions.assertThat | ||
import org.junit.jupiter.api.Test | ||
import org.junit.jupiter.api.assertAll | ||
|
||
internal class GameTest { | ||
|
||
@Test | ||
fun 게임을_한_라운드씩_진행() { | ||
val game = Game("fetus") | ||
repeat(3) { game.playRound(Answer("apple")) } | ||
|
||
assertAll( | ||
{assertThat(game.findTryCount()).isEqualTo(3)}, | ||
{assertThat(game.isPlaying).isTrue()} | ||
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.
|
||
) | ||
} | ||
|
||
@Test | ||
fun 게임을_한_라운드씩_진행하다_6라운드에_도달하면_게임종료() { | ||
val game = Game("fetus") | ||
repeat(6) { game.playRound(Answer("apple")) } | ||
|
||
assertAll( | ||
{assertThat(game.findTryCount()).isEqualTo(6)}, | ||
{assertThat(game.isPlaying).isFalse()} | ||
) | ||
} | ||
|
||
@Test | ||
fun 게임을_한_라운드씩_진행하다_정답을_맞추면_게임종료() { | ||
val game = Game("fetus") | ||
repeat(3) { game.playRound(Answer("fetus")) } | ||
|
||
assertAll( | ||
{assertThat(game.findTryCount()).isEqualTo(3)}, | ||
{assertThat(game.isPlaying).isFalse()} | ||
) | ||
} | ||
|
||
@Test | ||
fun 몇_번째_시도인지_계산() { | ||
val game = Game("fetus") | ||
repeat(6) { game.playRound(Answer("apple")) } | ||
|
||
assertThat(game.findTryCount()).isEqualTo(6) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package wordle.domain | ||
|
||
import org.assertj.core.api.Assertions.assertThat | ||
import org.junit.jupiter.api.Test | ||
import java.time.LocalDate | ||
|
||
internal class WordsTest { | ||
|
||
@Test | ||
fun 오늘의_단어를_선택() { | ||
val date = LocalDate.of(2022, 5, 12) | ||
|
||
assertThat(Words.pick(date)).isEqualTo("fetus") | ||
} | ||
} |
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.
게임이 종료되는 상황을 피할 수 있겠군요~!
try-catch
문을 활용해서 반영해보았습니다.