diff --git a/src/main/kotlin/racingcar/Application.kt b/src/main/kotlin/racingcar/Application.kt index 8688f7f131..64d2036264 100644 --- a/src/main/kotlin/racingcar/Application.kt +++ b/src/main/kotlin/racingcar/Application.kt @@ -1,9 +1,9 @@ package racingcar -class Application - fun main() { - val (cars, roundCount) = InputView() + val cars = InputView.readCars() + val roundCount = InputView.readRoundCount() + val racingGame = RacingGame( cars = cars, numberGenerator = RacingCarNumberGenerator() @@ -11,6 +11,9 @@ fun main() { repeat(roundCount) { val drivingCars = racingGame.round() - ResultView.print(drivingCars) + ResultView.printLocation(drivingCars) } + + val winners = racingGame.judge() + ResultView.printWinner(winners) } diff --git a/src/main/kotlin/racingcar/Car.kt b/src/main/kotlin/racingcar/Car.kt index 15c3a5de51..295c0c3a10 100644 --- a/src/main/kotlin/racingcar/Car.kt +++ b/src/main/kotlin/racingcar/Car.kt @@ -1,16 +1,27 @@ package racingcar class Car( - private var location: Int = 0, + private var _location: Int = DEFAULT_CAR_LOCATION, + val name: String, ) { - fun move(randNumber: Int) { - val rule = MovingJudgeRule.judge(randNumber) - val strategy = rule.strategy() + init { + require(isValidLength(name)) { "글자수는 5자를 넘을 수 없어요" } + } + + val location + get() = _location + fun move(number: Int) { + val rule = MovingJudgeRule.judge(number) + val strategy = rule.strategy + _location = strategy.move(_location) + } - location = strategy.move(location) + private fun isValidLength(name: String): Boolean { + return name.length <= VALID_CAR_NAME_LENGTH } - fun currentLocation(): Int { - return location + companion object { + private const val DEFAULT_CAR_LOCATION = 0 + private const val VALID_CAR_NAME_LENGTH = 5 } } diff --git a/src/main/kotlin/racingcar/InputView.kt b/src/main/kotlin/racingcar/InputView.kt index 7260d25cc0..bcd8a80f39 100644 --- a/src/main/kotlin/racingcar/InputView.kt +++ b/src/main/kotlin/racingcar/InputView.kt @@ -1,24 +1,26 @@ package racingcar -data class InputView( - val cars: List = readCars("자동차 대수는 몇 대인가요?"), - val roundCount: Int = readRoundCount("시도할 횟수는 몇 회인가요?"), -) { - companion object { - private fun readCars(message: String): List { - println(message) +object InputView { + fun readCars(): List { + println("경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분).") + val names = parseCarName(readln()) - val list = mutableListOf() - repeat(readln().toInt()) { - list.add(Car()) - } - - return list + val list = mutableListOf() + for (name in names) { + list.add( + Car(name = name) + ) } - private fun readRoundCount(message: String): Int { - println(message) - return readln().toInt() - } + return list + } + + fun readRoundCount(): Int { + println("시도할 횟수는 몇 회인가요?") + return readln().toInt() + } + + private fun parseCarName(input: String): List { + return input.split(",") } } diff --git a/src/main/kotlin/racingcar/MovingJudgeRule.kt b/src/main/kotlin/racingcar/MovingJudgeRule.kt index 4063cbf1be..2b8bfe00a2 100644 --- a/src/main/kotlin/racingcar/MovingJudgeRule.kt +++ b/src/main/kotlin/racingcar/MovingJudgeRule.kt @@ -2,15 +2,15 @@ package racingcar enum class MovingJudgeRule( private val expression: (Int) -> (Boolean), - private val strategy: MovingStrategy + val strategy: MovingStrategy ) { ADVANCE_RULE( - expression = { number -> number >= FORWARD_STRATEGY_BOUND }, + expression = { it >= FORWARD_STRATEGY_BOUND }, strategy = MovingStrategy.ADVANCE, ), STOP_RULE( - expression = { false }, + expression = { it < FORWARD_STRATEGY_BOUND }, strategy = MovingStrategy.STOP, ), ; @@ -19,10 +19,6 @@ enum class MovingJudgeRule( return expression(number) } - fun strategy(): MovingStrategy { - return strategy - } - companion object { private const val FORWARD_STRATEGY_BOUND = 4 diff --git a/src/main/kotlin/racingcar/NumberGenerator.kt b/src/main/kotlin/racingcar/NumberGenerator.kt index 97ce114911..80a1b470e4 100644 --- a/src/main/kotlin/racingcar/NumberGenerator.kt +++ b/src/main/kotlin/racingcar/NumberGenerator.kt @@ -6,6 +6,11 @@ interface NumberGenerator { class RacingCarNumberGenerator : NumberGenerator { override fun rand(): Int { - return (0..9).random() + return (RAND_NUMBER_START_BOUND..RAND_NUMBER_END_BOUND).random() + } + + companion object { + private const val RAND_NUMBER_START_BOUND = 0 + private const val RAND_NUMBER_END_BOUND = 9 } } diff --git a/src/main/kotlin/racingcar/README.md b/src/main/kotlin/racingcar/README.md index 7b9fcf814a..40391cc747 100644 --- a/src/main/kotlin/racingcar/README.md +++ b/src/main/kotlin/racingcar/README.md @@ -11,11 +11,15 @@ - [x] 숫자 생성기 0 ~ 9 사이의 무작위한 값을 생성해요. ### 레이싱 게임 (Racing Game) +- [x] 레이싱 게임에서 참여하는 자동차는 1대 이상이어야 합니다. - [x] 레이싱 게임은 자동차의 대수와 시도할 횟수를 입력할 수 있어요. -- [x] 레이싱 게임은 매 횟수마다 자동차의 현재 위치를 보여줘요. +- [x] 레이싱 게임은 자동차의 이름을 쉽표로 구분해요. +- [x] 레이싱 게임은 매 횟수마다 자동차의 이름과 현재 위치를 보여줘요. #### 자동차 (Car) - [x] 자동차는 입력한 숫자에 따라 이동해요. - [x] 0~3의 숫자를 입력하면 자동차는 멈춰요. - [x] 4 이상의 숫자를 입력하면 자동차는 1만큼 전진해요. +- [x] 자동차는 이름을 가질 수 있어요. + - [x] 자동차의 이름은 5자를 초과할 수 없어요. - [x] 자동차는 자신이 이동한 거리를 알 수 있어요. \ No newline at end of file diff --git a/src/main/kotlin/racingcar/RacingGame.kt b/src/main/kotlin/racingcar/RacingGame.kt index a2cebbf422..20b0446a6f 100644 --- a/src/main/kotlin/racingcar/RacingGame.kt +++ b/src/main/kotlin/racingcar/RacingGame.kt @@ -4,6 +4,11 @@ class RacingGame( private val cars: List, private val numberGenerator: NumberGenerator, ) { + + init { + requireCarExist(cars) + } + fun round(): List { for (car in cars) { val randNumber = numberGenerator.rand() @@ -12,4 +17,15 @@ class RacingGame( return cars } + + fun judge(): List { + val maxLocation = cars.maxOf { it.location } + return cars.filter { + it.location == maxLocation + } + } + + private fun requireCarExist(cars: List) { + require(cars.isNotEmpty()) { "레이싱 게임에서 참여하는 자동차는 1대 이상이어야 합니다." } + } } diff --git a/src/main/kotlin/racingcar/ResultView.kt b/src/main/kotlin/racingcar/ResultView.kt index 74ff59a772..0e2dd93046 100644 --- a/src/main/kotlin/racingcar/ResultView.kt +++ b/src/main/kotlin/racingcar/ResultView.kt @@ -1,14 +1,17 @@ package racingcar -class ResultView { - companion object { - fun print(cars: List) { - for (car in cars) { - val location = car.currentLocation() - repeat(location) { print("-") } - println() - } +object ResultView { + fun printLocation(cars: List) { + for (car in cars) { + val location = car.location + print(car.name + " : ") + repeat(location) { print("-") } println() } + println() + } + + fun printWinner(cars: List) { + println("${cars.joinToString { it.name }}가 최종 우승했습니다.") } } diff --git a/src/test/kotlin/racingcar/CarTest.kt b/src/test/kotlin/racingcar/CarTest.kt index 244a0c15e3..31736c1e08 100644 --- a/src/test/kotlin/racingcar/CarTest.kt +++ b/src/test/kotlin/racingcar/CarTest.kt @@ -6,25 +6,25 @@ import io.kotest.matchers.shouldBe class CarTest : StringSpec({ "자동차의 처음 위치는 0이에요" { - val car = Car() - car.currentLocation() shouldBe 0 + val car = Car(name = "car") + car.location shouldBe 0 } "자동차는 4 ~ 9의 숫자일 때 1 만큼 이동해요" { (4..9).forEach { number -> - val car = Car() + val car = Car(name = "car") car.move(number) - car.currentLocation() shouldBe 1 + car.location shouldBe 1 } } "자동차는 0~3의 숫자일 때 이동하지 않아요" { (0..3).forEach { number -> - val car = Car() + val car = Car(name = "car") car.move(number) - car.currentLocation() shouldBe 0 + car.location shouldBe 0 } } }) diff --git a/src/test/kotlin/racingcar/RacingGameTest.kt b/src/test/kotlin/racingcar/RacingGameTest.kt index 3edc4447ab..534251dcc5 100644 --- a/src/test/kotlin/racingcar/RacingGameTest.kt +++ b/src/test/kotlin/racingcar/RacingGameTest.kt @@ -4,6 +4,8 @@ import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import io.mockk.every import io.mockk.mockk +import org.junit.jupiter.api.assertThrows +import java.lang.IllegalArgumentException class RacingGameTest : StringSpec({ "레이싱 게임은 매 횟수마다 자동차의 현재 위치를 보여줘요" { @@ -11,12 +13,24 @@ class RacingGameTest : StringSpec({ every { mockNumberGenerator.rand() } returns 0 val racingGame = RacingGame( - cars = listOf(Car(0)), + cars = listOf(Car(0, "car")), numberGenerator = mockNumberGenerator ) val car = racingGame.round()[0] - car.currentLocation() shouldBe 0 + car.location shouldBe 0 + } + + "레이싱 게임에 참여하는 자동차는 1대 이상이어야 합니다." { + assertThrows { + val mockNumberGenerator = mockk() + every { mockNumberGenerator.rand() } returns 0 + + val racingGame = RacingGame( + cars = listOf(), + numberGenerator = mockNumberGenerator + ) + } } })