-
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 23 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,33 @@ | ||
package wordle.controller | ||
|
||
import wordle.domain.Answer | ||
import wordle.domain.Game | ||
import wordle.domain.Words | ||
import wordle.view.printErrorMessage | ||
import wordle.view.printResults | ||
import wordle.view.printStartMessage | ||
import wordle.view.requestInput | ||
import java.time.LocalDate | ||
|
||
class WordleController { | ||
|
||
fun run() { | ||
val game = Game(Words.pick(LocalDate.now())) | ||
printStartMessage(game.fixedCount) | ||
|
||
while (game.isPlaying) { | ||
val answer = requestAnswer() | ||
game.playRound(answer) | ||
printResults(game.results, game.isPlaying, game.findTryCount(), game.fixedCount) | ||
} | ||
} | ||
|
||
private fun requestAnswer(): Answer { | ||
return try { | ||
Answer(requestInput()) | ||
} catch (e: IllegalArgumentException) { | ||
printErrorMessage(e.message) | ||
requestAnswer() | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
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) | ||
(0 until WORD_SIZE).map { markExact(it, word, result, wordTable) } | ||
(0 until WORD_SIZE).map { markExist(it, 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 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 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 fixedCount = 6 | ||
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 { it == Mark.EXACT } | ||
|
||
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,24 @@ | ||
package wordle.domain | ||
|
||
import java.io.File | ||
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> = File("src/main/resources/words.txt").readLines() | ||
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,44 @@ | ||
package wordle.view | ||
|
||
import wordle.domain.Mark | ||
import wordle.domain.Results | ||
|
||
fun printStartMessage(fixedCount: Int) { | ||
println("WORDLE 을 $fixedCount 번 만에 맞춰 보세요.\n시도의 결과는 타일의 색 변화로 나타납니다.") | ||
} | ||
|
||
fun requestInput(): String { | ||
println("정답을 입력해 주세요.") | ||
return readln() | ||
} | ||
|
||
fun printResults(results: Results, isPlaying: Boolean, tryCount: Int, fixedCount: Int) { | ||
println() | ||
if (!isPlaying) { | ||
printTryCount(tryCount, fixedCount) | ||
} | ||
results.value.forEach { | ||
printResult(it) | ||
} | ||
println() | ||
} | ||
|
||
fun printTryCount(tryCount: Int, fixedCount: Int) { | ||
println("$tryCount/$fixedCount\n") | ||
} | ||
|
||
private fun printResult(result: List<Mark>) { | ||
val stringBuilder = StringBuilder() | ||
result.forEach { | ||
when (it) { | ||
Mark.NONE -> stringBuilder.append("⬜") | ||
Mark.EXIST -> stringBuilder.append("🟨") | ||
Mark.EXACT -> stringBuilder.append("🟩") | ||
} | ||
} | ||
println(stringBuilder.toString()) | ||
} | ||
|
||
fun printErrorMessage(message: String?) { | ||
println(message) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package practice | ||
|
||
import org.assertj.core.api.AssertionsForInterfaceTypes.assertThat | ||
import org.junit.jupiter.api.Test | ||
|
||
class DslTest { | ||
|
||
@Test | ||
fun `자기소개 프러퍼티 버전`() { | ||
val person = introduce { | ||
name = "최수연" | ||
age = 26 | ||
} | ||
assertThat(person.name).isEqualTo("최수연") | ||
assertThat(person.age).isEqualTo(26) | ||
} | ||
|
||
@Test | ||
fun `자기소개 함수 버전`() { | ||
val person = introduce { | ||
name("최수연") | ||
age(26) | ||
} | ||
assertThat(person.name).isEqualTo("최수연") | ||
assertThat(person.age).isEqualTo(26) | ||
} | ||
} | ||
|
||
private fun introduce(builder: PersonBuilder.() -> Unit): Person { | ||
// 람다에서 함수 형식을 지정가능 | ||
return PersonBuilder().apply(builder).build() | ||
} | ||
|
||
class PersonBuilder { | ||
lateinit var name: String | ||
var age: Int = 0 | ||
|
||
fun name(value: String) { | ||
name = value | ||
} | ||
|
||
fun age(value: Int) { | ||
age = value | ||
} | ||
|
||
fun build(): Person { | ||
return Person(name, age) | ||
} | ||
} | ||
|
||
data class Person(val name: String, val age: Int) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package study | ||
|
||
import org.assertj.core.api.Assertions.assertThat | ||
import org.assertj.core.groups.Tuple | ||
import org.junit.jupiter.api.Test | ||
|
||
class DslTest { | ||
@Test | ||
fun `페퍼 자기소개 프러퍼티 버전`() { | ||
val profile = introduce { | ||
name("최수연") | ||
company("우아한테크코스") | ||
skills { | ||
soft("A passion for problem solving") | ||
soft("Good communication skills") | ||
hard("Kotlin") | ||
} | ||
languages { | ||
"Korean" level 5 | ||
"English" level 3 | ||
} | ||
} | ||
assertThat(profile.name).isEqualTo("최수연") | ||
assertThat(profile.company).isEqualTo("우아한테크코스") | ||
assertThat(profile.skills.soft).contains("A passion for problem solving", "Good communication skills") | ||
assertThat(profile.skills.hard).contains("Kotlin") | ||
assertThat(profile.languages.languages).extracting("type", "level") | ||
.contains( | ||
Tuple.tuple("Korean", 5), | ||
Tuple.tuple("English", 3) | ||
) | ||
} | ||
} | ||
|
||
fun introduce(builder: ProfileBuilder.() -> Unit): Profile { | ||
return ProfileBuilder().apply(builder).build() | ||
} | ||
|
||
class ProfileBuilder { | ||
private lateinit var name: String | ||
private lateinit var company: String | ||
private lateinit var skills: Skills | ||
private lateinit var languages: Languages | ||
|
||
fun name(value: String) { | ||
name = value | ||
} | ||
|
||
fun company(value: String) { | ||
company = value | ||
} | ||
|
||
fun skills(skillValue: Skills.() -> Unit) { | ||
skills = Skills(mutableListOf(), mutableListOf()).apply(skillValue) | ||
} | ||
|
||
fun languages(languageSkill: Languages.() -> Unit) { | ||
languages = Languages(mutableListOf()).apply(languageSkill) | ||
} | ||
|
||
fun build(): Profile { | ||
return Profile(name, company, skills, languages) | ||
} | ||
} | ||
|
||
data class Profile(val name: String, val company: String, val skills: Skills, val languages: Languages) | ||
|
||
data class Skills(val hard: MutableList<String>, val soft: MutableList<String>) { | ||
fun hard(value: String) { | ||
hard.add(value) | ||
} | ||
|
||
fun soft(value: String) { | ||
soft.add(value) | ||
} | ||
} | ||
Comment on lines
+68
to
+76
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 class Languages(val languages: MutableList<Language>) { | ||
infix fun String.level(value: Int) = languages.add(Language(this, value)) | ||
} | ||
|
||
data class Language(val type: String, val level: Int) |
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
문을 활용해서 반영해보았습니다.