-
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] 파랑(이하은) 미션 제출합니다. #13
base: main
Are you sure you want to change the base?
Changes from 19 commits
5577af4
913234c
9adf4b7
490274e
6fd8eb7
41725cb
eb2e781
50de938
6ca90e7
6c29542
a77bf9e
0a62335
200333b
7812438
80fd53d
2d19c45
125d27d
668cbdd
6a295e8
ed3ab9f
d12e369
f45ae33
7832770
be18256
102e8d7
23bb4e4
6a28632
961eac9
8df1ac1
d7e35f8
ace29e4
e210cfe
9e9c283
fd86a0e
ac5ff8f
ad32c27
2370f77
a2d57b4
f35a779
7696783
fce9fe0
5693dcb
6e78472
3cdc51e
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,8 @@ | ||
package study | ||
|
||
import study.person.Person | ||
import study.person.PersonBuilder | ||
|
||
fun introduce(builder: PersonBuilder.() -> Unit): Person { | ||
return PersonBuilder().apply(builder).build() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package study.language | ||
|
||
data class Language(val language: String, val level: Int) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package study.language | ||
|
||
class Languages(val values: List<Language>) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package study.language | ||
|
||
class LanguagesBuilder { | ||
|
||
private val languages: MutableList<Language> = mutableListOf() | ||
|
||
infix fun String.level(level: Int) { | ||
languages.add(Language(this, level)) | ||
} | ||
|
||
fun build(): Languages { | ||
return Languages(languages) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package study.person | ||
|
||
import study.language.Languages | ||
import study.skill.Skills | ||
|
||
data class Person(val name: String, val company: String, val skills: Skills, val languages: Languages) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package study.person | ||
|
||
import study.language.LanguagesBuilder | ||
import study.language.Languages | ||
import study.skill.Skills | ||
import study.skill.SkillsBuilder | ||
|
||
class PersonBuilder { | ||
|
||
lateinit var name: String | ||
lateinit var company: String | ||
lateinit var skills: Skills | ||
lateinit var languages: Languages | ||
|
||
fun name(value: String) { | ||
name = value | ||
} | ||
|
||
fun company(value: String) { | ||
company = value | ||
} | ||
|
||
fun skills(builder: SkillsBuilder.() -> Unit) { | ||
skills = SkillsBuilder().apply(builder).build() | ||
} | ||
|
||
fun languages(builder: LanguagesBuilder.() -> Unit) { | ||
languages = LanguagesBuilder().apply(builder).build() | ||
} | ||
|
||
fun build(): Person { | ||
return Person(name, company, skills, languages) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package study.skill | ||
|
||
data class HardSkill(val value: String) : Skill |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package study.skill | ||
|
||
interface Skill | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package study.skill | ||
|
||
class Skills(val values: List<Skill>) { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package study.skill | ||
|
||
class SkillsBuilder { | ||
|
||
private val skills: MutableList<Skill> = mutableListOf() | ||
|
||
fun soft(value: String) { | ||
skills.add(SoftSkill(value)) | ||
} | ||
|
||
fun hard(value: String) { | ||
skills.add(HardSkill(value)) | ||
} | ||
|
||
fun build(): Skills { | ||
return Skills(skills) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package study.skill | ||
|
||
data class SoftSkill(val value: String) : Skill |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package wordle | ||
|
||
import wordle.domain.Game | ||
import wordle.domain.Word | ||
import wordle.util.WordsReader | ||
import wordle.view.InputView | ||
import wordle.view.OutputView | ||
|
||
fun main() { | ||
val game = Game(WordsReader.getWords()) | ||
OutputView.printStartMessage() | ||
doGame(game) | ||
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. play game이 더 자연스러운 것 같아서 변경했습니다👍 |
||
OutputView.printCount(game.count) | ||
OutputView.printResults(game.results) | ||
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. View에 대한 호출을 최소화해 보면 어떨까요? |
||
} | ||
|
||
private fun doGame(game: Game) { | ||
val answer = Word(InputView.inputAnswer()) | ||
game.match(answer) | ||
if (game.isGameOver(answer)) { | ||
return | ||
} | ||
OutputView.printResults(game.results) | ||
doGame(game) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package wordle.domain | ||
|
||
class Game(private val words: Words) { | ||
|
||
constructor(words: List<Word>) : this(Words(words)) | ||
|
||
var count: Int = 0 | ||
private set | ||
|
||
var results: MutableList<List<Tile>> = ArrayList() | ||
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. 뒷받침하는 프로퍼티(backing property)를 사용해 보세요. |
||
private set | ||
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 isGameOver(answer: Word): Boolean { | ||
return count == MAX_GAME_COUNT || words.isCorrect(answer) | ||
} | ||
|
||
fun match(answer: Word) { | ||
require(words.contains(answer)) { "등록된 단어가 아닙니다." } | ||
count++ | ||
results.add(words.check(answer)) | ||
} | ||
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. 그렇네요 수정했습니다! |
||
|
||
companion object { | ||
const val MAX_GAME_COUNT = 6 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package wordle.domain | ||
|
||
enum class Tile(val symbol: String) { | ||
|
||
YELLOW("🟨"), GREEN("🟩"), GRAY("⬜") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package wordle.domain | ||
|
||
data class Word(val value: String) { | ||
|
||
init { | ||
require(value.length == 5) { "단어의 길이는 5글자여야 합니다." } | ||
require(Regex("[a-zA-Z]*").matches(value)) { "단어에 영어가 아닌 글자나 공백이 포함될 수 없습니다." } | ||
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 isSameChar(other: Word, index: Int): Boolean { | ||
return this.value[index] == other.value[index] | ||
} | ||
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,65 @@ | ||||||||||||||||||||||||||
package wordle.domain | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
import java.time.LocalDate | ||||||||||||||||||||||||||
import java.time.temporal.ChronoUnit | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
class Words(private val values: List<Word>) { | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
private val answer: Word = findAnswer() | ||||||||||||||||||||||||||
private var answerMap: MutableMap<Char, Int> = HashMap() | ||||||||||||||||||||||||||
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. 문자를 비교해서 같은 경우 문자를 소진하기 때문에 문자의 개수를 저장할 필요가 있었습니다. 그렇기 때문에 문자 - 문자의 개수를 key, value로 저장하기 위해 HashMap을 사용했습니다! 이 부분이 궁금했던 거 맞나요? 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. 이해했습니다!! 역시 S.... 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. 코틀린은 읽기 전용 컬렉션과 변경 가능한 컬렉션을 구별해 제공합니다.
Suggested change
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
private fun findAnswer(): Word { | ||||||||||||||||||||||||||
val date: LocalDate = LocalDate.now() | ||||||||||||||||||||||||||
val standardDate: LocalDate = LocalDate.of(2021, 6, 19) | ||||||||||||||||||||||||||
val days: Int = ChronoUnit.DAYS.between(standardDate, date).toInt() | ||||||||||||||||||||||||||
return values[days % values.size] | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
fun contains(word: Word): Boolean = values.contains(word) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
fun isCorrect(word: Word): Boolean { | ||||||||||||||||||||||||||
return word == answer | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
fun check(word: Word): List<Tile> { | ||||||||||||||||||||||||||
answerMap = initAnswerMap() | ||||||||||||||||||||||||||
val result = MutableList(WORD_SIZE) { Tile.GRAY } | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
repeat(WORD_SIZE) { putTileIfSame(result, word, it) } | ||||||||||||||||||||||||||
repeat(WORD_SIZE) { putTileIfContains(result, word, it) } | ||||||||||||||||||||||||||
return result | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
private fun initAnswerMap(): MutableMap<Char, Int> { | ||||||||||||||||||||||||||
val answerMap: MutableMap<Char, Int> = HashMap() | ||||||||||||||||||||||||||
answer.value.forEach { | ||||||||||||||||||||||||||
answerMap[it] = answerMap.getOrDefault(it, 0) + 1 | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
return answerMap | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
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. 아래의 코드로 개선해 보면 어떨까요?
Suggested change
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
private fun putTileIfSame(result: MutableList<Tile>, word: Word, index: Int) { | ||||||||||||||||||||||||||
if (answer.isSameChar(word, index)) { | ||||||||||||||||||||||||||
calculateAnswerMap(word.value[index]) | ||||||||||||||||||||||||||
result[index] = Tile.GREEN | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
private fun putTileIfContains(result: MutableList<Tile>, word: Word, index: Int) { | ||||||||||||||||||||||||||
if (result[index] != Tile.GREEN && answerMap.containsKey(word.value[index])) { | ||||||||||||||||||||||||||
calculateAnswerMap(word.value[index]) | ||||||||||||||||||||||||||
result[index] = Tile.YELLOW | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
private fun calculateAnswerMap(key: Char) { | ||||||||||||||||||||||||||
answerMap.computeIfPresent(key) { _, v -> v - 1 } | ||||||||||||||||||||||||||
if (answerMap[key] == 0) { | ||||||||||||||||||||||||||
answerMap.remove(key) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
companion object { | ||||||||||||||||||||||||||
private const val WORD_SIZE = 5 | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package wordle.util | ||
|
||
import wordle.domain.Word | ||
import java.io.FileReader | ||
|
||
object WordsReader { | ||
|
||
fun getWords(): List<Word> { | ||
val path = "src/main/resources/words.txt" | ||
val reader = FileReader(path) | ||
return reader.readLines().map { Word(it) } | ||
} | ||
} | ||
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. txt 파일에서 문자열을 읽어와 Word list로 변환해주는 역할만 하고 있어서 util 클래스라고 생각했습니다! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package wordle.view | ||
|
||
object InputView { | ||
|
||
fun inputAnswer(): String { | ||
println("정답을 입력해 주세요.") | ||
return readln() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,24 @@ | ||||||
package wordle.view | ||||||
|
||||||
import wordle.domain.Game | ||||||
import wordle.domain.Tile | ||||||
|
||||||
object OutputView { | ||||||
|
||||||
fun printStartMessage() = println( | ||||||
"WORDLE을 ${Game.MAX_GAME_COUNT}번 만에 맞춰 보세요.\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. 아래의 코드로 개선해 보면 어떨까요?
Suggested change
|
||||||
) | ||||||
|
||||||
fun printResults(results: List<List<Tile>>) { | ||||||
println() | ||||||
results.forEach { printResult(it) } | ||||||
println() | ||||||
} | ||||||
|
||||||
private fun printResult(result: List<Tile>) { | ||||||
result.forEach { print(it.symbol) } | ||||||
println() | ||||||
} | ||||||
|
||||||
fun printCount(count: Int) = println("\n$count/${Game.MAX_GAME_COUNT}") | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package study | ||
|
||
import io.kotest.matchers.collections.shouldContainExactly | ||
import io.kotest.matchers.shouldBe | ||
import org.junit.jupiter.api.Test | ||
import study.language.Language | ||
import study.skill.HardSkill | ||
import study.skill.SoftSkill | ||
|
||
class DslTest { | ||
|
||
@Test | ||
fun `dsl introduce 만들어 보기`() { | ||
val resume = introduce { | ||
name("이하은") | ||
company("우아한테크코스") | ||
skills { | ||
soft("A passion for problem solving") | ||
soft("Good communication skills") | ||
hard("Kotlin") | ||
} | ||
languages { | ||
"Korean" level 5 | ||
"English" level 3 | ||
} | ||
} | ||
|
||
resume.name shouldBe "이하은" | ||
resume.company shouldBe "우아한테크코스" | ||
resume.skills.values shouldContainExactly listOf( | ||
SoftSkill("A passion for problem solving"), | ||
SoftSkill("Good communication skills"), | ||
HardSkill("Kotlin") | ||
) | ||
resume.languages.values shouldContainExactly listOf( | ||
Language("Korean", 5), | ||
Language("English", 3) | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package wordle.domain | ||
|
||
import io.kotest.assertions.throwables.shouldThrow | ||
import io.kotest.matchers.shouldBe | ||
import io.kotest.matchers.throwable.shouldHaveMessage | ||
import org.junit.jupiter.api.Test | ||
|
||
class GameTest { | ||
|
||
@Test | ||
fun `등록된 단어가 아닌 경우 예외가 발생한다`() { | ||
val game = Game(listOf(Word("apple"), Word("hello"), Word("spicy"))) | ||
|
||
shouldThrow<IllegalArgumentException> { game.match(Word("spell")) } | ||
.shouldHaveMessage("등록된 단어가 아닙니다.") | ||
} | ||
|
||
@Test | ||
fun `게임 종료여부를 확인한다`() { | ||
val game = Game(listOf(Word("apple"), Word("hello"), Word("spicy"))) | ||
repeat(6) { game.match(Word("apple")) } | ||
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. Game을 생성할 때 maxGameCount를 받아 match를 사용하지 않고도 테스트할 수 있도록 수정했습니다! |
||
game.isGameOver(Word("apple")) shouldBe true | ||
} | ||
} |
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.
Skill
,HardSkill
,SoftSkill
을 하나의 파일로 모으고 봉인된 클래스를 사용해 볼까요?https://kotlinlang.org/docs/sealed-classes.html
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.
sealed class는 이번에 처음 알게 되었네요! 말씀해주신대로 하나의 파일로 모아 Skill을 봉인된 클래스로 만들었습니다!