-
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] 로마(김범수) 미션 제출합니다. #12
base: main
Are you sure you want to change the base?
Changes from 21 commits
5577af4
913234c
9adf4b7
490274e
6fd8eb7
41725cb
eb2e781
50de938
6ca90e7
9a24bc9
88c3d73
c44d184
316ce9c
787cc36
6c159c8
14570c8
c671c8f
e0c0485
0223a01
ba60252
0fe17d0
fa76e29
5f11353
a06696b
8f373bd
50c25d1
1d22723
6df3404
37d95d8
ef7eab0
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,26 @@ | ||
package wordle | ||
|
||
import wordle.domain.Game | ||
import wordle.domain.TOTAL_ROUNDS_NUM | ||
import wordle.domain.Word | ||
import wordle.utils.WordsReader | ||
import wordle.view.InputView | ||
import wordle.view.OutputView | ||
|
||
fun main() { | ||
val game = Game(WordsReader.getWords()) | ||
OutputView.printStartMessage(TOTAL_ROUNDS_NUM) | ||
doGame(game) | ||
OutputView.printCount(game.count) | ||
OutputView.printResults(game.results) | ||
} | ||
|
||
private fun doGame(game: Game) { | ||
val answer = Word(InputView.inputAnswer()) | ||
game.match(answer) | ||
if (game.isGameOver(answer)) { | ||
return | ||
} | ||
OutputView.printResults(game.results) | ||
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. "제이슨이" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package wordle.domain | ||
|
||
const val TOTAL_ROUNDS_NUM = 6 | ||
|
||
class Game(private val words: Words) { | ||
|
||
var count: Int = 0 | ||
private set | ||
|
||
var results: MutableList<List<Tile>> = ArrayList() | ||
private set | ||
|
||
constructor(words: List<Word>) : this(Words(words)) | ||
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에서 List를 Words로 바꿔주는 이유가 있을까요? |
||
|
||
fun isGameOver(answer: Word): Boolean { | ||
return count >= TOTAL_ROUNDS_NUM || words.isCorrect(answer) | ||
} | ||
|
||
fun match(answer: Word) { | ||
require(words.contains(answer)) { "등록된 단어가 아닙니다." } | ||
results.add(words.check(answer)) | ||
count++ | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
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,32 @@ | ||||||||||||
package wordle.domain | ||||||||||||
|
||||||||||||
private const val WORD_SIZE = 5 | ||||||||||||
|
||||||||||||
class Word(_value: String) { | ||||||||||||
|
||||||||||||
val value: String = _value.lowercase() | ||||||||||||
|
||||||||||||
init { | ||||||||||||
require(value.length == WORD_SIZE) { "단어의 길이는 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.
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. "제이슨이" |
||||||||||||
|
||||||||||||
fun isSameChar(other: Word, index: Int): Boolean { | ||||||||||||
return this.value[index] == other.value[index] | ||||||||||||
} | ||||||||||||
|
||||||||||||
override fun equals(other: Any?): Boolean { | ||||||||||||
if (this === other) return true | ||||||||||||
if (javaClass != other?.javaClass) return false | ||||||||||||
|
||||||||||||
other as Word | ||||||||||||
|
||||||||||||
if (value != other.value) return false | ||||||||||||
|
||||||||||||
return true | ||||||||||||
} | ||||||||||||
|
||||||||||||
override fun hashCode(): Int { | ||||||||||||
return value.hashCode() | ||||||||||||
} | ||||||||||||
Comment on lines
+27
to
+40
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. data클래스로 만들면 equals hashcode가 필요 없지 않을까요? 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. 해당 클래스에서 lowerCase로 바꿔주는 작업을 위해서 주생성자에서 인자의 형식으로 값을 받습니다. |
||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
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() | ||
|
||
private fun findAnswer(): Word { | ||
val nowDate: LocalDate = LocalDate.now() | ||
val days: Int = ChronoUnit.DAYS.between(STANDARD_DATE, nowDate).toInt() | ||
return values[days % values.size] | ||
} | ||
|
||
fun contains(word: Word): Boolean { | ||
return values.contains(word) | ||
} | ||
|
||
fun isCorrect(word: Word): Boolean { | ||
return word == answer | ||
} | ||
|
||
fun check(word: Word): List<Tile> { | ||
answerMap = initAnswerMap() | ||
val result: MutableList<Tile> = ArrayList() | ||
repeat(5) { result.add(Tile.GRAY) } | ||
|
||
repeat(5) { result[it] = findTileBySameCheck(word, it) } | ||
repeat(5) { result[it] = findTileByContainCheck(result, word, it) } | ||
return result | ||
} | ||
|
||
private fun initAnswerMap(): MutableMap<Char, Int> { | ||
val answerMap: MutableMap<Char, Int> = HashMap() | ||
answer.value.forEach { | ||
answerMap.putIfAbsent(it, 0) | ||
answerMap.computeIfPresent(it) { _, v -> v + 1 } | ||
} | ||
return answerMap | ||
} | ||
|
||
private fun findTileBySameCheck(word: Word, index: Int): Tile { | ||
if (answer.isSameChar(word, index)) { | ||
calculateAnswerMap(word.value[index]) | ||
return Tile.GREEN | ||
} | ||
return Tile.GRAY | ||
} | ||
|
||
private fun findTileByContainCheck(result: List<Tile>, word: Word, index: Int): Tile { | ||
if (result[index] == Tile.GREEN) { | ||
return Tile.GREEN | ||
} | ||
if (answerMap.keys.contains(word.value[index])) { | ||
calculateAnswerMap(word.value[index]) | ||
return Tile.YELLOW | ||
} | ||
return Tile.GRAY | ||
} | ||
|
||
private fun calculateAnswerMap(key: Char) { | ||
answerMap.computeIfPresent(key) { _, v -> v - 1 } | ||
if (answerMap[key] == 0) { | ||
answerMap.remove(key) | ||
} | ||
} | ||
|
||
companion object { | ||
private val STANDARD_DATE = LocalDate.of(2021, 6, 19) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package wordle.utils | ||
|
||
import wordle.domain.Word | ||
import java.io.FileReader | ||
|
||
private const val WORDS_PATH = "src/main/resources/words.txt" | ||
|
||
object WordsReader { | ||
|
||
fun getWords(): List<Word> { | ||
val reader = FileReader(WORDS_PATH) | ||
return reader.readLines().map { Word(it) } | ||
} | ||
} |
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,27 @@ | ||||||
package wordle.view | ||||||
|
||||||
import wordle.domain.Tile | ||||||
|
||||||
object OutputView { | ||||||
|
||||||
fun printStartMessage(round: Int) { | ||||||
println("WORDLE을 {$round}번 만에 맞춰 보세요.") | ||||||
println("시도의 결과는 타일의 색 변화로 나타납니다.") | ||||||
} | ||||||
|
||||||
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() | ||||||
println("$count/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. 여기도 round를 매개변수로 받을 수 있겠네용
Suggested change
|
||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package study | ||
|
||
import io.kotest.assertions.assertSoftly | ||
import io.kotest.matchers.collections.shouldContainExactly | ||
import io.kotest.matchers.collections.shouldHaveSize | ||
import io.kotest.matchers.shouldBe | ||
import org.junit.jupiter.api.Test | ||
|
||
/* | ||
introduce { | ||
name("박재성") | ||
company("우아한형제들") | ||
skills { | ||
soft ("A passion for problem solving") | ||
soft ("Good communication skills") | ||
hard ("Kotlin") | ||
} | ||
languages { | ||
"Korean" level 5 | ||
"English" level 3 | ||
} | ||
} | ||
*/ | ||
class DslTest { | ||
|
||
@Test | ||
fun `자기소개 함수 버전`() { | ||
val person = introduce { | ||
name("김범수") | ||
company("우아한형제들") | ||
skills { | ||
soft("코틀린") | ||
soft("자바") | ||
hard("운동") | ||
} | ||
languages { | ||
"Korean" level 100 | ||
"English" level 10 | ||
"Japanese" level 0 | ||
} | ||
} | ||
|
||
assertSoftly(person) { | ||
it.name shouldBe "김범수" | ||
it.company shouldBe "우아한형제들" | ||
it.skills.soft shouldHaveSize 2 | ||
it.skills.hard shouldHaveSize 1 | ||
it.languages.values shouldHaveSize 3 | ||
it.languages.values shouldContainExactly listOf( | ||
Language("Korean", 100), | ||
Language("English", 10), | ||
Language("Japanese", 0) | ||
) | ||
} | ||
} | ||
} | ||
|
||
fun introduce(builder: PersonBuilder.() -> Unit): Person { | ||
return PersonBuilder().apply(builder).build() | ||
} | ||
|
||
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: Skills.() -> Unit) { | ||
skills = Skills(mutableListOf(), mutableListOf()).apply(builder) | ||
} | ||
|
||
fun languages(builder: Languages.() -> Unit) { | ||
languages = Languages(mutableListOf()).apply(builder) | ||
} | ||
|
||
fun build(): Person = Person(name, company, skills, languages) | ||
} | ||
|
||
data class Person(val name: String, val company: String, val skills: Skills, val languages: Languages) | ||
|
||
class Skills(val soft: MutableList<String>, val hard: MutableList<String>) { | ||
fun soft(value: String) { | ||
soft.add(value) | ||
} | ||
|
||
fun hard(value: String) { | ||
hard.add(value) | ||
} | ||
} | ||
|
||
class Languages(val values: MutableList<Language>) { | ||
|
||
infix fun String.level(level: Int) { | ||
values.add(Language(this, level)) | ||
} | ||
} | ||
|
||
data class Language(val value: String, val level: Int) | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
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 | ||
internal fun `게임 종료여부를 확인한다`() { | ||
val game = Game(listOf(Word("apple"), Word("hello"), Word("spicy"))) | ||
repeat(6) { game.match(Word("apple")) } | ||
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.
./gradlew ktlintCheck
이 실패하네요! 한번 확인해보시면 좋을 것 같아요(여기서 틀렸다는게 아니라 코멘트만 여기에 남긴거에요!)
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.
오 감사합니다! 수정하고 깜빡한 것 같아요 😥