-
Notifications
You must be signed in to change notification settings - Fork 206
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
[π 3λ¨κ³ - μ§λ’° μ°ΎκΈ°(κ²μ μ€ν)] μ μ’ ν λ―Έμ μ μΆν©λλ€. #487
base: jjongwa
Are you sure you want to change the base?
Changes from 13 commits
2e6a43c
d84a3d5
1721d41
8ce06c5
e92bd17
be1c3fb
b9edd3c
10d0733
912d671
49f3806
646f2f3
d956b42
19f1404
b9f4f1b
d9995d5
d7db7a3
2bb8cf2
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 |
---|---|---|
|
@@ -2,63 +2,68 @@ package minesweeper.domain | |
|
||
class Field( | ||
private val fieldInfo: FieldInfo, | ||
private val mineCount: MineCount, | ||
private val spotGenerator: SpotGenerator, | ||
private val minePositions: Set<Position>, | ||
) { | ||
private val width = fieldInfo.getWidth() | ||
val lines: List<FieldLine> = createField() | ||
private val spots: Map<Position, Spot> = createField() | ||
|
||
init { | ||
validateMineCount() | ||
} | ||
|
||
private fun createField(): List<FieldLine> { | ||
val spots = spotGenerator.generate(fieldInfo, mineCount) | ||
return spots.mapIndexed { index, spot -> | ||
if (spot is SafeSpot) { | ||
val y = index / width | ||
val x = index % width | ||
val nearbyMineCount = countAdjacentMines(spots, y, x) | ||
spot.updateNearbyMineCount(nearbyMineCount) | ||
private fun createField(): Map<Position, Spot> { | ||
return (0 until fieldInfo.getWidth() + 1).flatMap { x -> | ||
(0 until fieldInfo.getHeight() + 1).map { y -> | ||
Position(x, y) | ||
} | ||
}.associateWith { position -> | ||
when { | ||
minePositions.contains(position) -> MineSpot(position) | ||
else -> SafeSpot(position) | ||
} | ||
spot | ||
}.chunked(width).map { lineSpots -> | ||
FieldLine(lineSpots) | ||
} | ||
} | ||
|
||
private fun countAdjacentMines( | ||
spots: List<Spot>, | ||
y: Int, | ||
x: Int, | ||
): Int { | ||
return NEARBY.count { (dy, dx) -> | ||
val newY = y + dy | ||
val newX = x + dx | ||
isWithinBounds(newY, newX) && spots[newY * width + newX].isMine() | ||
} | ||
fun getFieldInfo(): FieldInfo { | ||
return fieldInfo | ||
} | ||
|
||
private fun isWithinBounds( | ||
y: Int, | ||
x: Int, | ||
): Boolean { | ||
return y in 0 until fieldInfo.getHeight() && x in 0 until width | ||
fun getSpot(position: Position): Spot { | ||
return spots[position] ?: throw IllegalArgumentException("ν΄λΉ μμΉμ λν Spotμ΄ μ‘΄μ¬νμ§ μμ΅λλ€.") | ||
} | ||
|
||
private fun validateMineCount() { | ||
val height = fieldInfo.getHeight() | ||
val totalSpots = height * width | ||
require(mineCount.count <= totalSpots) { "μ§λ’° κ°μλ νλμ μ΄ μ€νλ³΄λ€ λ§μ μ μμ΅λλ€." } | ||
val totalPossibleSpots = fieldInfo.getHeight() * fieldInfo.getWidth() | ||
require(minePositions.size <= totalPossibleSpots) { "μ§λ’° κ°μλ νλμ μ΄ μ€νλ³΄λ€ λ§μ μ μμ΅λλ€." } | ||
} | ||
|
||
companion object { | ||
private val NEARBY = | ||
listOf( | ||
Pair(-1, 0), | ||
Pair(0, -1), | ||
Pair(0, 1), | ||
Pair(1, 0), | ||
) | ||
fun openSpot(position: Position): OpenResult { | ||
val targetSpot = | ||
spots[position]?.let { | ||
if (it.isMine()) { | ||
return OpenResult.GameOver | ||
} | ||
it | ||
} as SafeSpot | ||
val openResult = targetSpot.open() | ||
targetSpot.calculateNearbyMineCount(minePositions) | ||
checkAndOpenNearbySpot(targetSpot) | ||
return openResult | ||
} | ||
|
||
private fun checkAndOpenNearbySpot(targetSpot: SafeSpot) { | ||
if (targetSpot.nearbyMineCount == 0) { | ||
openNearbySpots(targetSpot.position) | ||
} | ||
} | ||
|
||
private fun openNearbySpots(position: Position) { | ||
val nearbyPositions = position.nearbyPositions() | ||
nearbyPositions.forEach { | ||
spots[it]?.let { spot -> | ||
if (!spot.isOpened()) { | ||
openSpot(it) | ||
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. spot κ°μ²΄μ isNotOpened() νΉμ isClosed() ν¨μλ₯Ό μΆκ°ν΄μ€λ μ’κ² λ€λ μκ°μ΄ λλ€μ! 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. νμ€ν λΆμ λ¬Έμ μ°λ κ²λ³΄λ¨ isClosedλ₯Ό λ§λ€μ΄ μ¬μ©νλ νΈμ΄ ν¨μ¬ λ κ°λ μ±μ΄ μ’μ 보μ΄λ€μ! 2bb8cf2 |
||
} | ||
} | ||
} | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,8 @@ | ||
package minesweeper.domain | ||
|
||
fun interface SpotGenerator { | ||
fun interface MinePositionSelector { | ||
fun generate( | ||
fieldInfo: FieldInfo, | ||
mineCount: MineCount, | ||
): List<Spot> | ||
): Set<Position> | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package minesweeper.domain | ||
|
||
enum class NearbyDirection(private val coordinate: Position) { | ||
UP(Position(0, -1)), | ||
DOWN(Position(0, 1)), | ||
LEFT(Position(-1, 0)), | ||
RIGHT(Position(1, 0)), | ||
UP_LEFT(Position(-1, -1)), | ||
UP_RIGHT(Position(1, -1)), | ||
DOWN_LEFT(Position(-1, 1)), | ||
DOWN_RIGHT(Position(1, 1)), | ||
; | ||
|
||
fun dx(): Int { | ||
return coordinate.x | ||
} | ||
|
||
fun dy(): Int { | ||
return coordinate.y | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package minesweeper.domain | ||
|
||
data class Position(val x: Int, val y: Int) { | ||
fun nearbyPositions(): Set<Position> { | ||
return NearbyDirection.entries.map { direction -> | ||
Position( | ||
x + direction.dx(), | ||
y + direction.dy(), | ||
) | ||
}.toSet() | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,46 @@ | ||
package minesweeper.domain | ||
|
||
abstract class Spot(private val y: Int, private val x: Int) { | ||
sealed interface OpenResult { | ||
data object Success : OpenResult | ||
|
||
data object AlreadyOpened : OpenResult | ||
|
||
data object GameOver : OpenResult | ||
} | ||
|
||
sealed class Spot(val position: Position) { | ||
private var isOpened = false | ||
|
||
fun open(): OpenResult { | ||
if (isOpened) { | ||
return OpenResult.AlreadyOpened | ||
} | ||
isOpened = true | ||
return OpenResult.Success | ||
} | ||
|
||
fun isOpened(): Boolean { | ||
return isOpened | ||
} | ||
|
||
abstract fun isMine(): Boolean | ||
} | ||
|
||
class SafeSpot(position: Position) : Spot(position) { | ||
var nearbyMineCount: Int = 0 | ||
|
||
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. μΈλΆμμλ μ΄ νλλ₯Ό μ¬ν λΉ νμ§ λͺ»νλλ‘, setterλ₯Ό privateμΌλ‘ λ§μ보λ 건 μ΄λ¨κΉμ? 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. nearbyMineCountμ private set μ μ©νμ΅λλ€! d9995d5 |
||
override fun isMine(): Boolean { | ||
return false | ||
} | ||
|
||
fun calculateNearbyMineCount(minePositions: Set<Position>) { | ||
val nearbyPositions = position.nearbyPositions() | ||
this.nearbyMineCount = nearbyPositions.count { it in minePositions } | ||
} | ||
} | ||
|
||
class MineSpot(position: Position) : Spot(position) { | ||
override fun isMine(): Boolean { | ||
return 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.
μ΄ ν¨μλ fieldInfo λ©€λ²μ privateμ μ§μ μΈλΆμ νλ‘νΌν°λ₯Ό 곡κ°νλ ννλ‘ μ κ±°ν΄λ³Ό μ μκ² μ΄μ!
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.
λΆνμν getterλ₯Ό μ¬μ©νμ§ μκ³ νλμ privateμ μ κ±°νλ λ°©μμΌλ‘ λ³κ²½νμ΅λλ€! d7db7a3