-
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
feat: 준비준비와 파코님과의 과제입니다 #2
base: main
Are you sure you want to change the base?
Changes from all commits
5d8479e
eb31a9d
f2afbd3
5bab906
58b435b
35852c3
6fde073
3bb8164
6fc2288
991380a
7369f19
51876df
7bba0b9
3d994bf
050f619
de730f8
d9546f3
3bb172b
ced063d
759013e
1d39fa9
eb71087
cbf8f74
8e52fd5
0a32d12
b06d616
79f9e49
c4263dd
21a5791
1d526cd
1f4a332
ad3d1b5
6783141
5319a1a
6d6d054
e4dfdf0
4ee303d
6cc86be
beb1b18
2494155
605b476
dd9930b
828fb34
155f16a
72ebdb8
f57b1e5
64f24a9
5bce301
e544c0f
c0633eb
7e67435
842c92e
0fcd81e
293c4b6
00b8968
8bdfa9d
dd25c18
1f0c1bc
e4a4a08
aad4899
1f1a3af
6ecead7
7305a6a
d13c866
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,40 @@ | ||
package wordle.controller | ||
|
||
import wordle.domain.Answer | ||
import wordle.domain.Game | ||
import wordle.domain.ResultTiles | ||
import wordle.domain.Position | ||
import wordle.domain.Word | ||
import wordle.utils.WordsCreator | ||
import wordle.view.InputView | ||
import wordle.view.ResultView | ||
|
||
private const val LAST_PLAY_COUNT = 6 | ||
|
||
fun main() { | ||
val game = Game(Answer(WordsCreator(), Position(Answer.ANSWER_POSITION)).answer) | ||
|
||
Comment on lines
+14
to
+16
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객체에 Answer의 answer를 주는 게 아닌, Answer만 주도록 변경해 보세요. |
||
ResultView.printInit() | ||
|
||
playGame(game) | ||
} | ||
|
||
private fun playGame(game: Game) { | ||
val results = ResultTiles() | ||
|
||
var tryCount = 0 | ||
|
||
while (tryCount < LAST_PLAY_COUNT) { | ||
val inputWord = InputView.askWord(WordsCreator.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. 입력한 글자가 존재하는가에 대한 부분은 뷰에 대한 기능이 아닌, Wordle이라는 게임의 규칙이라고 봅니다. 글자가 존재하는가에 대한 기능을 도메인 로직으로 변경해 보세요. |
||
val resultTiles = game.play(Word(inputWord)) | ||
val combined = results.combine(resultTiles) | ||
ResultView.printAllResults(combined.resultTiles) | ||
|
||
tryCount++ | ||
|
||
if (game.isWinner(resultTiles)) { | ||
ResultView.printGamePlayCount(tryCount) | ||
break | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package wordle.domain | ||
|
||
import wordle.utils.Creator | ||
import java.time.LocalDate | ||
import java.time.temporal.ChronoUnit | ||
|
||
class Answer(private val creator: Creator, private val position: Position) { | ||
val answer: Word | ||
get() = createAnswer() | ||
|
||
fun createAnswer(): Word { | ||
return creator.createWords().findAnswer(position) | ||
} | ||
|
||
companion object { | ||
private val SUBTRACT_DATE_FOR_ANSWER = LocalDate.of(2021, 6, 19) | ||
val ANSWER_POSITION = ChronoUnit.DAYS.between(SUBTRACT_DATE_FOR_ANSWER, LocalDate.now()).toInt() | ||
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. 만약, wordle 게임을 다시 시작할 수 있도록 만들게 될 때, 23시 50분에 게임을 맞히고, 다음날 00시를 지나서 새로운 게임을 시작하게 된다면, ANSWER_POSITION 변경은 언제 이루어질까요? 🤔 |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package wordle.domain | ||
|
||
class Game(private val answer: Word) { | ||
|
||
fun play(input: Word): Tiles { | ||
val inputChars = input.value.toCharArray() | ||
val wordMatcher = WordMatcher(answer) | ||
|
||
return createResultTiles(inputChars, wordMatcher) | ||
} | ||
|
||
private fun createResultTiles(inputChars: CharArray, wordMatcher: WordMatcher): Tiles { | ||
return Tiles(inputChars.mapIndexed { index, it -> wordMatcher.match(it.toString(), index) }) | ||
} | ||
|
||
Comment on lines
+11
to
+15
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. 글자 하나의 비교를 String으로 비교하는 것이 아닌, Char로 비교해 보는 건 어떠실까요? |
||
fun isWinner(resultTiles: Tiles): Boolean { | ||
return resultTiles.isWinner() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,8 @@ | ||||||||||
package wordle.domain | ||||||||||
|
||||||||||
class Position(private val position: Int) { | ||||||||||
|
||||||||||
fun percent(value: Int): Int { | ||||||||||
return position % value | ||||||||||
} | ||||||||||
Comment on lines
+5
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.
Suggested change
해당 부분도 문(statement) 보다 식(expression)을 사용하면 코드가 간결해집니다. |
||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package wordle.domain | ||
|
||
class ResultTiles(val resultTiles: List<Tiles>) { | ||
|
||
constructor(): this(listOf()) | ||
|
||
Comment on lines
+2
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. 기본 값을 설정하는 것만으로도, constructor()를 삭제할 수 있지 않을까요? resultTiles에 대해 기본 값을 설정하도록 변경해 보세요. |
||
fun combine(newResults: Tiles): ResultTiles = ResultTiles(resultTiles + newResults) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package wordle.domain | ||
|
||
enum class Tile { | ||
YELLOW, | ||
GREEN, | ||
GRAY | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package wordle.domain | ||
|
||
@JvmInline | ||
value class Tiles(val tiles: List<Tile>) { | ||
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 isWinner(): Boolean { | ||
val count = tiles.count { it == Tile.GREEN } | ||
|
||
return count == TOTAL_TILE_COUNT | ||
} | ||
Comment on lines
+6
to
+10
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. Kotlin List API를 사용하여 해당 코드를 더 간결하게 만들 수 있지 않을까요? |
||
|
||
companion object { | ||
private const val TOTAL_TILE_COUNT = 5 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,31 @@ | ||||||||||||
package wordle.domain | ||||||||||||
|
||||||||||||
@JvmInline | ||||||||||||
value class Word(val value: String) { | ||||||||||||
|
||||||||||||
Comment on lines
+2
to
+5
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. value class 사용 매우 좋습니다! 👍 |
||||||||||||
init { | ||||||||||||
require(isWordSizeAndAlphabet()) { | ||||||||||||
WRONG_WORD_SIZE_MESSAGE | ||||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
private fun isWordSizeAndAlphabet() = value.length == WORD_SIZE && ALPHABET_REGEX.matches(value) | ||||||||||||
|
||||||||||||
Comment on lines
+11
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.
Suggested change
|
||||||||||||
fun findAlphabet(index: Int): String { | ||||||||||||
require(index >= FIRST_INDEX && index <= value.length) { INDEX_OUT_RANGE_MESSAGE } | ||||||||||||
|
||||||||||||
return value[index].toString() | ||||||||||||
} | ||||||||||||
|
||||||||||||
fun contains(compareValue: String): Boolean { | ||||||||||||
return value.contains(compareValue) | ||||||||||||
} | ||||||||||||
|
||||||||||||
companion object { | ||||||||||||
private const val FIRST_INDEX = 0 | ||||||||||||
private const val WORD_SIZE = 5 | ||||||||||||
private const val WRONG_WORD_SIZE_MESSAGE = "5글자여야합니다" | ||||||||||||
private const val INDEX_OUT_RANGE_MESSAGE = "인덱스 범위를 초과했습니다." | ||||||||||||
private val ALPHABET_REGEX = Regex("^[a-zA-Z]*$") | ||||||||||||
} | ||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package wordle.domain | ||
|
||
class WordMatcher(private val answer: Word) { | ||
|
||
fun match(alphabet: String, index: Int): Tile { | ||
if (alphabet == answer.findAlphabet(index)) { | ||
return Tile.GREEN | ||
} | ||
|
||
if (answer.contains(alphabet)) { | ||
return Tile.YELLOW | ||
} | ||
|
||
return Tile.GRAY | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,12 @@ | ||||||||||||||||||||||||
package wordle.domain | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
class Words(private val words: List<Word>) { | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
Comment on lines
+2
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. 일급 컬렉션을 사용하셨군요! 👍👍 |
||||||||||||||||||||||||
fun findAnswer(position: Position): Word { | ||||||||||||||||||||||||
return words[position.percent(words.size)] | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
fun contains(value: String): Boolean { | ||||||||||||||||||||||||
return words.contains(Word(value)) | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
Comment on lines
+5
to
+11
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
해당 경우에는 문(statement) 보다 간결성을 위하여 식(expression)으로 변경하는 게 좋지 않을까요? |
||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package wordle.utils | ||
|
||
import wordle.domain.Words | ||
|
||
interface Creator { | ||
|
||
fun createWords(): Words | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package wordle.utils | ||
|
||
import wordle.domain.Word | ||
import wordle.domain.Words | ||
import java.io.File | ||
|
||
class WordsCreator : Creator { | ||
|
||
override fun createWords(): Words { | ||
val wordsFile = read().readLines() | ||
return Words(wordsFile.map { Word(it) }) | ||
} | ||
|
||
private fun read(): File { | ||
return File(ClassLoader.getSystemResource(ANSWERS_TEXT_PATH).file) | ||
} | ||
|
||
companion object { | ||
private const val ANSWERS_TEXT_PATH = "words.txt" | ||
val WORDS = WordsCreator().createWords() | ||
} | ||
Comment on lines
+7
to
+21
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. WordsCreator를 통해서 words 리스트를 가지고 오게 된다면, words에 있는 단어를 가지고 오는 로직과, 답안 및 정답에 대해 체크할 수 있는 로직이 다른 객체에 책임을 가지게 된다고 생각합니다. |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package wordle.view | ||
|
||
import wordle.domain.Words | ||
|
||
object InputView { | ||
|
||
Comment on lines
+4
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. object 클래스를 사용한 view 구현 좋습니다! |
||
fun askWord(words: Words): String { | ||
while (true) { | ||
val input = question() | ||
if (words.contains(input)) { | ||
return input | ||
} | ||
|
||
println("올바르지 않은 단어입니다.") | ||
} | ||
} | ||
|
||
private fun question(): String { | ||
println("정답을 입력해 주세요.") | ||
return readln() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package wordle.view | ||
|
||
import wordle.domain.Tile | ||
import wordle.domain.Tiles | ||
|
||
private const val MAX_TRY_COUNT = 6 | ||
|
||
Comment on lines
+5
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. MAX_TRY_COUNT를 object 속이 아닌, 파일 내에 전역으로 선언해놓으신 이유가 있을까요? |
||
object ResultView { | ||
|
||
fun printInit() { | ||
println("WORDLE을 ${MAX_TRY_COUNT}번 만에 맞춰 보세요.\n시도의 결과는 타일의 색 변화로 나타납니다.") | ||
} | ||
|
||
fun printGamePlayCount(index: Int) { | ||
println("$index /$MAX_TRY_COUNT") | ||
} | ||
|
||
fun printAllResults(results: List<Tiles>) { | ||
results.forEach { | ||
printAllTiles(it.tiles) | ||
println() | ||
} | ||
} | ||
|
||
private fun printAllTiles(tiles: List<Tile>) { | ||
tiles.forEach { tile -> | ||
print(tile.viewTile()) | ||
} | ||
} | ||
|
||
private val Tile.viewTile: () -> String | ||
get() = { | ||
when (this) { | ||
Tile.GREEN -> "\uD83D\uDFE9" | ||
Tile.YELLOW -> "\uD83D\uDFE8" | ||
Tile.GRAY -> "⬜" | ||
} | ||
} | ||
Comment on lines
+31
to
+38
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. viewTile을 일급 함수로 만든 이유를 알 수 있을까요? 제 생각으로는 충분히 함수로도 표현이 가능하다고 생각합니다. |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package wordle.domain | ||
|
||
import org.assertj.core.api.Assertions.assertThat | ||
import org.junit.jupiter.api.Test | ||
|
||
class GameTest { | ||
|
||
@Test | ||
fun `6번의 기회안에 5글자를 모두 맞추면 성공한다`() { | ||
val givenAnswer = Word("words") | ||
Comment on lines
+8
to
+10
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. '6번의 기회안에 5글자를 모두 맞추면 성공한다' 라는 것보다는 '정답과 답안이 일치하면 게임이 끝난다'가 좋을 것 같습니다! 추가로, 7번 이상 입력했을 때의 테스트 케이스도 만들어 보시는 게 어떠신가요? |
||
val game = Game(givenAnswer) | ||
val givenInput = Word("words") | ||
|
||
val result = game.play(givenInput) | ||
val isWinner = game.isWinner(result) | ||
|
||
assertThat(isWinner).isTrue | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package wordle.domain | ||
|
||
import wordle.utils.Creator | ||
|
||
class MockingCreator : Creator { | ||
|
||
override fun createWords(): Words { | ||
return Words(listOf(Word("queen"), Word("chunk"), Word("awake"))) | ||
} | ||
} | ||
Comment on lines
+5
to
+10
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.txt를 읽어들여서 테스트를 하는 게 아닌, 목 데이터를 이용하여 테스트를 하셨군요! 👍👍👍 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package wordle.domain | ||
|
||
import org.assertj.core.api.Assertions.assertThat | ||
import org.junit.jupiter.params.ParameterizedTest | ||
import org.junit.jupiter.params.provider.CsvSource | ||
|
||
class PositionTest { | ||
|
||
@ParameterizedTest | ||
@CsvSource("10, 20, 10", "2, 5, 2", "0, 5, 0") | ||
fun `위치 값과 전달받은 분모값을 나눈다`(positionValue: Int, denominator: Int, expected: Int) { | ||
val position = Position(positionValue) | ||
|
||
assertThat(position.percent(denominator)).isEqualTo(expected) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package wordle.domain | ||
|
||
import org.assertj.core.api.Assertions.assertThat | ||
import org.junit.jupiter.api.Test | ||
|
||
class ResultsTest { | ||
|
||
@Test | ||
fun `기존에_존재하는_결과타일에_타일결과를_추가한다`() { | ||
val results = ResultTiles() | ||
|
||
val firstCombined = results.combine(Tiles(listOf(Tile.YELLOW, Tile.YELLOW, Tile.YELLOW, Tile.GRAY, Tile.YELLOW))) | ||
val secondCombined = firstCombined.combine(Tiles(listOf(Tile.GREEN, Tile.GRAY, Tile.GREEN, Tile.GRAY, Tile.YELLOW))) | ||
|
||
assertThat(secondCombined.resultTiles).containsExactly( | ||
Tiles(listOf(Tile.YELLOW, Tile.YELLOW, Tile.YELLOW, Tile.GRAY, Tile.YELLOW)), | ||
Tiles(listOf(Tile.GREEN, Tile.GRAY, Tile.GREEN, Tile.GRAY, Tile.YELLOW)) | ||
) | ||
} | ||
} |
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.
가장 중요한 로직에 대한 부분이 아직 구현이 안되었군요! 해당 로직에 대해서 구현해보세요 😊