-
Notifications
You must be signed in to change notification settings - Fork 199
Step3: 지뢰 찾기(게임 실행) #394
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
base: factoriall
Are you sure you want to change the base?
Step3: 지뢰 찾기(게임 실행) #394
Changes from all commits
e41601b
43d7f8c
ededb5b
47f58b8
d73d9dc
8ccf0c1
d68d518
38700da
ead061d
1a123f3
a8b8e73
ac533e3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package minesweeper | ||
|
|
||
| enum class Direction(val row: Int, val col: Int) { | ||
| NORTH(-1, 0), | ||
| NORTHEAST(-1, 1), | ||
| EAST(0, 1), | ||
| SOUTHEAST(1, 1), | ||
| SOUTH(1, 0), | ||
| SOUTHWEST(1, -1), | ||
| WEST(0, -1), | ||
| NORTHWEST(-1, -1) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,32 @@ | ||
| package minesweeper | ||
|
|
||
| fun main() { | ||
| val mineMap = InputView.getMineMap() | ||
| ResultView.showMap(mineMap) | ||
| val mineSweeper = MineSweeper(InputView.getMineMap()) | ||
| ResultView.start() | ||
|
|
||
| do { | ||
| val clicked = InputView.getClickedPoint() | ||
| val result = mineSweeper.click(clicked) | ||
| when (result) { | ||
| MineSweeper.ClickResult.CONTINUE -> { | ||
| ResultView.showMap(mineSweeper) | ||
| } | ||
|
|
||
| MineSweeper.ClickResult.ALREADY_CLICKED -> { | ||
| ResultView.showAlreadyClickedMessage() | ||
| } | ||
|
|
||
| MineSweeper.ClickResult.GAME_OVER -> { | ||
| ResultView.showGameOver() | ||
| break | ||
| } | ||
|
|
||
| MineSweeper.ClickResult.ERROR -> { | ||
| ResultView.showError() | ||
| break | ||
| } | ||
| } | ||
| } while (!mineSweeper.isDone) | ||
|
|
||
| if (mineSweeper.isDone) ResultView.showFinished() | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| package minesweeper | ||
|
|
||
| data class MapSize(val row: LineCount, val column: LineCount) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,7 @@ package minesweeper | |
| @JvmInline | ||
| value class MineCount(val count: Int) { | ||
| init { | ||
| require(count > 0) { | ||
| require(count >= 0) { | ||
|
Author
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. 이건 지뢰가 0개일 수도 있을거 같다는 생각에 바꿨습니다. |
||
| "Mine Number must be positive" | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,26 +1,49 @@ | ||
| package minesweeper | ||
|
|
||
| class MineMap( | ||
| val mineMapInfo: MineMapInfo, | ||
| createStrategy: MinePointCreateStrategy = RandomPointCreateStrategy() | ||
| val mineMap: Map<Point, MapTile>, | ||
| val mapInfo: MineMapInfo | ||
| ) { | ||
|
Comment on lines
3
to
6
Member
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. MineMap이 단순히 Map 컬렉션을 가지고있는 것 말고 큰 역할을 수행하고 있지 않네요! |
||
| private val mineList: MineList = | ||
| MineList.createMineList(mineMapInfo, createStrategy) | ||
| val mineMap: Map<Point, MapTile> = mutableMapOf<Point, MapTile>().apply { | ||
| for (mine in mineList.mineList) { | ||
| this[mine] = MapTile.Mine | ||
| val totalSize = mapInfo.totalNumber | ||
|
|
||
| companion object { | ||
| fun create( | ||
| mineMapInfo: MineMapInfo, | ||
| createStrategy: MinePointCreateStrategy = RandomPointCreateStrategy() | ||
| ): MineMap { | ||
| val mineList: MineList = | ||
| MineList.createMineList(mineMapInfo, createStrategy) | ||
|
|
||
| return MineMap( | ||
| emptyMap(mineMapInfo.mapSize).apply { | ||
| for (mine in mineList.mineList) { | ||
| this[mine] = MapTile.Mine | ||
| } | ||
|
|
||
| for (mine in mineList.mineList) { | ||
| createNear(this, mine, mineMapInfo.mapSize) | ||
| } | ||
| }, | ||
| mineMapInfo | ||
| ) | ||
| } | ||
|
|
||
| for (mine in mineList.mineList) { | ||
| createNear(this, mine) | ||
| private fun emptyMap(mapSize: MapSize): MutableMap<Point, MapTile> { | ||
| return mutableMapOf<Point, MapTile>().apply { | ||
| for (i in 1..mapSize.row.count) { | ||
| for (j in 1..mapSize.column.count) { | ||
| put(Point(i, j), MapTile.Blank(0)) | ||
| } | ||
| } | ||
| } | ||
|
Comment on lines
+32
to
+38
Author
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. Blank(0)인 Map을 초기화 시 넣게 했습니다. |
||
| } | ||
| } | ||
|
|
||
| private fun createNear(map: MutableMap<Point, MapTile>, mine: Point) { | ||
| val adjacentPoints = AdjacentPoints.create(mine, mineMapInfo.rowCnt, mineMapInfo.colCnt) | ||
| for (adj in adjacentPoints.points) { | ||
| val nearInfo = map.getOrDefault(adj, MapTile.Blank(0)) | ||
| if (nearInfo is MapTile.Blank) map[adj] = nearInfo + 1 | ||
| private fun createNear(map: MutableMap<Point, MapTile>, mine: Point, mapSize: MapSize) { | ||
| val adjacentPoints = mine.getAdjacentPoints(mapSize) | ||
| for (adj in adjacentPoints) { | ||
| val nearInfo = map[adj] | ||
| if (nearInfo is MapTile.Blank) map[adj] = nearInfo + 1 | ||
| } | ||
|
Comment on lines
+41
to
+46
Member
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. 지뢰 찾기를 진행하면서, 칸을 눌렀을 때, 칸의 숫자를 계산하도록 만드는 건 어떨까요? |
||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| package minesweeper | ||
|
|
||
| class MineSweeper( | ||
| val mineMap: MineMap, | ||
| clickedSet: Set<Point> = setOf(), | ||
| ) { | ||
| var clickedSet: Set<Point> = clickedSet | ||
| private set | ||
|
|
||
|
Comment on lines
+5
to
+9
Member
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. MineTile의 하위 구현체로, 열린 칸과 닫힌 칸을 표현해보는 건 어떨까요? |
||
| val isDone get() = clickedSet.size == mineMap.totalSize - mineMap.mapInfo.mineNumber | ||
|
|
||
| fun click(point: Point): ClickResult { | ||
| if (clickedSet.contains(point)) return ClickResult.ALREADY_CLICKED | ||
|
|
||
| return when (val clickedTile = mineMap.mineMap[point]) { | ||
| is MapTile.Mine -> ClickResult.GAME_OVER | ||
| is MapTile.Blank -> { | ||
| setBlankTile(point, clickedTile) | ||
| ClickResult.CONTINUE | ||
| } | ||
|
|
||
| else -> ClickResult.ERROR | ||
|
Author
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 특성상 null이 될 확률을 컴파일러 내에서 배제하기 힘들어서 ERROR를 따로 넣었습니다. 이를 없앨 수 있는 좋은 방법이 있을까요?
Member
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. mineMap.mineMap[point]이 null이 되는 건 게임을 진행하면서 결코 피할 수 없을 거라 생각해요. 오히려 게임 진행에 있어서 필수적인 흐름이란 생각이 드는데요, |
||
| } | ||
| } | ||
|
|
||
| private fun setBlankTile(point: Point, clickedTile: MapTile.Blank) { | ||
| setClickedPoint(point) | ||
| if (clickedTile.isNoMineNear) { | ||
| setAdjacentTilesClicked(point) | ||
| } | ||
| } | ||
|
|
||
| private fun setAdjacentTilesClicked(point: Point) { | ||
| val adjacent = point.getAdjacentPoints(mineMap.mapInfo.mapSize) | ||
| for (adj in adjacent) { | ||
| if (clickedSet.contains(adj)) continue | ||
|
|
||
| adjacentTileDfs(adj) | ||
| } | ||
| } | ||
|
|
||
| private fun adjacentTileDfs(adj: Point) { | ||
| val info = mineMap.mineMap[adj] | ||
| if (info is MapTile.Blank) { | ||
| setClickedPoint(adj) | ||
| if (info.isNoMineNear) setAdjacentTilesClicked(adj) | ||
| } | ||
| } | ||
|
Comment on lines
+33
to
+48
Author
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. 여기 DFS 구현한다고 했는데... 사실 이름이 크게 떠오르지 않아서 이상한 메소드를 쓴 것 같습니다. 혹시 네이밍 관련 좋은 이름이 있을까요?
Member
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 setClickedPoint(point: Point) { | ||
| clickedSet = clickedSet.plus(point) | ||
| } | ||
|
|
||
| enum class ClickResult { | ||
| GAME_OVER, ALREADY_CLICKED, CONTINUE, ERROR | ||
| } | ||
|
Factoriall marked this conversation as resolved.
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,18 @@ | ||
| package minesweeper | ||
|
|
||
| data class Point(val row: Int, val col: Int) | ||
| data class Point(val row: Int, val col: Int) { | ||
| fun getAdjacentPoints(mapSize: MapSize): List<Point> { | ||
|
Comment on lines
+3
to
+4
Member
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. Point객체 자체는 지뢰판의 영향 없이 그대로 주위 point 객체를 모두 반환하는 것이 더 자연스러워 보여요. |
||
| val mapRow = mapSize.row.count | ||
| val mapCol = mapSize.column.count | ||
| return buildList { | ||
| for (dir in Direction.values()) { | ||
| val nearPoint = Point(row + dir.row, col + dir.col) | ||
| if (nearPoint.isOutOfBound(mapRow, mapCol)) continue | ||
| add(nearPoint) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private fun Point.isOutOfBound(mapRow: Int, mapCol: Int): Boolean = | ||
| this.row < 1 || this.col < 1 || this.row > mapRow || this.col > mapCol | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,10 +2,16 @@ package minesweeper | |
|
|
||
| class RandomPointCreateStrategy : MinePointCreateStrategy { | ||
| override fun createMinePoints(mineMapInfo: MineMapInfo): List<Point> { | ||
| val tileNum = mineMapInfo.rowCnt * mineMapInfo.colCnt - 1 | ||
| val tileNum = mineMapInfo.rowNumber * mineMapInfo.columnNumber - 1 | ||
| return (0..tileNum).toList() | ||
| .shuffled() | ||
| .take(mineMapInfo.mineCnt) | ||
| .map { it.toPoint(mineMapInfo.rowCnt) } | ||
| .take(mineMapInfo.mineNumber) | ||
| .map { it.toPoint(mineMapInfo.rowNumber) } | ||
| } | ||
|
|
||
| private fun Int.toPoint(rowNum: Int): Point { | ||
| val rowIdx = this / rowNum | ||
| val colIdx = this % rowNum | ||
| return Point(rowIdx + 1, colIdx + 1) | ||
|
Author
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. 게임 실행을 살펴보니 1,1부터 시작하는 걸로 보여서 +1 붙여줬습니다. |
||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.