diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt new file mode 100644 index 000000000..f986e5a75 --- /dev/null +++ b/src/main/kotlin/Main.kt @@ -0,0 +1,45 @@ +fun main() { + val height = inputHeight() + val width = inputWidth() + val mineSize = inputMineSize() + + val mineSweeper = MineSweeper(width, height, mineSize, RandomMinePositionsGenerator(width, height)) + printMineSweeperMap(mineSweeper) +} + +private fun inputHeight(): Int { + println("높이를 입력하세요.") + return readln().toInt() +} + +private fun inputWidth(): Int { + println("너비를 입력하세요.") + return readln().toInt() +} + +private fun inputMineSize(): Int { + println("지뢰는 몇 개인가요?") + return readln().toInt() +} + +private fun printMineSweeperMap(mineSweeper: MineSweeper) { + println("지뢰찾기 게임 시작") + (0 until mineSweeper.height).forEach{ y -> + printRowOfMineSweeperMap(mineSweeper, y) + } +} + +private fun printRowOfMineSweeperMap(mineSweeper: MineSweeper, y: Int) { + val row = (0 until mineSweeper.width) + .map { x -> Position(y, x) } + .map { mineSweeper.cells().getValue(it) } + .joinToString(separator = " ") { statusToText(it) } + println(row) +} + +private fun statusToText(status: Status): String { + return when(status) { + Status.EMPTY -> "C" + Status.MINE -> "*" + } +} diff --git a/src/main/kotlin/MinePositionGenerator.kt b/src/main/kotlin/MinePositionGenerator.kt new file mode 100644 index 000000000..7a644cc35 --- /dev/null +++ b/src/main/kotlin/MinePositionGenerator.kt @@ -0,0 +1,4 @@ +fun interface MinePositionGenerator { + + fun generate(size: Int): List +} diff --git a/src/main/kotlin/MineSweeper.kt b/src/main/kotlin/MineSweeper.kt new file mode 100644 index 000000000..0465a4f00 --- /dev/null +++ b/src/main/kotlin/MineSweeper.kt @@ -0,0 +1,21 @@ +class MineSweeper( + val width: Int, + val height: Int, + mineSize: Int, + minePositionGenerator: MinePositionGenerator +) { + private val minePositions = minePositionGenerator.generate(mineSize) + + fun cells(): Map { + return Position.createInRange(width, height) + .associateWith { cellValue(it) } + } + + private fun cellValue(position: Position): Status { + return if (minePositions.contains(position)) { + Status.MINE + } else { + Status.EMPTY + } + } +} diff --git a/src/main/kotlin/Position.kt b/src/main/kotlin/Position.kt new file mode 100644 index 000000000..95afcd15c --- /dev/null +++ b/src/main/kotlin/Position.kt @@ -0,0 +1,11 @@ +data class Position(val y: Int, val x: Int) { + companion object { + fun createInRange(width: Int, height: Int): List { + return (0 until width).flatMap { x -> + (0 until height).map { y -> + Position(y, x) + } + } + } + } +} diff --git a/src/main/kotlin/RandomMinePositionsGenerator.kt b/src/main/kotlin/RandomMinePositionsGenerator.kt new file mode 100644 index 000000000..4937834d7 --- /dev/null +++ b/src/main/kotlin/RandomMinePositionsGenerator.kt @@ -0,0 +1,12 @@ +class RandomMinePositionsGenerator( + private val maxWidth: Int, + private val maxHeight: Int, +): MinePositionGenerator { + override fun generate(size: Int): List { + require(size <= maxWidth * maxHeight) { "생성할 수 있는 지뢰 위치 갯수보다 많이 생성하려합니다." } + + return Position.createInRange(maxWidth, maxHeight) + .shuffled() + .subList(0, size) + } +} diff --git a/src/main/kotlin/Status.kt b/src/main/kotlin/Status.kt new file mode 100644 index 000000000..a933c1948 --- /dev/null +++ b/src/main/kotlin/Status.kt @@ -0,0 +1,4 @@ +enum class Status { + EMPTY, + MINE, +} diff --git a/src/test/kotlin/MineSweeperTest.kt b/src/test/kotlin/MineSweeperTest.kt new file mode 100644 index 000000000..f434a9dc0 --- /dev/null +++ b/src/test/kotlin/MineSweeperTest.kt @@ -0,0 +1,43 @@ +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource + +class MineSweeperTest { + + @ParameterizedTest + @MethodSource("minePositionsForDrawMap") + fun `지뢰찾기 맵을 그린다`(minePositions: List) { + val mineSweeper = MineSweeper(5, 5, minePositions.size) { _ -> minePositions } + + val cells = mineSweeper.cells() + + assertThat(cells) + .isEqualTo(expectedMap(minePositions, 5, 5)) + } + + private fun expectedMap(minePositions: List, width:Int, height: Int): Map { + val expected = allPositions(width, height) + .associateWith { Status.EMPTY } + .toMutableMap() + minePositions.forEach { expected[it] = Status.MINE } + return expected + } + + private fun allPositions(width: Int, height: Int): List { + return (0 until width).flatMap { x -> + (0 until height).map { y -> + Position(y, x) + } + } + } + + companion object { + @JvmStatic + fun minePositionsForDrawMap(): List> { + return listOf( + listOf(), + listOf(Position(0, 0), Position(1, 1), Position(2, 2)) + ) + } + } +} diff --git a/src/test/kotlin/RandomMinePositionsGeneratorTest.kt b/src/test/kotlin/RandomMinePositionsGeneratorTest.kt new file mode 100644 index 000000000..938ce8163 --- /dev/null +++ b/src/test/kotlin/RandomMinePositionsGeneratorTest.kt @@ -0,0 +1,29 @@ +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import java.lang.IllegalArgumentException + +class RandomMinePositionsGeneratorTest { + + @Test + fun `랜덤 지뢰 위치 생성`() { + val generator = RandomMinePositionsGenerator(5, 5) + + val actual = generator.generate(5) + + assertThat(actual).hasSize(5) + assertThat(actual) + .hasSize(5) + .allMatch { it.x in (0 until 5) } + .allMatch { it.y in (0 until 5) } + } + + @Test + fun `생성할 수 있는 지뢰 위치 갯수 넘길 경우 예외 발`() { + val generator = RandomMinePositionsGenerator(2, 2) + + assertThrows { + generator.generate(5) + } + } +}